diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index be9a04fc..7c6281de 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -7,24 +7,33 @@ on:
pull_request:
branches:
- main
-
jobs:
php-cs-fixer:
name: "Lint & Analyse"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
+
+ # Setup the correct PHP version globally
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.3.16'
+
- name: Cache Composer dependencies
uses: actions/cache@v2
with:
path: /tmp/composer-cache
key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
+
- uses: php-actions/composer@v6
with:
progress: yes
- php_version: 8.3
+ php_version: 8.3.16
version: 2
+
- name: "PHP-CS-Fixer"
run: vendor/bin/php-cs-fixer fix --dry-run
+
- name: "PSalm"
run: vendor/bin/psalm --show-info=true
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index 5dfe5b82..107bb284 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -14,7 +14,7 @@ jobs:
name: "Running Unit Tests"
strategy:
matrix:
- php: ['8.1', '8.2', '8.3']
+ php: ['8.1.31', '8.2.27', '8.3.16']
steps:
- uses: actions/checkout@v2
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index c673ac50..344e0983 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -63,7 +63,6 @@
MultilineCommentOpeningClosingAloneFixer::name() => true,
MultilinePromotedPropertiesFixer::name() => true,
PhpUnitAssertArgumentsOrderFixer::name() => true,
- PhpdocNoSuperfluousParamFixer::name() => true,
PhpdocParamOrderFixer::name() => true,
StringableInterfaceFixer::name() => true,
])
diff --git a/README.md b/README.md
index 020a749c..4c9040f6 100644
--- a/README.md
+++ b/README.md
@@ -308,21 +308,16 @@ composer require nyholm/psr7 nyholm/psr7-server kriswallsmith/buzz
## Result formats/hydration
-In order to make the results of the bolt protocol and the http uniform, the driver provides result formatters (aka hydrators). The client is configurable with these formatters. You can even implement your own.
+In order to make the results of the bolt protocol and the http uniform, the driver provides and summarizes the results.
-The default formatter is the `\Laudis\Neo4j\Formatters\OGMFormatter`, which is explained extensively in [the result format section](#accessing-the-results).
+The default formatter is the `\Laudis\Neo4j\Formatters\SummarizedResultFormatter`, which is explained extensively in [the result format section](#accessing-the-results).
-The driver provides three formatters by default, which are all found in the Formatter namespace:
- - `\Laudis\Neo4j\Formatter\BasicFormatter` which erases all the Cypher types and simply returns every value in the resulting map as a [scalar](https://www.php.net/manual/en/function.is-scalar.php), null or array value.
- - `\Laudis\Neo4j\Formatter\OGMFormatter` which maps the cypher types to php types as explained [here](#accessing-the-results).
- - `\Laudis\Neo4j\Formatter\SummarizedResultFormatter` which decorates any formatter and adds an extensive result summary.
+`\Laudis\Neo4j\Formatter\SummarizedResultFormatter` adds an extensive result summary.
The client builder provides an easy way to change the formatter:
```php
-$client = \Laudis\Neo4j\ClientBuilder::create()
- ->withFormatter(\Laudis\Neo4j\Formatter\SummarizedResultFormatter::create())
- ->build();
+$client = \Laudis\Neo4j\ClientBuilder::create()->build();
/**
* The client will now return a result, decorated with a summary.
@@ -339,8 +334,6 @@ $summary = $summarisedResult->getSummary();
$result = $summarisedResult->getResult();
```
-In order to use a custom formatter, implement the `Laudis\Neo4j\Contracts\FormatterInterface` and provide it when using the client builder.
-
## Concepts
The driver API described [here](https://neo4j.com/docs/driver-manual/current/) is the main target of the driver. Because of this, the client is nothing more than a driver manager. The driver creates sessions. A session runs queries through a transaction.
diff --git a/composer.json b/composer.json
index ffbc9954..1c5a9221 100644
--- a/composer.json
+++ b/composer.json
@@ -48,9 +48,9 @@
"nyholm/psr7": "^1.3",
"nyholm/psr7-server": "^1.0",
"kriswallsmith/buzz": "^1.2",
- "vimeo/psalm": "^5.0",
- "friendsofphp/php-cs-fixer": "3.15.0",
- "psalm/plugin-phpunit": "^0.18",
+ "vimeo/psalm": "^6.5",
+ "friendsofphp/php-cs-fixer": "3.68.5",
+ "psalm/plugin-phpunit": "^0.19",
"monolog/monolog": "^2.2",
"symfony/uid": "^5.0",
"symfony/var-dumper": "^5.0",
diff --git a/psalm.xml b/psalm.xml
index fb61f69c..def87552 100755
--- a/psalm.xml
+++ b/psalm.xml
@@ -30,18 +30,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Authentication/BasicAuth.php b/src/Authentication/BasicAuth.php
index 7f6fb256..82ef2d65 100644
--- a/src/Authentication/BasicAuth.php
+++ b/src/Authentication/BasicAuth.php
@@ -38,7 +38,8 @@ public function __construct(
private readonly string $username,
private readonly string $password,
private readonly ?Neo4jLogger $logger,
- ) {}
+ ) {
+ }
/**
* @throws Exception
diff --git a/src/Authentication/KerberosAuth.php b/src/Authentication/KerberosAuth.php
index 3c57d32f..51656d65 100644
--- a/src/Authentication/KerberosAuth.php
+++ b/src/Authentication/KerberosAuth.php
@@ -40,11 +40,13 @@ final class KerberosAuth implements AuthenticateInterface
public function __construct(
private readonly string $token,
private readonly ?Neo4jLogger $logger,
- ) {}
+ ) {
+ }
public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface
{
$this->logger?->log(LogLevel::DEBUG, 'Authenticating using KerberosAuth');
+
/**
* @psalm-suppress ImpureMethodCall Request is a pure object:
*
diff --git a/src/Authentication/NoAuth.php b/src/Authentication/NoAuth.php
index 51d246cb..69d7ad07 100644
--- a/src/Authentication/NoAuth.php
+++ b/src/Authentication/NoAuth.php
@@ -38,12 +38,14 @@ final class NoAuth implements AuthenticateInterface
* @pure
*/
public function __construct(
- private readonly ?Neo4jLogger $logger
- ) {}
+ private readonly ?Neo4jLogger $logger,
+ ) {
+ }
public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface
{
$this->logger?->log(LogLevel::DEBUG, 'Authentication disabled');
+
/**
* @psalm-suppress ImpureMethodCall Request is a pure object:
*
diff --git a/src/Authentication/OpenIDConnectAuth.php b/src/Authentication/OpenIDConnectAuth.php
index 18b2b796..c7421cbf 100644
--- a/src/Authentication/OpenIDConnectAuth.php
+++ b/src/Authentication/OpenIDConnectAuth.php
@@ -36,12 +36,14 @@ final class OpenIDConnectAuth implements AuthenticateInterface
*/
public function __construct(
private readonly string $token,
- private readonly ?Neo4jLogger $logger
- ) {}
+ private readonly ?Neo4jLogger $logger,
+ ) {
+ }
public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface
{
$this->logger?->log(LogLevel::DEBUG, 'Authenticating using OpenIDConnectAuth');
+
/**
* @psalm-suppress ImpureMethodCall Request is a pure object:
*
diff --git a/src/Basic/Client.php b/src/Basic/Client.php
index afb6db66..0264cebc 100644
--- a/src/Basic/Client.php
+++ b/src/Basic/Client.php
@@ -18,19 +18,13 @@
use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Types\CypherList;
-use Laudis\Neo4j\Types\CypherMap;
-/**
- * @implements ClientInterface>
- */
final class Client implements ClientInterface
{
- /**
- * @param ClientInterface> $client
- */
public function __construct(
- private readonly ClientInterface $client
- ) {}
+ private readonly ClientInterface $client,
+ ) {
+ }
public function run(string $statement, iterable $parameters = [], ?string $alias = null): SummarizedResult
{
diff --git a/src/Basic/Driver.php b/src/Basic/Driver.php
index 7b73a876..937949d9 100644
--- a/src/Basic/Driver.php
+++ b/src/Basic/Driver.php
@@ -17,25 +17,19 @@
use Laudis\Neo4j\Contracts\DriverInterface;
use Laudis\Neo4j\Databags\DriverConfiguration;
use Laudis\Neo4j\Databags\SessionConfiguration;
-use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\DriverFactory;
use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
-use Laudis\Neo4j\Types\CypherMap;
use Psr\Http\Message\UriInterface;
-/**
- * @implements DriverInterface>
- */
final class Driver implements DriverInterface
{
/**
- * @param DriverInterface> $driver
- *
* @psalm-external-mutation-free
*/
public function __construct(
- private readonly DriverInterface $driver
- ) {}
+ private readonly DriverInterface $driver,
+ ) {
+ }
/**
* @psalm-mutation-free
@@ -52,7 +46,6 @@ public function verifyConnectivity(?SessionConfiguration $config = null): bool
public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null): self
{
- /** @var DriverInterface> */
$driver = DriverFactory::create($uri, $configuration, $authenticate, SummarizedResultFormatter::create());
return new self($driver);
diff --git a/src/Basic/Session.php b/src/Basic/Session.php
index cb31416f..e417055e 100644
--- a/src/Basic/Session.php
+++ b/src/Basic/Session.php
@@ -19,33 +19,24 @@
use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Types\CypherList;
-use Laudis\Neo4j\Types\CypherMap;
-/**
- * @implements SessionInterface>
- */
final class Session implements SessionInterface
{
- /**
- * @param SessionInterface> $session
- */
public function __construct(
- private readonly SessionInterface $session
- ) {}
+ private readonly SessionInterface $session,
+ ) {
+ }
/**
* @param iterable $statements
*
- * @return CypherList>
+ * @return CypherList
*/
public function runStatements(iterable $statements, ?TransactionConfiguration $config = null): CypherList
{
return $this->session->runStatements($statements, $config);
}
- /**
- * @return SummarizedResult
- */
public function runStatement(Statement $statement, ?TransactionConfiguration $config = null): SummarizedResult
{
return $this->session->runStatement($statement, $config);
@@ -53,8 +44,6 @@ public function runStatement(Statement $statement, ?TransactionConfiguration $co
/**
* @param iterable $parameters
- *
- * @return SummarizedResult
*/
public function run(string $statement, iterable $parameters = [], ?TransactionConfiguration $config = null): SummarizedResult
{
diff --git a/src/Basic/UnmanagedTransaction.php b/src/Basic/UnmanagedTransaction.php
index 233e582d..d30f820e 100644
--- a/src/Basic/UnmanagedTransaction.php
+++ b/src/Basic/UnmanagedTransaction.php
@@ -17,33 +17,22 @@
use Laudis\Neo4j\Databags\Statement;
use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Types\CypherList;
-use Laudis\Neo4j\Types\CypherMap;
-/**
- * @implements UnmanagedTransactionInterface>
- */
final class UnmanagedTransaction implements UnmanagedTransactionInterface
{
- /**
- * @param UnmanagedTransactionInterface> $tsx
- */
public function __construct(
- private readonly UnmanagedTransactionInterface $tsx
- ) {}
+ private readonly UnmanagedTransactionInterface $tsx,
+ ) {
+ }
/**
* @param iterable $parameters
- *
- * @return SummarizedResult
*/
public function run(string $statement, iterable $parameters = []): SummarizedResult
{
return $this->tsx->run($statement, $parameters);
}
- /**
- * @return SummarizedResult
- */
public function runStatement(Statement $statement): SummarizedResult
{
return $this->tsx->runStatement($statement);
@@ -52,7 +41,7 @@ public function runStatement(Statement $statement): SummarizedResult
/**
* @param iterable $statements
*
- * @return CypherList>
+ * @return CypherList
*/
public function runStatements(iterable $statements): CypherList
{
@@ -62,7 +51,7 @@ public function runStatements(iterable $statements): CypherList
/**
* @param iterable $statements
*
- * @return CypherList>
+ * @return CypherList
*/
public function commit(iterable $statements = []): CypherList
{
diff --git a/src/Bolt/BoltConnection.php b/src/Bolt/BoltConnection.php
index 93f3b3fa..07a41a48 100644
--- a/src/Bolt/BoltConnection.php
+++ b/src/Bolt/BoltConnection.php
@@ -27,22 +27,23 @@
use Laudis\Neo4j\Common\Neo4jLogger;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Laudis\Neo4j\Contracts\ConnectionInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Databags\BookmarkHolder;
use Laudis\Neo4j\Databags\DatabaseInfo;
use Laudis\Neo4j\Databags\Neo4jError;
use Laudis\Neo4j\Enum\AccessMode;
use Laudis\Neo4j\Enum\ConnectionProtocol;
use Laudis\Neo4j\Exception\Neo4jException;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Laudis\Neo4j\Types\CypherList;
use Psr\Http\Message\UriInterface;
use Psr\Log\LogLevel;
+use Throwable;
use WeakReference;
/**
* @implements ConnectionInterface
*
- * @psalm-import-type BoltMeta from FormatterInterface
+ * @psalm-import-type BoltMeta from SummarizedResultFormatter
*/
class BoltConnection implements ConnectionInterface
{
@@ -80,7 +81,8 @@ public function __construct(
/** @psalm-readonly */
private readonly ConnectionConfiguration $config,
private readonly ?Neo4jLogger $logger,
- ) {}
+ ) {
+ }
public function getEncryptionLevel(): string
{
@@ -247,7 +249,7 @@ public function run(
?string $database,
?float $timeout,
BookmarkHolder $holder,
- ?AccessMode $mode
+ ?AccessMode $mode,
): array {
$extra = $this->buildRunExtra($database, $timeout, $holder, $mode);
$this->logger?->log(LogLevel::DEBUG, 'RUN', $extra);
@@ -255,6 +257,7 @@ public function run(
->run($text, $parameters, $extra)
->getResponse();
$this->assertNoFailure($response);
+
/** @var BoltMeta */
return $response->content;
}
@@ -341,7 +344,7 @@ public function close(): void
unset($this->boltProtocol); // has to be set to null as the sockets don't recover nicely contrary to what the underlying code might lead you to believe;
}
- } catch (\Throwable) {
+ } catch (Throwable) {
}
}
diff --git a/src/Bolt/BoltDriver.php b/src/Bolt/BoltDriver.php
index 7c21c484..c8eb3d91 100644
--- a/src/Bolt/BoltDriver.php
+++ b/src/Bolt/BoltDriver.php
@@ -23,50 +23,34 @@
use Laudis\Neo4j\Common\Uri;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Laudis\Neo4j\Contracts\DriverInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Contracts\SessionInterface;
use Laudis\Neo4j\Databags\DriverConfiguration;
use Laudis\Neo4j\Databags\SessionConfiguration;
-use Laudis\Neo4j\Formatter\OGMFormatter;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Psr\Http\Message\UriInterface;
use Psr\Log\LogLevel;
/**
* Drives a singular bolt connections.
*
- * @template T
- *
- * @implements DriverInterface
- *
- * @psalm-import-type OGMResults from OGMFormatter
+ * @psalm-import-type OGMResults from SummarizedResultFormatter
*/
final class BoltDriver implements DriverInterface
{
/**
- * @param FormatterInterface $formatter
- *
* @psalm-mutation-free
*/
public function __construct(
private readonly UriInterface $parsedUrl,
private readonly ConnectionPool $pool,
- private readonly FormatterInterface $formatter
- ) {}
+ private readonly SummarizedResultFormatter $formatter,
+ ) {
+ }
/**
- * @template U
- *
- * @param FormatterInterface $formatter
- *
- * @return (
- * func_num_args() is 5
- * ? self
- * : self
- * )
- *
* @psalm-suppress MixedReturnTypeCoercion
*/
- public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null, ?FormatterInterface $formatter = null): self
+ public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null, ?SummarizedResultFormatter $formatter = null): self
{
if (is_string($uri)) {
$uri = Uri::create($uri);
@@ -80,7 +64,7 @@ public static function create(string|UriInterface $uri, ?DriverConfiguration $co
return new self(
$uri,
ConnectionPool::create($uri, $authenticate, $configuration, $semaphore),
- $formatter ?? OGMFormatter::create(),
+ $formatter ?? SummarizedResultFormatter::create(),
);
}
diff --git a/src/Bolt/BoltResult.php b/src/Bolt/BoltResult.php
index dd243b28..7d9c7408 100644
--- a/src/Bolt/BoltResult.php
+++ b/src/Bolt/BoltResult.php
@@ -21,12 +21,12 @@
use function in_array;
use Iterator;
-use Laudis\Neo4j\Contracts\FormatterInterface;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
/**
- * @psalm-import-type BoltCypherStats from FormatterInterface
+ * @psalm-import-type BoltCypherStats from SummarizedResultFormatter
*
- * @implements Iterator
+ * @implements Iterator>
*/
final class BoltResult implements Iterator
{
@@ -39,8 +39,9 @@ final class BoltResult implements Iterator
public function __construct(
private readonly BoltConnection $connection,
private readonly int $fetchSize,
- private readonly int $qid
- ) {}
+ private readonly int $qid,
+ ) {
+ }
public function getFetchSize(): int
{
@@ -70,7 +71,7 @@ public function getIt(): Generator
}
/**
- * @return Generator
+ * @return Generator>
*/
public function iterator(): Generator
{
@@ -112,10 +113,15 @@ private function fetchResults(): void
}
/**
- * @return list
+ * @psalm-suppress InvalidNullableReturnType
+ *
+ * @return list
*/
public function current(): array
{
+ /**
+ * @psalm-suppress NullableReturnStatement
+ */
return $this->getIt()->current();
}
diff --git a/src/Bolt/BoltUnmanagedTransaction.php b/src/Bolt/BoltUnmanagedTransaction.php
index 9a547551..bd318169 100644
--- a/src/Bolt/BoltUnmanagedTransaction.php
+++ b/src/Bolt/BoltUnmanagedTransaction.php
@@ -14,16 +14,16 @@
namespace Laudis\Neo4j\Bolt;
use Bolt\enum\ServerState;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface;
use Laudis\Neo4j\Databags\BookmarkHolder;
use Laudis\Neo4j\Databags\SessionConfiguration;
use Laudis\Neo4j\Databags\Statement;
+use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Enum\TransactionState;
use Laudis\Neo4j\Exception\ClientException;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Laudis\Neo4j\ParameterHelper;
-use Laudis\Neo4j\Types\AbstractCypherSequence;
use Laudis\Neo4j\Types\CypherList;
use function microtime;
@@ -33,35 +33,33 @@
/**
* Manages a transaction over the bolt protocol.
*
- * @template T
- *
- * @implements UnmanagedTransactionInterface
- *
- * @psalm-import-type BoltMeta from FormatterInterface
+ * @psalm-import-type BoltMeta from SummarizedResultFormatter
*/
final class BoltUnmanagedTransaction implements UnmanagedTransactionInterface
{
private TransactionState $state = TransactionState::ACTIVE;
- /**
- * @param FormatterInterface $formatter
- */
public function __construct(
/** @psalm-readonly */
private readonly ?string $database,
/**
* @psalm-readonly
*/
- private readonly FormatterInterface $formatter,
+ private readonly SummarizedResultFormatter $formatter,
/** @psalm-readonly */
private readonly BoltConnection $connection,
private readonly SessionConfiguration $config,
private readonly TransactionConfiguration $tsxConfig,
- private readonly BookmarkHolder $bookmarkHolder
- ) {}
+ private readonly BookmarkHolder $bookmarkHolder,
+ ) {
+ }
/**
+ * @param iterable $statements
+ *
* @throws ClientException|Throwable
+ *
+ * @return CypherList
*/
public function commit(iterable $statements = []): CypherList
{
@@ -81,10 +79,8 @@ public function commit(iterable $statements = []): CypherList
// Force the results to pull all the results.
// After a commit, the connection will be in the ready state, making it impossible to use PULL
- $tbr = $this->runStatements($statements)->each(static function ($list) {
- if ($list instanceof AbstractCypherSequence) {
- $list->preload();
- }
+ $tbr = $this->runStatements($statements)->each(static function (CypherList $list) {
+ $list->preload();
});
$this->connection->commit();
@@ -116,7 +112,7 @@ public function rollback(): void
/**
* @throws Throwable
*/
- public function run(string $statement, iterable $parameters = [])
+ public function run(string $statement, iterable $parameters = []): SummarizedResult
{
return $this->runStatement(new Statement($statement, $parameters));
}
@@ -124,7 +120,7 @@ public function run(string $statement, iterable $parameters = [])
/**
* @throws Throwable
*/
- public function runStatement(Statement $statement)
+ public function runStatement(Statement $statement): SummarizedResult
{
$parameters = ParameterHelper::formatParameters($statement->getParameters(), $this->connection->getProtocol());
$start = microtime(true);
@@ -161,11 +157,14 @@ public function runStatement(Statement $statement)
}
/**
+ * @param iterable $statements
+ *
* @throws Throwable
+ *
+ * @return CypherList
*/
public function runStatements(iterable $statements): CypherList
{
- /** @var list $tbr */
$tbr = [];
foreach ($statements as $statement) {
$tbr[] = $this->runStatement($statement);
diff --git a/src/Bolt/Connection.php b/src/Bolt/Connection.php
index f4171649..e1145af0 100644
--- a/src/Bolt/Connection.php
+++ b/src/Bolt/Connection.php
@@ -22,8 +22,9 @@ class Connection
*/
public function __construct(
private readonly IConnection $connection,
- private readonly string $ssl
- ) {}
+ private readonly string $ssl,
+ ) {
+ }
public function getIConnection(): IConnection
{
diff --git a/src/Bolt/ConnectionPool.php b/src/Bolt/ConnectionPool.php
index 2b2bfa25..33bf5e97 100644
--- a/src/Bolt/ConnectionPool.php
+++ b/src/Bolt/ConnectionPool.php
@@ -43,14 +43,15 @@ public function __construct(
private readonly SemaphoreInterface $semaphore,
private readonly BoltFactory $factory,
private readonly ConnectionRequestData $data,
- private readonly ?Neo4jLogger $logger
- ) {}
+ private readonly ?Neo4jLogger $logger,
+ ) {
+ }
public static function create(
UriInterface $uri,
AuthenticateInterface $auth,
DriverConfiguration $conf,
- SemaphoreInterface $semaphore
+ SemaphoreInterface $semaphore,
): self {
return new self(
$semaphore,
diff --git a/src/Bolt/Session.php b/src/Bolt/Session.php
index 8d895d59..9365aee3 100644
--- a/src/Bolt/Session.php
+++ b/src/Bolt/Session.php
@@ -18,7 +18,6 @@
use Laudis\Neo4j\Common\Neo4jLogger;
use Laudis\Neo4j\Common\TransactionHelper;
use Laudis\Neo4j\Contracts\ConnectionPoolInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Contracts\SessionInterface;
use Laudis\Neo4j\Contracts\TransactionInterface;
use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface;
@@ -26,19 +25,17 @@
use Laudis\Neo4j\Databags\BookmarkHolder;
use Laudis\Neo4j\Databags\SessionConfiguration;
use Laudis\Neo4j\Databags\Statement;
+use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Enum\AccessMode;
use Laudis\Neo4j\Exception\Neo4jException;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Laudis\Neo4j\Neo4j\Neo4jConnectionPool;
use Laudis\Neo4j\Types\CypherList;
use Psr\Log\LogLevel;
/**
* A session using bolt connections.
- *
- * @template ResultFormat
- *
- * @implements SessionInterface
*/
final class Session implements SessionInterface
{
@@ -47,7 +44,6 @@ final class Session implements SessionInterface
/**
* @param ConnectionPool|Neo4jConnectionPool $pool
- * @param FormatterInterface $formatter
*
* @psalm-mutation-free
*/
@@ -58,11 +54,16 @@ public function __construct(
/**
* @psalm-readonly
*/
- private readonly FormatterInterface $formatter
+ private readonly SummarizedResultFormatter $formatter,
) {
$this->bookmarkHolder = new BookmarkHolder(Bookmark::from($config->getBookmarks()));
}
+ /**
+ * @param iterable $statements
+ *
+ * @return CypherList
+ */
public function runStatements(iterable $statements, ?TransactionConfiguration $config = null): CypherList
{
$tbr = [];
@@ -84,12 +85,12 @@ public function openTransaction(?iterable $statements = null, ?TransactionConfig
return $this->beginTransaction($statements, $this->mergeTsxConfig($config));
}
- public function runStatement(Statement $statement, ?TransactionConfiguration $config = null)
+ public function runStatement(Statement $statement, ?TransactionConfiguration $config = null): SummarizedResult
{
return $this->runStatements([$statement], $config)->first();
}
- public function run(string $statement, iterable $parameters = [], ?TransactionConfiguration $config = null)
+ public function run(string $statement, iterable $parameters = [], ?TransactionConfiguration $config = null): SummarizedResult
{
return $this->runStatement(new Statement($statement, $parameters), $config);
}
@@ -121,6 +122,9 @@ public function transaction(callable $tsxHandler, ?TransactionConfiguration $con
return $this->writeTransaction($tsxHandler, $config);
}
+ /**
+ * @param iterable $statements
+ */
public function beginTransaction(?iterable $statements = null, ?TransactionConfiguration $config = null): UnmanagedTransactionInterface
{
$this->getLogger()?->log(LogLevel::INFO, 'Beginning transaction', ['statements' => $statements, 'config' => $config]);
@@ -133,11 +137,11 @@ public function beginTransaction(?iterable $statements = null, ?TransactionConfi
}
/**
- * @return UnmanagedTransactionInterface
+ * @return UnmanagedTransactionInterface
*/
private function beginInstantTransaction(
SessionConfiguration $config,
- TransactionConfiguration $tsxConfig
+ TransactionConfiguration $tsxConfig,
): TransactionInterface {
$this->getLogger()?->log(LogLevel::INFO, 'Starting instant transaction', ['config' => $tsxConfig]);
$connection = $this->acquireConnection($tsxConfig, $config);
diff --git a/src/Bolt/SocketConnectionFactory.php b/src/Bolt/SocketConnectionFactory.php
index 482d885a..0d2a97d7 100644
--- a/src/Bolt/SocketConnectionFactory.php
+++ b/src/Bolt/SocketConnectionFactory.php
@@ -20,8 +20,9 @@
final class SocketConnectionFactory implements BasicConnectionFactoryInterface
{
public function __construct(
- private readonly StreamConnectionFactory $factory
- ) {}
+ private readonly StreamConnectionFactory $factory,
+ ) {
+ }
public function create(UriConfiguration $config): Connection
{
diff --git a/src/Bolt/SystemWideConnectionFactory.php b/src/Bolt/SystemWideConnectionFactory.php
index 893be0a5..fe2c1598 100644
--- a/src/Bolt/SystemWideConnectionFactory.php
+++ b/src/Bolt/SystemWideConnectionFactory.php
@@ -28,8 +28,9 @@ class SystemWideConnectionFactory implements BasicConnectionFactoryInterface
* @param SocketConnectionFactory|StreamConnectionFactory $factory
*/
private function __construct(
- private $factory
- ) {}
+ private $factory,
+ ) {
+ }
/**
* @psalm-suppress InvalidNullableReturnType
diff --git a/src/Bolt/UriConfiguration.php b/src/Bolt/UriConfiguration.php
index 225449b5..c61c4b6d 100644
--- a/src/Bolt/UriConfiguration.php
+++ b/src/Bolt/UriConfiguration.php
@@ -23,8 +23,9 @@ public function __construct(
private readonly ?int $port,
private readonly string $sslLevel,
private readonly array $sslConfiguration,
- private readonly ?float $timeout
- ) {}
+ private readonly ?float $timeout,
+ ) {
+ }
public function getHost(): string
{
diff --git a/src/BoltFactory.php b/src/BoltFactory.php
index bd0e555d..1d50d3c2 100644
--- a/src/BoltFactory.php
+++ b/src/BoltFactory.php
@@ -42,8 +42,9 @@ public function __construct(
private readonly BasicConnectionFactoryInterface $connectionFactory,
private readonly ProtocolFactory $protocolFactory,
private readonly SslConfigurationFactory $sslConfigurationFactory,
- private readonly ?Neo4jLogger $logger = null
- ) {}
+ private readonly ?Neo4jLogger $logger = null,
+ ) {
+ }
public static function create(?Neo4jLogger $logger): self
{
@@ -87,13 +88,13 @@ public function canReuseConnection(ConnectionInterface $connection, ConnectionRe
$databaseInfo = $connection->getDatabaseInfo();
$database = $databaseInfo?->getName();
- return $connection->getServerAddress()->getHost() === $data->getUri()->getHost() &&
- $connection->getServerAddress()->getPort() === $data->getUri()->getPort() &&
- $connection->getAuthentication()->toString($data->getUri()) === $data->getAuth()->toString($data->getUri()) &&
- $connection->getEncryptionLevel() === $this->sslConfigurationFactory->create($data->getUri(), $data->getSslConfig())[0] &&
- $connection->getUserAgent() === $data->getUserAgent() &&
- $connection->getAccessMode() === $config->getAccessMode() &&
- $database === $config->getDatabase();
+ return $connection->getServerAddress()->getHost() === $data->getUri()->getHost()
+ && $connection->getServerAddress()->getPort() === $data->getUri()->getPort()
+ && $connection->getAuthentication()->toString($data->getUri()) === $data->getAuth()->toString($data->getUri())
+ && $connection->getEncryptionLevel() === $this->sslConfigurationFactory->create($data->getUri(), $data->getSslConfig())[0]
+ && $connection->getUserAgent() === $data->getUserAgent()
+ && $connection->getAccessMode() === $config->getAccessMode()
+ && $database === $config->getDatabase();
}
public function reuseConnection(BoltConnection $connection, SessionConfiguration $sessionConfig): BoltConnection
diff --git a/src/Client.php b/src/Client.php
index 9d862061..4a114f66 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -21,39 +21,35 @@
use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface;
use Laudis\Neo4j\Databags\SessionConfiguration;
use Laudis\Neo4j\Databags\Statement;
+use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Enum\AccessMode;
use Laudis\Neo4j\Types\CypherList;
/**
* A collection of drivers with methods to run queries though them.
- *
- * @template ResultFormat
- *
- * @implements ClientInterface
*/
final class Client implements ClientInterface
{
/**
- * @var array>>
+ * @var array>
*/
private array $boundTransactions = [];
/**
- * @var array>
+ * @var array
*/
private array $boundSessions = [];
/**
* @psalm-mutation-free
- *
- * @param DriverSetupManager $driverSetups
*/
public function __construct(
private readonly DriverSetupManager $driverSetups,
private readonly SessionConfiguration $defaultSessionConfiguration,
- private readonly TransactionConfiguration $defaultTransactionConfiguration
- ) {}
+ private readonly TransactionConfiguration $defaultTransactionConfiguration,
+ ) {
+ }
public function getDriverSetups(): DriverSetupManager
{
@@ -70,12 +66,12 @@ public function getDefaultTransactionConfiguration(): TransactionConfiguration
return $this->defaultTransactionConfiguration;
}
- public function run(string $statement, iterable $parameters = [], ?string $alias = null)
+ public function run(string $statement, iterable $parameters = [], ?string $alias = null): SummarizedResult
{
return $this->runStatement(Statement::create($statement, $parameters), $alias);
}
- public function runStatement(Statement $statement, ?string $alias = null)
+ public function runStatement(Statement $statement, ?string $alias = null): SummarizedResult
{
return $this->runStatements([$statement], $alias)->first();
}
@@ -84,8 +80,9 @@ private function getRunner(?string $alias = null): TransactionInterface|SessionI
{
$alias ??= $this->driverSetups->getDefaultAlias();
- if (array_key_exists($alias, $this->boundTransactions) &&
- count($this->boundTransactions[$alias]) > 0) {
+ if (array_key_exists($alias, $this->boundTransactions)
+ && count($this->boundTransactions[$alias]) > 0) {
+ /** @psalm-suppress PossiblyNullArrayOffset */
return $this->boundTransactions[$alias][array_key_last($this->boundTransactions[$alias])];
}
@@ -126,9 +123,6 @@ public function getDriver(?string $alias): DriverInterface
return $this->driverSetups->getDriver($this->defaultSessionConfiguration, $alias);
}
- /**
- * @return SessionInterface
- */
private function startSession(?string $alias, SessionConfiguration $configuration): SessionInterface
{
return $this->getDriver($alias)->createSession($configuration);
@@ -187,7 +181,7 @@ public function rollbackBoundTransaction(?string $alias = null, int $depth = 1):
}
/**
- * @param callable(UnmanagedTransactionInterface): void $handler
+ * @param callable(UnmanagedTransactionInterface): void $handler
*/
private function popTransactions(callable $handler, ?string $alias = null, int $depth = 1): void
{
diff --git a/src/ClientBuilder.php b/src/ClientBuilder.php
index 0ec4b497..42a79232 100644
--- a/src/ClientBuilder.php
+++ b/src/ClientBuilder.php
@@ -20,24 +20,18 @@
use Laudis\Neo4j\Common\Uri;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Laudis\Neo4j\Contracts\ClientInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Databags\DriverConfiguration;
use Laudis\Neo4j\Databags\DriverSetup;
use Laudis\Neo4j\Databags\SessionConfiguration;
-use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Exception\UnsupportedScheme;
-use Laudis\Neo4j\Formatter\OGMFormatter;
use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
-use Laudis\Neo4j\Types\CypherMap;
use Psr\Log\LoggerInterface;
/**
* Immutable factory for creating a client.
*
- * @template T
- *
- * @psalm-import-type OGMTypes from OGMFormatter
+ * @psalm-import-type OGMTypes from SummarizedResultFormatter
*/
final class ClientBuilder
{
@@ -45,8 +39,6 @@ final class ClientBuilder
/**
* @psalm-mutation-free
- *
- * @param DriverSetupManager $driverSetups
*/
public function __construct(
/** @psalm-readonly */
@@ -54,12 +46,11 @@ public function __construct(
/** @psalm-readonly */
private TransactionConfiguration $defaultTransactionConfig,
private DriverSetupManager $driverSetups,
- ) {}
+ ) {
+ }
/**
* Creates a client builder with default configurations and an OGMFormatter.
- *
- * @return ClientBuilder>>
*/
public static function create(?string $logLevel = null, ?LoggerInterface $logger = null): ClientBuilder
{
@@ -77,8 +68,6 @@ public static function create(?string $logLevel = null, ?LoggerInterface $logger
/**
* @psalm-mutation-free
- *
- * @return self
*/
public function withDriver(string $alias, string $url, ?AuthenticateInterface $authentication = null, ?int $priority = 0): self
{
@@ -91,8 +80,6 @@ public function withDriver(string $alias, string $url, ?AuthenticateInterface $a
/**
* @psalm-external-mutation-free
- *
- * @return self
*/
private function withParsedUrl(string $alias, Uri $uri, AuthenticateInterface $authentication, int $priority): self
{
@@ -111,8 +98,6 @@ private function withParsedUrl(string $alias, Uri $uri, AuthenticateInterface $a
/**
* Sets the default connection to the given alias.
*
- * @return self
- *
* @psalm-mutation-free
*/
public function withDefaultDriver(string $alias): self
@@ -124,15 +109,9 @@ public function withDefaultDriver(string $alias): self
}
/**
- * @template U
- *
- * @param FormatterInterface $formatter
- *
- * @return self
- *
* @psalm-mutation-free
*/
- public function withFormatter(FormatterInterface $formatter): self
+ public function withFormatter(SummarizedResultFormatter $formatter): self
{
return new self(
$this->defaultSessionConfig,
@@ -142,8 +121,6 @@ public function withFormatter(FormatterInterface $formatter): self
}
/**
- * @return ClientInterface
- *
* @psalm-mutation-free
*/
public function build(): ClientInterface
@@ -156,8 +133,6 @@ public function build(): ClientInterface
}
/**
- * @return self
- *
* @psalm-mutation-free
*/
public function withDefaultDriverConfiguration(DriverConfiguration $config): self
@@ -170,8 +145,6 @@ public function withDefaultDriverConfiguration(DriverConfiguration $config): sel
}
/**
- * @return self
- *
* @psalm-mutation-free
*/
public function withDefaultSessionConfiguration(SessionConfiguration $config): self
@@ -183,8 +156,6 @@ public function withDefaultSessionConfiguration(SessionConfiguration $config): s
}
/**
- * @return self
- *
* @psalm-mutation-free
*/
public function withDefaultTransactionConfiguration(TransactionConfiguration $config): self
diff --git a/src/Common/Cache.php b/src/Common/Cache.php
index 5f37089d..9b225e78 100644
--- a/src/Common/Cache.php
+++ b/src/Common/Cache.php
@@ -47,7 +47,9 @@ class Cache implements CacheInterface
private array $items = [];
private static ?self $instance = null;
- private function __construct() {}
+ private function __construct()
+ {
+ }
public static function getInstance(): self
{
@@ -158,12 +160,11 @@ public function getMultiple($keys, $default = null): Generator
}
/**
- * @param iterable $values
- * @param int|DateInterval|null $ttl
+ * @param iterable $values
*
* @throws InvalidCacheArgumentException
*/
- public function setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool
+ public function setMultiple(iterable $values, int|DateInterval|null $ttl = null): bool
{
/**
* @var mixed $key
@@ -185,15 +186,15 @@ public function setMultiple(iterable $values, null|int|DateInterval $ttl = null)
*/
private function assertValidKey(string $key): void
{
- if ($key === '' ||
- str_contains($key, '{') ||
- str_contains($key, '}') ||
- str_contains($key, '(') ||
- str_contains($key, ')') ||
- str_contains($key, '/') ||
- str_contains($key, '\\') ||
- str_contains($key, '@') ||
- str_contains($key, ':')
+ if ($key === ''
+ || str_contains($key, '{')
+ || str_contains($key, '}')
+ || str_contains($key, '(')
+ || str_contains($key, ')')
+ || str_contains($key, '/')
+ || str_contains($key, '\\')
+ || str_contains($key, '@')
+ || str_contains($key, ':')
) {
throw new InvalidCacheArgumentException();
}
diff --git a/src/Common/ConnectionConfiguration.php b/src/Common/ConnectionConfiguration.php
index fe791c34..3d69fa43 100644
--- a/src/Common/ConnectionConfiguration.php
+++ b/src/Common/ConnectionConfiguration.php
@@ -33,8 +33,9 @@ public function __construct(
private readonly ConnectionProtocol $protocol,
private readonly AccessMode $accessMode,
private readonly ?DatabaseInfo $databaseInfo,
- private readonly string $encryptionLevel
- ) {}
+ private readonly string $encryptionLevel,
+ ) {
+ }
public function getServerAgent(): string
{
diff --git a/src/Common/DriverSetupManager.php b/src/Common/DriverSetupManager.php
index bb2639d2..f5d674bf 100644
--- a/src/Common/DriverSetupManager.php
+++ b/src/Common/DriverSetupManager.php
@@ -22,11 +22,11 @@
use InvalidArgumentException;
use Laudis\Neo4j\Authentication\Authenticate;
use Laudis\Neo4j\Contracts\DriverInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Databags\DriverConfiguration;
use Laudis\Neo4j\Databags\DriverSetup;
use Laudis\Neo4j\Databags\SessionConfiguration;
use Laudis\Neo4j\DriverFactory;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use const PHP_INT_MIN;
@@ -36,28 +36,24 @@
use function sprintf;
-/**
- * @template ResultFormat
- */
class DriverSetupManager implements Countable
{
private const DEFAULT_DRIVER_CONFIG = 'bolt://localhost:7687';
/** @var array> */
private array $driverSetups = [];
- /** @var array> */
+ /** @var array */
private array $drivers = [];
private ?string $default = null;
/**
* @psalm-mutation-free
- *
- * @param FormatterInterface $formatter
*/
public function __construct(
- private FormatterInterface $formatter,
- private DriverConfiguration $configuration
- ) {}
+ private SummarizedResultFormatter $formatter,
+ private DriverConfiguration $configuration,
+ ) {
+ }
public function getDriverConfiguration(): DriverConfiguration
{
@@ -85,8 +81,9 @@ public function withSetup(DriverSetup $setup, ?string $alias = null, ?int $prior
$setups = $this->driverSetups;
- /** @var SplPriorityQueue */
- $setups[$alias] ??= new SplPriorityQueue();
+ /** @var SplPriorityQueue $splPriorityQueue */
+ $splPriorityQueue = new SplPriorityQueue();
+ $setups[$alias] ??= $splPriorityQueue;
/** @psalm-suppress ImpureMethodCall */
$setups[$alias]->insert($setup, $priority ?? 0);
@@ -101,9 +98,6 @@ public function hasDriver(string $alias): bool
return array_key_exists($alias, $this->driverSetups);
}
- /**
- * @return DriverInterface
- */
public function getDriver(SessionConfiguration $config, ?string $alias = null): DriverInterface
{
$alias ??= $this->decideAlias($alias);
@@ -193,7 +187,7 @@ public function count(): int
*
* @psalm-mutation-free
*/
- public function withFormatter(FormatterInterface $formatter): self
+ public function withFormatter(SummarizedResultFormatter $formatter): self
{
$tbr = clone $this;
$tbr->formatter = $formatter;
diff --git a/src/Common/Neo4jLogger.php b/src/Common/Neo4jLogger.php
index 60276411..40aab4df 100644
--- a/src/Common/Neo4jLogger.php
+++ b/src/Common/Neo4jLogger.php
@@ -33,7 +33,8 @@ class Neo4jLogger
public function __construct(
private readonly string $level,
private readonly ?LoggerInterface $logger,
- ) {}
+ ) {
+ }
public function log(string $level, string $message, array $context = []): void
{
diff --git a/src/Common/SingleThreadedSemaphore.php b/src/Common/SingleThreadedSemaphore.php
index ada6f4a8..77c8dbe5 100644
--- a/src/Common/SingleThreadedSemaphore.php
+++ b/src/Common/SingleThreadedSemaphore.php
@@ -27,8 +27,9 @@ class SingleThreadedSemaphore implements SemaphoreInterface
private static array $instances = [];
private function __construct(
- private readonly int $max
- ) {}
+ private readonly int $max,
+ ) {
+ }
public static function create(string $key, int $max): self
{
diff --git a/src/Common/SysVSemaphore.php b/src/Common/SysVSemaphore.php
index 0845fa21..c3d872f2 100644
--- a/src/Common/SysVSemaphore.php
+++ b/src/Common/SysVSemaphore.php
@@ -30,8 +30,9 @@
class SysVSemaphore implements SemaphoreInterface
{
private function __construct(
- private readonly \SysvSemaphore $semaphore
- ) {}
+ private readonly \SysvSemaphore $semaphore,
+ ) {
+ }
public static function create(string $key, int $max): self
{
diff --git a/src/Common/TransactionHelper.php b/src/Common/TransactionHelper.php
index a9be078e..83c7e086 100644
--- a/src/Common/TransactionHelper.php
+++ b/src/Common/TransactionHelper.php
@@ -13,10 +13,10 @@
namespace Laudis\Neo4j\Common;
+use Laudis\Neo4j\Contracts\CypherSequence;
use Laudis\Neo4j\Contracts\TransactionInterface;
use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface;
use Laudis\Neo4j\Exception\Neo4jException;
-use Laudis\Neo4j\Types\AbstractCypherSequence;
final class TransactionHelper
{
@@ -24,10 +24,9 @@ final class TransactionHelper
/**
* @template U
- * @template T
*
- * @param callable():UnmanagedTransactionInterface $tsxFactory
- * @param callable(TransactionInterface):U $tsxHandler
+ * @param callable():UnmanagedTransactionInterface $tsxFactory
+ * @param callable(TransactionInterface):U $tsxHandler
*
* @return U
*/
@@ -56,7 +55,7 @@ public static function retry(callable $tsxFactory, callable $tsxHandler)
private static function triggerLazyResult(mixed $tbr): void
{
- if ($tbr instanceof AbstractCypherSequence) {
+ if ($tbr instanceof CypherSequence) {
$tbr->preload();
}
}
diff --git a/src/Common/Uri.php b/src/Common/Uri.php
index 6d2393b5..59275a42 100644
--- a/src/Common/Uri.php
+++ b/src/Common/Uri.php
@@ -38,8 +38,9 @@ public function __construct(
private readonly ?int $port,
private string $path,
private readonly string $query,
- private readonly string $fragment
- ) {}
+ private readonly string $fragment,
+ ) {
+ }
/**
* @pure
diff --git a/src/Contracts/ClientInterface.php b/src/Contracts/ClientInterface.php
index 5a5f1d4c..389c35b7 100644
--- a/src/Contracts/ClientInterface.php
+++ b/src/Contracts/ClientInterface.php
@@ -14,15 +14,11 @@
namespace Laudis\Neo4j\Contracts;
use Laudis\Neo4j\Databags\Statement;
+use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Exception\Neo4jException;
use Laudis\Neo4j\Types\CypherList;
-/**
- * @template ResultFormat
- *
- * @extends TransactionInterface
- */
interface ClientInterface extends TransactionInterface
{
/**
@@ -31,19 +27,15 @@ interface ClientInterface extends TransactionInterface
* @param iterable $parameters
*
* @throws Neo4jException
- *
- * @return ResultFormat
*/
- public function run(string $statement, iterable $parameters = [], ?string $alias = null);
+ public function run(string $statement, iterable $parameters = [], ?string $alias = null): SummarizedResult;
/**
* Runs a one off transaction with the provided statement over the connection with the provided alias or the master alias otherwise.
*
* @throws Neo4jException
- *
- * @return ResultFormat
*/
- public function runStatement(Statement $statement, ?string $alias = null);
+ public function runStatement(Statement $statement, ?string $alias = null): SummarizedResult;
/**
* Runs a one off transaction with the provided statements over the connection with the provided alias or the master alias otherwise.
@@ -52,7 +44,7 @@ public function runStatement(Statement $statement, ?string $alias = null);
*
* @throws Neo4jException
*
- * @return CypherList
+ * @return CypherList
*/
public function runStatements(iterable $statements, ?string $alias = null): CypherList;
@@ -62,8 +54,6 @@ public function runStatements(iterable $statements, ?string $alias = null): Cyph
* @param iterable|null $statements
*
* @throws Neo4jException
- *
- * @return UnmanagedTransactionInterface
*/
public function beginTransaction(?iterable $statements = null, ?string $alias = null, ?TransactionConfiguration $config = null): UnmanagedTransactionInterface;
@@ -71,8 +61,6 @@ public function beginTransaction(?iterable $statements = null, ?string $alias =
* Gets the driver with the provided alias. Gets the default driver if no alias is provided.
*
* The driver is guaranteed to have its connectivity verified at least once during its lifetime.
- *
- * @return DriverInterface
*/
public function getDriver(?string $alias): DriverInterface;
@@ -84,7 +72,7 @@ public function hasDriver(string $alias): bool;
/**
* @template U
*
- * @param callable(TransactionInterface):U $tsxHandler
+ * @param callable(TransactionInterface):U $tsxHandler
*
* @return U
*/
@@ -93,7 +81,7 @@ public function writeTransaction(callable $tsxHandler, ?string $alias = null, ?T
/**
* @template U
*
- * @param callable(TransactionInterface):U $tsxHandler
+ * @param callable(TransactionInterface):U $tsxHandler
*
* @return U
*/
@@ -104,7 +92,7 @@ public function readTransaction(callable $tsxHandler, ?string $alias = null, ?Tr
*
* @template U
*
- * @param callable(TransactionInterface):U $tsxHandler
+ * @param callable(TransactionInterface):U $tsxHandler
*
* @return U
*/
diff --git a/src/Contracts/CypherSequence.php b/src/Contracts/CypherSequence.php
new file mode 100644
index 00000000..a5de4e24
--- /dev/null
+++ b/src/Contracts/CypherSequence.php
@@ -0,0 +1,204 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Laudis\Neo4j\Contracts;
+
+use Countable;
+use Iterator;
+use JsonSerializable;
+use Laudis\Neo4j\Types\CypherList;
+use Laudis\Neo4j\Types\CypherMap;
+use Laudis\Neo4j\Types\Map;
+
+/**
+ * Abstract immutable sequence with basic functional methods.
+ *
+ * @template TValue
+ */
+interface CypherSequence extends Countable, JsonSerializable
+{
+ /**
+ * Copies the sequence.
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function copy(): self;
+
+ /**
+ * Returns whether the sequence is empty.
+ *
+ * @psalm-suppress UnusedForeachValue
+ */
+ public function isEmpty(): bool;
+
+ /**
+ * Creates a new sequence by merging this one with the provided iterable. When the iterable is not a list, the provided values will override the existing items in case of a key collision.
+ *
+ * @template NewValue
+ *
+ * @param iterable $values
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function merge(iterable $values): self;
+
+ /**
+ * Checks if the sequence contains the given key.
+ */
+ public function hasKey(string|int $key): bool;
+
+ /**
+ * Checks if the sequence contains the given value. The equality check is strict.
+ */
+ public function hasValue(mixed $value): bool;
+
+ /**
+ * Creates a filtered the sequence with the provided callback.
+ *
+ * @param callable(TValue, array-key):bool $callback
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function filter(callable $callback): self;
+
+ /**
+ * Maps the values of this sequence to a new one with the provided callback.
+ *
+ * @template ReturnType
+ *
+ * @param callable(TValue, array-key):ReturnType $callback
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function map(callable $callback): self;
+
+ /**
+ * Reduces this sequence with the given callback.
+ *
+ * @template TInitial
+ *
+ * @param TInitial|null $initial
+ * @param callable(TInitial|null, TValue, array-key):TInitial $callback
+ *
+ * @return TInitial
+ */
+ public function reduce(callable $callback, mixed $initial = null): mixed;
+
+ /**
+ * Finds the position of the value within the sequence.
+ *
+ * @return false|array-key returns the key of the value if it is found, false otherwise
+ */
+ public function find(mixed $value): false|string|int;
+
+ /**
+ * Creates a reversed sequence.
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function reversed(): self;
+
+ /**
+ * Slices a new sequence starting from the given offset with a certain length.
+ * If the length is null it will slice the entire remainder starting from the offset.
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function slice(int $offset, ?int $length = null): self;
+
+ /**
+ * Creates a sorted sequence. If the comparator is null it will use natural ordering.
+ *
+ * @param (callable(TValue, TValue):int)|null $comparator
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function sorted(?callable $comparator = null): self;
+
+ /**
+ * Creates a list from the arrays and objects in the sequence whose values corresponding with the provided key.
+ *
+ * @return CypherList
+ *
+ * @psalm-mutation-free
+ */
+ public function pluck(string|int $key): CypherList;
+
+ /**
+ * Uses the values found at the provided key as the key for the new Map.
+ *
+ * @return CypherMap
+ *
+ * @psalm-mutation-free
+ */
+ public function keyBy(string|int $key): CypherMap;
+
+ /**
+ * Joins the values within the sequence together with the provided glue. If the glue is null, it will be an empty string.
+ */
+ public function join(?string $glue = null): string;
+
+ /**
+ * Iterates over the sequence and applies the callable.
+ *
+ * @param callable(TValue, array-key):void $callable
+ *
+ * @return self
+ */
+ public function each(callable $callable): self;
+
+ /**
+ * Returns the sequence as an array.
+ *
+ * @return array
+ */
+ public function toArray(): array;
+
+ /**
+ * Returns the sequence as an array.
+ *
+ * @return array
+ */
+ public function toRecursiveArray(): array;
+
+ /**
+ * @return Iterator
+ */
+ public function getGenerator(): Iterator;
+
+ /**
+ * @return self
+ */
+ public function withCacheLimit(int $cacheLimit): self;
+
+ /**
+ * Preload the lazy evaluation.
+ */
+ public function preload(): void;
+
+ public function __serialize(): array;
+}
diff --git a/src/Contracts/DriverInterface.php b/src/Contracts/DriverInterface.php
index 9d9b0018..a3902963 100644
--- a/src/Contracts/DriverInterface.php
+++ b/src/Contracts/DriverInterface.php
@@ -14,22 +14,18 @@
namespace Laudis\Neo4j\Contracts;
use Laudis\Neo4j\Databags\SessionConfiguration;
-use Laudis\Neo4j\Formatter\CypherList;
-use Laudis\Neo4j\Formatter\CypherMap;
+use Laudis\Neo4j\Types\CypherList;
+use Laudis\Neo4j\Types\CypherMap;
/**
* The driver creates sessions for carrying out work.
*
- * @template ResultFormat
- *
* @psalm-type ParsedUrl = array{host: string, pass: string|null, path: string, port: int, query: array, scheme: string, user: string|null}
* @psalm-type BasicDriver = DriverInterface>>
*/
interface DriverInterface
{
/**
- * @return SessionInterface
- *
* @psalm-mutation-free
*/
public function createSession(?SessionConfiguration $config = null): SessionInterface;
diff --git a/src/Contracts/FormatterInterface.php b/src/Contracts/FormatterInterface.php
deleted file mode 100644
index d2198dfe..00000000
--- a/src/Contracts/FormatterInterface.php
+++ /dev/null
@@ -1,78 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Laudis\Neo4j\Contracts;
-
-use Bolt\Bolt;
-use Laudis\Neo4j\Bolt\BoltConnection;
-use Laudis\Neo4j\Bolt\BoltResult;
-use Laudis\Neo4j\Databags\BookmarkHolder;
-use Laudis\Neo4j\Databags\Statement;
-
-/**
- * A formatter (aka Hydrator) is reponsible for formatting the incoming results of the driver.
- *
- * @psalm-type CypherStats = array{
- * nodes_created: int,
- * nodes_deleted: int,
- * relationships_created: int,
- * relationships_deleted: int,
- * properties_set: int,
- * labels_added: int,
- * labels_removed: int,
- * indexes_added: int,
- * indexes_removed: int,
- * constraints_added: int,
- * constraints_removed: int,
- * contains_updates: bool,
- * contains_system_updates?: bool,
- * system_updates?: int
- * }
- * @psalm-type BoltCypherStats = array{
- * nodes-created?: int,
- * nodes-deleted?: int,
- * relationships-created?: int,
- * relationships-deleted?: int,
- * properties-set?: int,
- * labels-added?: int,
- * labels-removed?: int,
- * indexes-added?: int,
- * indexes-removed?: int,
- * constraints-added?: int,
- * constraints-removed?: int,
- * contains-updates?: bool,
- * contains-system-updates?: bool,
- * system-updates?: int,
- * db?: string
- * }
- * @psalm-type CypherError = array{code: string, message: string}
- * @psalm-type CypherRowResponse = array{row: list>}
- * @psalm-type CypherResponse = array{columns:list, data:list, stats?:CypherStats}
- * @psalm-type CypherResponseSet = array{results: list, errors: list}
- * @psalm-type BoltMeta = array{t_first: int, fields: list, qid ?: int}
- *
- * @template ResultFormat
- *
- * @deprecated Next major version will only use SummarizedResultFormatter
- */
-interface FormatterInterface
-{
- /**
- * Formats the results of the bolt protocol to the unified format.
- *
- * @param BoltMeta $meta
- *
- * @return ResultFormat
- */
- public function formatBoltResult(array $meta, BoltResult $result, BoltConnection $connection, float $runStart, float $resultAvailableAfter, Statement $statement, BookmarkHolder $holder);
-}
diff --git a/src/Contracts/SessionInterface.php b/src/Contracts/SessionInterface.php
index 57eeeb5b..1694c9a5 100644
--- a/src/Contracts/SessionInterface.php
+++ b/src/Contracts/SessionInterface.php
@@ -15,16 +15,13 @@
use Laudis\Neo4j\Databags\Bookmark;
use Laudis\Neo4j\Databags\Statement;
+use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\TransactionConfiguration;
use Laudis\Neo4j\Exception\Neo4jException;
use Laudis\Neo4j\Types\CypherList;
/**
* A lightweight container for causally chained sequences of transactions to carry out work.
- *
- * @template ResultFormat
- *
- * @extends TransactionInterface
*/
interface SessionInterface extends TransactionInterface
{
@@ -33,35 +30,28 @@ interface SessionInterface extends TransactionInterface
*
* @throws Neo4jException
*
- * @return CypherList
+ * @return CypherList
*/
public function runStatements(iterable $statements, ?TransactionConfiguration $config = null): CypherList;
- /**
- * @return ResultFormat
- */
- public function runStatement(Statement $statement, ?TransactionConfiguration $config = null);
+ public function runStatement(Statement $statement, ?TransactionConfiguration $config = null): SummarizedResult;
/**
* @param iterable $parameters
- *
- * @return ResultFormat
*/
- public function run(string $statement, iterable $parameters = [], ?TransactionConfiguration $config = null);
+ public function run(string $statement, iterable $parameters = [], ?TransactionConfiguration $config = null): SummarizedResult;
/**
* @psalm-param iterable|null $statements
*
* @throws Neo4jException
- *
- * @return UnmanagedTransactionInterface
*/
public function beginTransaction(?iterable $statements = null, ?TransactionConfiguration $config = null): UnmanagedTransactionInterface;
/**
* @template HandlerResult
*
- * @param callable(TransactionInterface):HandlerResult $tsxHandler
+ * @param callable(TransactionInterface):HandlerResult $tsxHandler
*
* @return HandlerResult
*/
@@ -70,7 +60,7 @@ public function writeTransaction(callable $tsxHandler, ?TransactionConfiguration
/**
* @template HandlerResult
*
- * @param callable(TransactionInterface):HandlerResult $tsxHandler
+ * @param callable(TransactionInterface):HandlerResult $tsxHandler
*
* @return HandlerResult
*/
@@ -79,7 +69,7 @@ public function readTransaction(callable $tsxHandler, ?TransactionConfiguration
/**
* @template HandlerResult
*
- * @param callable(TransactionInterface):HandlerResult $tsxHandler
+ * @param callable(TransactionInterface):HandlerResult $tsxHandler
*
* @return HandlerResult
*/
diff --git a/src/Contracts/TransactionInterface.php b/src/Contracts/TransactionInterface.php
index d4c77ed0..ff615d8f 100644
--- a/src/Contracts/TransactionInterface.php
+++ b/src/Contracts/TransactionInterface.php
@@ -14,36 +14,30 @@
namespace Laudis\Neo4j\Contracts;
use Laudis\Neo4j\Databags\Statement;
+use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Exception\Neo4jException;
use Laudis\Neo4j\Types\CypherList;
/**
* Transactions are atomic units of work that may contain one or more query.
*
- * @template ResultFormat
- *
* @see https://neo4j.com/docs/cypher-manual/current/introduction/transactions/
*/
interface TransactionInterface
{
/**
* @param iterable $parameters
- *
- * @return ResultFormat
*/
- public function run(string $statement, iterable $parameters = []);
+ public function run(string $statement, iterable $parameters = []): SummarizedResult;
- /**
- * @return ResultFormat
- */
- public function runStatement(Statement $statement);
+ public function runStatement(Statement $statement): SummarizedResult;
/**
* @param iterable $statements
*
* @throws Neo4jException
*
- * @return CypherList
+ * @return CypherList
*/
public function runStatements(iterable $statements): CypherList;
}
diff --git a/src/Contracts/UnmanagedTransactionInterface.php b/src/Contracts/UnmanagedTransactionInterface.php
index 25f19e65..f04697a0 100644
--- a/src/Contracts/UnmanagedTransactionInterface.php
+++ b/src/Contracts/UnmanagedTransactionInterface.php
@@ -14,15 +14,12 @@
namespace Laudis\Neo4j\Contracts;
use Laudis\Neo4j\Databags\Statement;
+use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Types\CypherList;
/**
* An unmanaged transaction needs to be committed or rolled back manually.
*
- * @template T
- *
- * @extends TransactionInterface
- *
* @see https://neo4j.com/docs/cypher-manual/current/introduction/transactions/
*/
interface UnmanagedTransactionInterface extends TransactionInterface
@@ -32,7 +29,7 @@ interface UnmanagedTransactionInterface extends TransactionInterface
*
* @param iterable $statements
*
- * @return CypherList
+ * @return CypherList
*/
public function commit(iterable $statements = []): CypherList;
diff --git a/src/Databags/BookmarkHolder.php b/src/Databags/BookmarkHolder.php
index 48109d19..9789a054 100644
--- a/src/Databags/BookmarkHolder.php
+++ b/src/Databags/BookmarkHolder.php
@@ -16,8 +16,9 @@
final class BookmarkHolder
{
public function __construct(
- private Bookmark $bookmark
- ) {}
+ private Bookmark $bookmark,
+ ) {
+ }
public function getBookmark(): Bookmark
{
diff --git a/src/Databags/ConnectionRequestData.php b/src/Databags/ConnectionRequestData.php
index b5772b1a..764f8573 100644
--- a/src/Databags/ConnectionRequestData.php
+++ b/src/Databags/ConnectionRequestData.php
@@ -26,8 +26,9 @@ public function __construct(
private readonly UriInterface $uri,
private readonly AuthenticateInterface $auth,
private readonly string $userAgent,
- private readonly SslConfiguration $config
- ) {}
+ private readonly SslConfiguration $config,
+ ) {
+ }
public function getHostname(): string
{
diff --git a/src/Databags/DatabaseInfo.php b/src/Databags/DatabaseInfo.php
index 2f88cab0..0cd90e15 100644
--- a/src/Databags/DatabaseInfo.php
+++ b/src/Databags/DatabaseInfo.php
@@ -25,8 +25,9 @@
final class DatabaseInfo extends AbstractCypherObject
{
public function __construct(
- private readonly string $name
- ) {}
+ private readonly string $name,
+ ) {
+ }
/**
* Returns the name of the database.
diff --git a/src/Databags/DriverConfiguration.php b/src/Databags/DriverConfiguration.php
index edbd355d..68c35a87 100644
--- a/src/Databags/DriverConfiguration.php
+++ b/src/Databags/DriverConfiguration.php
@@ -46,21 +46,21 @@ final class DriverConfiguration
private ?Neo4jLogger $logger;
/**
- * @param callable():(CacheInterface|null)|CacheInterface|null $cache
+ * @param callable():(CacheInterface|null)|CacheInterface|null $cache
* @param callable():(SemaphoreFactoryInterface|null)|SemaphoreFactoryInterface|null $semaphore
- * @param string|null $logLevel The log level to use. If null, LogLevel::INFO is used.
+ * @param string|null $logLevel The log level to use. If null, LogLevel::INFO is used.
*
* @psalm-external-mutation-free
*/
public function __construct(
- private string|null $userAgent,
+ private ?string $userAgent,
private SslConfiguration $sslConfig,
- private int|null $maxPoolSize,
+ private ?int $maxPoolSize,
CacheInterface|callable|null $cache,
- private float|null $acquireConnectionTimeout,
+ private ?float $acquireConnectionTimeout,
callable|SemaphoreFactoryInterface|null $semaphore,
?string $logLevel,
- ?LoggerInterface $logger
+ ?LoggerInterface $logger,
) {
$this->cache = $cache;
$this->semaphoreFactory = $semaphore;
diff --git a/src/Databags/DriverSetup.php b/src/Databags/DriverSetup.php
index 67066fc4..00317c06 100644
--- a/src/Databags/DriverSetup.php
+++ b/src/Databags/DriverSetup.php
@@ -25,8 +25,9 @@ final class DriverSetup
{
public function __construct(
private readonly UriInterface $uri,
- private readonly AuthenticateInterface $auth
- ) {}
+ private readonly AuthenticateInterface $auth,
+ ) {
+ }
public function getAuth(): AuthenticateInterface
{
diff --git a/src/Databags/InputPosition.php b/src/Databags/InputPosition.php
index 7e81d118..ca548925 100644
--- a/src/Databags/InputPosition.php
+++ b/src/Databags/InputPosition.php
@@ -23,8 +23,9 @@ final class InputPosition
public function __construct(
private readonly int $column,
private readonly int $line,
- private readonly int $offset
- ) {}
+ private readonly int $offset,
+ ) {
+ }
/**
* The column number referred to by the position; column numbers start at 1.
diff --git a/src/Databags/Neo4jError.php b/src/Databags/Neo4jError.php
index d592ebed..dab5f788 100644
--- a/src/Databags/Neo4jError.php
+++ b/src/Databags/Neo4jError.php
@@ -30,8 +30,9 @@ public function __construct(
private readonly ?string $message,
private readonly string $classification,
private readonly string $category,
- private readonly string $title
- ) {}
+ private readonly string $title,
+ ) {
+ }
/**
* @pure
diff --git a/src/Databags/Notification.php b/src/Databags/Notification.php
index c372ccba..f11daf40 100644
--- a/src/Databags/Notification.php
+++ b/src/Databags/Notification.php
@@ -25,8 +25,9 @@ public function __construct(
private readonly string $description,
private readonly ?InputPosition $inputPosition,
private readonly string $severity,
- private readonly string $title
- ) {}
+ private readonly string $title,
+ ) {
+ }
/**
* Returns a notification code for the discovered issue.
diff --git a/src/Databags/Pair.php b/src/Databags/Pair.php
index 05585b39..247a9383 100644
--- a/src/Databags/Pair.php
+++ b/src/Databags/Pair.php
@@ -29,8 +29,9 @@ final class Pair
*/
public function __construct(
private $key,
- private $value
- ) {}
+ private $value,
+ ) {
+ }
/**
* @return TKey
diff --git a/src/Databags/Plan.php b/src/Databags/Plan.php
index 024653b7..76a317c0 100644
--- a/src/Databags/Plan.php
+++ b/src/Databags/Plan.php
@@ -37,8 +37,9 @@ public function __construct(
private readonly CypherMap $arguments,
private readonly CypherList $list,
private readonly CypherList $identifiers,
- private readonly string $operator
- ) {}
+ private readonly string $operator,
+ ) {
+ }
/**
* Returns the arguments for the operator.
diff --git a/src/Databags/ProfiledPlan.php b/src/Databags/ProfiledPlan.php
index 045cd25d..64c325d4 100644
--- a/src/Databags/ProfiledPlan.php
+++ b/src/Databags/ProfiledPlan.php
@@ -38,8 +38,9 @@ public function __construct(
private readonly int $pageCacheHits,
private readonly int $pageCacheMisses,
private readonly int $records,
- private readonly int $time
- ) {}
+ private readonly int $time,
+ ) {
+ }
/**
* @return CypherList
diff --git a/src/Databags/ResultSummary.php b/src/Databags/ResultSummary.php
index 6992ecda..a5aa05f8 100644
--- a/src/Databags/ResultSummary.php
+++ b/src/Databags/ResultSummary.php
@@ -46,8 +46,9 @@ public function __construct(
private readonly QueryTypeEnum $queryType,
private readonly float $resultAvailableAfter,
private readonly float $resultConsumedAfter,
- private readonly ServerInfo $serverInfo
- ) {}
+ private readonly ServerInfo $serverInfo,
+ ) {
+ }
/**
* The counters for amount of operations the query triggered.
diff --git a/src/Databags/ServerInfo.php b/src/Databags/ServerInfo.php
index 7408103c..ce8fa011 100644
--- a/src/Databags/ServerInfo.php
+++ b/src/Databags/ServerInfo.php
@@ -29,8 +29,9 @@ final class ServerInfo extends AbstractCypherObject
public function __construct(
private readonly UriInterface $address,
private readonly ConnectionProtocol $protocol,
- private readonly string $agent
- ) {}
+ private readonly string $agent,
+ ) {
+ }
/**
* Returns the uri of the server the query was executed.
diff --git a/src/Databags/SessionConfiguration.php b/src/Databags/SessionConfiguration.php
index 56adcdc9..397e864a 100644
--- a/src/Databags/SessionConfiguration.php
+++ b/src/Databags/SessionConfiguration.php
@@ -37,18 +37,19 @@ final class SessionConfiguration
*/
public function __construct(
private readonly ?string $database = null,
- private readonly int|null $fetchSize = null,
- private readonly AccessMode|null $accessMode = null,
- private readonly array|null $bookmarks = null,
+ private readonly ?int $fetchSize = null,
+ private readonly ?AccessMode $accessMode = null,
+ private readonly ?array $bookmarks = null,
private readonly ?Neo4jLogger $logger = null,
- ) {}
+ ) {
+ }
/**
* @pure
*
* @param list|null $bookmarks
*/
- public static function create(string|null $database = null, int|null $fetchSize = null, AccessMode|null $defaultAccessMode = null, array|null $bookmarks = null, ?Neo4jLogger $logger = null): self
+ public static function create(?string $database = null, ?int $fetchSize = null, ?AccessMode $defaultAccessMode = null, ?array $bookmarks = null, ?Neo4jLogger $logger = null): self
{
return new self($database, $fetchSize, $defaultAccessMode, $bookmarks, $logger);
}
diff --git a/src/Databags/SslConfiguration.php b/src/Databags/SslConfiguration.php
index 58741c77..07fd5c4e 100644
--- a/src/Databags/SslConfiguration.php
+++ b/src/Databags/SslConfiguration.php
@@ -22,8 +22,9 @@ final class SslConfiguration
{
public function __construct(
private SslMode $mode,
- private bool $verifyPeer
- ) {}
+ private bool $verifyPeer,
+ ) {
+ }
public function getMode(): SslMode
{
diff --git a/src/Databags/Statement.php b/src/Databags/Statement.php
index f3cc3731..a7ddd8a4 100644
--- a/src/Databags/Statement.php
+++ b/src/Databags/Statement.php
@@ -31,8 +31,9 @@ final class Statement extends AbstractCypherObject
*/
public function __construct(
private readonly string $text,
- private readonly iterable $parameters
- ) {}
+ private readonly iterable $parameters,
+ ) {
+ }
/**
* @pure
diff --git a/src/Databags/SummarizedResult.php b/src/Databags/SummarizedResult.php
index e72249a2..15a4dd22 100644
--- a/src/Databags/SummarizedResult.php
+++ b/src/Databags/SummarizedResult.php
@@ -14,49 +14,32 @@
namespace Laudis\Neo4j\Databags;
use Generator;
-use Laudis\Neo4j\Types\AbstractCypherSequence;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Laudis\Neo4j\Types\CypherList;
+use Laudis\Neo4j\Types\CypherMap;
/**
* A result containing the values and the summary.
*
- * @template TValue
+ * @psalm-import-type OGMTypes from SummarizedResultFormatter
*
- * @extends CypherList
+ * @extends CypherList>
*/
final class SummarizedResult extends CypherList
{
private ?ResultSummary $summary = null;
/**
- * @param iterable|callable():Generator $iterable
+ * @param iterable>|callable():Generator> $iterable
*
* @psalm-mutation-free
*/
- public function __construct(?ResultSummary &$summary, $iterable = [])
+ public function __construct(?ResultSummary &$summary, iterable|callable $iterable = [])
{
parent::__construct($iterable);
$this->summary = &$summary;
}
- /**
- * @template Value
- *
- * @param callable():(\Generator) $operation
- *
- * @return static
- *
- * @psalm-mutation-free
- */
- protected function withOperation($operation): AbstractCypherSequence
- {
- /**
- * @psalm-suppress UnsafeInstantiation
- * @psalm-suppress ImpurePropertyAssignment
- */
- return new self($this->summary, $operation);
- }
-
/**
* Returns the result summary.
*/
@@ -70,6 +53,9 @@ public function getSummary(): ResultSummary
return $this->summary;
}
+ /**
+ * @return CypherList>
+ */
public function getResults(): CypherList
{
return new CypherList($this);
diff --git a/src/Databags/SummaryCounters.php b/src/Databags/SummaryCounters.php
index 6df81975..9fb4efbc 100644
--- a/src/Databags/SummaryCounters.php
+++ b/src/Databags/SummaryCounters.php
@@ -38,8 +38,9 @@ public function __construct(
private readonly int $constraintsRemoved = 0,
private readonly bool $containsUpdates = false,
private readonly bool $containsSystemUpdates = false,
- private readonly int $systemUpdates = 0
- ) {}
+ private readonly int $systemUpdates = 0,
+ ) {
+ }
/**
* Whether the query contained any updates.
diff --git a/src/Databags/TransactionConfiguration.php b/src/Databags/TransactionConfiguration.php
index c1dacf0e..bc9a68d9 100644
--- a/src/Databags/TransactionConfiguration.php
+++ b/src/Databags/TransactionConfiguration.php
@@ -28,9 +28,10 @@ final class TransactionConfiguration
* @param iterable|null $metaData
*/
public function __construct(
- private float|null $timeout = null,
- private iterable|null $metaData = null
- ) {}
+ private ?float $timeout = null,
+ private ?iterable $metaData = null,
+ ) {
+ }
/**
* @pure
@@ -38,7 +39,7 @@ public function __construct(
* @param float|null $timeout timeout in seconds
* @param iterable|null $metaData
*/
- public static function create(float|null $timeout = null, iterable|null $metaData = null): self
+ public static function create(?float $timeout = null, ?iterable $metaData = null): self
{
return new self($timeout, $metaData);
}
@@ -74,7 +75,7 @@ public function getTimeout(): ?float
*
* @param float|null $timeout timeout in seconds
*/
- public function withTimeout(float|null $timeout): self
+ public function withTimeout(?float $timeout): self
{
return new self($timeout, $this->metaData);
}
@@ -84,7 +85,7 @@ public function withTimeout(float|null $timeout): self
*
* @param iterable|null $metaData
*/
- public function withMetaData(iterable|null $metaData): self
+ public function withMetaData(?iterable $metaData): self
{
return new self($this->timeout, $metaData);
}
diff --git a/src/DriverFactory.php b/src/DriverFactory.php
index aa48c00e..32a16cdd 100644
--- a/src/DriverFactory.php
+++ b/src/DriverFactory.php
@@ -20,34 +20,23 @@
use Laudis\Neo4j\Common\Uri;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Laudis\Neo4j\Contracts\DriverInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Databags\DriverConfiguration;
use Laudis\Neo4j\Exception\UnsupportedScheme;
-use Laudis\Neo4j\Formatter\OGMFormatter;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Laudis\Neo4j\Neo4j\Neo4jDriver;
use Psr\Http\Message\UriInterface;
/**
* Factory for creating drivers directly.
*
- * @psalm-import-type OGMResults from OGMFormatter
+ * @psalm-import-type OGMResults from SummarizedResultFormatter
*/
final class DriverFactory
{
/**
- * @template U
- *
- * @param FormatterInterface $formatter
- *
* @throws UnsupportedScheme
- *
- * @return (
- * func_num_args() is 4
- * ? DriverInterface
- * : DriverInterface
- * )
*/
- public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null, ?FormatterInterface $formatter = null): DriverInterface
+ public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null, ?SummarizedResultFormatter $formatter = null): DriverInterface
{
if (is_string($uri)) {
$uri = Uri::create($uri);
@@ -67,18 +56,7 @@ public static function create(string|UriInterface $uri, ?DriverConfiguration $co
throw UnsupportedScheme::make($scheme, ['bolt', 'bolt+s', 'bolt+ssc', 'neo4j', 'neo4j+s', 'neo4j+ssc']);
}
- /**
- * @template U
- *
- * @param FormatterInterface $formatter
- *
- * @return (
- * func_num_args() is 4
- * ? DriverInterface
- * : DriverInterface
- * )
- */
- private static function createBoltDriver(string|UriInterface $uri, ?DriverConfiguration $configuration, ?AuthenticateInterface $authenticate, ?FormatterInterface $formatter = null): DriverInterface
+ private static function createBoltDriver(string|UriInterface $uri, ?DriverConfiguration $configuration, ?AuthenticateInterface $authenticate, ?SummarizedResultFormatter $formatter = null): DriverInterface
{
if ($formatter !== null) {
return BoltDriver::create($uri, $configuration, $authenticate, $formatter);
@@ -87,18 +65,7 @@ private static function createBoltDriver(string|UriInterface $uri, ?DriverConfig
return BoltDriver::create($uri, $configuration, $authenticate);
}
- /**
- * @template U
- *
- * @param FormatterInterface $formatter
- *
- * @return (
- * func_num_args() is 4
- * ? DriverInterface
- * : DriverInterface
- * )
- */
- private static function createNeo4jDriver(string|UriInterface $uri, ?DriverConfiguration $configuration, ?AuthenticateInterface $authenticate, ?FormatterInterface $formatter = null): DriverInterface
+ private static function createNeo4jDriver(string|UriInterface $uri, ?DriverConfiguration $configuration, ?AuthenticateInterface $authenticate, ?SummarizedResultFormatter $formatter = null): DriverInterface
{
if ($formatter !== null) {
return Neo4jDriver::create($uri, $configuration, $authenticate, $formatter);
diff --git a/src/Formatter/BasicFormatter.php b/src/Formatter/BasicFormatter.php
deleted file mode 100644
index 766a0d13..00000000
--- a/src/Formatter/BasicFormatter.php
+++ /dev/null
@@ -1,235 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Laudis\Neo4j\Formatter;
-
-use function array_key_exists;
-
-use Bolt\protocol\v1\structures\Path;
-
-use function gettype;
-use function is_array;
-use function is_object;
-use function is_string;
-
-use Laudis\Neo4j\Bolt\BoltConnection;
-use Laudis\Neo4j\Bolt\BoltResult;
-use Laudis\Neo4j\Contracts\ConnectionInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
-use Laudis\Neo4j\Databags\Bookmark;
-use Laudis\Neo4j\Databags\BookmarkHolder;
-use Laudis\Neo4j\Databags\Statement;
-use Laudis\Neo4j\Types\CypherList;
-use Laudis\Neo4j\Types\CypherMap;
-use Psr\Http\Message\RequestInterface;
-use Psr\Http\Message\ResponseInterface;
-use stdClass;
-use UnexpectedValueException;
-
-/**
- * Formats the result in basic CypherLists and CypherMaps. All cypher types are erased so that the map only contains scalar, null or array values.
- *
- * @psalm-type BasicResults = CypherList>
- *
- * @implements FormatterInterface
- *
- * @deprecated Next major version will only use SummarizedResultFormatter
- */
-final class BasicFormatter implements FormatterInterface
-{
- /**
- * Creates a new instance of itself.
- *
- * @pure
- */
- public static function create(): self
- {
- return new self();
- }
-
- /**
- * @param array{fields: array, qid?: int, t_first: int} $meta
- *
- * @return CypherList>
- */
- public function formatBoltResult(array $meta, BoltResult $result, BoltConnection $connection, float $runStart, float $resultAvailableAfter, Statement $statement, BookmarkHolder $holder): CypherList
- {
- $tbr = (new CypherList(function () use ($meta, $result) {
- foreach ($result as $row) {
- yield $this->formatRow($meta, $row);
- }
- }))->withCacheLimit($result->getFetchSize());
-
- $connection->subscribeResult($tbr);
- $result->addFinishedCallback(function (array $response) use ($holder) {
- if (array_key_exists('bookmark', $response) && is_string($response['bookmark'])) {
- $holder->setBookmark(new Bookmark([$response['bookmark']]));
- }
- });
-
- return $tbr;
- }
-
- /**
- * @psalm-mutation-free
- */
- public function formatHttpResult(ResponseInterface $response, stdClass $body, ?ConnectionInterface $connection = null, ?float $resultsAvailableAfter = null, ?float $resultsConsumedAfter = null, ?iterable $statements = null): CypherList
- {
- /** @var list>> */
- $tbr = [];
-
- /** @var stdClass $results */
- foreach ($body->results as $results) {
- $tbr[] = $this->buildResult($results);
- }
-
- return new CypherList($tbr);
- }
-
- /**
- * @return CypherList>
- *
- * @psalm-mutation-free
- */
- private function buildResult(stdClass $result): CypherList
- {
- /** @var list> */
- $tbr = [];
-
- /** @var list $columns */
- $columns = (array) $result->columns;
- /** @var stdClass $dataRow */
- foreach ($result->data as $dataRow) {
- /** @var array $map */
- $map = [];
- /** @var list */
- $vector = $dataRow->row;
- foreach ($columns as $index => $key) {
- // Removes the stdClasses from the json objects
- /** @var scalar|array|null */
- $decoded = json_decode(json_encode($vector[$index], JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR);
- $map[$key] = $decoded;
- }
- $tbr[] = new CypherMap($map);
- }
-
- return new CypherList($tbr);
- }
-
- /**
- * @param array{fields: array, qid?: int, t_first: int} $meta
- *
- * @return CypherMap
- */
- private function formatRow(array $meta, array $result): CypherMap
- {
- /** @var array $map */
- $map = [];
- foreach ($meta['fields'] as $i => $column) {
- $map[$column] = $this->mapValue($result[$i]);
- }
-
- return new CypherMap($map);
- }
-
- private function mapPath(Path $path): array
- {
- $relationships = $path->rels;
- $nodes = $path->nodes;
- $tbr = [];
- /**
- * @var mixed $node
- */
- foreach ($nodes as $i => $node) {
- /** @var mixed */
- $tbr[] = $node;
- if (array_key_exists($i, $relationships)) {
- /** @var mixed */
- $tbr[] = $relationships[$i];
- }
- }
-
- return $tbr;
- }
-
- /**
- * @return scalar|array|null
- */
- private function mapValue(mixed $value): float|array|bool|int|string|null
- {
- if ($value instanceof Path) {
- $value = $this->mapPath($value);
- }
-
- if (is_object($value)) {
- return $this->objectToProperty($value);
- }
-
- if ($value === null || is_scalar($value)) {
- return $value;
- }
-
- if (is_array($value)) {
- return $this->remapObjectsInArray($value);
- }
-
- throw new UnexpectedValueException('Did not expect to receive value of type: '.gettype($value));
- }
-
- private function objectToProperty(object $object): array
- {
- if ($object instanceof Path) {
- return $this->mapPath($object);
- }
-
- if (!method_exists($object, 'properties')) {
- $message = 'Cannot handle objects without a properties method. Class given: '.$object::class;
- throw new UnexpectedValueException($message);
- }
-
- /** @var array */
- return $object->properties();
- }
-
- private function remapObjectsInArray(array $value): array
- {
- /**
- * @psalm-var mixed $variable
- */
- foreach ($value as $key => $variable) {
- if (is_object($variable)) {
- $value[$key] = $this->objectToProperty($variable);
- }
- }
-
- return $value;
- }
-
- /**
- * @psalm-mutation-free
- */
- public function decorateRequest(RequestInterface $request, ConnectionInterface $connection): RequestInterface
- {
- return $request;
- }
-
- /**
- * @psalm-mutation-free
- */
- public function statementConfigOverride(ConnectionInterface $connection): array
- {
- return [
- 'resultDataContents' => ['ROW'],
- ];
- }
-}
diff --git a/src/Formatter/OGMFormatter.php b/src/Formatter/OGMFormatter.php
deleted file mode 100644
index 6de6b90e..00000000
--- a/src/Formatter/OGMFormatter.php
+++ /dev/null
@@ -1,116 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Laudis\Neo4j\Formatter;
-
-use function array_key_exists;
-
-use Laudis\Neo4j\Bolt\BoltConnection;
-use Laudis\Neo4j\Bolt\BoltResult;
-use Laudis\Neo4j\Contracts\FormatterInterface;
-use Laudis\Neo4j\Databags\Bookmark;
-use Laudis\Neo4j\Databags\BookmarkHolder;
-use Laudis\Neo4j\Databags\Statement;
-use Laudis\Neo4j\Formatter\Specialised\BoltOGMTranslator;
-use Laudis\Neo4j\Types\Cartesian3DPoint;
-use Laudis\Neo4j\Types\CartesianPoint;
-use Laudis\Neo4j\Types\CypherList;
-use Laudis\Neo4j\Types\CypherMap;
-use Laudis\Neo4j\Types\Date;
-use Laudis\Neo4j\Types\DateTime;
-use Laudis\Neo4j\Types\DateTimeZoneId;
-use Laudis\Neo4j\Types\Duration;
-use Laudis\Neo4j\Types\LocalDateTime;
-use Laudis\Neo4j\Types\LocalTime;
-use Laudis\Neo4j\Types\Node;
-use Laudis\Neo4j\Types\Path;
-use Laudis\Neo4j\Types\Relationship;
-use Laudis\Neo4j\Types\Time;
-use Laudis\Neo4j\Types\WGS843DPoint;
-use Laudis\Neo4j\Types\WGS84Point;
-
-/**
- * Formats the result in a basic OGM (Object Graph Mapping) format by mapping al cypher types to types found in the \Laudis\Neo4j\Types namespace.
- *
- * @see https://neo4j.com/docs/driver-manual/current/cypher-workflow/#driver-type-mapping
- *
- * @psalm-type OGMTypes = string|int|float|bool|null|Date|DateTime|Duration|LocalDateTime|LocalTime|Time|Node|Relationship|Path|Cartesian3DPoint|CartesianPoint|WGS84Point|WGS843DPoint|DateTimeZoneId|CypherList|CypherMap
- * @psalm-type OGMResults = CypherList>
- *
- * @psalm-import-type BoltMeta from FormatterInterface
- *
- * @implements FormatterInterface>>
- *
- * @deprecated Next major version will only use SummarizedResultFormatter
- */
-final class OGMFormatter implements FormatterInterface
-{
- /**
- * @psalm-mutation-free
- */
- public function __construct(
- private readonly BoltOGMTranslator $boltTranslator,
- ) {}
-
- /**
- * Creates a new instance of itself.
- *
- * @pure
- */
- public static function create(): OGMFormatter
- {
- return new self(new BoltOGMTranslator());
- }
-
- /**
- * @param BoltMeta $meta
- *
- * @return CypherList>
- */
- public function formatBoltResult(array $meta, BoltResult $result, BoltConnection $connection, float $runStart, float $resultAvailableAfter, Statement $statement, BookmarkHolder $holder): CypherList
- {
- $tbr = (new CypherList(function () use ($result, $meta) {
- foreach ($result as $row) {
- yield $this->formatRow($meta, $row);
- }
- }))->withCacheLimit($result->getFetchSize());
-
- $connection->subscribeResult($tbr);
- $result->addFinishedCallback(function (array $response) use ($holder) {
- if (array_key_exists('bookmark', $response) && is_string($response['bookmark'])) {
- $holder->setBookmark(new Bookmark([$response['bookmark']]));
- }
- });
-
- return $tbr;
- }
-
- /**
- * @param BoltMeta $meta
- * @param list $result
- *
- * @return CypherMap
- *
- * @psalm-mutation-free
- */
- private function formatRow(array $meta, array $result): CypherMap
- {
- /** @var array $map */
- $map = [];
- foreach ($meta['fields'] as $i => $column) {
- $map[$column] = $this->boltTranslator->mapValueToType($result[$i]);
- }
-
- return new CypherMap($map);
- }
-}
diff --git a/src/Formatter/Specialised/BoltOGMTranslator.php b/src/Formatter/Specialised/BoltOGMTranslator.php
index bdeb5ecd..16165eab 100644
--- a/src/Formatter/Specialised/BoltOGMTranslator.php
+++ b/src/Formatter/Specialised/BoltOGMTranslator.php
@@ -26,7 +26,7 @@
use Bolt\protocol\v1\structures\Relationship as BoltRelationship;
use Bolt\protocol\v1\structures\Time as BoltTime;
use Bolt\protocol\v1\structures\UnboundRelationship as BoltUnboundRelationship;
-use Laudis\Neo4j\Formatter\OGMFormatter;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Laudis\Neo4j\Types\Abstract3DPoint;
use Laudis\Neo4j\Types\AbstractPoint;
use Laudis\Neo4j\Types\Cartesian3DPoint;
@@ -51,7 +51,7 @@
/**
* Translates Bolt objects to Driver Types.
*
- * @psalm-import-type OGMTypes from OGMFormatter
+ * @psalm-import-type OGMTypes from SummarizedResultFormatter
*
* @psalm-immutable
*
@@ -107,6 +107,7 @@ private function makeFromBoltNode(BoltNode $node): Node
if ($node instanceof \Bolt\protocol\v5\structures\Node) {
$elementId = $node->element_id;
}
+
/**
* @psalm-suppress MixedArgumentTypeCoercion
*/
@@ -219,7 +220,7 @@ private function makeFromBoltUnboundRelationship(BoltUnboundRelationship $rel):
);
}
- private function makeFromBoltPoint2D(BoltPoint2d $x): AbstractPoint
+ private function makeFromBoltPoint2D(BoltPoint2D $x): AbstractPoint
{
if ($x->srid === CartesianPoint::SRID) {
return new CartesianPoint($x->x, $x->y);
diff --git a/src/Formatter/SummarizedResultFormatter.php b/src/Formatter/SummarizedResultFormatter.php
index 27b29842..6c345897 100644
--- a/src/Formatter/SummarizedResultFormatter.php
+++ b/src/Formatter/SummarizedResultFormatter.php
@@ -18,7 +18,7 @@
use Laudis\Neo4j\Bolt\BoltConnection;
use Laudis\Neo4j\Bolt\BoltResult;
-use Laudis\Neo4j\Contracts\FormatterInterface;
+use Laudis\Neo4j\Databags\Bookmark;
use Laudis\Neo4j\Databags\BookmarkHolder;
use Laudis\Neo4j\Databags\DatabaseInfo;
use Laudis\Neo4j\Databags\ResultSummary;
@@ -27,38 +27,87 @@
use Laudis\Neo4j\Databags\SummarizedResult;
use Laudis\Neo4j\Databags\SummaryCounters;
use Laudis\Neo4j\Enum\QueryTypeEnum;
+use Laudis\Neo4j\Formatter\Specialised\BoltOGMTranslator;
+use Laudis\Neo4j\Types\Cartesian3DPoint;
+use Laudis\Neo4j\Types\CartesianPoint;
use Laudis\Neo4j\Types\CypherList;
use Laudis\Neo4j\Types\CypherMap;
+use Laudis\Neo4j\Types\Date;
+use Laudis\Neo4j\Types\DateTime;
+use Laudis\Neo4j\Types\DateTimeZoneId;
+use Laudis\Neo4j\Types\Duration;
+use Laudis\Neo4j\Types\LocalDateTime;
+use Laudis\Neo4j\Types\LocalTime;
+use Laudis\Neo4j\Types\Node;
+use Laudis\Neo4j\Types\Path;
+use Laudis\Neo4j\Types\Relationship;
+use Laudis\Neo4j\Types\Time;
+use Laudis\Neo4j\Types\WGS843DPoint;
+use Laudis\Neo4j\Types\WGS84Point;
use function microtime;
/**
* Decorates the result of the provided format with an extensive summary.
*
- * @psalm-import-type CypherResponseSet from \Laudis\Neo4j\Contracts\FormatterInterface
- * @psalm-import-type CypherResponse from \Laudis\Neo4j\Contracts\FormatterInterface
- * @psalm-import-type BoltCypherStats from \Laudis\Neo4j\Contracts\FormatterInterface
- * @psalm-import-type OGMResults from \Laudis\Neo4j\Formatter\OGMFormatter
- * @psalm-import-type OGMTypes from \Laudis\Neo4j\Formatter\OGMFormatter
- *
- * @implements FormatterInterface>>
+ * @psalm-type OGMTypes = string|int|float|bool|null|Date|DateTime|Duration|LocalDateTime|LocalTime|Time|Node|Relationship|Path|Cartesian3DPoint|CartesianPoint|WGS84Point|WGS843DPoint|DateTimeZoneId|CypherList|CypherMap
+ * @psalm-type OGMResults = CypherList>
+ * @psalm-type CypherStats = array{
+ * nodes_created: int,
+ * nodes_deleted: int,
+ * relationships_created: int,
+ * relationships_deleted: int,
+ * properties_set: int,
+ * labels_added: int,
+ * labels_removed: int,
+ * indexes_added: int,
+ * indexes_removed: int,
+ * constraints_added: int,
+ * constraints_removed: int,
+ * contains_updates: bool,
+ * contains_system_updates?: bool,
+ * system_updates?: int
+ * }
+ * @psalm-type BoltCypherStats = array{
+ * nodes-created?: int,
+ * nodes-deleted?: int,
+ * relationships-created?: int,
+ * relationships-deleted?: int,
+ * properties-set?: int,
+ * labels-added?: int,
+ * labels-removed?: int,
+ * indexes-added?: int,
+ * indexes-removed?: int,
+ * constraints-added?: int,
+ * constraints-removed?: int,
+ * contains-updates?: bool,
+ * contains-system-updates?: bool,
+ * system-updates?: int,
+ * db?: string
+ * }
+ * @psalm-type CypherError = array{code: string, message: string}
+ * @psalm-type CypherRowResponse = array{row: list>}
+ * @psalm-type CypherResponse = array{columns:list, data:list, stats?:CypherStats}
+ * @psalm-type CypherResponseSet = array{results: list, errors: list}
+ * @psalm-type BoltMeta = array{t_first: int, fields: list, qid ?: int}
*/
-final class SummarizedResultFormatter implements FormatterInterface
+final class SummarizedResultFormatter
{
/**
* @pure
*/
public static function create(): self
{
- return new self(OGMFormatter::create());
+ return new self(new BoltOGMTranslator());
}
/**
* @psalm-mutation-free
*/
public function __construct(
- private readonly OGMFormatter $formatter
- ) {}
+ private readonly BoltOGMTranslator $translator,
+ ) {
+ }
/**
* @param array{stats?: BoltCypherStats}&array $response
@@ -97,40 +146,87 @@ public function formatBoltStats(array $response): SummaryCounters
);
}
+ /**
+ * @param BoltMeta $meta
+ */
public function formatBoltResult(array $meta, BoltResult $result, BoltConnection $connection, float $runStart, float $resultAvailableAfter, Statement $statement, BookmarkHolder $holder): SummarizedResult
{
/** @var ResultSummary|null $summary */
$summary = null;
- $result->addFinishedCallback(function (array $response) use ($connection, $statement, $runStart, $resultAvailableAfter, &$summary) {
- /** @var BoltCypherStats $response */
- $stats = $this->formatBoltStats($response);
- $resultConsumedAfter = microtime(true) - $runStart;
- $db = $response['db'] ?? '';
- $summary = new ResultSummary(
- $stats,
- new DatabaseInfo($db),
- new CypherList(),
- null,
- null,
- $statement,
- QueryTypeEnum::fromCounters($stats),
- $resultAvailableAfter,
- $resultConsumedAfter,
- new ServerInfo(
- $connection->getServerAddress(),
- $connection->getProtocol(),
- $connection->getServerAgent()
- )
- );
- });
+ $result->addFinishedCallback(
+ function (mixed $response) use ($connection, $statement, $runStart, $resultAvailableAfter, &$summary) {
+ /** @var array{stats?: BoltCypherStats}&array $response */
+ $stats = $this->formatBoltStats($response);
+ $resultConsumedAfter = microtime(true) - $runStart;
+ $db = $response['stats']['db'] ?? '';
+ $summary = new ResultSummary(
+ $stats,
+ new DatabaseInfo($db),
+ new CypherList(),
+ null,
+ null,
+ $statement,
+ QueryTypeEnum::fromCounters($stats),
+ $resultAvailableAfter,
+ $resultConsumedAfter,
+ new ServerInfo(
+ $connection->getServerAddress(),
+ $connection->getProtocol(),
+ $connection->getServerAgent()
+ )
+ );
+ });
- $formattedResult = $this->formatter->formatBoltResult($meta, $result, $connection, $runStart, $resultAvailableAfter, $statement, $holder);
+ $formattedResult = $this->processBoltResult($meta, $result, $connection, $holder);
/**
- * @psalm-suppress MixedArgument
- *
* @var SummarizedResult>
*/
- return (new SummarizedResult($summary, $formattedResult))->withCacheLimit($result->getFetchSize());
+ return new SummarizedResult($summary, (new CypherList($formattedResult))->withCacheLimit($result->getFetchSize()));
+ }
+
+ /**
+ * @param BoltMeta $meta
+ *
+ * @return CypherList>
+ */
+ private function processBoltResult(array $meta, BoltResult $result, BoltConnection $connection, BookmarkHolder $holder): CypherList
+ {
+ $tbr = (new CypherList(function () use ($result, $meta) {
+ foreach ($result as $row) {
+ yield $this->formatRow($meta, $row);
+ }
+ }))->withCacheLimit($result->getFetchSize());
+
+ $connection->subscribeResult($tbr);
+ $result->addFinishedCallback(function (array $response) use ($holder) {
+ if (array_key_exists('bookmark', $response) && is_string($response['bookmark'])) {
+ $holder->setBookmark(new Bookmark([$response['bookmark']]));
+ }
+ });
+
+ return $tbr;
+ }
+
+ /**
+ * @psalm-mutation-free
+ *
+ * @param BoltMeta $meta
+ *
+ * @return CypherMap
+ */
+ private function formatRow(array $meta, array $result): CypherMap
+ {
+ /** @var array $map */
+ $map = [];
+ if (!array_key_exists('fields', $meta)) {
+ return new CypherMap($map);
+ }
+
+ foreach ($meta['fields'] as $i => $column) {
+ $map[$column] = $this->translator->mapValueToType($result[$i]);
+ }
+
+ return new CypherMap($map);
}
}
diff --git a/src/Neo4j/Neo4jConnectionPool.php b/src/Neo4j/Neo4jConnectionPool.php
index e3a064be..e4ad758d 100644
--- a/src/Neo4j/Neo4jConnectionPool.php
+++ b/src/Neo4j/Neo4jConnectionPool.php
@@ -74,14 +74,15 @@ public function __construct(
private readonly CacheInterface $cache,
private readonly AddressResolverInterface $resolver,
private readonly ?Neo4jLogger $logger,
- ) {}
+ ) {
+ }
public static function create(
UriInterface $uri,
AuthenticateInterface $auth,
DriverConfiguration $conf,
AddressResolverInterface $resolver,
- SemaphoreInterface $semaphore
+ SemaphoreInterface $semaphore,
): self {
return new self(
$semaphore,
diff --git a/src/Neo4j/Neo4jDriver.php b/src/Neo4j/Neo4jDriver.php
index 004be4d7..54bb1f5a 100644
--- a/src/Neo4j/Neo4jDriver.php
+++ b/src/Neo4j/Neo4jDriver.php
@@ -26,50 +26,34 @@
use Laudis\Neo4j\Contracts\AddressResolverInterface;
use Laudis\Neo4j\Contracts\AuthenticateInterface;
use Laudis\Neo4j\Contracts\DriverInterface;
-use Laudis\Neo4j\Contracts\FormatterInterface;
use Laudis\Neo4j\Contracts\SessionInterface;
use Laudis\Neo4j\Databags\DriverConfiguration;
use Laudis\Neo4j\Databags\SessionConfiguration;
-use Laudis\Neo4j\Formatter\OGMFormatter;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use Psr\Http\Message\UriInterface;
use Psr\Log\LogLevel;
/**
* Driver for auto client-side routing.
*
- * @template T
- *
- * @implements DriverInterface
- *
- * @psalm-import-type OGMResults from OGMFormatter
+ * @psalm-import-type OGMResults from SummarizedResultFormatter
*/
final class Neo4jDriver implements DriverInterface
{
/**
- * @param FormatterInterface $formatter
- *
* @psalm-mutation-free
*/
public function __construct(
private readonly UriInterface $parsedUrl,
private readonly Neo4jConnectionPool $pool,
- private readonly FormatterInterface $formatter
- ) {}
+ private readonly SummarizedResultFormatter $formatter,
+ ) {
+ }
/**
- * @template U
- *
- * @param FormatterInterface $formatter
- *
- * @return (
- * func_num_args() is 5
- * ? self
- * : self
- * )
- *
* @psalm-suppress MixedReturnTypeCoercion
*/
- public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null, ?FormatterInterface $formatter = null, ?AddressResolverInterface $resolver = null): self
+ public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null, ?SummarizedResultFormatter $formatter = null, ?AddressResolverInterface $resolver = null): self
{
if (is_string($uri)) {
$uri = Uri::create($uri);
@@ -84,7 +68,7 @@ public static function create(string|UriInterface $uri, ?DriverConfiguration $co
return new self(
$uri,
Neo4jConnectionPool::create($uri, $authenticate, $configuration, $resolver, $semaphore),
- $formatter ?? OGMFormatter::create(),
+ $formatter ?? SummarizedResultFormatter::create(),
);
}
diff --git a/src/Neo4j/RoutingTable.php b/src/Neo4j/RoutingTable.php
index 3b5b2b76..4e6d7539 100644
--- a/src/Neo4j/RoutingTable.php
+++ b/src/Neo4j/RoutingTable.php
@@ -29,8 +29,9 @@ final class RoutingTable
*/
public function __construct(
private readonly iterable $servers,
- private readonly int $ttl
- ) {}
+ private readonly int $ttl,
+ ) {
+ }
/**
* Returns the time to live in seconds.
diff --git a/src/ParameterHelper.php b/src/ParameterHelper.php
index 3d7c20fa..520e23c8 100644
--- a/src/ParameterHelper.php
+++ b/src/ParameterHelper.php
@@ -81,7 +81,7 @@ public static function asMap(iterable $iterable): CypherMap
*/
public static function asParameter(
mixed $value,
- ConnectionProtocol $protocol
+ ConnectionProtocol $protocol,
): iterable|int|float|bool|string|stdClass|IStructure|null {
return self::cypherMapToStdClass($value) ??
self::emptySequenceToArray($value) ??
@@ -121,8 +121,8 @@ private static function filterInvalidType(mixed $value): mixed
private static function emptySequenceToArray(mixed $value): ?array
{
- if ((($value instanceof CypherList || $value instanceof CypherMap) && $value->count() === 0) ||
- (is_array($value) && count($value) === 0)) {
+ if ((($value instanceof CypherList || $value instanceof CypherMap) && $value->count() === 0)
+ || (is_array($value) && count($value) === 0)) {
return [];
}
@@ -159,7 +159,7 @@ private static function filledIterableToArray(mixed $value, ConnectionProtocol $
}
/**
- * @param iterable $parameters
+ * @param iterable $parameters
*
* @return CypherMap
*/
diff --git a/src/TypeCaster.php b/src/TypeCaster.php
index cd0ecd41..1168091f 100644
--- a/src/TypeCaster.php
+++ b/src/TypeCaster.php
@@ -117,7 +117,7 @@ public static function toArray(mixed $value): ?array
$tbr = [];
/** @var mixed $x */
foreach ($value as $x) {
- /** @var mixed */
+ /** @psalm-suppress MixedAssignment */
$tbr[] = $x;
}
diff --git a/src/Types/Abstract3DPoint.php b/src/Types/Abstract3DPoint.php
index 9d645e70..320c84a2 100644
--- a/src/Types/Abstract3DPoint.php
+++ b/src/Types/Abstract3DPoint.php
@@ -37,7 +37,7 @@ public function convertToBolt(): IStructure
public function __construct(
float $x,
float $y,
- private float $z
+ private float $z,
) {
parent::__construct($x, $y);
}
diff --git a/src/Types/AbstractPoint.php b/src/Types/AbstractPoint.php
index ac5da8cf..90765c2f 100644
--- a/src/Types/AbstractPoint.php
+++ b/src/Types/AbstractPoint.php
@@ -33,8 +33,9 @@ abstract class AbstractPoint extends AbstractPropertyObject implements PointInte
{
public function __construct(
private readonly float $x,
- private readonly float $y
- ) {}
+ private readonly float $y,
+ ) {
+ }
abstract public function getCrs(): string;
diff --git a/src/Types/AbstractPropertyObject.php b/src/Types/AbstractPropertyObject.php
index 3b37ee9c..673e6cf9 100644
--- a/src/Types/AbstractPropertyObject.php
+++ b/src/Types/AbstractPropertyObject.php
@@ -15,11 +15,12 @@
use BadMethodCallException;
use Laudis\Neo4j\Contracts\HasPropertiesInterface;
+use Laudis\Neo4j\Formatter\SummarizedResultFormatter;
use function sprintf;
/**
- * @psalm-import-type OGMTypes from \Laudis\Neo4j\Formatter\OGMFormatter
+ * @psalm-import-type OGMTypes from SummarizedResultFormatter
*
* @template PropertyTypes
* @template ObjectTypes
diff --git a/src/Types/ArrayList.php b/src/Types/ArrayList.php
deleted file mode 100644
index b41fe92f..00000000
--- a/src/Types/ArrayList.php
+++ /dev/null
@@ -1,255 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Laudis\Neo4j\Types;
-
-use AppendIterator;
-use ArrayIterator;
-use Generator;
-
-use function is_array;
-use function is_callable;
-use function is_iterable;
-
-use Laudis\Neo4j\Exception\RuntimeTypeException;
-use Laudis\Neo4j\TypeCaster;
-use OutOfBoundsException;
-
-/**
- * An immutable ordered sequence of items.
- *
- * @template TValue
- *
- * @extends AbstractCypherSequence
- */
-class ArrayList extends AbstractCypherSequence
-{
- /**
- * @param iterable|callable():Generator $iterable
- *
- * @psalm-mutation-free
- */
- public function __construct($iterable = [])
- {
- if (is_array($iterable)) {
- $iterable = new ArrayIterator($iterable);
- }
-
- $this->generator = static function () use ($iterable): Generator {
- $i = 0;
- /** @var Generator $it */
- $it = is_callable($iterable) ? $iterable() : $iterable;
- foreach ($it as $value) {
- yield $i => $value;
- ++$i;
- }
- };
- }
-
- /**
- * @template Value
- *
- * @param callable():(\Generator) $operation
- *
- * @return static
- *
- * @psalm-mutation-free
- */
- protected function withOperation($operation): AbstractCypherSequence
- {
- /** @psalm-suppress UnsafeInstantiation */
- return new static($operation);
- }
-
- /**
- * Returns the first element in the sequence.
- *
- * @return TValue
- */
- public function first()
- {
- foreach ($this as $value) {
- return $value;
- }
-
- throw new OutOfBoundsException('Cannot grab first element of an empty list');
- }
-
- /**
- * Returns the last element in the sequence.
- *
- * @return TValue
- */
- public function last()
- {
- if ($this->isEmpty()) {
- throw new OutOfBoundsException('Cannot grab last element of an empty list');
- }
-
- $array = $this->toArray();
-
- return $array[count($array) - 1];
- }
-
- /**
- * @template NewValue
- *
- * @param iterable $values
- *
- * @psalm-suppress LessSpecificImplementedReturnType
- * @psalm-suppress ImplementedReturnTypeMismatch
- *
- * @return static
- *
- * @psalm-mutation-free
- */
- public function merge($values): ArrayList
- {
- return $this->withOperation(function () use ($values): Generator {
- $iterator = new AppendIterator();
-
- $iterator->append($this);
- $iterator->append(new self($values));
-
- yield from $iterator;
- });
- }
-
- /**
- * Gets the nth element in the list.
- *
- * @throws OutOfBoundsException
- *
- * @return TValue
- */
- public function get(int $key)
- {
- return $this->offsetGet($key);
- }
-
- public function getAsString(int $key): string
- {
- $value = $this->get($key);
- $tbr = TypeCaster::toString($value);
- if ($tbr === null) {
- throw new RuntimeTypeException($value, 'string');
- }
-
- return $tbr;
- }
-
- public function getAsInt(int $key): int
- {
- $value = $this->get($key);
- $tbr = TypeCaster::toInt($value);
- if ($tbr === null) {
- throw new RuntimeTypeException($value, 'int');
- }
-
- return $tbr;
- }
-
- public function getAsFloat(int $key): float
- {
- $value = $this->get($key);
- $tbr = TypeCaster::toFloat($value);
- if ($tbr === null) {
- throw new RuntimeTypeException($value, 'float');
- }
-
- return $tbr;
- }
-
- public function getAsBool(int $key): bool
- {
- $value = $this->get($key);
- $tbr = TypeCaster::toBool($value);
- if ($tbr === null) {
- throw new RuntimeTypeException($value, 'bool');
- }
-
- return $tbr;
- }
-
- /**
- * @return null
- */
- public function getAsNull(int $key)
- {
- /** @psalm-suppress UnusedMethodCall */
- $this->get($key);
-
- return TypeCaster::toNull();
- }
-
- /**
- * @template U
- *
- * @param class-string $class
- *
- * @return U
- */
- public function getAsObject(int $key, string $class): object
- {
- $value = $this->get($key);
- $tbr = TypeCaster::toClass($value, $class);
- if ($tbr === null) {
- throw new RuntimeTypeException($value, $class);
- }
-
- return $tbr;
- }
-
- /**
- * @return Map
- */
- public function getAsMap(int $key): Map
- {
- $value = $this->get($key);
- if (!is_iterable($value)) {
- throw new RuntimeTypeException($value, Map::class);
- }
-
- /** @psalm-suppress MixedArgumentTypeCoercion */
- return new Map($value);
- }
-
- /**
- * @return ArrayList
- */
- public function getAsArrayList(int $key): ArrayList
- {
- $value = $this->get($key);
- if (!is_iterable($value)) {
- throw new RuntimeTypeException($value, self::class);
- }
-
- /** @psalm-suppress MixedArgumentTypeCoercion */
- return new ArrayList($value);
- }
-
- /**
- * @template Value
- *
- * @param iterable $iterable
- *
- * @return static
- *
- * @pure
- */
- public static function fromIterable(iterable $iterable): ArrayList
- {
- /** @psalm-suppress UnsafeInstantiation */
- return new static($iterable);
- }
-}
diff --git a/src/Types/CypherList.php b/src/Types/CypherList.php
index 29864c7f..4ea30b35 100644
--- a/src/Types/CypherList.php
+++ b/src/Types/CypherList.php
@@ -13,18 +13,238 @@
namespace Laudis\Neo4j\Types;
+use AppendIterator;
+use ArrayAccess;
+use ArrayIterator;
+use Generator;
+
+use function is_array;
+use function is_callable;
+
+use Iterator;
+use Laudis\Neo4j\Contracts\CypherSequence;
use Laudis\Neo4j\Exception\RuntimeTypeException;
use Laudis\Neo4j\TypeCaster;
+use OutOfBoundsException;
/**
* An immutable ordered sequence of items.
*
* @template TValue
*
- * @extends ArrayList
+ * @implements CypherSequence
+ * @implements Iterator
+ * @implements ArrayAccess
*/
-class CypherList extends ArrayList
+class CypherList implements CypherSequence, Iterator, ArrayAccess
{
+ /**
+ * @use CypherSequenceTrait
+ */
+ use CypherSequenceTrait;
+
+ /**
+ * @param iterable|callable():Generator $iterable
+ *
+ * @psalm-mutation-free
+ */
+ public function __construct(iterable|callable $iterable = [])
+ {
+ if (is_array($iterable)) {
+ $iterable = new ArrayIterator($iterable);
+ }
+
+ $this->generator = static function () use ($iterable): Generator {
+ $i = 0;
+ /** @var Generator $it */
+ $it = is_callable($iterable) ? $iterable() : $iterable;
+ foreach ($it as $value) {
+ yield $i => $value;
+ ++$i;
+ }
+ };
+ }
+
+ /**
+ * @template Value
+ *
+ * @param callable():(Generator) $operation
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ protected function withOperation(callable $operation): self
+ {
+ return new self($operation);
+ }
+
+ /**
+ * Returns the first element in the sequence.
+ *
+ * @return TValue
+ */
+ public function first()
+ {
+ foreach ($this as $value) {
+ return $value;
+ }
+
+ throw new OutOfBoundsException('Cannot grab first element of an empty list');
+ }
+
+ /**
+ * Returns the last element in the sequence.
+ *
+ * @return TValue
+ */
+ public function last()
+ {
+ if ($this->isEmpty()) {
+ throw new OutOfBoundsException('Cannot grab last element of an empty list');
+ }
+
+ $array = $this->toArray();
+
+ return $array[count($array) - 1];
+ }
+
+ /**
+ * @template NewValue
+ *
+ * @param iterable $values
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ public function merge(iterable $values): self
+ {
+ return $this->withOperation(function () use ($values): Generator {
+ $iterator = new AppendIterator();
+
+ $iterator->append($this);
+ $iterator->append(new self($values));
+
+ yield from $iterator;
+ });
+ }
+
+ /**
+ * Gets the nth element in the list.
+ *
+ * @throws OutOfBoundsException
+ *
+ * @return TValue
+ */
+ public function get(int $key)
+ {
+ return $this->offsetGet($key);
+ }
+
+ public function getAsString(int $key): string
+ {
+ $value = $this->get($key);
+ $tbr = TypeCaster::toString($value);
+ if ($tbr === null) {
+ throw new RuntimeTypeException($value, 'string');
+ }
+
+ return $tbr;
+ }
+
+ public function getAsInt(int $key): int
+ {
+ $value = $this->get($key);
+ $tbr = TypeCaster::toInt($value);
+ if ($tbr === null) {
+ throw new RuntimeTypeException($value, 'int');
+ }
+
+ return $tbr;
+ }
+
+ public function getAsFloat(int $key): float
+ {
+ $value = $this->get($key);
+ $tbr = TypeCaster::toFloat($value);
+ if ($tbr === null) {
+ throw new RuntimeTypeException($value, 'float');
+ }
+
+ return $tbr;
+ }
+
+ public function getAsBool(int $key): bool
+ {
+ $value = $this->get($key);
+ $tbr = TypeCaster::toBool($value);
+ if ($tbr === null) {
+ throw new RuntimeTypeException($value, 'bool');
+ }
+
+ return $tbr;
+ }
+
+ /**
+ * @return null
+ */
+ public function getAsNull(int $key)
+ {
+ /** @psalm-suppress UnusedMethodCall */
+ $this->get($key);
+
+ return TypeCaster::toNull();
+ }
+
+ /**
+ * @template U
+ *
+ * @param class-string $class
+ *
+ * @return U
+ */
+ public function getAsObject(int $key, string $class): object
+ {
+ $value = $this->get($key);
+ $tbr = TypeCaster::toClass($value, $class);
+ if ($tbr === null) {
+ throw new RuntimeTypeException($value, $class);
+ }
+
+ return $tbr;
+ }
+
+ /**
+ * @return CypherMap
+ */
+ public function getAsMap(int $key): CypherMap
+ {
+ return $this->getAsCypherMap($key);
+ }
+
+ /**
+ * @return CypherList
+ */
+ public function getAsList(int $key): CypherList
+ {
+ return $this->getAsCypherList($key);
+ }
+
+ /**
+ * @template Value
+ *
+ * @param iterable $iterable
+ *
+ * @return self
+ *
+ * @pure
+ */
+ public static function fromIterable(iterable $iterable): self
+ {
+ return new self($iterable);
+ }
+
/**
* @return CypherMap
*/
@@ -117,4 +337,92 @@ public function getAsWGS843DPoint(int $key): WGS843DPoint
{
return $this->getAsObject($key, WGS843DPoint::class);
}
+
+ public function key(): int
+ {
+ /** @var int */
+ return $this->cacheKey();
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray(): array
+ {
+ $this->preload();
+
+ /** @var array */
+ return $this->cache;
+ }
+
+ /**
+ * @param callable(TValue, int):bool $callback
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ final public function filter(callable $callback): self
+ {
+ return $this->withOperation(function () use ($callback) {
+ foreach ($this as $key => $value) {
+ if ($callback($value, $key)) {
+ yield $key => $value;
+ }
+ }
+ });
+ }
+
+ /**
+ * @template ReturnType
+ *
+ * @param callable(TValue, int):ReturnType $callback
+ *
+ * @return self
+ *
+ * @psalm-suppress ImplementedReturnTypeMismatch
+ *
+ * @psalm-mutation-free
+ */
+ final public function map(callable $callback): self
+ {
+ return $this->withOperation(function () use ($callback) {
+ foreach ($this as $key => $value) {
+ yield $key => $callback($value, $key);
+ }
+ });
+ }
+
+ /**
+ * @template TInitial
+ *
+ * @param TInitial|null $initial
+ * @param callable(TInitial|null, TValue, int):TInitial $callback
+ *
+ * @return TInitial
+ */
+ final public function reduce(callable $callback, mixed $initial = null): mixed
+ {
+ foreach ($this as $key => $value) {
+ $initial = $callback($initial, $value, $key);
+ }
+
+ return $initial;
+ }
+
+ /**
+ * Iterates over the sequence and applies the callable.
+ *
+ * @param callable(TValue, int):void $callable
+ *
+ * @return self
+ */
+ public function each(callable $callable): self
+ {
+ foreach ($this as $key => $value) {
+ $callable($value, $key);
+ }
+
+ return $this;
+ }
}
diff --git a/src/Types/CypherMap.php b/src/Types/CypherMap.php
index 2a78832d..270354df 100644
--- a/src/Types/CypherMap.php
+++ b/src/Types/CypherMap.php
@@ -13,202 +13,658 @@
namespace Laudis\Neo4j\Types;
+use ArrayAccess;
+use ArrayIterator;
+
use function func_num_args;
+use Generator;
+use Iterator;
+use Laudis\Neo4j\Contracts\CypherSequence;
+use Laudis\Neo4j\Databags\Pair;
use Laudis\Neo4j\Exception\RuntimeTypeException;
use Laudis\Neo4j\TypeCaster;
+use OutOfBoundsException;
+use stdClass;
/**
* An immutable ordered map of items.
*
* @template TValue
*
- * @extends Map
+ * @implements CypherSequence
+ * @implements ArrayAccess
+ * @implements Iterator
*/
-final class CypherMap extends Map
+final class CypherMap implements CypherSequence, ArrayAccess, Iterator
{
/**
- * @return CypherMap
+ * @use CypherSequenceTrait
+ */
+ use CypherSequenceTrait {
+ jsonSerialize as jsonSerializeTrait;
+ }
+
+ /**
+ * @param iterable|callable():Generator $iterable
+ *
+ * @psalm-mutation-free
*/
- public function getAsCypherMap(string $key, mixed $default = null): CypherMap
+ public function __construct(iterable|callable $iterable = [])
{
- if (func_num_args() === 1) {
- $value = $this->get($key);
+ if (is_array($iterable)) {
+ $i = 0;
+ foreach ($iterable as $key => $value) {
+ if (!$this->isStringable($key)) {
+ $key = (string) $i;
+ }
+ /** @var string $key */
+ $this->keyCache[] = $key;
+ /** @var TValue $value */
+ $this->cache[$key] = $value;
+ ++$i;
+ }
+ /** @var ArrayIterator */
+ $it = new ArrayIterator([]);
+ $this->generator = $it;
+ $this->generatorPosition = count($this->keyCache);
} else {
- /** @var mixed */
- $value = $this->get($key, $default);
+ $this->generator = function () use ($iterable): Generator {
+ $i = 0;
+ /** @var Generator $it */
+ $it = is_callable($iterable) ? $iterable() : $iterable;
+ /** @var mixed $key */
+ foreach ($it as $key => $value) {
+ if ($this->isStringable($key)) {
+ yield (string) $key => $value;
+ } else {
+ yield (string) $i => $value;
+ }
+ ++$i;
+ }
+ };
}
- $tbr = TypeCaster::toCypherMap($value);
- if ($tbr === null) {
- throw new RuntimeTypeException($value, self::class);
+ }
+
+ /**
+ * @template Value
+ *
+ * @param callable():(Generator) $operation
+ *
+ * @return self
+ *
+ * @psalm-mutation-free
+ */
+ protected function withOperation(callable $operation): CypherMap
+ {
+ return new self($operation);
+ }
+
+ /**
+ * Returns the first pair in the map.
+ *
+ * @return Pair
+ */
+ public function first(): Pair
+ {
+ foreach ($this as $key => $value) {
+ return new Pair($key, $value);
}
+ throw new OutOfBoundsException('Cannot grab first element of an empty map');
+ }
- return $tbr;
+ /**
+ * Returns the last pair in the map.
+ *
+ * @return Pair
+ */
+ public function last(): Pair
+ {
+ $array = $this->toArray();
+ if (count($array) === 0) {
+ throw new OutOfBoundsException('Cannot grab last element of an empty map');
+ }
+
+ $key = array_key_last($array);
+
+ return new Pair($key, $array[$key]);
}
/**
- * @return CypherList
+ * Returns the pair at the nth position of the map.
+ *
+ * @return Pair
+ */
+ public function skip(int $position): Pair
+ {
+ $i = 0;
+ foreach ($this as $key => $value) {
+ if ($i === $position) {
+ return new Pair($key, $value);
+ }
+ ++$i;
+ }
+
+ throw new OutOfBoundsException(sprintf('Cannot skip to a pair at position: %s', $position));
+ }
+
+ /**
+ * Returns the keys in the map in order.
+ *
+ * @return CypherList
+ *
+ * @psalm-suppress UnusedForeachValue
+ */
+ public function keys(): CypherList
+ {
+ return CypherList::fromIterable((function () {
+ foreach ($this as $key => $value) {
+ yield $key;
+ }
+ })());
+ }
+
+ /**
+ * Returns the pairs in the map in order.
+ *
+ * @return CypherList>
+ */
+ public function pairs(): CypherList
+ {
+ return CypherList::fromIterable((function () {
+ foreach ($this as $key => $value) {
+ yield new Pair($key, $value);
+ }
+ })());
+ }
+
+ /**
+ * Create a new map sorted by keys. Natural ordering will be used if no comparator is provided.
+ *
+ * @param (callable(string, string):int)|null $comparator
+ *
+ * @return self
*/
- public function getAsCypherList(string $key, mixed $default = null): CypherList
+ public function ksorted(?callable $comparator = null): CypherMap
+ {
+ return $this->withOperation(function () use ($comparator) {
+ $pairs = $this->pairs()->sorted(static function (Pair $x, Pair $y) use ($comparator) {
+ if ($comparator !== null) {
+ return $comparator($x->getKey(), $y->getKey());
+ }
+
+ return $x->getKey() <=> $y->getKey();
+ });
+
+ foreach ($pairs as $pair) {
+ yield $pair->getKey() => $pair->getValue();
+ }
+ });
+ }
+
+ /**
+ * Returns the values in the map in order.
+ *
+ * @return CypherList
+ */
+ public function values(): CypherList
+ {
+ return CypherList::fromIterable((function () {
+ yield from $this;
+ })());
+ }
+
+ /**
+ * Creates a new map using exclusive or on the keys.
+ *
+ * @param iterable $map
+ *
+ * @return self