From 00d950e86a54d008875598297270a1b84e6dab05 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Fri, 24 Jan 2025 14:32:08 +0530 Subject: [PATCH 1/2] WIP --- README.md | 15 +- src/Basic/UnmanagedTransaction.php | 9 +- src/Bolt/BoltConnection.php | 4 +- src/Bolt/BoltUnmanagedTransaction.php | 9 +- src/Bolt/Session.php | 16 +- src/Common/DriverSetupManager.php | 8 +- src/Contracts/ClientInterface.php | 26 +- src/Contracts/FormatterInterface.php | 117 ----- src/Contracts/SessionInterface.php | 24 +- src/Contracts/TransactionInterface.php | 14 +- .../UnmanagedTransactionInterface.php | 6 +- src/Databags/SummarizedResult.php | 12 +- src/DriverFactory.php | 4 +- src/Formatter/OGMFormatter.php | 175 ------- .../Specialised/BoltOGMTranslator.php | 4 +- src/Formatter/SummarizedResultFormatter.php | 195 ++++---- src/Http/HttpHelper.php | 12 +- src/Http/HttpUnmanagedTransaction.php | 11 +- src/Neo4j/Neo4jDriver.php | 15 +- src/Types/AbstractPropertyObject.php | 3 +- src/Types/CypherMap.php | 4 +- src/Types/Node.php | 3 +- src/Types/Relationship.php | 4 +- src/Types/UnboundRelationship.php | 3 +- .../Integration/BoltDriverIntegrationTest.php | 3 - tests/Integration/ClientIntegrationTest.php | 9 +- tests/Integration/ComplexQueryTest.php | 5 +- .../OGMFormatterIntegrationTest.php | 451 ------------------ 28 files changed, 169 insertions(+), 992 deletions(-) delete mode 100644 src/Contracts/FormatterInterface.php delete mode 100644 src/Formatter/OGMFormatter.php delete mode 100644 tests/Integration/OGMFormatterIntegrationTest.php diff --git a/README.md b/README.md index 020a749c..48f55aed 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 a 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/src/Basic/UnmanagedTransaction.php b/src/Basic/UnmanagedTransaction.php index 233e582d..beef7e1e 100644 --- a/src/Basic/UnmanagedTransaction.php +++ b/src/Basic/UnmanagedTransaction.php @@ -33,17 +33,12 @@ public function __construct( /** * @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 +47,7 @@ public function runStatement(Statement $statement): SummarizedResult /** * @param iterable $statements * - * @return CypherList> + * @return CypherList */ public function runStatements(iterable $statements): CypherList { @@ -62,7 +57,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..40710426 100644 --- a/src/Bolt/BoltConnection.php +++ b/src/Bolt/BoltConnection.php @@ -27,13 +27,13 @@ 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; @@ -42,7 +42,7 @@ /** * @implements ConnectionInterface * - * @psalm-import-type BoltMeta from FormatterInterface + * @psalm-import-type BoltMeta from SummarizedResultFormatter */ class BoltConnection implements ConnectionInterface { diff --git a/src/Bolt/BoltUnmanagedTransaction.php b/src/Bolt/BoltUnmanagedTransaction.php index 9a547551..d8254348 100644 --- a/src/Bolt/BoltUnmanagedTransaction.php +++ b/src/Bolt/BoltUnmanagedTransaction.php @@ -14,7 +14,6 @@ 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; @@ -22,6 +21,7 @@ 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; @@ -37,22 +37,19 @@ * * @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, diff --git a/src/Bolt/Session.php b/src/Bolt/Session.php index 8d895d59..f8042597 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,7 +54,7 @@ public function __construct( /** * @psalm-readonly */ - private readonly FormatterInterface $formatter + private readonly SummarizedResultFormatter $formatter ) { $this->bookmarkHolder = new BookmarkHolder(Bookmark::from($config->getBookmarks())); } @@ -84,12 +80,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); } @@ -133,7 +129,7 @@ public function beginTransaction(?iterable $statements = null, ?TransactionConfi } /** - * @return UnmanagedTransactionInterface + * @return UnmanagedTransactionInterface */ private function beginInstantTransaction( SessionConfiguration $config, diff --git a/src/Common/DriverSetupManager.php b/src/Common/DriverSetupManager.php index bb2639d2..78274b17 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; @@ -51,11 +51,9 @@ class DriverSetupManager implements Countable /** * @psalm-mutation-free - * - * @param FormatterInterface $formatter */ public function __construct( - private FormatterInterface $formatter, + private SummarizedResultFormatter $formatter, private DriverConfiguration $configuration ) {} @@ -193,7 +191,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/Contracts/ClientInterface.php b/src/Contracts/ClientInterface.php index 5a5f1d4c..e07a7ec4 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/FormatterInterface.php b/src/Contracts/FormatterInterface.php deleted file mode 100644 index ded59d2e..00000000 --- a/src/Contracts/FormatterInterface.php +++ /dev/null @@ -1,117 +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 JsonException; -use Laudis\Neo4j\Bolt\BoltConnection; -use Laudis\Neo4j\Bolt\BoltResult; -use Laudis\Neo4j\Databags\BookmarkHolder; -use Laudis\Neo4j\Databags\Statement; -use Laudis\Neo4j\Http\HttpConnection; -use Laudis\Neo4j\Types\CypherList; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; -use stdClass; - -/** - * 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); - - /** - * Formats the results of the HTTP protocol to the unified format. - * - * @param iterable $statements - * - * @throws JsonException - * - * @return CypherList - * - * @psalm-mutation-free - */ - public function formatHttpResult(ResponseInterface $response, stdClass $body, HttpConnection $connection, float $resultsAvailableAfter, float $resultsConsumedAfter, iterable $statements): CypherList; - - /** - * Decorates a request to make make sure it requests the correct format. - * - * @see https://neo4j.com/docs/http-api/current/actions/result-format/ - * - * @psalm-mutation-free - */ - public function decorateRequest(RequestInterface $request, ConnectionInterface $connection): RequestInterface; - - /** - * Overrides the statement config of the HTTP protocol. - * - * @see https://neo4j.com/docs/http-api/current/actions/result-format/ - * - * @return array{resultDataContents?: list<'GRAPH'|'ROW'|'REST'>, includeStats?:bool} - * - * @psalm-mutation-free - */ - public function statementConfigOverride(ConnectionInterface $connection): array; -} diff --git a/src/Contracts/SessionInterface.php b/src/Contracts/SessionInterface.php index 57eeeb5b..b7b39e61 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..329af20d 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..09dabaa7 100644 --- a/src/Contracts/UnmanagedTransactionInterface.php +++ b/src/Contracts/UnmanagedTransactionInterface.php @@ -19,10 +19,6 @@ /** * 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 +28,7 @@ interface UnmanagedTransactionInterface extends TransactionInterface * * @param iterable $statements * - * @return CypherList + * @return CypherList */ public function commit(iterable $statements = []): CypherList; diff --git a/src/Databags/SummarizedResult.php b/src/Databags/SummarizedResult.php index e72249a2..846966eb 100644 --- a/src/Databags/SummarizedResult.php +++ b/src/Databags/SummarizedResult.php @@ -20,20 +20,18 @@ /** * A result containing the values and the summary. * - * @template TValue - * - * @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; @@ -42,9 +40,9 @@ public function __construct(?ResultSummary &$summary, $iterable = []) /** * @template Value * - * @param callable():(\Generator) $operation + * @param callable():(Generator) $operation * - * @return static + * @return static * * @psalm-mutation-free */ diff --git a/src/DriverFactory.php b/src/DriverFactory.php index 85d7f3b4..e5d04c32 100644 --- a/src/DriverFactory.php +++ b/src/DriverFactory.php @@ -22,7 +22,7 @@ use Laudis\Neo4j\Contracts\DriverInterface; use Laudis\Neo4j\Contracts\FormatterInterface; use Laudis\Neo4j\Databags\DriverConfiguration; -use Laudis\Neo4j\Formatter\OGMFormatter; +use Laudis\Neo4j\Formatter\SummarizedResultFormatter; use Laudis\Neo4j\Http\HttpDriver; use Laudis\Neo4j\Neo4j\Neo4jDriver; use Psr\Http\Message\UriInterface; @@ -30,7 +30,7 @@ /** * Factory for creating drivers directly. * - * @psalm-import-type OGMResults from OGMFormatter + * @psalm-import-type OGMResults from SummarizedResultFormatter */ final class DriverFactory { diff --git a/src/Formatter/OGMFormatter.php b/src/Formatter/OGMFormatter.php deleted file mode 100644 index b2a03e08..00000000 --- a/src/Formatter/OGMFormatter.php +++ /dev/null @@ -1,175 +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\ConnectionInterface; -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\Formatter\Specialised\JoltHttpOGMTranslator; -use Laudis\Neo4j\Formatter\Specialised\LegacyHttpOGMTranslator; -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 Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; -use stdClass; - -use function version_compare; - -/** - * 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, - private readonly JoltHttpOGMTranslator $joltTranslator, - private readonly LegacyHttpOGMTranslator $legacyHttpTranslator - ) {} - - /** - * Creates a new instance of itself. - * - * @pure - */ - public static function create(): OGMFormatter - { - return new self(new BoltOGMTranslator(), new JoltHttpOGMTranslator(), new LegacyHttpOGMTranslator()); - } - - /** - * @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; - } - - /** - * @psalm-mutation-free - */ - public function formatHttpResult( - ResponseInterface $response, - stdClass $body, - ConnectionInterface $connection, - float $resultsAvailableAfter, - float $resultsConsumedAfter, - iterable $statements - ): CypherList { - return $this->decideTranslator($connection)->formatHttpResult( - $response, - $body, - $connection, - $resultsAvailableAfter, - $resultsConsumedAfter, - $statements - ); - } - - /** - * @psalm-mutation-free - */ - private function decideTranslator(ConnectionInterface $connection): LegacyHttpOGMTranslator|JoltHttpOGMTranslator - { - if (version_compare($connection->getServerAgent(), '4.2.5') <= 0) { - return $this->legacyHttpTranslator; - } - - return $this->joltTranslator; - } - - /** - * @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); - } - - /** - * @psalm-mutation-free - */ - public function decorateRequest(RequestInterface $request, ConnectionInterface $connection): RequestInterface - { - return $this->decideTranslator($connection)->decorateRequest($request); - } - - /** - * @psalm-mutation-free - */ - public function statementConfigOverride(ConnectionInterface $connection): array - { - return $this->decideTranslator($connection)->statementConfigOverride(); - } -} diff --git a/src/Formatter/Specialised/BoltOGMTranslator.php b/src/Formatter/Specialised/BoltOGMTranslator.php index bdeb5ecd..43213190 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 * diff --git a/src/Formatter/SummarizedResultFormatter.php b/src/Formatter/SummarizedResultFormatter.php index c8864f76..42294913 100644 --- a/src/Formatter/SummarizedResultFormatter.php +++ b/src/Formatter/SummarizedResultFormatter.php @@ -18,8 +18,7 @@ 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\DatabaseInfo; use Laudis\Neo4j\Databags\ResultSummary; @@ -28,106 +27,87 @@ use Laudis\Neo4j\Databags\SummarizedResult; use Laudis\Neo4j\Databags\SummaryCounters; use Laudis\Neo4j\Enum\QueryTypeEnum; -use Laudis\Neo4j\Http\HttpConnection; +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; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; -use stdClass; -use UnexpectedValueException; - /** * 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 CypherList> $results - * - * @return SummarizedResult> - * - * @psalm-mutation-free - */ - public function formatHttpStats(stdClass $response, HttpConnection $connection, Statement $statement, float $resultAvailableAfter, float $resultConsumedAfter, CypherList $results): SummarizedResult - { - if (isset($response->summary) && $response->summary instanceof stdClass) { - /** @var stdClass $stats */ - $stats = $response->summary->stats; - } elseif (isset($response->stats)) { - /** @var stdClass $stats */ - $stats = $response->stats; - } else { - throw new UnexpectedValueException('No stats found in the response set'); - } - - /** - * @psalm-suppress MixedPropertyFetch - * @psalm-suppress MixedArgument - */ - $counters = new SummaryCounters( - $stats->nodes_created ?? 0, - $stats->nodes_deleted ?? 0, - $stats->relationships_created ?? 0, - $stats->relationships_deleted ?? 0, - $stats->properties_set ?? 0, - $stats->labels_added ?? 0, - $stats->labels_removed ?? 0, - $stats->indexes_added ?? 0, - $stats->indexes_removed ?? 0, - $stats->constraints_added ?? 0, - $stats->constraints_removed ?? 0, - $stats->contains_updates ?? false, - $stats->contains_system_updates ?? false, - $stats->system_updates ?? 0, - ); - - $summary = new ResultSummary( - $counters, - $connection->getDatabaseInfo(), - new CypherList(), - null, - null, - $statement, - QueryTypeEnum::fromCounters($counters), - $resultAvailableAfter, - $resultConsumedAfter, - new ServerInfo( - $connection->getServerAddress(), - $connection->getProtocol(), - $connection->getServerAgent() - ) - ); - - /** @var SummarizedResult> */ - return new SummarizedResult($summary, $results); - } - /** * @param array{stats?: BoltCypherStats}&array $response * @@ -170,7 +150,6 @@ public function formatBoltResult(array $meta, BoltResult $result, BoltConnection /** @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'] ?? ''; @@ -192,7 +171,7 @@ public function formatBoltResult(array $meta, BoltResult $result, BoltConnection ); }); - $formattedResult = $this->formatter->formatBoltResult($meta, $result, $connection, $runStart, $resultAvailableAfter, $statement, $holder); + $formattedResult = $this->processBoltResult($meta, $result, $connection, $runStart, $resultAvailableAfter, $statement, $holder); /** * @psalm-suppress MixedArgument @@ -203,43 +182,43 @@ public function formatBoltResult(array $meta, BoltResult $result, BoltConnection } /** - * @psalm-mutation-free + * @param BoltMeta $meta * - * @psalm-suppress ImpureMethodCall + * @return CypherList> */ - public function formatHttpResult(ResponseInterface $response, stdClass $body, HttpConnection $connection, float $resultsAvailableAfter, float $resultsConsumedAfter, iterable $statements): CypherList + private function processBoltResult(array $meta, BoltResult $result, BoltConnection $connection, float $runStart, float $resultAvailableAfter, Statement $statement, BookmarkHolder $holder): CypherList { - /** @var list>> */ - $tbr = []; - - $toDecorate = $this->formatter->formatHttpResult($response, $body, $connection, $resultsAvailableAfter, $resultsConsumedAfter, $statements); - $i = 0; - foreach ($statements as $statement) { - /** @var list $results */ - $results = $body->results; - $result = $results[$i]; - $tbr[] = $this->formatHttpStats($result, $connection, $statement, $resultsAvailableAfter, $resultsConsumedAfter, $toDecorate->get($i)); - ++$i; - } + $tbr = (new CypherList(function () use ($result, $meta) { + foreach ($result as $row) { + yield $this->formatRow($meta, $row); + } + }))->withCacheLimit($result->getFetchSize()); - return new CypherList($tbr); - } + $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']])); + } + }); - /** - * @psalm-mutation-free - */ - public function decorateRequest(RequestInterface $request, ConnectionInterface $connection): RequestInterface - { - return $this->formatter->decorateRequest($request, $connection); + return $tbr; } /** * @psalm-mutation-free */ - public function statementConfigOverride(ConnectionInterface $connection): array + private function formatRow(array $meta, array $result): CypherMap { - return array_merge($this->formatter->statementConfigOverride($connection), [ - 'includeStats' => true, - ]); + /** @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/Http/HttpHelper.php b/src/Http/HttpHelper.php index 822e6880..9f059be7 100644 --- a/src/Http/HttpHelper.php +++ b/src/Http/HttpHelper.php @@ -13,6 +13,7 @@ namespace Laudis\Neo4j\Http; +use Laudis\Neo4j\Formatter\SummarizedResultFormatter; use function array_key_first; use function array_merge; use function count; @@ -23,7 +24,6 @@ use JsonException; use Laudis\Neo4j\Contracts\ConnectionInterface; -use Laudis\Neo4j\Contracts\FormatterInterface; use Laudis\Neo4j\Databags\Neo4jError; use Laudis\Neo4j\Databags\Statement; use Laudis\Neo4j\Exception\Neo4jException; @@ -36,7 +36,7 @@ /** * Helper functions for the http protocol. * - * @psalm-import-type CypherResponseSet from \Laudis\Neo4j\Contracts\FormatterInterface + * @psalm-import-type CypherResponseSet from SummarizedResultFormatter */ final class HttpHelper { @@ -198,7 +198,7 @@ public static function splitJoltSingleton(stdClass $joltSingleton): array * * @throws JsonException */ - public static function statementsToJson(ConnectionInterface $connection, FormatterInterface $formatter, iterable $statements): string + public static function statementsToJson(ConnectionInterface $connection, SummarizedResultFormatter $formatter, iterable $statements): string { $tbr = []; foreach ($statements as $statement) { @@ -207,7 +207,11 @@ public static function statementsToJson(ConnectionInterface $connection, Formatt 'resultDataContents' => [], 'includeStats' => false, ]; - $st = array_merge($st, $formatter->statementConfigOverride($connection)); + /** @noinspection PhpUndefinedMethodInspection */ + $st = array_merge( + $st, + $formatter->statementConfigOverride($connection) + ); $parameters = ParameterHelper::formatParameters($statement->getParameters(), $connection->getProtocol()); $st['parameters'] = $parameters->count() === 0 ? new stdClass() : $parameters->toArray(); $tbr[] = $st; diff --git a/src/Http/HttpUnmanagedTransaction.php b/src/Http/HttpUnmanagedTransaction.php index 7de93457..48c0a3a9 100644 --- a/src/Http/HttpUnmanagedTransaction.php +++ b/src/Http/HttpUnmanagedTransaction.php @@ -13,11 +13,12 @@ namespace Laudis\Neo4j\Http; +use Laudis\Neo4j\Databags\SummarizedResult; +use Laudis\Neo4j\Formatter\SummarizedResultFormatter; use function array_intersect; use function array_unique; use Laudis\Neo4j\Common\TransactionHelper; -use Laudis\Neo4j\Contracts\FormatterInterface; use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface; use Laudis\Neo4j\Databags\Neo4jError; use Laudis\Neo4j\Databags\Statement; @@ -44,8 +45,6 @@ final class HttpUnmanagedTransaction implements UnmanagedTransactionInterface /** * @psalm-mutation-free - * - * @param FormatterInterface $formatter */ public function __construct( /** @psalm-readonly */ @@ -57,15 +56,15 @@ public function __construct( /** * @psalm-readonly */ - private readonly FormatterInterface $formatter + private readonly SummarizedResultFormatter $formatter ) {} - public function run(string $statement, iterable $parameters = []) + public function run(string $statement, iterable $parameters = []): SummarizedResult { return $this->runStatement(new Statement($statement, $parameters)); } - public function runStatement(Statement $statement) + public function runStatement(Statement $statement): SummarizedResult { return $this->runStatements([$statement])->first(); } diff --git a/src/Neo4j/Neo4jDriver.php b/src/Neo4j/Neo4jDriver.php index 004be4d7..321092fe 100644 --- a/src/Neo4j/Neo4jDriver.php +++ b/src/Neo4j/Neo4jDriver.php @@ -26,11 +26,10 @@ 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; @@ -41,26 +40,22 @@ * * @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 @@ -69,7 +64,7 @@ public function __construct( * * @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 +79,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/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/CypherMap.php b/src/Types/CypherMap.php index 2a78832d..ebbdc028 100644 --- a/src/Types/CypherMap.php +++ b/src/Types/CypherMap.php @@ -35,7 +35,7 @@ public function getAsCypherMap(string $key, mixed $default = null): CypherMap if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ + /** @var mixed $value */ $value = $this->get($key, $default); } $tbr = TypeCaster::toCypherMap($value); @@ -54,7 +54,7 @@ public function getAsCypherList(string $key, mixed $default = null): CypherList if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ + /** @var mixed $value */ $value = $this->get($key, $default); } $tbr = TypeCaster::toCypherList($value); diff --git a/src/Types/Node.php b/src/Types/Node.php index b747781b..0c660714 100644 --- a/src/Types/Node.php +++ b/src/Types/Node.php @@ -14,13 +14,14 @@ namespace Laudis\Neo4j\Types; use Laudis\Neo4j\Exception\PropertyDoesNotExistException; +use Laudis\Neo4j\Formatter\SummarizedResultFormatter; use function sprintf; /** * A Node class representing a Node in cypher. * - * @psalm-import-type OGMTypes from \Laudis\Neo4j\Formatter\OGMFormatter + * @psalm-import-type OGMTypes from SummarizedResultFormatter * * @psalm-immutable * @psalm-immutable diff --git a/src/Types/Relationship.php b/src/Types/Relationship.php index 280a6679..0528d58d 100644 --- a/src/Types/Relationship.php +++ b/src/Types/Relationship.php @@ -13,10 +13,12 @@ namespace Laudis\Neo4j\Types; +use Laudis\Neo4j\Formatter\SummarizedResultFormatter; + /** * A Relationship class representing a Relationship in cypher. * - * @psalm-import-type OGMTypes from \Laudis\Neo4j\Formatter\OGMFormatter + * @psalm-import-type OGMTypes from SummarizedResultFormatter * * @psalm-immutable */ diff --git a/src/Types/UnboundRelationship.php b/src/Types/UnboundRelationship.php index 6f7a53a4..1148c960 100644 --- a/src/Types/UnboundRelationship.php +++ b/src/Types/UnboundRelationship.php @@ -14,13 +14,14 @@ namespace Laudis\Neo4j\Types; use Laudis\Neo4j\Exception\PropertyDoesNotExistException; +use Laudis\Neo4j\Formatter\SummarizedResultFormatter; use function sprintf; /** * A relationship without any nodes attached to it. * - * @psalm-import-type OGMTypes from \Laudis\Neo4j\Formatter\OGMFormatter + * @psalm-import-type OGMTypes from SummarizedResultFormatter * * @psalm-immutable * diff --git a/tests/Integration/BoltDriverIntegrationTest.php b/tests/Integration/BoltDriverIntegrationTest.php index 2b449cf5..31987e9a 100644 --- a/tests/Integration/BoltDriverIntegrationTest.php +++ b/tests/Integration/BoltDriverIntegrationTest.php @@ -16,7 +16,6 @@ use Bolt\error\ConnectException; use Exception; use Laudis\Neo4j\Bolt\BoltDriver; -use Laudis\Neo4j\Databags\SummarizedResult; use Laudis\Neo4j\Neo4j\Neo4jDriver; use Laudis\Neo4j\Tests\EnvironmentAwareIntegrationTest; use Throwable; @@ -84,7 +83,6 @@ public function testBookmarkUpdates(): void $this->assertTrue($bookmark->isEmpty()); $previousBookmark = $bookmark; - /** @var SummarizedResult $result */ $result = $session->run('MATCH (x) RETURN x'); $result->preload(); @@ -93,7 +91,6 @@ public function testBookmarkUpdates(): void $this->assertNotEquals($previousBookmark->values(), $bookmark->values()); $previousBookmark = $bookmark; - /** @var SummarizedResult $result */ $result = $session->run('CREATE (x:Node)'); $result->preload(); diff --git a/tests/Integration/ClientIntegrationTest.php b/tests/Integration/ClientIntegrationTest.php index 1e1e072a..e47bd4fa 100644 --- a/tests/Integration/ClientIntegrationTest.php +++ b/tests/Integration/ClientIntegrationTest.php @@ -127,19 +127,19 @@ public function testAvailabilityFullImplementation(): void public function testTransactionFunction(): void { $result = $this->getSession()->transaction( - static fn (TransactionInterface $tsx) => $tsx->run('UNWIND [1] AS x RETURN x')->first()->getAsInt('x') + static fn (TransactionInterface $tsx): int => $tsx->run('UNWIND [1] AS x RETURN x')->first()->getAsInt('x') ); self::assertEquals(1, $result); $result = $this->getSession()->readTransaction( - static fn (TransactionInterface $tsx) => $tsx->run('UNWIND [1] AS x RETURN x')->first()->getAsInt('x') + static fn (TransactionInterface $tsx): int => $tsx->run('UNWIND [1] AS x RETURN x')->first()->getAsInt('x') ); self::assertEquals(1, $result); $result = $this->getSession()->writeTransaction( - static fn (TransactionInterface $tsx) => $tsx->run('UNWIND [1] AS x RETURN x')->first()->getAsInt('x') + static fn (TransactionInterface $tsx): int => $tsx->run('UNWIND [1] AS x RETURN x')->first()->getAsInt('x') ); self::assertEquals(1, $result); @@ -346,7 +346,6 @@ public function testRedundantAcquire(): void $driver = $this->getDriver('bolt'); $reflection = new ReflectionClass($driver); $property = $reflection->getProperty('driver'); - $property->setAccessible(true); /** @var DriverInterface $driver */ $driver = $property->getValue($driver); @@ -356,13 +355,11 @@ public function testRedundantAcquire(): void $reflection = new ReflectionClass($driver); $poolProp = $reflection->getProperty('pool'); - $poolProp->setAccessible(true); /** @var ConnectionPool $pool */ $pool = $poolProp->getValue($driver); $reflection = new ReflectionClass($pool); $connectionProp = $reflection->getProperty('activeConnections'); - $connectionProp->setAccessible(true); /** @var array $activeConnections */ $activeConnections = $connectionProp->getValue($pool); diff --git a/tests/Integration/ComplexQueryTest.php b/tests/Integration/ComplexQueryTest.php index efcb3709..a0e812d5 100644 --- a/tests/Integration/ComplexQueryTest.php +++ b/tests/Integration/ComplexQueryTest.php @@ -48,8 +48,7 @@ public function testValidListParameterHelper(): void public function testMergeTransactionFunction(): void { $this->expectException(Neo4jException::class); - $this->getSession()->writeTransaction(static fn (TSX $tsx) => /** @psalm-suppress ALL */ -$tsx->run('MERGE (x {y: "z"}:X) return x')->first() + $this->getSession()->writeTransaction(static fn (TSX $tsx): string => $tsx->run('MERGE (x {y: "z"}:X) return x')->first() ->getAsMap('x') ->getAsString('y')); } @@ -126,7 +125,7 @@ public function testPath(): void self::assertEquals( [['attribute' => 'xy'], ['attribute' => 'yz']], /** @psalm-suppress MissingClosureReturnType */ - $result->getAsCypherList('y')->map(static fn ($r) => /** + $result->getAsCypherList('y')->map(static fn (mixed $r): array => /** * @psalm-suppress MixedMethodCall * * @var array diff --git a/tests/Integration/OGMFormatterIntegrationTest.php b/tests/Integration/OGMFormatterIntegrationTest.php deleted file mode 100644 index 185809b7..00000000 --- a/tests/Integration/OGMFormatterIntegrationTest.php +++ /dev/null @@ -1,451 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Laudis\Neo4j\Tests\Integration; - -use function compact; - -use DateInterval; - -use function json_encode; - -use Laudis\Neo4j\Contracts\PointInterface; -use Laudis\Neo4j\Contracts\TransactionInterface; -use Laudis\Neo4j\Tests\EnvironmentAwareIntegrationTest; -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\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 function range; -use function sprintf; - -/** - * @psalm-suppress MixedArrayAccess - */ -final class OGMFormatterIntegrationTest extends EnvironmentAwareIntegrationTest -{ - public function testNull(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN null as x')); - - self::assertNull($results->first()->get('x')); - } - - public function testList(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN range(5, 15) as list, range(16, 35) as list2')); - - $list = $results->first()->get('list'); - $list2 = $results->first()->get('list2'); - - self::assertInstanceOf(CypherList::class, $list); - self::assertInstanceOf(CypherList::class, $list2); - self::assertEquals(range(5, 15), $list->toArray()); - self::assertEquals(range(16, 35), $list2->toArray()); - self::assertEquals(json_encode(range(5, 15), JSON_THROW_ON_ERROR), json_encode($list, JSON_THROW_ON_ERROR)); - self::assertEquals(json_encode(range(16, 35), JSON_THROW_ON_ERROR), json_encode($list2, JSON_THROW_ON_ERROR)); - } - - public function testMap(): void - { - $map = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN {a: "b", c: "d"} as map')->first()->get('map')); - self::assertInstanceOf(CypherMap::class, $map); - $array = $map->toArray(); - ksort($array); - self::assertEquals(['a' => 'b', 'c' => 'd'], $array); - } - - public function testBoolean(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN true as bool1, false as bool2')); - - self::assertEquals(1, $results->count()); - self::assertIsBool($results->first()->get('bool1')); - self::assertIsBool($results->first()->get('bool2')); - } - - public function testInteger(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run(<<count()); - self::assertEquals(1, $results[0]['x.num']); - self::assertEquals(2, $results[1]['x.num']); - self::assertEquals(3, $results[2]['x.num']); - } - - public function testFloat(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN 0.1 AS float')); - - self::assertIsFloat($results->first()->get('float')); - } - - public function testString(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN "abc" AS string')); - - self::assertIsString($results->first()->get('string')); - } - - public function testDate(): void - { - $results = $this->getSession()->transaction(function (TransactionInterface $tsx) { - $query = $this->articlesQuery(); - $query .= 'RETURN article.datePublished as published_at'; - - return $tsx->run($query); - }); - - self::assertEquals(3, $results->count()); - - $publishedAt = $results[0]['published_at']; - self::assertInstanceOf(Date::class, $publishedAt); - self::assertEquals(18048, $publishedAt->getDays()); - self::assertEquals( - json_encode(['days' => 18048], JSON_THROW_ON_ERROR), - json_encode($publishedAt, JSON_THROW_ON_ERROR)); - self::assertEquals(18048, $publishedAt->days); - - self::assertInstanceOf(Date::class, $results[1]['published_at']); - self::assertEquals(18049, $results[1]['published_at']->getDays()); - self::assertEquals( - json_encode(['days' => 18049], JSON_THROW_ON_ERROR), - json_encode($results[1]['published_at'], JSON_THROW_ON_ERROR)); - - self::assertInstanceOf(Date::class, $results[2]['published_at']); - self::assertEquals(18742, $results[2]['published_at']->getDays()); - self::assertEquals( - json_encode(['days' => 18742], JSON_THROW_ON_ERROR), - json_encode($results[2]['published_at'], JSON_THROW_ON_ERROR)); - } - - public function testTime(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN time("12:00:00.000000000") AS time')); - - $time = $results->first()->get('time'); - self::assertInstanceOf(Time::class, $time); - self::assertEquals(12.0 * 60 * 60 * 1_000_000_000, $time->getNanoSeconds()); - self::assertEquals(12.0 * 60 * 60 * 1_000_000_000, $time->nanoSeconds); - } - - public function testLocalTime(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN localtime("12") AS time')); - - /** @var LocalTime $time */ - $time = $results->first()->get('time'); - self::assertInstanceOf(LocalTime::class, $time); - self::assertEquals(43_200_000_000_000, $time->getNanoseconds()); - - $results = $this->getSession()->run('RETURN localtime("09:23:42.000") AS time', []); - - /** @var LocalTime $time */ - $time = $results->first()->get('time'); - self::assertInstanceOf(LocalTime::class, $time); - self::assertEquals(33_822_000_000_000, $time->getNanoseconds()); - self::assertEquals(33_822_000_000_000, $time->nanoseconds); - } - - public function testDateTime(): void - { - $results = $this->getSession()->transaction(function (TransactionInterface $tsx) { - $query = $this->articlesQuery(); - $query .= 'RETURN article.created as created_at'; - - return $tsx->run($query); - }); - - self::assertEquals(3, $results->count()); - - $createdAt = $results[0]['created_at']; - self::assertInstanceOf(DateTime::class, $createdAt); - if ($createdAt->isLegacy()) { - self::assertEquals(1_559_414_432, $createdAt->getSeconds()); - } else { - self::assertEquals(1_559_410_832, $createdAt->getSeconds()); - } - - self::assertEquals(142_000_000, $createdAt->getNanoseconds()); - self::assertEquals(3600, $createdAt->getTimeZoneOffsetSeconds()); - self::assertEquals(142_000_000, $createdAt->getNanoseconds()); - self::assertEquals(3600, $createdAt->getTimeZoneOffsetSeconds()); - - if ($createdAt->isLegacy()) { - self::assertEquals('{"seconds":1559414432,"nanoseconds":142000000,"tzOffsetSeconds":3600}', json_encode($createdAt, JSON_THROW_ON_ERROR)); - } else { - self::assertEquals('{"seconds":1559410832,"nanoseconds":142000000,"tzOffsetSeconds":3600}', json_encode($createdAt, JSON_THROW_ON_ERROR)); - } - } - - public function testLocalDateTime(): void - { - $result = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN localdatetime() as local')->first()->get('local')); - - self::assertInstanceOf(LocalDateTime::class, $result); - $date = $result->toDateTime(); - self::assertEquals($result->getSeconds(), $date->getTimestamp()); - } - - public function testDuration(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run(<<count()); - self::assertEquals(new Duration(0, 14, 58320, 0), $results[0]['aDuration']); - $duration = $results[1]['aDuration']; - self::assertInstanceOf(Duration::class, $duration); - self::assertEquals(new Duration(5, 1, 43200, 0), $duration); - self::assertEquals(5, $duration->getMonths()); - self::assertEquals(1, $duration->getDays()); - self::assertEquals(43200, $duration->getSeconds()); - self::assertEquals(0, $duration->getNanoseconds()); - self::assertEquals(new Duration(0, 22, 71509, 500_000_000), $results[2]['aDuration']); - self::assertEquals(new Duration(0, 17, 43200, 0), $results[3]['aDuration']); - self::assertEquals(new Duration(0, 0, 91, 123_456_789), $results[4]['aDuration']); - self::assertEquals(new Duration(0, 0, 91, 123_456_789), $results[5]['aDuration']); - - self::assertEquals(5, $duration->getMonths()); - self::assertEquals(1, $duration->getDays()); - self::assertEquals(43200, $duration->getSeconds()); - self::assertEquals(0, $duration->getNanoseconds()); - $interval = new DateInterval(sprintf('P%dM%dDT%dS', 5, 1, 43200)); - self::assertEquals($interval, $duration->toDateInterval()); - self::assertEquals('{"months":5,"days":1,"seconds":43200,"nanoseconds":0}', json_encode($duration, JSON_THROW_ON_ERROR)); - } - - public function testPoint(): void - { - $result = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run('RETURN point({x: 3, y: 4}) AS point')); - self::assertInstanceOf(CypherList::class, $result); - $row = $result->first(); - self::assertInstanceOf(CypherMap::class, $row); - $point = $row->get('point'); - - self::assertInstanceOf(CartesianPoint::class, $point); - self::assertEquals(3.0, $point->getX()); - self::assertEquals(4.0, $point->getY()); - self::assertEquals('cartesian', $point->getCrs()); - self::assertGreaterThan(0, $point->getSrid()); - self::assertEquals(3.0, $point->x); - self::assertEquals(4.0, $point->y); - self::assertEquals('cartesian', $point->crs); - self::assertGreaterThan(0, $point->srid); - self::assertEquals( - json_encode([ - 'x' => 3, - 'y' => 4, - 'crs' => 'cartesian', - 'srid' => 7203, - ], JSON_THROW_ON_ERROR), - json_encode($point, JSON_THROW_ON_ERROR) - ); - } - - public function testNode(): void - { - $uuid = 'cc60fd69-a92b-47f3-9674-2f27f3437d66'; - $email = 'a@b.c'; - $type = 'pepperoni'; - - $arguments = compact('email', 'uuid', 'type'); - - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run( - 'MERGE (u:User{email: $email})-[:LIKES]->(p:Food:Pizza {type: $type}) ON CREATE SET u.uuid=$uuid RETURN u, p', - $arguments - )); - - self::assertEquals(1, $results->count()); - - /** @var Node $u */ - $u = $results[0]['u']; - self::assertInstanceOf(Node::class, $u); - self::assertEquals(['User'], $u->getLabels()->toArray()); - self::assertEquals($email, $u->getProperties()['email']); - self::assertEquals($uuid, $u->getProperties()['uuid']); - self::assertEquals($email, $u->email); - self::assertEquals($uuid, $u->uuid); - self::assertEquals( - json_encode([ - 'id' => $u->getId(), - 'labels' => $u->getLabels()->jsonSerialize(), - 'properties' => $u->getProperties()->jsonSerialize(), - ], JSON_THROW_ON_ERROR), - json_encode($u, JSON_THROW_ON_ERROR)); - - /** @var Node $p */ - $p = $results[0]['p']; - self::assertInstanceOf(Node::class, $p); - self::assertEquals(['Food', 'Pizza'], $p->getLabels()->toArray()); - self::assertEquals($type, $p->getProperties()['type']); - self::assertEquals( - json_encode([ - 'id' => $p->getId(), - 'labels' => $p->getLabels()->jsonSerialize(), - 'properties' => $p->getProperties()->jsonSerialize(), - ], JSON_THROW_ON_ERROR), - json_encode($p, JSON_THROW_ON_ERROR) - ); - } - - public function testRelationship(): void - { - $result = $this->getSession()->transaction(static function (TransactionInterface $tsx) { - $tsx->run('MATCH (n) DETACH DELETE n'); - - return $tsx->run('MERGE (x:X {x: 1}) - [xy:XY {x: 1, y: 1}] -> (y:Y {y: 1}) RETURN xy')->first()->get('xy'); - }); - - self::assertInstanceOf(Relationship::class, $result); - self::assertEquals('XY', $result->getType()); - self::assertEquals(['x' => 1, 'y' => 1], $result->getProperties()->toArray()); - self::assertEquals(1, $result->x); - self::assertEquals(1, $result->y); - self::assertEquals( - json_encode([ - 'id' => $result->getId(), - 'type' => $result->getType(), - 'properties' => $result->getProperties(), - 'startNodeId' => $result->getStartNodeId(), - 'endNodeId' => $result->getEndNodeId(), - ], JSON_THROW_ON_ERROR), - json_encode($result, JSON_THROW_ON_ERROR) - ); - } - - public function testPath(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run(<<<'CYPHER' -MERGE (b:Node {x:$x}) - [:HasNode {attribute: $xy}] -> (:Node {y:$y}) - [:HasNode {attribute: $yz}] -> (:Node {z:$z}) -WITH b -MATCH (x:Node) - [y:HasNode*2] -> (z:Node) -RETURN x, y, z -CYPHER, ['x' => 'x', 'xy' => 'xy', 'y' => 'y', 'yz' => 'yz', 'z' => 'z'])); - - self::assertEquals(1, $results->count()); - } - - public function testPath2(): void - { - $results = $this->getSession()->transaction(static fn (TransactionInterface $tsx) => $tsx->run(<<<'CYPHER' -CREATE path = (a:Node {x:$x}) - [b:HasNode {attribute: $xy}] -> (c:Node {y:$y}) - [d:HasNode {attribute: $yz}] -> (e:Node {z:$z}) -RETURN path -CYPHER, ['x' => 'x', 'xy' => 'xy', 'y' => 'y', 'yz' => 'yz', 'z' => 'z'])); - - self::assertEquals(1, $results->count()); - $path = $results->first()->get('path'); - - self::assertInstanceOf(Path::class, $path); - self::assertCount(2, $path->getRelationships()); - self::assertCount(3, $path->getNodes()); - - self::assertEquals(['x' => 'x'], $path->getNodes()->get(0)->getProperties()->toArray()); - self::assertEquals(['y' => 'y'], $path->getNodes()->get(1)->getProperties()->toArray()); - self::assertEquals(['z' => 'z'], $path->getNodes()->get(2)->getProperties()->toArray()); - self::assertEquals(['attribute' => 'xy'], $path->getRelationships()->get(0)->getProperties()->toArray()); - self::assertEquals(['attribute' => 'yz'], $path->getRelationships()->get(1)->getProperties()->toArray()); - } - - public function testPropertyTypes(): void - { - $result = $this->getSession(['neo4j', 'bolt'])->transaction(static fn (TransactionInterface $tsx) => $tsx->run(<<first()->get('a'); - - self::assertInstanceOf(Node::class, $node); - self::assertInstanceOf(PointInterface::class, $node->thePoint); - self::assertInstanceOf(CypherList::class, $node->theList); - self::assertInstanceOf(Date::class, $node->theDate); - self::assertInstanceOf(DateTime::class, $node->theDateTime); - self::assertInstanceOf(Duration::class, $node->theDuration); - self::assertInstanceOf(LocalDateTime::class, $node->theLocalDateTime); - self::assertInstanceOf(LocalTime::class, $node->theLocalTime); - self::assertInstanceOf(Time::class, $node->theTime); - } - - private function articlesQuery(): string - { - return << Date: Fri, 24 Jan 2025 16:07:02 +0530 Subject: [PATCH 2/2] feat: Remove OGMFormatter and FormatterInterface --- src/Basic/Client.php | 7 - src/Basic/Driver.php | 6 - src/Basic/Session.php | 13 +- src/Basic/UnmanagedTransaction.php | 7 - src/Bolt/BoltDriver.php | 27 +- src/Bolt/BoltResult.php | 4 +- src/Bolt/BoltUnmanagedTransaction.php | 20 +- src/Bolt/Session.php | 3 + src/Client.php | 21 +- src/ClientBuilder.php | 32 +-- src/Common/DriverSetupManager.php | 8 +- src/Common/TransactionHelper.php | 5 +- src/Contracts/ClientInterface.php | 2 +- src/Contracts/DriverInterface.php | 8 +- src/Contracts/SessionInterface.php | 2 +- src/Contracts/TransactionInterface.php | 2 +- .../UnmanagedTransactionInterface.php | 3 +- src/DriverFactory.php | 41 +-- src/Formatter/BasicFormatter.php | 235 ------------------ src/Formatter/OGMFormatter.php | 0 src/Formatter/SummarizedResultFormatter.php | 44 ++-- src/Neo4j/Neo4jDriver.php | 12 - src/ParameterHelper.php | 2 + src/Types/AbstractCypherSequence.php | 2 + src/Types/CypherMap.php | 2 + 25 files changed, 74 insertions(+), 434 deletions(-) delete mode 100644 src/Formatter/BasicFormatter.php delete mode 100644 src/Formatter/OGMFormatter.php diff --git a/src/Basic/Client.php b/src/Basic/Client.php index afb6db66..d6df61b4 100644 --- a/src/Basic/Client.php +++ b/src/Basic/Client.php @@ -18,16 +18,9 @@ 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 ) {} diff --git a/src/Basic/Driver.php b/src/Basic/Driver.php index 7b73a876..f84f8eee 100644 --- a/src/Basic/Driver.php +++ b/src/Basic/Driver.php @@ -23,14 +23,9 @@ 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( @@ -52,7 +47,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..8052fc4a 100644 --- a/src/Basic/Session.php +++ b/src/Basic/Session.php @@ -21,14 +21,8 @@ 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 ) {} @@ -36,16 +30,13 @@ public function __construct( /** * @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 beef7e1e..03a4631d 100644 --- a/src/Basic/UnmanagedTransaction.php +++ b/src/Basic/UnmanagedTransaction.php @@ -17,16 +17,9 @@ 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 ) {} diff --git a/src/Bolt/BoltDriver.php b/src/Bolt/BoltDriver.php index 7c21c484..a3625a2f 100644 --- a/src/Bolt/BoltDriver.php +++ b/src/Bolt/BoltDriver.php @@ -23,50 +23,33 @@ 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 +63,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..81c491ab 100644 --- a/src/Bolt/BoltResult.php +++ b/src/Bolt/BoltResult.php @@ -13,6 +13,7 @@ namespace Laudis\Neo4j\Bolt; +use Laudis\Neo4j\Formatter\SummarizedResultFormatter; use function array_splice; use function count; @@ -21,10 +22,9 @@ use function in_array; use Iterator; -use Laudis\Neo4j\Contracts\FormatterInterface; /** - * @psalm-import-type BoltCypherStats from FormatterInterface + * @psalm-import-type BoltCypherStats from SummarizedResultFormatter * * @implements Iterator */ diff --git a/src/Bolt/BoltUnmanagedTransaction.php b/src/Bolt/BoltUnmanagedTransaction.php index d8254348..80692275 100644 --- a/src/Bolt/BoltUnmanagedTransaction.php +++ b/src/Bolt/BoltUnmanagedTransaction.php @@ -18,6 +18,7 @@ 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; @@ -33,10 +34,6 @@ /** * Manages a transaction over the bolt protocol. * - * @template T - * - * @implements UnmanagedTransactionInterface - * * @psalm-import-type BoltMeta from SummarizedResultFormatter */ final class BoltUnmanagedTransaction implements UnmanagedTransactionInterface @@ -59,6 +56,8 @@ public function __construct( /** * @throws ClientException|Throwable + * + * @return CypherList */ public function commit(iterable $statements = []): CypherList { @@ -78,10 +77,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(); @@ -113,7 +110,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)); } @@ -121,7 +118,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); @@ -159,10 +156,11 @@ public function runStatement(Statement $statement) /** * @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/Session.php b/src/Bolt/Session.php index f8042597..137a44c3 100644 --- a/src/Bolt/Session.php +++ b/src/Bolt/Session.php @@ -59,6 +59,9 @@ public function __construct( $this->bookmarkHolder = new BookmarkHolder(Bookmark::from($config->getBookmarks())); } + /** + * @return CypherList + */ public function runStatements(iterable $statements, ?TransactionConfiguration $config = null): CypherList { $tbr = []; diff --git a/src/Client.php b/src/Client.php index 9d862061..7e5aaa54 100644 --- a/src/Client.php +++ b/src/Client.php @@ -21,33 +21,30 @@ 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 + * @param DriverSetupManager $driverSetups */ public function __construct( private readonly DriverSetupManager $driverSetups, @@ -70,12 +67,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(); } @@ -86,6 +83,7 @@ private function getRunner(?string $alias = null): TransactionInterface|SessionI 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 +124,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 +182,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..791c8d5f 100644 --- a/src/ClientBuilder.php +++ b/src/ClientBuilder.php @@ -20,14 +20,12 @@ 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; @@ -35,9 +33,7 @@ /** * Immutable factory for creating a client. * - * @template T - * - * @psalm-import-type OGMTypes from OGMFormatter + * @psalm-import-type OGMTypes from SummarizedResultFormatter */ final class ClientBuilder { @@ -46,7 +42,7 @@ final class ClientBuilder /** * @psalm-mutation-free * - * @param DriverSetupManager $driverSetups + * @param DriverSetupManager $driverSetups */ public function __construct( /** @psalm-readonly */ @@ -58,8 +54,6 @@ public function __construct( /** * 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 +71,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 +83,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 +101,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 +112,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 +124,6 @@ public function withFormatter(FormatterInterface $formatter): self } /** - * @return ClientInterface - * * @psalm-mutation-free */ public function build(): ClientInterface @@ -156,8 +136,6 @@ public function build(): ClientInterface } /** - * @return self - * * @psalm-mutation-free */ public function withDefaultDriverConfiguration(DriverConfiguration $config): self @@ -170,8 +148,6 @@ public function withDefaultDriverConfiguration(DriverConfiguration $config): sel } /** - * @return self - * * @psalm-mutation-free */ public function withDefaultSessionConfiguration(SessionConfiguration $config): self @@ -183,8 +159,6 @@ public function withDefaultSessionConfiguration(SessionConfiguration $config): s } /** - * @return self - * * @psalm-mutation-free */ public function withDefaultTransactionConfiguration(TransactionConfiguration $config): self diff --git a/src/Common/DriverSetupManager.php b/src/Common/DriverSetupManager.php index 78274b17..28b5646b 100644 --- a/src/Common/DriverSetupManager.php +++ b/src/Common/DriverSetupManager.php @@ -36,16 +36,13 @@ 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; @@ -99,9 +96,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); diff --git a/src/Common/TransactionHelper.php b/src/Common/TransactionHelper.php index a9be078e..f756c5fb 100644 --- a/src/Common/TransactionHelper.php +++ b/src/Common/TransactionHelper.php @@ -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 */ diff --git a/src/Contracts/ClientInterface.php b/src/Contracts/ClientInterface.php index e07a7ec4..389c35b7 100644 --- a/src/Contracts/ClientInterface.php +++ b/src/Contracts/ClientInterface.php @@ -44,7 +44,7 @@ public function runStatement(Statement $statement, ?string $alias = null): Summa * * @throws Neo4jException * - * @return CypherList + * @return CypherList */ public function runStatements(iterable $statements, ?string $alias = null): CypherList; 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/SessionInterface.php b/src/Contracts/SessionInterface.php index b7b39e61..1694c9a5 100644 --- a/src/Contracts/SessionInterface.php +++ b/src/Contracts/SessionInterface.php @@ -30,7 +30,7 @@ interface SessionInterface extends TransactionInterface * * @throws Neo4jException * - * @return CypherList + * @return CypherList */ public function runStatements(iterable $statements, ?TransactionConfiguration $config = null): CypherList; diff --git a/src/Contracts/TransactionInterface.php b/src/Contracts/TransactionInterface.php index 329af20d..ff615d8f 100644 --- a/src/Contracts/TransactionInterface.php +++ b/src/Contracts/TransactionInterface.php @@ -37,7 +37,7 @@ public function runStatement(Statement $statement): SummarizedResult; * * @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 09dabaa7..f04697a0 100644 --- a/src/Contracts/UnmanagedTransactionInterface.php +++ b/src/Contracts/UnmanagedTransactionInterface.php @@ -14,6 +14,7 @@ namespace Laudis\Neo4j\Contracts; use Laudis\Neo4j\Databags\Statement; +use Laudis\Neo4j\Databags\SummarizedResult; use Laudis\Neo4j\Types\CypherList; /** @@ -28,7 +29,7 @@ interface UnmanagedTransactionInterface extends TransactionInterface * * @param iterable $statements * - * @return CypherList + * @return CypherList */ public function commit(iterable $statements = []): CypherList; diff --git a/src/DriverFactory.php b/src/DriverFactory.php index b0627101..32a16cdd 100644 --- a/src/DriverFactory.php +++ b/src/DriverFactory.php @@ -20,10 +20,9 @@ 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\SummarizedResultFormatter; -use Laudis\Neo4j\Http\HttpDriver; use Laudis\Neo4j\Neo4j\Neo4jDriver; use Psr\Http\Message\UriInterface; @@ -35,19 +34,9 @@ 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 e69de29b..00000000 diff --git a/src/Formatter/SummarizedResultFormatter.php b/src/Formatter/SummarizedResultFormatter.php index 42294913..215e2d7f 100644 --- a/src/Formatter/SummarizedResultFormatter.php +++ b/src/Formatter/SummarizedResultFormatter.php @@ -149,27 +149,29 @@ public function formatBoltResult(array $meta, BoltResult $result, BoltConnection { /** @var ResultSummary|null $summary */ $summary = null; - $result->addFinishedCallback(function (array $response) use ($connection, $statement, $runStart, $resultAvailableAfter, &$summary) { - $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( + /** @param {array{stats?: BoltCypherStats}&array} $response */ + function (mixed $response) use ($connection, $statement, $runStart, $resultAvailableAfter, &$summary) { + $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() + ) + ); + }); $formattedResult = $this->processBoltResult($meta, $result, $connection, $runStart, $resultAvailableAfter, $statement, $holder); diff --git a/src/Neo4j/Neo4jDriver.php b/src/Neo4j/Neo4jDriver.php index 321092fe..768d8bf6 100644 --- a/src/Neo4j/Neo4jDriver.php +++ b/src/Neo4j/Neo4jDriver.php @@ -36,10 +36,6 @@ /** * Driver for auto client-side routing. * - * @template T - * - * @implements DriverInterface - * * @psalm-import-type OGMResults from SummarizedResultFormatter */ final class Neo4jDriver implements DriverInterface @@ -54,14 +50,6 @@ public function __construct( ) {} /** - * @template U - * - * @return ( - * func_num_args() is 5 - * ? self - * : self - * ) - * * @psalm-suppress MixedReturnTypeCoercion */ public static function create(string|UriInterface $uri, ?DriverConfiguration $configuration = null, ?AuthenticateInterface $authenticate = null, ?SummarizedResultFormatter $formatter = null, ?AddressResolverInterface $resolver = null): self diff --git a/src/ParameterHelper.php b/src/ParameterHelper.php index 3d7c20fa..0cf857ea 100644 --- a/src/ParameterHelper.php +++ b/src/ParameterHelper.php @@ -78,6 +78,8 @@ public static function asMap(iterable $iterable): CypherMap /** * @return iterable|scalar|stdClass|IStructure|null + * + * @param \DateTime|array|object|stdClass $value */ public static function asParameter( mixed $value, diff --git a/src/Types/AbstractCypherSequence.php b/src/Types/AbstractCypherSequence.php index 1225f45c..8b82e344 100644 --- a/src/Types/AbstractCypherSequence.php +++ b/src/Types/AbstractCypherSequence.php @@ -553,6 +553,8 @@ public function preload(): void /** * @psalm-mutation-free + * + * @param {int|string|object} $key */ protected function isStringable(mixed $key): bool { diff --git a/src/Types/CypherMap.php b/src/Types/CypherMap.php index ebbdc028..5037299e 100644 --- a/src/Types/CypherMap.php +++ b/src/Types/CypherMap.php @@ -33,6 +33,7 @@ final class CypherMap extends Map public function getAsCypherMap(string $key, mixed $default = null): CypherMap { if (func_num_args() === 1) { + /** @var mixed $value */ $value = $this->get($key); } else { /** @var mixed $value */ @@ -52,6 +53,7 @@ public function getAsCypherMap(string $key, mixed $default = null): CypherMap public function getAsCypherList(string $key, mixed $default = null): CypherList { if (func_num_args() === 1) { + /** @var mixed $value */ $value = $this->get($key); } else { /** @var mixed $value */