From 7913dac3a0ad012da61672218944e56e65e6ffa8 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Sat, 1 Feb 2025 19:07:01 +0530 Subject: [PATCH 1/8] refactor: Add BoltMessages and BoltMessageFactory --- src/Bolt/BoltConnection.php | 64 ++++++++++++----------- src/Bolt/Messages/BoltBeginMessage.php | 43 +++++++++++++++ src/Bolt/Messages/BoltCommitMessage.php | 42 +++++++++++++++ src/Bolt/Messages/BoltDiscardMessage.php | 43 +++++++++++++++ src/Bolt/Messages/BoltGoodbyeMessage.php | 42 +++++++++++++++ src/Bolt/Messages/BoltPullMessage.php | 43 +++++++++++++++ src/Bolt/Messages/BoltResetMessage.php | 42 +++++++++++++++ src/Bolt/Messages/BoltRollbackMessage.php | 42 +++++++++++++++ src/Bolt/Messages/BoltRunMessage.php | 49 +++++++++++++++++ src/Contracts/BoltMessage.php | 47 +++++++++++++++++ 10 files changed, 426 insertions(+), 31 deletions(-) create mode 100644 src/Bolt/Messages/BoltBeginMessage.php create mode 100644 src/Bolt/Messages/BoltCommitMessage.php create mode 100644 src/Bolt/Messages/BoltDiscardMessage.php create mode 100644 src/Bolt/Messages/BoltGoodbyeMessage.php create mode 100644 src/Bolt/Messages/BoltPullMessage.php create mode 100644 src/Bolt/Messages/BoltResetMessage.php create mode 100644 src/Bolt/Messages/BoltRollbackMessage.php create mode 100644 src/Bolt/Messages/BoltRunMessage.php create mode 100644 src/Contracts/BoltMessage.php diff --git a/src/Bolt/BoltConnection.php b/src/Bolt/BoltConnection.php index 93f3b3fa..249f285e 100644 --- a/src/Bolt/BoltConnection.php +++ b/src/Bolt/BoltConnection.php @@ -23,6 +23,7 @@ use Bolt\protocol\V5_3; use Bolt\protocol\V5_4; use Exception; +use Laudis\Neo4j\Bolt\Messages\BoltGoodbyeMessage; use Laudis\Neo4j\Common\ConnectionConfiguration; use Laudis\Neo4j\Common\Neo4jLogger; use Laudis\Neo4j\Contracts\AuthenticateInterface; @@ -37,6 +38,7 @@ use Laudis\Neo4j\Types\CypherList; use Psr\Http\Message\UriInterface; use Psr\Log\LogLevel; +use Throwable; use WeakReference; /** @@ -46,6 +48,8 @@ */ class BoltConnection implements ConnectionInterface { + private BoltMessageFactory $messageFactory; + /** * @note We are using references to "subscribed results" to maintain backwards compatibility and try and strike * a balance between performance and ease of use. @@ -80,7 +84,9 @@ public function __construct( /** @psalm-readonly */ private readonly ConnectionConfiguration $config, private readonly ?Neo4jLogger $logger, - ) {} + ) { + $this->messageFactory = new BoltMessageFactory($this->protocol(), $this->logger); + } public function getEncryptionLevel(): string { @@ -194,10 +200,8 @@ public function consumeResults(): void */ public function reset(): void { - $this->logger?->log(LogLevel::DEBUG, 'RESET'); - $response = $this->protocol() - ->reset() - ->getResponse(); + $message = $this->messageFactory->createResetMessage(); + $response = $message->send()->getResponse(); $this->assertNoFailure($response); $this->subscribedResults = []; } @@ -212,10 +216,8 @@ public function begin(?string $database, ?float $timeout, BookmarkHolder $holder $this->consumeResults(); $extra = $this->buildRunExtra($database, $timeout, $holder, AccessMode::WRITE()); - $this->logger?->log(LogLevel::DEBUG, 'BEGIN', $extra); - $response = $this->protocol() - ->begin($extra) - ->getResponse(); + $message = $this->messageFactory->createBeginMessage($extra); + $response = $message->send()->getResponse(); $this->assertNoFailure($response); } @@ -227,10 +229,9 @@ public function begin(?string $database, ?float $timeout, BookmarkHolder $holder public function discard(?int $qid): void { $extra = $this->buildResultExtra(null, $qid); - $this->logger?->log(LogLevel::DEBUG, 'DISCARD', $extra); - $response = $this->protocol() - ->discard($extra) - ->getResponse(); + + $message = $this->messageFactory->createDiscardMessage($extra); + $response = $message->send()->getResponse(); $this->assertNoFailure($response); } @@ -247,14 +248,13 @@ public function run( ?string $database, ?float $timeout, BookmarkHolder $holder, - ?AccessMode $mode + ?AccessMode $mode, ): array { $extra = $this->buildRunExtra($database, $timeout, $holder, $mode); - $this->logger?->log(LogLevel::DEBUG, 'RUN', $extra); - $response = $this->protocol() - ->run($text, $parameters, $extra) - ->getResponse(); + $message = $this->messageFactory->createRunMessage($text, $parameters, $extra); + $response = $message->send()->getResponse(); $this->assertNoFailure($response); + /** @var BoltMeta */ return $response->content; } @@ -266,12 +266,10 @@ public function run( */ public function commit(): void { - $this->logger?->log(LogLevel::DEBUG, 'COMMIT'); $this->consumeResults(); - $response = $this->protocol() - ->commit() - ->getResponse(); + $message = $this->messageFactory->createCommitMessage(); + $response = $message->send()->getResponse(); $this->assertNoFailure($response); } @@ -282,12 +280,10 @@ public function commit(): void */ public function rollback(): void { - $this->logger?->log(LogLevel::DEBUG, 'ROLLBACK'); $this->consumeResults(); - $response = $this->protocol() - ->rollback() - ->getResponse(); + $message = $this->messageFactory->createRollbackMessage(); + $response = $message->send()->getResponse(); $this->assertNoFailure($response); } @@ -313,8 +309,10 @@ public function pull(?int $qid, ?int $fetchSize): array $this->logger?->log(LogLevel::DEBUG, 'PULL', $extra); $tbr = []; + $message = $this->messageFactory->createPullMessage($extra); + /** @var Response $response */ - foreach ($this->protocol()->pull($extra)->getResponses() as $response) { + foreach ($message->send()->getResponses() as $response) { $this->assertNoFailure($response); $tbr[] = $response->content; } @@ -336,12 +334,15 @@ public function close(): void $this->consumeResults(); } - $this->logger?->log(LogLevel::DEBUG, 'GOODBYE'); - $this->protocol()->goodbye(); + $message = new BoltGoodbyeMessage( + $this->protocol(), + $this->logger + ); + $message->send(); unset($this->boltProtocol); // has to be set to null as the sockets don't recover nicely contrary to what the underlying code might lead you to believe; } - } catch (\Throwable) { + } catch (Throwable) { } } @@ -403,7 +404,8 @@ private function assertNoFailure(Response $response): void { if ($response->signature === Signature::FAILURE) { $this->logger?->log(LogLevel::ERROR, 'FAILURE'); - $resetResponse = $this->protocol()->reset()->getResponse(); + $message = $this->messageFactory->createResetMessage(); + $resetResponse = $message->send()->getResponse(); $this->subscribedResults = []; if ($resetResponse->signature === Signature::FAILURE) { throw new Neo4jException([Neo4jError::fromBoltResponse($resetResponse), Neo4jError::fromBoltResponse($response)]); diff --git a/src/Bolt/Messages/BoltBeginMessage.php b/src/Bolt/Messages/BoltBeginMessage.php new file mode 100644 index 00000000..339c2e92 --- /dev/null +++ b/src/Bolt/Messages/BoltBeginMessage.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltBeginMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly array $extra, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltBeginMessage + { + $this->logger?->log(LogLevel::DEBUG, 'BEGIN', $this->extra); + $this->protocol->begin($this->extra); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltCommitMessage.php b/src/Bolt/Messages/BoltCommitMessage.php new file mode 100644 index 00000000..becb54cf --- /dev/null +++ b/src/Bolt/Messages/BoltCommitMessage.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltCommitMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltCommitMessage + { + $this->logger?->log(LogLevel::DEBUG, 'COMMIT'); + $this->protocol->commit(); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltDiscardMessage.php b/src/Bolt/Messages/BoltDiscardMessage.php new file mode 100644 index 00000000..e35adfbc --- /dev/null +++ b/src/Bolt/Messages/BoltDiscardMessage.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltDiscardMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly array $extra, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltDiscardMessage + { + $this->logger?->log(LogLevel::DEBUG, 'DISCARD', $this->extra); + $this->protocol->discard($this->extra); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltGoodbyeMessage.php b/src/Bolt/Messages/BoltGoodbyeMessage.php new file mode 100644 index 00000000..a944cadb --- /dev/null +++ b/src/Bolt/Messages/BoltGoodbyeMessage.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltGoodbyeMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltGoodbyeMessage + { + $this->logger?->log(LogLevel::DEBUG, 'GOODBYE'); + $this->protocol->goodbye(); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltPullMessage.php b/src/Bolt/Messages/BoltPullMessage.php new file mode 100644 index 00000000..ab141786 --- /dev/null +++ b/src/Bolt/Messages/BoltPullMessage.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltPullMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly array $extra, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltPullMessage + { + $this->logger?->log(LogLevel::DEBUG, 'PULL', $this->extra); + $this->protocol->pull($this->extra); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltResetMessage.php b/src/Bolt/Messages/BoltResetMessage.php new file mode 100644 index 00000000..223e3d01 --- /dev/null +++ b/src/Bolt/Messages/BoltResetMessage.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltResetMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltResetMessage + { + $this->logger?->log(LogLevel::DEBUG, 'RESET'); + $this->protocol->reset(); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltRollbackMessage.php b/src/Bolt/Messages/BoltRollbackMessage.php new file mode 100644 index 00000000..170d376c --- /dev/null +++ b/src/Bolt/Messages/BoltRollbackMessage.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltRollbackMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltRollbackMessage + { + $this->logger?->log(LogLevel::DEBUG, 'ROLLBACK'); + $this->protocol->rollback(); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltRunMessage.php b/src/Bolt/Messages/BoltRunMessage.php new file mode 100644 index 00000000..b07911fe --- /dev/null +++ b/src/Bolt/Messages/BoltRunMessage.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltRunMessage extends BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly string $text, + private readonly array $parameters, + private readonly array $extra, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + public function send(): BoltRunMessage + { + $this->logger?->log(LogLevel::DEBUG, 'RUN', [ + 'text' => $this->text, + 'parameters' => $this->parameters, + 'extra' => $this->extra, + ]); + $this->protocol->run($this->text, $this->parameters, $this->extra); + + return $this; + } +} diff --git a/src/Contracts/BoltMessage.php b/src/Contracts/BoltMessage.php new file mode 100644 index 00000000..7cbed4c6 --- /dev/null +++ b/src/Contracts/BoltMessage.php @@ -0,0 +1,47 @@ + + * + * 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\protocol\Response; +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Iterator; + +abstract class BoltMessage +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + ) { + } + + /** + * Sends the Bolt message. + */ + abstract public function send(): BoltMessage; + + public function getResponse(): Response + { + return $this->protocol->getResponse(); + } + + /** @return Iterator */ + public function getResponses(): Iterator + { + return $this->protocol->getResponses(); + } +} From 4b217c8602dc835ebde7d03e4e2ec6228408c56f Mon Sep 17 00:00:00 2001 From: exaby73 Date: Sat, 1 Feb 2025 19:17:25 +0530 Subject: [PATCH 2/8] refactor: Create factory for messages --- src/Bolt/BoltConnection.php | 6 +-- src/Bolt/BoltMessageFactory.php | 82 +++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 src/Bolt/BoltMessageFactory.php diff --git a/src/Bolt/BoltConnection.php b/src/Bolt/BoltConnection.php index 249f285e..0e558226 100644 --- a/src/Bolt/BoltConnection.php +++ b/src/Bolt/BoltConnection.php @@ -23,7 +23,6 @@ use Bolt\protocol\V5_3; use Bolt\protocol\V5_4; use Exception; -use Laudis\Neo4j\Bolt\Messages\BoltGoodbyeMessage; use Laudis\Neo4j\Common\ConnectionConfiguration; use Laudis\Neo4j\Common\Neo4jLogger; use Laudis\Neo4j\Contracts\AuthenticateInterface; @@ -334,10 +333,7 @@ public function close(): void $this->consumeResults(); } - $message = new BoltGoodbyeMessage( - $this->protocol(), - $this->logger - ); + $message = $this->messageFactory->createGoodbyeMessage(); $message->send(); unset($this->boltProtocol); // has to be set to null as the sockets don't recover nicely contrary to what the underlying code might lead you to believe; diff --git a/src/Bolt/BoltMessageFactory.php b/src/Bolt/BoltMessageFactory.php new file mode 100644 index 00000000..64eaa6d9 --- /dev/null +++ b/src/Bolt/BoltMessageFactory.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Bolt\Messages\BoltBeginMessage; +use Laudis\Neo4j\Bolt\Messages\BoltCommitMessage; +use Laudis\Neo4j\Bolt\Messages\BoltDiscardMessage; +use Laudis\Neo4j\Bolt\Messages\BoltGoodbyeMessage; +use Laudis\Neo4j\Bolt\Messages\BoltPullMessage; +use Laudis\Neo4j\Bolt\Messages\BoltResetMessage; +use Laudis\Neo4j\Bolt\Messages\BoltRollbackMessage; +use Laudis\Neo4j\Bolt\Messages\BoltRunMessage; +use Laudis\Neo4j\Common\Neo4jLogger; + +/** + * Factory class for creating Bolt protocol messages. + */ +final class BoltMessageFactory +{ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly ?Neo4jLogger $logger = null, + ) { + } + + public function createResetMessage(): BoltResetMessage + { + return new BoltResetMessage($this->protocol, $this->logger); + } + + public function createBeginMessage(array $extra): BoltBeginMessage + { + return new BoltBeginMessage($this->protocol, $extra, $this->logger); + } + + public function createDiscardMessage(array $extra): BoltDiscardMessage + { + return new BoltDiscardMessage($this->protocol, $extra, $this->logger); + } + + public function createRunMessage(string $text, array $parameters, array $extra): BoltRunMessage + { + return new BoltRunMessage($this->protocol, $text, $parameters, $extra, $this->logger); + } + + public function createCommitMessage(): BoltCommitMessage + { + return new BoltCommitMessage($this->protocol, $this->logger); + } + + public function createRollbackMessage(): BoltRollbackMessage + { + return new BoltRollbackMessage($this->protocol, $this->logger); + } + + public function createPullMessage(array $extra): BoltPullMessage + { + return new BoltPullMessage($this->protocol, $extra, $this->logger); + } + + public function createGoodbyeMessage(): BoltGoodbyeMessage + { + return new BoltGoodbyeMessage($this->protocol, $this->logger); + } +} From 55d9fa3fd719d46cdd8815a55c126fb805e0e428 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Sat, 1 Feb 2025 19:21:18 +0530 Subject: [PATCH 3/8] test: Fix debug log test --- tests/Integration/Neo4jLoggerTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/Integration/Neo4jLoggerTest.php b/tests/Integration/Neo4jLoggerTest.php index 40f9bd06..64817f9e 100644 --- a/tests/Integration/Neo4jLoggerTest.php +++ b/tests/Integration/Neo4jLoggerTest.php @@ -83,7 +83,11 @@ static function (string $message, array $context) use (&$infoLogs) { [ 'RUN', [ - 'mode' => 'w', + 'text' => 'RETURN 1 as test', + 'parameters' => [], + 'extra' => [ + 'mode' => 'w', + ], ], ], [ From de60b32f8a89a1962b84923b98149b36156e969c Mon Sep 17 00:00:00 2001 From: exaby73 Date: Sat, 1 Feb 2025 19:22:24 +0530 Subject: [PATCH 4/8] lint: Remove unecessary @var annotation --- src/Bolt/BoltConnection.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Bolt/BoltConnection.php b/src/Bolt/BoltConnection.php index 0e558226..d92003c7 100644 --- a/src/Bolt/BoltConnection.php +++ b/src/Bolt/BoltConnection.php @@ -310,7 +310,6 @@ public function pull(?int $qid, ?int $fetchSize): array $tbr = []; $message = $this->messageFactory->createPullMessage($extra); - /** @var Response $response */ foreach ($message->send()->getResponses() as $response) { $this->assertNoFailure($response); $tbr[] = $response->content; From abfe43f1a9307ed8ddf9abb5b2690ac197f22072 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Wed, 5 Feb 2025 13:12:09 +0530 Subject: [PATCH 5/8] feat: Update PHP CS Fixer and run fix --- .php-cs-fixer.dist.php | 1 - composer.json | 2 +- src/Authentication/BasicAuth.php | 3 +- src/Authentication/KerberosAuth.php | 4 +- src/Authentication/NoAuth.php | 6 +- src/Authentication/OpenIDConnectAuth.php | 6 +- src/Basic/Client.php | 5 +- src/Basic/Driver.php | 5 +- src/Basic/Session.php | 5 +- src/Basic/UnmanagedTransaction.php | 5 +- src/Bolt/BoltDriver.php | 5 +- src/Bolt/BoltResult.php | 5 +- src/Bolt/BoltUnmanagedTransaction.php | 5 +- src/Bolt/Connection.php | 5 +- src/Bolt/ConnectionPool.php | 7 +- src/Bolt/Session.php | 4 +- src/Bolt/SocketConnectionFactory.php | 5 +- src/Bolt/SystemWideConnectionFactory.php | 5 +- src/Bolt/UriConfiguration.php | 5 +- src/BoltFactory.php | 19 +++--- src/Client.php | 9 +-- src/ClientBuilder.php | 3 +- src/Common/Cache.php | 27 ++++---- src/Common/ConnectionConfiguration.php | 5 +- src/Common/DriverSetupManager.php | 5 +- src/Common/Neo4jLogger.php | 3 +- src/Common/SingleThreadedSemaphore.php | 5 +- src/Common/SysVSemaphore.php | 5 +- src/Common/Uri.php | 5 +- src/Databags/BookmarkHolder.php | 5 +- src/Databags/ConnectionRequestData.php | 5 +- src/Databags/DatabaseInfo.php | 5 +- src/Databags/DriverConfiguration.php | 12 ++-- src/Databags/DriverSetup.php | 5 +- src/Databags/InputPosition.php | 5 +- src/Databags/Neo4jError.php | 5 +- src/Databags/Notification.php | 5 +- src/Databags/Pair.php | 5 +- src/Databags/Plan.php | 5 +- src/Databags/ProfiledPlan.php | 5 +- src/Databags/ResultSummary.php | 5 +- src/Databags/ServerInfo.php | 5 +- src/Databags/SessionConfiguration.php | 11 +-- src/Databags/SslConfiguration.php | 5 +- src/Databags/Statement.php | 5 +- src/Databags/SummarizedResult.php | 2 +- src/Databags/SummaryCounters.php | 5 +- src/Databags/TransactionConfiguration.php | 13 ++-- src/Formatter/BasicFormatter.php | 2 - src/Formatter/OGMFormatter.php | 3 +- .../Specialised/BoltOGMTranslator.php | 3 +- src/Formatter/SummarizedResultFormatter.php | 5 +- src/Neo4j/Neo4jConnectionPool.php | 5 +- src/Neo4j/Neo4jDriver.php | 5 +- src/Neo4j/RoutingTable.php | 5 +- src/ParameterHelper.php | 6 +- src/TypeCaster.php | 1 - src/Types/Abstract3DPoint.php | 2 +- src/Types/AbstractPoint.php | 5 +- src/Types/ArrayList.php | 2 +- src/Types/CypherMap.php | 2 - src/Types/Date.php | 5 +- src/Types/DateTime.php | 5 +- src/Types/DateTimeZoneId.php | 5 +- src/Types/Duration.php | 5 +- src/Types/LocalDateTime.php | 5 +- src/Types/LocalTime.php | 5 +- src/Types/Map.php | 9 +-- src/Types/Node.php | 5 +- src/Types/Path.php | 5 +- src/Types/Relationship.php | 2 +- src/Types/Time.php | 5 +- src/Types/UnboundRelationship.php | 5 +- .../TransactionIntegrationTest.php | 68 +++++++++---------- tests/Unit/CypherMapTest.php | 2 +- tests/Unit/ParameterHelperTest.php | 4 +- 76 files changed, 259 insertions(+), 209 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index c673ac50..344e0983 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -63,7 +63,6 @@ MultilineCommentOpeningClosingAloneFixer::name() => true, MultilinePromotedPropertiesFixer::name() => true, PhpUnitAssertArgumentsOrderFixer::name() => true, - PhpdocNoSuperfluousParamFixer::name() => true, PhpdocParamOrderFixer::name() => true, StringableInterfaceFixer::name() => true, ]) diff --git a/composer.json b/composer.json index ffbc9954..be6b2da8 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "nyholm/psr7-server": "^1.0", "kriswallsmith/buzz": "^1.2", "vimeo/psalm": "^5.0", - "friendsofphp/php-cs-fixer": "3.15.0", + "friendsofphp/php-cs-fixer": "3.68.5", "psalm/plugin-phpunit": "^0.18", "monolog/monolog": "^2.2", "symfony/uid": "^5.0", diff --git a/src/Authentication/BasicAuth.php b/src/Authentication/BasicAuth.php index 7f6fb256..82ef2d65 100644 --- a/src/Authentication/BasicAuth.php +++ b/src/Authentication/BasicAuth.php @@ -38,7 +38,8 @@ public function __construct( private readonly string $username, private readonly string $password, private readonly ?Neo4jLogger $logger, - ) {} + ) { + } /** * @throws Exception diff --git a/src/Authentication/KerberosAuth.php b/src/Authentication/KerberosAuth.php index 3c57d32f..51656d65 100644 --- a/src/Authentication/KerberosAuth.php +++ b/src/Authentication/KerberosAuth.php @@ -40,11 +40,13 @@ final class KerberosAuth implements AuthenticateInterface public function __construct( private readonly string $token, private readonly ?Neo4jLogger $logger, - ) {} + ) { + } public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface { $this->logger?->log(LogLevel::DEBUG, 'Authenticating using KerberosAuth'); + /** * @psalm-suppress ImpureMethodCall Request is a pure object: * diff --git a/src/Authentication/NoAuth.php b/src/Authentication/NoAuth.php index 51d246cb..69d7ad07 100644 --- a/src/Authentication/NoAuth.php +++ b/src/Authentication/NoAuth.php @@ -38,12 +38,14 @@ final class NoAuth implements AuthenticateInterface * @pure */ public function __construct( - private readonly ?Neo4jLogger $logger - ) {} + private readonly ?Neo4jLogger $logger, + ) { + } public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface { $this->logger?->log(LogLevel::DEBUG, 'Authentication disabled'); + /** * @psalm-suppress ImpureMethodCall Request is a pure object: * diff --git a/src/Authentication/OpenIDConnectAuth.php b/src/Authentication/OpenIDConnectAuth.php index 18b2b796..c7421cbf 100644 --- a/src/Authentication/OpenIDConnectAuth.php +++ b/src/Authentication/OpenIDConnectAuth.php @@ -36,12 +36,14 @@ final class OpenIDConnectAuth implements AuthenticateInterface */ public function __construct( private readonly string $token, - private readonly ?Neo4jLogger $logger - ) {} + private readonly ?Neo4jLogger $logger, + ) { + } public function authenticateHttp(RequestInterface $request, UriInterface $uri, string $userAgent): RequestInterface { $this->logger?->log(LogLevel::DEBUG, 'Authenticating using OpenIDConnectAuth'); + /** * @psalm-suppress ImpureMethodCall Request is a pure object: * diff --git a/src/Basic/Client.php b/src/Basic/Client.php index afb6db66..38cb6011 100644 --- a/src/Basic/Client.php +++ b/src/Basic/Client.php @@ -29,8 +29,9 @@ final class Client implements ClientInterface * @param ClientInterface> $client */ public function __construct( - private readonly ClientInterface $client - ) {} + private readonly ClientInterface $client, + ) { + } public function run(string $statement, iterable $parameters = [], ?string $alias = null): SummarizedResult { diff --git a/src/Basic/Driver.php b/src/Basic/Driver.php index 7b73a876..423aafeb 100644 --- a/src/Basic/Driver.php +++ b/src/Basic/Driver.php @@ -34,8 +34,9 @@ final class Driver implements DriverInterface * @psalm-external-mutation-free */ public function __construct( - private readonly DriverInterface $driver - ) {} + private readonly DriverInterface $driver, + ) { + } /** * @psalm-mutation-free diff --git a/src/Basic/Session.php b/src/Basic/Session.php index cb31416f..472fc63b 100644 --- a/src/Basic/Session.php +++ b/src/Basic/Session.php @@ -30,8 +30,9 @@ final class Session implements SessionInterface * @param SessionInterface> $session */ public function __construct( - private readonly SessionInterface $session - ) {} + private readonly SessionInterface $session, + ) { + } /** * @param iterable $statements diff --git a/src/Basic/UnmanagedTransaction.php b/src/Basic/UnmanagedTransaction.php index 233e582d..00ccc63a 100644 --- a/src/Basic/UnmanagedTransaction.php +++ b/src/Basic/UnmanagedTransaction.php @@ -28,8 +28,9 @@ final class UnmanagedTransaction implements UnmanagedTransactionInterface * @param UnmanagedTransactionInterface> $tsx */ public function __construct( - private readonly UnmanagedTransactionInterface $tsx - ) {} + private readonly UnmanagedTransactionInterface $tsx, + ) { + } /** * @param iterable $parameters diff --git a/src/Bolt/BoltDriver.php b/src/Bolt/BoltDriver.php index 7c21c484..bfd041f0 100644 --- a/src/Bolt/BoltDriver.php +++ b/src/Bolt/BoltDriver.php @@ -50,8 +50,9 @@ final class BoltDriver implements DriverInterface public function __construct( private readonly UriInterface $parsedUrl, private readonly ConnectionPool $pool, - private readonly FormatterInterface $formatter - ) {} + private readonly FormatterInterface $formatter, + ) { + } /** * @template U diff --git a/src/Bolt/BoltResult.php b/src/Bolt/BoltResult.php index dd243b28..3bd28bf6 100644 --- a/src/Bolt/BoltResult.php +++ b/src/Bolt/BoltResult.php @@ -39,8 +39,9 @@ final class BoltResult implements Iterator public function __construct( private readonly BoltConnection $connection, private readonly int $fetchSize, - private readonly int $qid - ) {} + private readonly int $qid, + ) { + } public function getFetchSize(): int { diff --git a/src/Bolt/BoltUnmanagedTransaction.php b/src/Bolt/BoltUnmanagedTransaction.php index 9a547551..5b8cdf16 100644 --- a/src/Bolt/BoltUnmanagedTransaction.php +++ b/src/Bolt/BoltUnmanagedTransaction.php @@ -57,8 +57,9 @@ public function __construct( private readonly BoltConnection $connection, private readonly SessionConfiguration $config, private readonly TransactionConfiguration $tsxConfig, - private readonly BookmarkHolder $bookmarkHolder - ) {} + private readonly BookmarkHolder $bookmarkHolder, + ) { + } /** * @throws ClientException|Throwable diff --git a/src/Bolt/Connection.php b/src/Bolt/Connection.php index f4171649..e1145af0 100644 --- a/src/Bolt/Connection.php +++ b/src/Bolt/Connection.php @@ -22,8 +22,9 @@ class Connection */ public function __construct( private readonly IConnection $connection, - private readonly string $ssl - ) {} + private readonly string $ssl, + ) { + } public function getIConnection(): IConnection { diff --git a/src/Bolt/ConnectionPool.php b/src/Bolt/ConnectionPool.php index 2b2bfa25..33bf5e97 100644 --- a/src/Bolt/ConnectionPool.php +++ b/src/Bolt/ConnectionPool.php @@ -43,14 +43,15 @@ public function __construct( private readonly SemaphoreInterface $semaphore, private readonly BoltFactory $factory, private readonly ConnectionRequestData $data, - private readonly ?Neo4jLogger $logger - ) {} + private readonly ?Neo4jLogger $logger, + ) { + } public static function create( UriInterface $uri, AuthenticateInterface $auth, DriverConfiguration $conf, - SemaphoreInterface $semaphore + SemaphoreInterface $semaphore, ): self { return new self( $semaphore, diff --git a/src/Bolt/Session.php b/src/Bolt/Session.php index 8d895d59..8ad4f7e2 100644 --- a/src/Bolt/Session.php +++ b/src/Bolt/Session.php @@ -58,7 +58,7 @@ public function __construct( /** * @psalm-readonly */ - private readonly FormatterInterface $formatter + private readonly FormatterInterface $formatter, ) { $this->bookmarkHolder = new BookmarkHolder(Bookmark::from($config->getBookmarks())); } @@ -137,7 +137,7 @@ public function beginTransaction(?iterable $statements = null, ?TransactionConfi */ private function beginInstantTransaction( SessionConfiguration $config, - TransactionConfiguration $tsxConfig + TransactionConfiguration $tsxConfig, ): TransactionInterface { $this->getLogger()?->log(LogLevel::INFO, 'Starting instant transaction', ['config' => $tsxConfig]); $connection = $this->acquireConnection($tsxConfig, $config); diff --git a/src/Bolt/SocketConnectionFactory.php b/src/Bolt/SocketConnectionFactory.php index 482d885a..0d2a97d7 100644 --- a/src/Bolt/SocketConnectionFactory.php +++ b/src/Bolt/SocketConnectionFactory.php @@ -20,8 +20,9 @@ final class SocketConnectionFactory implements BasicConnectionFactoryInterface { public function __construct( - private readonly StreamConnectionFactory $factory - ) {} + private readonly StreamConnectionFactory $factory, + ) { + } public function create(UriConfiguration $config): Connection { diff --git a/src/Bolt/SystemWideConnectionFactory.php b/src/Bolt/SystemWideConnectionFactory.php index 893be0a5..fe2c1598 100644 --- a/src/Bolt/SystemWideConnectionFactory.php +++ b/src/Bolt/SystemWideConnectionFactory.php @@ -28,8 +28,9 @@ class SystemWideConnectionFactory implements BasicConnectionFactoryInterface * @param SocketConnectionFactory|StreamConnectionFactory $factory */ private function __construct( - private $factory - ) {} + private $factory, + ) { + } /** * @psalm-suppress InvalidNullableReturnType diff --git a/src/Bolt/UriConfiguration.php b/src/Bolt/UriConfiguration.php index 225449b5..c61c4b6d 100644 --- a/src/Bolt/UriConfiguration.php +++ b/src/Bolt/UriConfiguration.php @@ -23,8 +23,9 @@ public function __construct( private readonly ?int $port, private readonly string $sslLevel, private readonly array $sslConfiguration, - private readonly ?float $timeout - ) {} + private readonly ?float $timeout, + ) { + } public function getHost(): string { diff --git a/src/BoltFactory.php b/src/BoltFactory.php index bd0e555d..1d50d3c2 100644 --- a/src/BoltFactory.php +++ b/src/BoltFactory.php @@ -42,8 +42,9 @@ public function __construct( private readonly BasicConnectionFactoryInterface $connectionFactory, private readonly ProtocolFactory $protocolFactory, private readonly SslConfigurationFactory $sslConfigurationFactory, - private readonly ?Neo4jLogger $logger = null - ) {} + private readonly ?Neo4jLogger $logger = null, + ) { + } public static function create(?Neo4jLogger $logger): self { @@ -87,13 +88,13 @@ public function canReuseConnection(ConnectionInterface $connection, ConnectionRe $databaseInfo = $connection->getDatabaseInfo(); $database = $databaseInfo?->getName(); - return $connection->getServerAddress()->getHost() === $data->getUri()->getHost() && - $connection->getServerAddress()->getPort() === $data->getUri()->getPort() && - $connection->getAuthentication()->toString($data->getUri()) === $data->getAuth()->toString($data->getUri()) && - $connection->getEncryptionLevel() === $this->sslConfigurationFactory->create($data->getUri(), $data->getSslConfig())[0] && - $connection->getUserAgent() === $data->getUserAgent() && - $connection->getAccessMode() === $config->getAccessMode() && - $database === $config->getDatabase(); + return $connection->getServerAddress()->getHost() === $data->getUri()->getHost() + && $connection->getServerAddress()->getPort() === $data->getUri()->getPort() + && $connection->getAuthentication()->toString($data->getUri()) === $data->getAuth()->toString($data->getUri()) + && $connection->getEncryptionLevel() === $this->sslConfigurationFactory->create($data->getUri(), $data->getSslConfig())[0] + && $connection->getUserAgent() === $data->getUserAgent() + && $connection->getAccessMode() === $config->getAccessMode() + && $database === $config->getDatabase(); } public function reuseConnection(BoltConnection $connection, SessionConfiguration $sessionConfig): BoltConnection diff --git a/src/Client.php b/src/Client.php index 9d862061..f228a917 100644 --- a/src/Client.php +++ b/src/Client.php @@ -52,8 +52,9 @@ final class Client implements ClientInterface public function __construct( private readonly DriverSetupManager $driverSetups, private readonly SessionConfiguration $defaultSessionConfiguration, - private readonly TransactionConfiguration $defaultTransactionConfiguration - ) {} + private readonly TransactionConfiguration $defaultTransactionConfiguration, + ) { + } public function getDriverSetups(): DriverSetupManager { @@ -84,8 +85,8 @@ private function getRunner(?string $alias = null): TransactionInterface|SessionI { $alias ??= $this->driverSetups->getDefaultAlias(); - if (array_key_exists($alias, $this->boundTransactions) && - count($this->boundTransactions[$alias]) > 0) { + if (array_key_exists($alias, $this->boundTransactions) + && count($this->boundTransactions[$alias]) > 0) { return $this->boundTransactions[$alias][array_key_last($this->boundTransactions[$alias])]; } diff --git a/src/ClientBuilder.php b/src/ClientBuilder.php index 0ec4b497..38628f0b 100644 --- a/src/ClientBuilder.php +++ b/src/ClientBuilder.php @@ -54,7 +54,8 @@ public function __construct( /** @psalm-readonly */ private TransactionConfiguration $defaultTransactionConfig, private DriverSetupManager $driverSetups, - ) {} + ) { + } /** * Creates a client builder with default configurations and an OGMFormatter. diff --git a/src/Common/Cache.php b/src/Common/Cache.php index 5f37089d..9b225e78 100644 --- a/src/Common/Cache.php +++ b/src/Common/Cache.php @@ -47,7 +47,9 @@ class Cache implements CacheInterface private array $items = []; private static ?self $instance = null; - private function __construct() {} + private function __construct() + { + } public static function getInstance(): self { @@ -158,12 +160,11 @@ public function getMultiple($keys, $default = null): Generator } /** - * @param iterable $values - * @param int|DateInterval|null $ttl + * @param iterable $values * * @throws InvalidCacheArgumentException */ - public function setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool + public function setMultiple(iterable $values, int|DateInterval|null $ttl = null): bool { /** * @var mixed $key @@ -185,15 +186,15 @@ public function setMultiple(iterable $values, null|int|DateInterval $ttl = null) */ private function assertValidKey(string $key): void { - if ($key === '' || - str_contains($key, '{') || - str_contains($key, '}') || - str_contains($key, '(') || - str_contains($key, ')') || - str_contains($key, '/') || - str_contains($key, '\\') || - str_contains($key, '@') || - str_contains($key, ':') + if ($key === '' + || str_contains($key, '{') + || str_contains($key, '}') + || str_contains($key, '(') + || str_contains($key, ')') + || str_contains($key, '/') + || str_contains($key, '\\') + || str_contains($key, '@') + || str_contains($key, ':') ) { throw new InvalidCacheArgumentException(); } diff --git a/src/Common/ConnectionConfiguration.php b/src/Common/ConnectionConfiguration.php index fe791c34..3d69fa43 100644 --- a/src/Common/ConnectionConfiguration.php +++ b/src/Common/ConnectionConfiguration.php @@ -33,8 +33,9 @@ public function __construct( private readonly ConnectionProtocol $protocol, private readonly AccessMode $accessMode, private readonly ?DatabaseInfo $databaseInfo, - private readonly string $encryptionLevel - ) {} + private readonly string $encryptionLevel, + ) { + } public function getServerAgent(): string { diff --git a/src/Common/DriverSetupManager.php b/src/Common/DriverSetupManager.php index bb2639d2..1701072a 100644 --- a/src/Common/DriverSetupManager.php +++ b/src/Common/DriverSetupManager.php @@ -56,8 +56,9 @@ class DriverSetupManager implements Countable */ public function __construct( private FormatterInterface $formatter, - private DriverConfiguration $configuration - ) {} + private DriverConfiguration $configuration, + ) { + } public function getDriverConfiguration(): DriverConfiguration { diff --git a/src/Common/Neo4jLogger.php b/src/Common/Neo4jLogger.php index 60276411..40aab4df 100644 --- a/src/Common/Neo4jLogger.php +++ b/src/Common/Neo4jLogger.php @@ -33,7 +33,8 @@ class Neo4jLogger public function __construct( private readonly string $level, private readonly ?LoggerInterface $logger, - ) {} + ) { + } public function log(string $level, string $message, array $context = []): void { diff --git a/src/Common/SingleThreadedSemaphore.php b/src/Common/SingleThreadedSemaphore.php index ada6f4a8..77c8dbe5 100644 --- a/src/Common/SingleThreadedSemaphore.php +++ b/src/Common/SingleThreadedSemaphore.php @@ -27,8 +27,9 @@ class SingleThreadedSemaphore implements SemaphoreInterface private static array $instances = []; private function __construct( - private readonly int $max - ) {} + private readonly int $max, + ) { + } public static function create(string $key, int $max): self { diff --git a/src/Common/SysVSemaphore.php b/src/Common/SysVSemaphore.php index 0845fa21..c3d872f2 100644 --- a/src/Common/SysVSemaphore.php +++ b/src/Common/SysVSemaphore.php @@ -30,8 +30,9 @@ class SysVSemaphore implements SemaphoreInterface { private function __construct( - private readonly \SysvSemaphore $semaphore - ) {} + private readonly \SysvSemaphore $semaphore, + ) { + } public static function create(string $key, int $max): self { diff --git a/src/Common/Uri.php b/src/Common/Uri.php index 6d2393b5..59275a42 100644 --- a/src/Common/Uri.php +++ b/src/Common/Uri.php @@ -38,8 +38,9 @@ public function __construct( private readonly ?int $port, private string $path, private readonly string $query, - private readonly string $fragment - ) {} + private readonly string $fragment, + ) { + } /** * @pure diff --git a/src/Databags/BookmarkHolder.php b/src/Databags/BookmarkHolder.php index 48109d19..9789a054 100644 --- a/src/Databags/BookmarkHolder.php +++ b/src/Databags/BookmarkHolder.php @@ -16,8 +16,9 @@ final class BookmarkHolder { public function __construct( - private Bookmark $bookmark - ) {} + private Bookmark $bookmark, + ) { + } public function getBookmark(): Bookmark { diff --git a/src/Databags/ConnectionRequestData.php b/src/Databags/ConnectionRequestData.php index b5772b1a..764f8573 100644 --- a/src/Databags/ConnectionRequestData.php +++ b/src/Databags/ConnectionRequestData.php @@ -26,8 +26,9 @@ public function __construct( private readonly UriInterface $uri, private readonly AuthenticateInterface $auth, private readonly string $userAgent, - private readonly SslConfiguration $config - ) {} + private readonly SslConfiguration $config, + ) { + } public function getHostname(): string { diff --git a/src/Databags/DatabaseInfo.php b/src/Databags/DatabaseInfo.php index 2f88cab0..0cd90e15 100644 --- a/src/Databags/DatabaseInfo.php +++ b/src/Databags/DatabaseInfo.php @@ -25,8 +25,9 @@ final class DatabaseInfo extends AbstractCypherObject { public function __construct( - private readonly string $name - ) {} + private readonly string $name, + ) { + } /** * Returns the name of the database. diff --git a/src/Databags/DriverConfiguration.php b/src/Databags/DriverConfiguration.php index edbd355d..68c35a87 100644 --- a/src/Databags/DriverConfiguration.php +++ b/src/Databags/DriverConfiguration.php @@ -46,21 +46,21 @@ final class DriverConfiguration private ?Neo4jLogger $logger; /** - * @param callable():(CacheInterface|null)|CacheInterface|null $cache + * @param callable():(CacheInterface|null)|CacheInterface|null $cache * @param callable():(SemaphoreFactoryInterface|null)|SemaphoreFactoryInterface|null $semaphore - * @param string|null $logLevel The log level to use. If null, LogLevel::INFO is used. + * @param string|null $logLevel The log level to use. If null, LogLevel::INFO is used. * * @psalm-external-mutation-free */ public function __construct( - private string|null $userAgent, + private ?string $userAgent, private SslConfiguration $sslConfig, - private int|null $maxPoolSize, + private ?int $maxPoolSize, CacheInterface|callable|null $cache, - private float|null $acquireConnectionTimeout, + private ?float $acquireConnectionTimeout, callable|SemaphoreFactoryInterface|null $semaphore, ?string $logLevel, - ?LoggerInterface $logger + ?LoggerInterface $logger, ) { $this->cache = $cache; $this->semaphoreFactory = $semaphore; diff --git a/src/Databags/DriverSetup.php b/src/Databags/DriverSetup.php index 67066fc4..00317c06 100644 --- a/src/Databags/DriverSetup.php +++ b/src/Databags/DriverSetup.php @@ -25,8 +25,9 @@ final class DriverSetup { public function __construct( private readonly UriInterface $uri, - private readonly AuthenticateInterface $auth - ) {} + private readonly AuthenticateInterface $auth, + ) { + } public function getAuth(): AuthenticateInterface { diff --git a/src/Databags/InputPosition.php b/src/Databags/InputPosition.php index 7e81d118..ca548925 100644 --- a/src/Databags/InputPosition.php +++ b/src/Databags/InputPosition.php @@ -23,8 +23,9 @@ final class InputPosition public function __construct( private readonly int $column, private readonly int $line, - private readonly int $offset - ) {} + private readonly int $offset, + ) { + } /** * The column number referred to by the position; column numbers start at 1. diff --git a/src/Databags/Neo4jError.php b/src/Databags/Neo4jError.php index d592ebed..dab5f788 100644 --- a/src/Databags/Neo4jError.php +++ b/src/Databags/Neo4jError.php @@ -30,8 +30,9 @@ public function __construct( private readonly ?string $message, private readonly string $classification, private readonly string $category, - private readonly string $title - ) {} + private readonly string $title, + ) { + } /** * @pure diff --git a/src/Databags/Notification.php b/src/Databags/Notification.php index c372ccba..f11daf40 100644 --- a/src/Databags/Notification.php +++ b/src/Databags/Notification.php @@ -25,8 +25,9 @@ public function __construct( private readonly string $description, private readonly ?InputPosition $inputPosition, private readonly string $severity, - private readonly string $title - ) {} + private readonly string $title, + ) { + } /** * Returns a notification code for the discovered issue. diff --git a/src/Databags/Pair.php b/src/Databags/Pair.php index 05585b39..247a9383 100644 --- a/src/Databags/Pair.php +++ b/src/Databags/Pair.php @@ -29,8 +29,9 @@ final class Pair */ public function __construct( private $key, - private $value - ) {} + private $value, + ) { + } /** * @return TKey diff --git a/src/Databags/Plan.php b/src/Databags/Plan.php index 024653b7..76a317c0 100644 --- a/src/Databags/Plan.php +++ b/src/Databags/Plan.php @@ -37,8 +37,9 @@ public function __construct( private readonly CypherMap $arguments, private readonly CypherList $list, private readonly CypherList $identifiers, - private readonly string $operator - ) {} + private readonly string $operator, + ) { + } /** * Returns the arguments for the operator. diff --git a/src/Databags/ProfiledPlan.php b/src/Databags/ProfiledPlan.php index 045cd25d..64c325d4 100644 --- a/src/Databags/ProfiledPlan.php +++ b/src/Databags/ProfiledPlan.php @@ -38,8 +38,9 @@ public function __construct( private readonly int $pageCacheHits, private readonly int $pageCacheMisses, private readonly int $records, - private readonly int $time - ) {} + private readonly int $time, + ) { + } /** * @return CypherList diff --git a/src/Databags/ResultSummary.php b/src/Databags/ResultSummary.php index 6992ecda..a5aa05f8 100644 --- a/src/Databags/ResultSummary.php +++ b/src/Databags/ResultSummary.php @@ -46,8 +46,9 @@ public function __construct( private readonly QueryTypeEnum $queryType, private readonly float $resultAvailableAfter, private readonly float $resultConsumedAfter, - private readonly ServerInfo $serverInfo - ) {} + private readonly ServerInfo $serverInfo, + ) { + } /** * The counters for amount of operations the query triggered. diff --git a/src/Databags/ServerInfo.php b/src/Databags/ServerInfo.php index 7408103c..ce8fa011 100644 --- a/src/Databags/ServerInfo.php +++ b/src/Databags/ServerInfo.php @@ -29,8 +29,9 @@ final class ServerInfo extends AbstractCypherObject public function __construct( private readonly UriInterface $address, private readonly ConnectionProtocol $protocol, - private readonly string $agent - ) {} + private readonly string $agent, + ) { + } /** * Returns the uri of the server the query was executed. diff --git a/src/Databags/SessionConfiguration.php b/src/Databags/SessionConfiguration.php index 56adcdc9..397e864a 100644 --- a/src/Databags/SessionConfiguration.php +++ b/src/Databags/SessionConfiguration.php @@ -37,18 +37,19 @@ final class SessionConfiguration */ public function __construct( private readonly ?string $database = null, - private readonly int|null $fetchSize = null, - private readonly AccessMode|null $accessMode = null, - private readonly array|null $bookmarks = null, + private readonly ?int $fetchSize = null, + private readonly ?AccessMode $accessMode = null, + private readonly ?array $bookmarks = null, private readonly ?Neo4jLogger $logger = null, - ) {} + ) { + } /** * @pure * * @param list|null $bookmarks */ - public static function create(string|null $database = null, int|null $fetchSize = null, AccessMode|null $defaultAccessMode = null, array|null $bookmarks = null, ?Neo4jLogger $logger = null): self + public static function create(?string $database = null, ?int $fetchSize = null, ?AccessMode $defaultAccessMode = null, ?array $bookmarks = null, ?Neo4jLogger $logger = null): self { return new self($database, $fetchSize, $defaultAccessMode, $bookmarks, $logger); } diff --git a/src/Databags/SslConfiguration.php b/src/Databags/SslConfiguration.php index 58741c77..07fd5c4e 100644 --- a/src/Databags/SslConfiguration.php +++ b/src/Databags/SslConfiguration.php @@ -22,8 +22,9 @@ final class SslConfiguration { public function __construct( private SslMode $mode, - private bool $verifyPeer - ) {} + private bool $verifyPeer, + ) { + } public function getMode(): SslMode { diff --git a/src/Databags/Statement.php b/src/Databags/Statement.php index f3cc3731..a7ddd8a4 100644 --- a/src/Databags/Statement.php +++ b/src/Databags/Statement.php @@ -31,8 +31,9 @@ final class Statement extends AbstractCypherObject */ public function __construct( private readonly string $text, - private readonly iterable $parameters - ) {} + private readonly iterable $parameters, + ) { + } /** * @pure diff --git a/src/Databags/SummarizedResult.php b/src/Databags/SummarizedResult.php index e72249a2..636ae10b 100644 --- a/src/Databags/SummarizedResult.php +++ b/src/Databags/SummarizedResult.php @@ -42,7 +42,7 @@ public function __construct(?ResultSummary &$summary, $iterable = []) /** * @template Value * - * @param callable():(\Generator) $operation + * @param callable():(Generator) $operation * * @return static * diff --git a/src/Databags/SummaryCounters.php b/src/Databags/SummaryCounters.php index 6df81975..9fb4efbc 100644 --- a/src/Databags/SummaryCounters.php +++ b/src/Databags/SummaryCounters.php @@ -38,8 +38,9 @@ public function __construct( private readonly int $constraintsRemoved = 0, private readonly bool $containsUpdates = false, private readonly bool $containsSystemUpdates = false, - private readonly int $systemUpdates = 0 - ) {} + private readonly int $systemUpdates = 0, + ) { + } /** * Whether the query contained any updates. diff --git a/src/Databags/TransactionConfiguration.php b/src/Databags/TransactionConfiguration.php index c1dacf0e..bc9a68d9 100644 --- a/src/Databags/TransactionConfiguration.php +++ b/src/Databags/TransactionConfiguration.php @@ -28,9 +28,10 @@ final class TransactionConfiguration * @param iterable|null $metaData */ public function __construct( - private float|null $timeout = null, - private iterable|null $metaData = null - ) {} + private ?float $timeout = null, + private ?iterable $metaData = null, + ) { + } /** * @pure @@ -38,7 +39,7 @@ public function __construct( * @param float|null $timeout timeout in seconds * @param iterable|null $metaData */ - public static function create(float|null $timeout = null, iterable|null $metaData = null): self + public static function create(?float $timeout = null, ?iterable $metaData = null): self { return new self($timeout, $metaData); } @@ -74,7 +75,7 @@ public function getTimeout(): ?float * * @param float|null $timeout timeout in seconds */ - public function withTimeout(float|null $timeout): self + public function withTimeout(?float $timeout): self { return new self($timeout, $this->metaData); } @@ -84,7 +85,7 @@ public function withTimeout(float|null $timeout): self * * @param iterable|null $metaData */ - public function withMetaData(iterable|null $metaData): self + public function withMetaData(?iterable $metaData): self { return new self($this->timeout, $metaData); } diff --git a/src/Formatter/BasicFormatter.php b/src/Formatter/BasicFormatter.php index 766a0d13..8f86a584 100644 --- a/src/Formatter/BasicFormatter.php +++ b/src/Formatter/BasicFormatter.php @@ -151,10 +151,8 @@ private function mapPath(Path $path): array * @var mixed $node */ foreach ($nodes as $i => $node) { - /** @var mixed */ $tbr[] = $node; if (array_key_exists($i, $relationships)) { - /** @var mixed */ $tbr[] = $relationships[$i]; } } diff --git a/src/Formatter/OGMFormatter.php b/src/Formatter/OGMFormatter.php index 6de6b90e..8cdc1304 100644 --- a/src/Formatter/OGMFormatter.php +++ b/src/Formatter/OGMFormatter.php @@ -60,7 +60,8 @@ final class OGMFormatter implements FormatterInterface */ public function __construct( private readonly BoltOGMTranslator $boltTranslator, - ) {} + ) { + } /** * Creates a new instance of itself. diff --git a/src/Formatter/Specialised/BoltOGMTranslator.php b/src/Formatter/Specialised/BoltOGMTranslator.php index bdeb5ecd..a53e4c4a 100644 --- a/src/Formatter/Specialised/BoltOGMTranslator.php +++ b/src/Formatter/Specialised/BoltOGMTranslator.php @@ -107,6 +107,7 @@ private function makeFromBoltNode(BoltNode $node): Node if ($node instanceof \Bolt\protocol\v5\structures\Node) { $elementId = $node->element_id; } + /** * @psalm-suppress MixedArgumentTypeCoercion */ @@ -219,7 +220,7 @@ private function makeFromBoltUnboundRelationship(BoltUnboundRelationship $rel): ); } - private function makeFromBoltPoint2D(BoltPoint2d $x): AbstractPoint + private function makeFromBoltPoint2D(BoltPoint2D $x): AbstractPoint { if ($x->srid === CartesianPoint::SRID) { return new CartesianPoint($x->x, $x->y); diff --git a/src/Formatter/SummarizedResultFormatter.php b/src/Formatter/SummarizedResultFormatter.php index 27b29842..131e5790 100644 --- a/src/Formatter/SummarizedResultFormatter.php +++ b/src/Formatter/SummarizedResultFormatter.php @@ -57,8 +57,9 @@ public static function create(): self * @psalm-mutation-free */ public function __construct( - private readonly OGMFormatter $formatter - ) {} + private readonly OGMFormatter $formatter, + ) { + } /** * @param array{stats?: BoltCypherStats}&array $response diff --git a/src/Neo4j/Neo4jConnectionPool.php b/src/Neo4j/Neo4jConnectionPool.php index e3a064be..e4ad758d 100644 --- a/src/Neo4j/Neo4jConnectionPool.php +++ b/src/Neo4j/Neo4jConnectionPool.php @@ -74,14 +74,15 @@ public function __construct( private readonly CacheInterface $cache, private readonly AddressResolverInterface $resolver, private readonly ?Neo4jLogger $logger, - ) {} + ) { + } public static function create( UriInterface $uri, AuthenticateInterface $auth, DriverConfiguration $conf, AddressResolverInterface $resolver, - SemaphoreInterface $semaphore + SemaphoreInterface $semaphore, ): self { return new self( $semaphore, diff --git a/src/Neo4j/Neo4jDriver.php b/src/Neo4j/Neo4jDriver.php index 004be4d7..1e0a51cd 100644 --- a/src/Neo4j/Neo4jDriver.php +++ b/src/Neo4j/Neo4jDriver.php @@ -53,8 +53,9 @@ final class Neo4jDriver implements DriverInterface public function __construct( private readonly UriInterface $parsedUrl, private readonly Neo4jConnectionPool $pool, - private readonly FormatterInterface $formatter - ) {} + private readonly FormatterInterface $formatter, + ) { + } /** * @template U diff --git a/src/Neo4j/RoutingTable.php b/src/Neo4j/RoutingTable.php index 3b5b2b76..4e6d7539 100644 --- a/src/Neo4j/RoutingTable.php +++ b/src/Neo4j/RoutingTable.php @@ -29,8 +29,9 @@ final class RoutingTable */ public function __construct( private readonly iterable $servers, - private readonly int $ttl - ) {} + private readonly int $ttl, + ) { + } /** * Returns the time to live in seconds. diff --git a/src/ParameterHelper.php b/src/ParameterHelper.php index 3d7c20fa..35ae31d2 100644 --- a/src/ParameterHelper.php +++ b/src/ParameterHelper.php @@ -81,7 +81,7 @@ public static function asMap(iterable $iterable): CypherMap */ public static function asParameter( mixed $value, - ConnectionProtocol $protocol + ConnectionProtocol $protocol, ): iterable|int|float|bool|string|stdClass|IStructure|null { return self::cypherMapToStdClass($value) ?? self::emptySequenceToArray($value) ?? @@ -121,8 +121,8 @@ private static function filterInvalidType(mixed $value): mixed private static function emptySequenceToArray(mixed $value): ?array { - if ((($value instanceof CypherList || $value instanceof CypherMap) && $value->count() === 0) || - (is_array($value) && count($value) === 0)) { + if ((($value instanceof CypherList || $value instanceof CypherMap) && $value->count() === 0) + || (is_array($value) && count($value) === 0)) { return []; } diff --git a/src/TypeCaster.php b/src/TypeCaster.php index cd0ecd41..053ca90c 100644 --- a/src/TypeCaster.php +++ b/src/TypeCaster.php @@ -117,7 +117,6 @@ public static function toArray(mixed $value): ?array $tbr = []; /** @var mixed $x */ foreach ($value as $x) { - /** @var mixed */ $tbr[] = $x; } diff --git a/src/Types/Abstract3DPoint.php b/src/Types/Abstract3DPoint.php index 9d645e70..320c84a2 100644 --- a/src/Types/Abstract3DPoint.php +++ b/src/Types/Abstract3DPoint.php @@ -37,7 +37,7 @@ public function convertToBolt(): IStructure public function __construct( float $x, float $y, - private float $z + private float $z, ) { parent::__construct($x, $y); } diff --git a/src/Types/AbstractPoint.php b/src/Types/AbstractPoint.php index ac5da8cf..90765c2f 100644 --- a/src/Types/AbstractPoint.php +++ b/src/Types/AbstractPoint.php @@ -33,8 +33,9 @@ abstract class AbstractPoint extends AbstractPropertyObject implements PointInte { public function __construct( private readonly float $x, - private readonly float $y - ) {} + private readonly float $y, + ) { + } abstract public function getCrs(): string; diff --git a/src/Types/ArrayList.php b/src/Types/ArrayList.php index b41fe92f..30e6747f 100644 --- a/src/Types/ArrayList.php +++ b/src/Types/ArrayList.php @@ -59,7 +59,7 @@ public function __construct($iterable = []) /** * @template Value * - * @param callable():(\Generator) $operation + * @param callable():(Generator) $operation * * @return static * diff --git a/src/Types/CypherMap.php b/src/Types/CypherMap.php index 2a78832d..22426e80 100644 --- a/src/Types/CypherMap.php +++ b/src/Types/CypherMap.php @@ -35,7 +35,6 @@ public function getAsCypherMap(string $key, mixed $default = null): CypherMap if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } $tbr = TypeCaster::toCypherMap($value); @@ -54,7 +53,6 @@ public function getAsCypherList(string $key, mixed $default = null): CypherList if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } $tbr = TypeCaster::toCypherList($value); diff --git a/src/Types/Date.php b/src/Types/Date.php index 070d5ec8..ed85650d 100644 --- a/src/Types/Date.php +++ b/src/Types/Date.php @@ -31,8 +31,9 @@ final class Date extends AbstractPropertyObject implements BoltConvertibleInterface { public function __construct( - private readonly int $days - ) {} + private readonly int $days, + ) { + } /** * The amount of days since unix epoch. diff --git a/src/Types/DateTime.php b/src/Types/DateTime.php index f4a1b421..6d565c0c 100644 --- a/src/Types/DateTime.php +++ b/src/Types/DateTime.php @@ -34,8 +34,9 @@ public function __construct( private readonly int $seconds, private readonly int $nanoseconds, private readonly int $tzOffsetSeconds, - private readonly bool $legacy - ) {} + private readonly bool $legacy, + ) { + } /** * Returns whether this DateTime Type follows conventions up until Neo4j version 4. diff --git a/src/Types/DateTimeZoneId.php b/src/Types/DateTimeZoneId.php index b734aebd..1ebf88a1 100644 --- a/src/Types/DateTimeZoneId.php +++ b/src/Types/DateTimeZoneId.php @@ -40,8 +40,9 @@ final class DateTimeZoneId extends AbstractPropertyObject implements BoltConvert public function __construct( private readonly int $seconds, private readonly int $nanoseconds, - private readonly string $tzId - ) {} + private readonly string $tzId, + ) { + } /** * Returns the amount of seconds since unix epoch. diff --git a/src/Types/Duration.php b/src/Types/Duration.php index cf0dd293..611468b6 100644 --- a/src/Types/Duration.php +++ b/src/Types/Duration.php @@ -31,8 +31,9 @@ public function __construct( private readonly int $months, private readonly int $days, private readonly int $seconds, - private readonly int $nanoseconds - ) {} + private readonly int $nanoseconds, + ) { + } /** * The amount of months in the duration. diff --git a/src/Types/LocalDateTime.php b/src/Types/LocalDateTime.php index 114d09b6..0c851fbe 100644 --- a/src/Types/LocalDateTime.php +++ b/src/Types/LocalDateTime.php @@ -35,8 +35,9 @@ final class LocalDateTime extends AbstractPropertyObject implements BoltConverti { public function __construct( private readonly int $seconds, - private readonly int $nanoseconds - ) {} + private readonly int $nanoseconds, + ) { + } /** * The amount of seconds since the unix epoch. diff --git a/src/Types/LocalTime.php b/src/Types/LocalTime.php index f53b8644..24a8ba05 100644 --- a/src/Types/LocalTime.php +++ b/src/Types/LocalTime.php @@ -26,8 +26,9 @@ final class LocalTime extends AbstractPropertyObject implements BoltConvertibleInterface { public function __construct( - private readonly int $nanoseconds - ) {} + private readonly int $nanoseconds, + ) { + } /** * The nanoseconds that have passed since midnight. diff --git a/src/Types/Map.php b/src/Types/Map.php index 2ca1c775..712f0b9d 100644 --- a/src/Types/Map.php +++ b/src/Types/Map.php @@ -89,7 +89,7 @@ public function __construct($iterable = []) /** * @template Value * - * @param callable():(\Generator) $operation + * @param callable():(Generator) $operation * * @return static * @@ -364,7 +364,6 @@ public function getAsString(string $key, mixed $default = null): string if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } $tbr = TypeCaster::toString($value); @@ -380,7 +379,6 @@ public function getAsInt(string $key, mixed $default = null): int if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } $tbr = TypeCaster::toInt($value); @@ -396,7 +394,6 @@ public function getAsFloat(string $key, mixed $default = null): float if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } $tbr = TypeCaster::toFloat($value); @@ -412,7 +409,6 @@ public function getAsBool(string $key, mixed $default = null): bool if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } $tbr = TypeCaster::toBool($value); @@ -448,7 +444,6 @@ public function getAsObject(string $key, string $class, mixed $default = null): if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } $tbr = TypeCaster::toClass($value, $class); @@ -467,7 +462,6 @@ public function getAsMap(string $key, mixed $default = null): Map if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } @@ -486,7 +480,6 @@ public function getAsArrayList(string $key, mixed $default = null): ArrayList if (func_num_args() === 1) { $value = $this->get($key); } else { - /** @var mixed */ $value = $this->get($key, $default); } if (!is_iterable($value)) { diff --git a/src/Types/Node.php b/src/Types/Node.php index b747781b..45f95e55 100644 --- a/src/Types/Node.php +++ b/src/Types/Node.php @@ -38,8 +38,9 @@ public function __construct( private readonly int $id, private readonly CypherList $labels, private readonly CypherMap $properties, - private readonly ?string $elementId - ) {} + private readonly ?string $elementId, + ) { + } /** * The labels on the node. diff --git a/src/Types/Path.php b/src/Types/Path.php index 2e18a9c2..a52c77b3 100644 --- a/src/Types/Path.php +++ b/src/Types/Path.php @@ -30,8 +30,9 @@ final class Path extends AbstractPropertyObject public function __construct( private readonly CypherList $nodes, private readonly CypherList $relationships, - private readonly CypherList $ids - ) {} + private readonly CypherList $ids, + ) { + } /** * Returns the node in the path. diff --git a/src/Types/Relationship.php b/src/Types/Relationship.php index 280a6679..1973bc24 100644 --- a/src/Types/Relationship.php +++ b/src/Types/Relationship.php @@ -31,7 +31,7 @@ public function __construct( private readonly int $endNodeId, string $type, CypherMap $properties, - ?string $elementId + ?string $elementId, ) { parent::__construct($id, $type, $properties, $elementId); } diff --git a/src/Types/Time.php b/src/Types/Time.php index 626c16e7..54f87719 100644 --- a/src/Types/Time.php +++ b/src/Types/Time.php @@ -27,8 +27,9 @@ final class Time extends AbstractPropertyObject implements BoltConvertibleInterf { public function __construct( private readonly int $nanoSeconds, - private readonly int $tzOffsetSeconds - ) {} + private readonly int $tzOffsetSeconds, + ) { + } /** * @return array{nanoSeconds: int, tzOffsetSeconds: int} diff --git a/src/Types/UnboundRelationship.php b/src/Types/UnboundRelationship.php index 6f7a53a4..ddf49c80 100644 --- a/src/Types/UnboundRelationship.php +++ b/src/Types/UnboundRelationship.php @@ -35,8 +35,9 @@ public function __construct( private readonly int $id, private readonly string $type, private readonly CypherMap $properties, - private readonly ?string $elementId - ) {} + private readonly ?string $elementId, + ) { + } public function getElementId(): ?string { diff --git a/tests/Integration/TransactionIntegrationTest.php b/tests/Integration/TransactionIntegrationTest.php index e575129b..cbd56b2a 100644 --- a/tests/Integration/TransactionIntegrationTest.php +++ b/tests/Integration/TransactionIntegrationTest.php @@ -279,40 +279,40 @@ public function testRollbackInvalid(): void self::assertFalse($tsx->isCommitted()); } -// /** -// * TODO - rework this test -// * @dataProvider connectionAliases -// * @noinspection PhpUnusedLocalVariableInspection -// * @psalm-suppress UnusedVariable -// */ -// public function testCorrectConnectionReuse(): void -// { -// $driver = $this->getSession()->getDriver($alias); -// if (!$driver instanceof BoltDriver) { -// self::markTestSkipped('Can only white box test bolt driver'); -// } -// -// $poolReflection = new ReflectionClass(Connection::class); -// $poolReflection->setStaticPropertyValue('connectionCache', []); -// -// $this->getSession()->run('MATCH (x) RETURN x', []); -// $this->getSession()->run('MATCH (x) RETURN x', []); -// $this->getSession()->run('MATCH (x) RETURN x', []); -// $this->getSession()->run('MATCH (x) RETURN x', []); -// $a = $this->getSession()->beginTransaction([]); -// $b = $this->getSession()->beginTransaction([]); -// $this->getSession()->run('MATCH (x) RETURN x', []); -// -// $poolReflection = new ReflectionClass(ConnectionPool::class); -// /** @var array $cache */ -// $cache = $poolReflection->getStaticPropertyValue('connectionCache'); -// -// $key = array_key_first($cache); -// self::assertIsString($key); -// self::assertArrayHasKey($key, $cache); -// /** @psalm-suppress MixedArgument */ -// self::assertCount(3, $cache[$key]); -// } + // /** + // * TODO - rework this test + // * @dataProvider connectionAliases + // * @noinspection PhpUnusedLocalVariableInspection + // * @psalm-suppress UnusedVariable + // */ + // public function testCorrectConnectionReuse(): void + // { + // $driver = $this->getSession()->getDriver($alias); + // if (!$driver instanceof BoltDriver) { + // self::markTestSkipped('Can only white box test bolt driver'); + // } + // + // $poolReflection = new ReflectionClass(Connection::class); + // $poolReflection->setStaticPropertyValue('connectionCache', []); + // + // $this->getSession()->run('MATCH (x) RETURN x', []); + // $this->getSession()->run('MATCH (x) RETURN x', []); + // $this->getSession()->run('MATCH (x) RETURN x', []); + // $this->getSession()->run('MATCH (x) RETURN x', []); + // $a = $this->getSession()->beginTransaction([]); + // $b = $this->getSession()->beginTransaction([]); + // $this->getSession()->run('MATCH (x) RETURN x', []); + // + // $poolReflection = new ReflectionClass(ConnectionPool::class); + // /** @var array $cache */ + // $cache = $poolReflection->getStaticPropertyValue('connectionCache'); + // + // $key = array_key_first($cache); + // self::assertIsString($key); + // self::assertArrayHasKey($key, $cache); + // /** @psalm-suppress MixedArgument */ + // self::assertCount(3, $cache[$key]); + // } #[DoesNotPerformAssertions] public function testTransactionRunNoConsumeResult(): void diff --git a/tests/Unit/CypherMapTest.php b/tests/Unit/CypherMapTest.php index 848112b7..fc63025f 100644 --- a/tests/Unit/CypherMapTest.php +++ b/tests/Unit/CypherMapTest.php @@ -426,7 +426,7 @@ public function testSkipInvalid(): void public function testInvalidConstruct(): void { /** @psalm-suppress MissingTemplateParam */ - $map = new CypherMap(new class() implements IteratorAggregate { + $map = new CypherMap(new class implements IteratorAggregate { public function getIterator(): Generator { yield new stdClass() => 'x'; diff --git a/tests/Unit/ParameterHelperTest.php b/tests/Unit/ParameterHelperTest.php index 83ce18a0..b45e294b 100644 --- a/tests/Unit/ParameterHelperTest.php +++ b/tests/Unit/ParameterHelperTest.php @@ -36,7 +36,7 @@ public static function setUpBeforeClass(): void * @psalm-suppress MixedPropertyTypeCoercion * @psalm-suppress MissingTemplateParam */ - self::$invalidIterable = new class() implements Iterator { + self::$invalidIterable = new class implements Iterator { private bool $initial = true; public function current(): int @@ -144,7 +144,7 @@ public function testAsParameterEmptyArray(): void public function testStringable(): void { - $result = ParameterHelper::asParameter(new class() implements Stringable { + $result = ParameterHelper::asParameter(new class implements Stringable { public function __toString(): string { return 'abc'; From 401b24116a9e6dc59433d94dbb3221b9ed8bafc9 Mon Sep 17 00:00:00 2001 From: p123-stack Date: Fri, 2 May 2025 11:11:40 +0530 Subject: [PATCH 6/8] Extracted HELLO, LOGON, and LOGOFF message creation into dedicated classes (#254) * Extracted HELLO, LOGON, and LOGOFF message creation into dedicated classes - Updated BasicAuth, KerberosAuth, NoAuth, and OpenIDConnectAuth to utilize new message classes - Added unit tests for each message class and authentication strategy * Fixed cluster tests --------- Co-authored-by: pratikshazalte69 --- .../integration-test-cluster-neo4j-4.yml | 5 +- .../integration-test-cluster-neo4j-5.yml | 5 +- src/Authentication/Authenticate.php | 10 +- src/Authentication/BasicAuth.php | 61 +++++--- src/Authentication/KerberosAuth.php | 65 ++++----- src/Authentication/NoAuth.php | 56 +++---- src/Authentication/OpenIDConnectAuth.php | 64 ++++---- src/Bolt/BoltMessageFactory.php | 24 ++- src/Bolt/Messages/BoltHelloMessage.php | 57 ++++++++ src/Bolt/Messages/BoltLogoffMessage.php | 55 +++++++ src/Bolt/Messages/BoltLogonMessage.php | 60 ++++++++ src/ClientBuilder.php | 3 - .../Integration/BoltDriverIntegrationTest.php | 8 + tests/Unit/BasicAuthTest.php | 104 +++++++++++++ tests/Unit/KerberosAuthTest.php | 110 ++++++++++++++ tests/Unit/NoAuthTest.php | 137 ++++++++++++++++++ tests/Unit/OpenIDConnectionAuthTest.php | 122 ++++++++++++++++ 17 files changed, 813 insertions(+), 133 deletions(-) create mode 100644 src/Bolt/Messages/BoltHelloMessage.php create mode 100644 src/Bolt/Messages/BoltLogoffMessage.php create mode 100644 src/Bolt/Messages/BoltLogonMessage.php create mode 100644 tests/Unit/BasicAuthTest.php create mode 100644 tests/Unit/KerberosAuthTest.php create mode 100644 tests/Unit/NoAuthTest.php create mode 100644 tests/Unit/OpenIDConnectionAuthTest.php diff --git a/.github/workflows/integration-test-cluster-neo4j-4.yml b/.github/workflows/integration-test-cluster-neo4j-4.yml index f835e40d..70366c83 100644 --- a/.github/workflows/integration-test-cluster-neo4j-4.yml +++ b/.github/workflows/integration-test-cluster-neo4j-4.yml @@ -14,9 +14,6 @@ jobs: strategy: matrix: php: [8.1, 8.3] - env: - PHP_VERSION: ${{ matrix.php }} - CONNECTION: neo4j://neo4j:testtest@localhost:7688 name: "Running on PHP ${{ matrix.php }} in a Neo4j 4.4 cluster" steps: @@ -24,7 +21,7 @@ jobs: - name: Populate .env run: | echo "PHP_VERSION=${{ matrix.php }}" > .env - echo "CONNECTION=neo4j://neo4j:testtest@neo4j" >> .env + echo "CONNECTION=neo4j://neo4j:testtest@server1" >> .env - uses: hoverkraft-tech/compose-action@v2.0.2 name: Start services with: diff --git a/.github/workflows/integration-test-cluster-neo4j-5.yml b/.github/workflows/integration-test-cluster-neo4j-5.yml index 43d19227..58b32d6a 100644 --- a/.github/workflows/integration-test-cluster-neo4j-5.yml +++ b/.github/workflows/integration-test-cluster-neo4j-5.yml @@ -14,9 +14,6 @@ jobs: strategy: matrix: php: [8.1, 8.3] - env: - PHP_VERSION: ${{ matrix.php }} - CONNECTION: neo4j://neo4j:testtest@localhost:7687 name: "Running on PHP ${{ matrix.php }} with a Neo4j 5.20-enterprise cluster" steps: @@ -24,7 +21,7 @@ jobs: - name: Populate .env run: | echo "PHP_VERSION=${{ matrix.php }}" > .env - echo "CONNECTION=neo4j://neo4j:testtest@neo4j" >> .env + echo "CONNECTION=neo4j://neo4j:testtest@server1" >> .env - uses: hoverkraft-tech/compose-action@v2.0.2 name: Start services with: diff --git a/src/Authentication/Authenticate.php b/src/Authentication/Authenticate.php index 2b711146..7e5cd742 100644 --- a/src/Authentication/Authenticate.php +++ b/src/Authentication/Authenticate.php @@ -35,13 +35,13 @@ final class Authenticate */ public static function basic(string $username, string $password, ?Neo4jLogger $logger = null): BasicAuth { + /** @psalm-suppress ImpureMethodCall Uri is a pure object */ + return new BasicAuth($username, $password, $logger); } /** * Authenticate using a kerberos token. - * - * @pure */ public static function kerberos(string $token, ?Neo4jLogger $logger = null): KerberosAuth { @@ -50,8 +50,6 @@ public static function kerberos(string $token, ?Neo4jLogger $logger = null): Ker /** * Authenticate using a OpenID Connect token. - * - * @pure */ public static function oidc(string $token, ?Neo4jLogger $logger = null): OpenIDConnectAuth { @@ -60,8 +58,6 @@ public static function oidc(string $token, ?Neo4jLogger $logger = null): OpenIDC /** * Don't authenticate at all. - * - * @pure */ public static function disabled(?Neo4jLogger $logger = null): NoAuth { @@ -70,8 +66,6 @@ public static function disabled(?Neo4jLogger $logger = null): NoAuth /** * Authenticate from information found in the url. - * - * @pure */ public static function fromUrl(UriInterface $uri, ?Neo4jLogger $logger = null): AuthenticateInterface { diff --git a/src/Authentication/BasicAuth.php b/src/Authentication/BasicAuth.php index 82ef2d65..a1d7bdcc 100644 --- a/src/Authentication/BasicAuth.php +++ b/src/Authentication/BasicAuth.php @@ -20,20 +20,17 @@ use Bolt\protocol\V5_3; use Bolt\protocol\V5_4; use Exception; +use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; use Psr\Http\Message\UriInterface; -use Psr\Log\LogLevel; /** * Authenticates connections using a basic username and password. */ final class BasicAuth implements AuthenticateInterface { - /** - * @psalm-external-mutation-free - */ public function __construct( private readonly string $username, private readonly string $password, @@ -48,36 +45,60 @@ public function __construct( */ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array { + $factory = $this->createMessageFactory($protocol); + if (method_exists($protocol, 'logon')) { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent]); - $protocol->hello(['user_agent' => $userAgent]); + $helloMetadata = ['user_agent' => $userAgent]; + + $factory->createHelloMessage($helloMetadata)->send(); $response = ResponseHelper::getResponse($protocol); - $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'basic', 'principal' => $this->username]); - $protocol->logon([ + + $credentials = [ 'scheme' => 'basic', 'principal' => $this->username, 'credentials' => $this->password, - ]); + ]; + + $factory->createLogonMessage($credentials)->send(); ResponseHelper::getResponse($protocol); /** @var array{server: string, connection_id: string, hints: list} */ return $response->content; - } else { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent, 'scheme' => 'basic', 'principal' => $this->username]); - $protocol->hello([ - 'user_agent' => $userAgent, - 'scheme' => 'basic', - 'principal' => $this->username, - 'credentials' => $this->password, - ]); - - /** @var array{server: string, connection_id: string, hints: list} */ - return ResponseHelper::getResponse($protocol)->content; } + + $helloMetadata = [ + 'user_agent' => $userAgent, + 'scheme' => 'basic', + 'principal' => $this->username, + 'credentials' => $this->password, + ]; + + $factory->createHelloMessage($helloMetadata)->send(); + + /** @var array{server: string, connection_id: string, hints: list} */ + return ResponseHelper::getResponse($protocol)->content; + } + + /** + * @throws Exception + */ + public function logoff(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): void + { + $factory = $this->createMessageFactory($protocol); + $factory->createLogoffMessage()->send(); + ResponseHelper::getResponse($protocol); } public function toString(UriInterface $uri): string { return sprintf('Basic %s:%s@%s:%s', $this->username, '######', $uri->getHost(), $uri->getPort() ?? ''); } + + /** + * Helper to create message factory. + */ + private function createMessageFactory(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): BoltMessageFactory + { + return new BoltMessageFactory($protocol, $this->logger); + } } diff --git a/src/Authentication/KerberosAuth.php b/src/Authentication/KerberosAuth.php index 51656d65..e50a27dc 100644 --- a/src/Authentication/KerberosAuth.php +++ b/src/Authentication/KerberosAuth.php @@ -20,6 +20,7 @@ use Bolt\protocol\V5_3; use Bolt\protocol\V5_4; use Exception; +use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; @@ -34,9 +35,6 @@ */ final class KerberosAuth implements AuthenticateInterface { - /** - * @psalm-external-mutation-free - */ public function __construct( private readonly string $token, private readonly ?Neo4jLogger $logger, @@ -47,11 +45,6 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s { $this->logger?->log(LogLevel::DEBUG, 'Authenticating using KerberosAuth'); - /** - * @psalm-suppress ImpureMethodCall Request is a pure object: - * - * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects - */ return $request->withHeader('Authorization', 'Kerberos '.$this->token) ->withHeader('User-Agent', $userAgent); } @@ -63,36 +56,40 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s */ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array { - if (method_exists($protocol, 'logon')) { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent]); - $protocol->hello(['user_agent' => $userAgent]); - $response = ResponseHelper::getResponse($protocol); - $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'kerberos', 'principal' => '']); - $protocol->logon([ - 'scheme' => 'kerberos', - 'principal' => '', - 'credentials' => $this->token, - ]); - ResponseHelper::getResponse($protocol); - - /** @var array{server: string, connection_id: string, hints: list} */ - return $response->content; - } else { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent, 'scheme' => 'kerberos', 'principal' => '']); - $protocol->hello([ - 'user_agent' => $userAgent, - 'scheme' => 'kerberos', - 'principal' => '', - 'credentials' => $this->token, - ]); - - /** @var array{server: string, connection_id: string, hints: list} */ - return ResponseHelper::getResponse($protocol)->content; - } + $factory = $this->createMessageFactory($protocol); + + $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent]); + + $factory->createHelloMessage(['user_agent' => $userAgent])->send(); + + $response = ResponseHelper::getResponse($protocol); + + $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'kerberos', 'principal' => '']); + + $factory->createLogonMessage([ + 'scheme' => 'kerberos', + 'principal' => '', + 'credentials' => $this->token, + ])->send(); + + ResponseHelper::getResponse($protocol); + + /** + * @var array{server: string, connection_id: string, hints: list} + */ + return $response->content; } public function toString(UriInterface $uri): string { return sprintf('Kerberos %s@%s:%s', $this->token, $uri->getHost(), $uri->getPort() ?? ''); } + + /** + * Helper to create the message factory. + */ + private function createMessageFactory(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): BoltMessageFactory + { + return new BoltMessageFactory($protocol, $this->logger); + } } diff --git a/src/Authentication/NoAuth.php b/src/Authentication/NoAuth.php index 69d7ad07..80b1b1b9 100644 --- a/src/Authentication/NoAuth.php +++ b/src/Authentication/NoAuth.php @@ -20,6 +20,7 @@ use Bolt\protocol\V5_3; use Bolt\protocol\V5_4; use Exception; +use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; @@ -29,14 +30,8 @@ use function sprintf; -/** - * Doesn't authenticate connections. - */ final class NoAuth implements AuthenticateInterface { - /** - * @pure - */ public function __construct( private readonly ?Neo4jLogger $logger, ) { @@ -46,11 +41,6 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s { $this->logger?->log(LogLevel::DEBUG, 'Authentication disabled'); - /** - * @psalm-suppress ImpureMethodCall Request is a pure object: - * - * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects - */ return $request->withHeader('User-Agent', $userAgent); } @@ -61,32 +51,46 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s */ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array { + $factory = $this->createMessageFactory($protocol); + if (method_exists($protocol, 'logon')) { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent]); - $protocol->hello(['user_agent' => $userAgent]); + $helloMetadata = ['user_agent' => $userAgent]; + + $factory->createHelloMessage($helloMetadata)->send(); $response = ResponseHelper::getResponse($protocol); - $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'none']); - $protocol->logon([ - 'scheme' => 'none', - ]); + + $factory->createLogonMessage(['scheme' => 'none'])->send(); ResponseHelper::getResponse($protocol); /** @var array{server: string, connection_id: string, hints: list} */ return $response->content; - } else { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent, 'scheme' => 'none']); - $protocol->hello([ - 'user_agent' => $userAgent, - 'scheme' => 'none', - ]); - - /** @var array{server: string, connection_id: string, hints: list} */ - return ResponseHelper::getResponse($protocol)->content; } + + $helloMetadata = [ + 'user_agent' => $userAgent, + 'scheme' => 'none', + ]; + + $factory->createHelloMessage($helloMetadata)->send(); + + /** @var array{server: string, connection_id: string, hints: list} */ + return ResponseHelper::getResponse($protocol)->content; + } + + public function logoff(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): void + { + $factory = $this->createMessageFactory($protocol); + $factory->createLogoffMessage()->send(); + ResponseHelper::getResponse($protocol); } public function toString(UriInterface $uri): string { return sprintf('No Auth %s:%s', $uri->getHost(), $uri->getPort() ?? ''); } + + private function createMessageFactory(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): BoltMessageFactory + { + return new BoltMessageFactory($protocol, $this->logger); + } } diff --git a/src/Authentication/OpenIDConnectAuth.php b/src/Authentication/OpenIDConnectAuth.php index c7421cbf..bb7a134f 100644 --- a/src/Authentication/OpenIDConnectAuth.php +++ b/src/Authentication/OpenIDConnectAuth.php @@ -20,6 +20,7 @@ use Bolt\protocol\V5_3; use Bolt\protocol\V5_4; use Exception; +use Laudis\Neo4j\Bolt\BoltMessageFactory; use Laudis\Neo4j\Common\Neo4jLogger; use Laudis\Neo4j\Common\ResponseHelper; use Laudis\Neo4j\Contracts\AuthenticateInterface; @@ -29,11 +30,8 @@ use function sprintf; -final class OpenIDConnectAuth implements AuthenticateInterface +class OpenIDConnectAuth implements AuthenticateInterface { - /** - * @psalm-external-mutation-free - */ public function __construct( private readonly string $token, private readonly ?Neo4jLogger $logger, @@ -44,11 +42,6 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s { $this->logger?->log(LogLevel::DEBUG, 'Authenticating using OpenIDConnectAuth'); - /** - * @psalm-suppress ImpureMethodCall Request is a pure object: - * - * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects - */ return $request->withHeader('Authorization', 'Bearer '.$this->token) ->withHeader('User-Agent', $userAgent); } @@ -60,34 +53,39 @@ public function authenticateHttp(RequestInterface $request, UriInterface $uri, s */ public function authenticateBolt(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, string $userAgent): array { - if (method_exists($protocol, 'logon')) { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent]); - $protocol->hello(['user_agent' => $userAgent]); - $response = ResponseHelper::getResponse($protocol); - $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'bearer']); - $protocol->logon([ - 'scheme' => 'bearer', - 'credentials' => $this->token, - ]); - ResponseHelper::getResponse($protocol); - - /** @var array{server: string, connection_id: string, hints: list} */ - return $response->content; - } else { - $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent, 'scheme' => 'bearer']); - $protocol->hello([ - 'user_agent' => $userAgent, - 'scheme' => 'bearer', - 'credentials' => $this->token, - ]); - - /** @var array{server: string, connection_id: string, hints: list} */ - return ResponseHelper::getResponse($protocol)->content; - } + $factory = $this->createMessageFactory($protocol); + + $this->logger?->log(LogLevel::DEBUG, 'HELLO', ['user_agent' => $userAgent]); + + $factory->createHelloMessage(['user_agent' => $userAgent])->send(); + + $response = ResponseHelper::getResponse($protocol); + + $this->logger?->log(LogLevel::DEBUG, 'LOGON', ['scheme' => 'bearer']); + + $factory->createLogonMessage([ + 'scheme' => 'bearer', + 'credentials' => $this->token, + ])->send(); + + ResponseHelper::getResponse($protocol); + + /** + * @var array{server: string, connection_id: string, hints: list} + */ + return $response->content; } public function toString(UriInterface $uri): string { return sprintf('OpenId %s@%s:%s', $this->token, $uri->getHost(), $uri->getPort() ?? ''); } + + /** + * Helper to create the message factory. + */ + public function createMessageFactory(V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol): BoltMessageFactory + { + return new BoltMessageFactory($protocol, $this->logger); + } } diff --git a/src/Bolt/BoltMessageFactory.php b/src/Bolt/BoltMessageFactory.php index 64eaa6d9..589ed672 100644 --- a/src/Bolt/BoltMessageFactory.php +++ b/src/Bolt/BoltMessageFactory.php @@ -23,6 +23,9 @@ use Laudis\Neo4j\Bolt\Messages\BoltCommitMessage; use Laudis\Neo4j\Bolt\Messages\BoltDiscardMessage; use Laudis\Neo4j\Bolt\Messages\BoltGoodbyeMessage; +use Laudis\Neo4j\Bolt\Messages\BoltHelloMessage; +use Laudis\Neo4j\Bolt\Messages\BoltLogoffMessage; +use Laudis\Neo4j\Bolt\Messages\BoltLogonMessage; use Laudis\Neo4j\Bolt\Messages\BoltPullMessage; use Laudis\Neo4j\Bolt\Messages\BoltResetMessage; use Laudis\Neo4j\Bolt\Messages\BoltRollbackMessage; @@ -32,7 +35,7 @@ /** * Factory class for creating Bolt protocol messages. */ -final class BoltMessageFactory +class BoltMessageFactory { public function __construct( private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, @@ -75,6 +78,25 @@ public function createPullMessage(array $extra): BoltPullMessage return new BoltPullMessage($this->protocol, $extra, $this->logger); } + public function createHelloMessage(array $extra): BoltHelloMessage + { + /** @var array $extra */ + + return new BoltHelloMessage($this->protocol, $extra, $this->logger); + } + + public function createLogonMessage(array $credentials): BoltLogonMessage + { + /** @var array $credentials */ + + return new BoltLogonMessage($this->protocol, $credentials, $this->logger); + } + + public function createLogoffMessage(): BoltLogoffMessage + { + return new BoltLogoffMessage($this->protocol, $this->logger); + } + public function createGoodbyeMessage(): BoltGoodbyeMessage { return new BoltGoodbyeMessage($this->protocol, $this->logger); diff --git a/src/Bolt/Messages/BoltHelloMessage.php b/src/Bolt/Messages/BoltHelloMessage.php new file mode 100644 index 00000000..cbc3d4be --- /dev/null +++ b/src/Bolt/Messages/BoltHelloMessage.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\error\BoltException; +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +final class BoltHelloMessage extends BoltMessage +{ + /** + * Constructor for the BoltHelloMessage. + * + * @param V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol The protocol connection + * @param array $metadata The metadata for the HELLO message (like user agent, supported versions) + * @param Neo4jLogger|null $logger Optional logger for debugging purposes + */ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly array $metadata, + private readonly ?Neo4jLogger $logger = null, + ) { + parent::__construct($protocol); + } + + /** + * Sends the HELLO message to the server. + * + * @throws BoltException + */ + public function send(): BoltHelloMessage + { + $this->logger?->log(LogLevel::DEBUG, 'HELLO', $this->metadata); + + $this->protocol->hello($this->metadata); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltLogoffMessage.php b/src/Bolt/Messages/BoltLogoffMessage.php new file mode 100644 index 00000000..2eda2cd6 --- /dev/null +++ b/src/Bolt/Messages/BoltLogoffMessage.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +/** + * A message that issues a LOGOFF request to the server to terminate the connection. + */ +class BoltLogoffMessage extends BoltMessage +{ + /** + * @param V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol The Bolt protocol version + * @param Neo4jLogger|null $logger Optional logger for logging purposes + */ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly ?Neo4jLogger $logger = null, + ) { + parent::__construct($protocol); + } + + /** + * Sends the LOGOFF request to the server to disconnect. + * + * @return BoltLogoffMessage The current instance of the message + */ + public function send(): BoltLogoffMessage + { + $this->logger?->log(LogLevel::DEBUG, 'LOGOFF', []); + /** @psalm-suppress PossiblyUndefinedMethod */ + $this->protocol->logoff(); + + return $this; + } +} diff --git a/src/Bolt/Messages/BoltLogonMessage.php b/src/Bolt/Messages/BoltLogonMessage.php new file mode 100644 index 00000000..eeeec0b2 --- /dev/null +++ b/src/Bolt/Messages/BoltLogonMessage.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Bolt\Messages; + +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Bolt\protocol\V5_1; +use Bolt\protocol\V5_2; +use Bolt\protocol\V5_3; +use Bolt\protocol\V5_4; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Contracts\BoltMessage; +use Psr\Log\LogLevel; + +/** + * A message that issues a LOGON request to the server for authentication. + */ +final class BoltLogonMessage extends BoltMessage +{ + /** + * @param V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol The Bolt protocol version + * @param array $credentials The credentials for the LOGON request (e.g., username and password) + * @param Neo4jLogger|null $logger Optional logger for logging purposes + */ + public function __construct( + private readonly V4_4|V5|V5_1|V5_2|V5_3|V5_4 $protocol, + private readonly array $credentials, + private readonly ?Neo4jLogger $logger, + ) { + parent::__construct($protocol); + } + + /** + * Sends the LOGON request to the server with the provided credentials. + * + * @return BoltLogonMessage The current instance of the message + */ + public function send(): BoltLogonMessage + { + $toLog = $this->credentials; + unset($toLog['credentials']); + + $this->logger?->log(LogLevel::DEBUG, 'LOGON', $toLog); + /** @psalm-suppress PossiblyUndefinedMethod */ + $this->protocol->logon($this->credentials); + + return $this; + } +} diff --git a/src/ClientBuilder.php b/src/ClientBuilder.php index 42a79232..aed74592 100644 --- a/src/ClientBuilder.php +++ b/src/ClientBuilder.php @@ -66,9 +66,6 @@ public static function create(?string $logLevel = null, ?LoggerInterface $logger ); } - /** - * @psalm-mutation-free - */ public function withDriver(string $alias, string $url, ?AuthenticateInterface $authentication = null, ?int $priority = 0): self { $uri = Uri::create($url); diff --git a/tests/Integration/BoltDriverIntegrationTest.php b/tests/Integration/BoltDriverIntegrationTest.php index 31987e9a..1912bb54 100644 --- a/tests/Integration/BoltDriverIntegrationTest.php +++ b/tests/Integration/BoltDriverIntegrationTest.php @@ -29,6 +29,10 @@ final class BoltDriverIntegrationTest extends EnvironmentAwareIntegrationTest */ public function testValidHostname(): void { + if (!str_contains($this->getUri()->getScheme(), 'bolt')) { + $this->markTestSkipped('This test only works with Bolt drivers.'); + } + $results = BoltDriver::create($this->getUri()) ->createSession() ->run('RETURN 1 AS x'); @@ -43,6 +47,10 @@ public function testValidHostname(): void */ public function testValidUrl(): void { + if (!str_contains($this->getUri()->getScheme(), 'bolt')) { + $this->markTestSkipped('This test only works with Bolt drivers.'); + } + $ip = gethostbyname($this->getUri()->getHost()); try { $results = BoltDriver::create($this->getUri()->withHost($ip)->__toString()) diff --git a/tests/Unit/BasicAuthTest.php b/tests/Unit/BasicAuthTest.php new file mode 100644 index 00000000..626a93a0 --- /dev/null +++ b/tests/Unit/BasicAuthTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Tests\Unit; + +use Bolt\enum\Message; +use Bolt\enum\Signature; +use Bolt\protocol\Response; +use Bolt\protocol\V5; +use Laudis\Neo4j\Authentication\BasicAuth; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Exception\Neo4jException; +use PHPUnit\Framework\MockObject\Exception; +use PHPUnit\Framework\TestCase; +use Psr\Http\Message\UriInterface; + +class BasicAuthTest extends TestCase +{ + private BasicAuth $auth; + private string $username = 'neo4j'; + private string $password = 'test'; + + protected function setUp(): void + { + $logger = $this->createMock(Neo4jLogger::class); + $this->auth = new BasicAuth($this->username, $this->password, $logger); + } + + public function testToString(): void + { + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $this->auth->toString($uri); + + $this->assertSame('Basic neo4j:######@localhost:7687', $result); + } + + /** + * @throws Exception + * @throws \Exception + */ + public function testAuthenticateBoltSuccess(): void + { + $userAgent = 'neo4j-client/1.0'; + + $protocol = $this->createMock(V5::class); + + $response = new Response( + Message::HELLO, + Signature::SUCCESS, + ['server' => 'neo4j-server', 'connection_id' => '12345', 'hints' => []] + ); + + $protocol->expects($this->once()) + ->method('getResponse') + ->willReturn($response); + + $result = $this->auth->authenticateBolt($protocol, $userAgent); + $this->assertArrayHasKey('server', $result); + $this->assertSame('neo4j-server', $result['server']); + $this->assertSame('12345', $result['connection_id']); + } + + public function testAuthenticateBoltFailure(): void + { + $this->expectException(Neo4jException::class); + + $protocol = $this->createMock(V5::class); + $response = new Response( + Message::HELLO, + Signature::FAILURE, + ['code' => 'Neo.ClientError.Security.Unauthorized', 'message' => 'Invalid credentials'] + ); + + $protocol->method('getResponse')->willReturn($response); + + $this->auth->authenticateBolt($protocol, 'neo4j-client/1.0'); + } + + public function testEmptyCredentials(): void + { + $emptyAuth = new BasicAuth('', '', null); + + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $emptyAuth->toString($uri); + + $this->assertSame('Basic :######@localhost:7687', $result); + } +} diff --git a/tests/Unit/KerberosAuthTest.php b/tests/Unit/KerberosAuthTest.php new file mode 100644 index 00000000..d60a359e --- /dev/null +++ b/tests/Unit/KerberosAuthTest.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Tests\Unit; + +use Bolt\enum\Message; +use Bolt\enum\Signature; +use Bolt\protocol\Response; +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Laudis\Neo4j\Authentication\KerberosAuth; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Exception\Neo4jException; +use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; + +class KerberosAuthTest extends TestCase +{ + private KerberosAuth $auth; + + protected function setUp(): void + { + $logger = $this->createMock(Neo4jLogger::class); + $this->auth = new KerberosAuth('test-token', $logger); + } + + public function testAuthenticateHttpSuccess(): void + { + $request = $this->createMock(RequestInterface::class); + $request->expects($this->exactly(2)) + ->method('withHeader') + ->willReturnSelf(); + + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $auth = new KerberosAuth('test-token', null); + $result = $auth->authenticateHttp($request, $uri, 'neo4j-client/1.0'); + + $this->assertSame($request, $result); + } + + public function testAuthenticateBoltFailureV5(): void + { + $this->expectException(Neo4jException::class); + + $protocol = $this->createMock(V5::class); + $response = new Response( + Message::HELLO, + Signature::FAILURE, + ['code' => 'Neo.ClientError.Security.Unauthorized', 'message' => 'Invalid credentials'] + ); + + $protocol->method('getResponse')->willReturn($response); + + $this->auth->authenticateBolt($protocol, 'neo4j-client/1.0'); + } + + public function testAuthenticateBoltFailureV4(): void + { + $this->expectException(Neo4jException::class); + + $protocol = $this->createMock(V4_4::class); + $response = new Response( + Message::HELLO, + Signature::FAILURE, + ['code' => 'Neo.ClientError.Security.Unauthorized', 'message' => 'Invalid credentials'] + ); + + $protocol->method('getResponse')->willReturn($response); + + $this->auth->authenticateBolt($protocol, 'neo4j-client/1.0'); + } + + public function testToString(): void + { + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $this->auth->toString($uri); + + $this->assertSame('Kerberos test-token@localhost:7687', $result); + } + + public function testEmptyCredentials(): void + { + $emptyAuth = new KerberosAuth('', null); + + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $emptyAuth->toString($uri); + + $this->assertSame('Kerberos @localhost:7687', $result); + } +} diff --git a/tests/Unit/NoAuthTest.php b/tests/Unit/NoAuthTest.php new file mode 100644 index 00000000..ddfc4274 --- /dev/null +++ b/tests/Unit/NoAuthTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Tests\Unit; + +use Bolt\enum\Message; +use Bolt\enum\Signature; +use Bolt\protocol\Response; +use Bolt\protocol\V4_4; +use Bolt\protocol\V5; +use Laudis\Neo4j\Authentication\NoAuth; +use Laudis\Neo4j\Common\Neo4jLogger; +use Laudis\Neo4j\Exception\Neo4jException; +use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; + +class NoAuthTest extends TestCase +{ + private NoAuth $auth; + + protected function setUp(): void + { + $logger = $this->createMock(Neo4jLogger::class); + $this->auth = new NoAuth($logger); + } + + public function testAuthenticateHttpSuccess(): void + { + $request = $this->createMock(RequestInterface::class); + $request->expects($this->once()) + ->method('withHeader') + ->with('User-Agent', 'neo4j-client/1.0') + ->willReturnSelf(); + + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $this->auth->authenticateHttp($request, $uri, 'neo4j-client/1.0'); + $this->assertSame($request, $result); + } + + public function testAuthenticateBoltSuccessV5(): void + { + $userAgent = 'neo4j-client/1.0'; + + $protocol = $this->createMock(V5::class); + + $response = new Response( + Message::HELLO, + Signature::SUCCESS, + ['server' => 'neo4j-server', 'connection_id' => '12345', 'hints' => []] + ); + + $protocol->expects($this->once()) + ->method('getResponse') + ->willReturn($response); + + $result = $this->auth->authenticateBolt($protocol, $userAgent); + $this->assertArrayHasKey('server', $result); + $this->assertSame('neo4j-server', $result['server']); + $this->assertSame('12345', $result['connection_id']); + } + + public function testAuthenticateBoltFailureV5(): void + { + $this->expectException(Neo4jException::class); + + $protocol = $this->createMock(V5::class); + $response = new Response( + Message::HELLO, + Signature::FAILURE, + ['code' => 'Neo.ClientError.Security.Unauthorized', 'message' => 'Invalid credentials'] + ); + + $protocol->method('getResponse')->willReturn($response); + + $this->auth->authenticateBolt($protocol, 'neo4j-client/1.0'); + } + + public function testAuthenticateBoltSuccessV4(): void + { + $userAgent = 'neo4j-client/1.0'; + + $protocol = $this->createMock(V4_4::class); + + $response = new Response( + Message::HELLO, + Signature::SUCCESS, + ['server' => 'neo4j-server', 'connection_id' => '12345', 'hints' => []] + ); + + $protocol->expects($this->once()) + ->method('getResponse') + ->willReturn($response); + + $result = $this->auth->authenticateBolt($protocol, $userAgent); + $this->assertArrayHasKey('server', $result); + $this->assertSame('neo4j-server', $result['server']); + $this->assertSame('12345', $result['connection_id']); + } + + public function testToString(): void + { + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $this->auth->toString($uri); + + $this->assertSame('No Auth localhost:7687', $result); + } + + public function testEmptyCredentials(): void + { + $emptyAuth = new NoAuth(null); + + $uri = $this->createMock(UriInterface::class); + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $emptyAuth->toString($uri); + + $this->assertSame('No Auth localhost:7687', $result); + } +} diff --git a/tests/Unit/OpenIDConnectionAuthTest.php b/tests/Unit/OpenIDConnectionAuthTest.php new file mode 100644 index 00000000..75f6b2da --- /dev/null +++ b/tests/Unit/OpenIDConnectionAuthTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Laudis\Neo4j\Tests\Unit; + +use Laudis\Neo4j\Authentication\OpenIDConnectAuth; +use Laudis\Neo4j\Common\Neo4jLogger; +use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; + +class OpenIDConnectionAuthTest extends TestCase +{ + private OpenIDConnectAuth $auth; + + protected function setUp(): void + { + $this->auth = new OpenIDConnectAuth('test-token', $this->createMock(Neo4jLogger::class)); + } + + public function testAuthenticateHttpSuccess(): void + { + $request = $this->createMock(RequestInterface::class); + $uri = $this->createMock(UriInterface::class); + + $request->expects($this->exactly(2)) + ->method('withHeader') + ->willReturnSelf(); + + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $this->auth->authenticateHttp($request, $uri, 'neo4j-client/1.0'); + + $this->assertSame($request, $result); + } + + public function testAuthenticateHttpAddsAuthorizationHeader(): void + { + $request = $this->createMock(RequestInterface::class); + $uri = $this->createMock(UriInterface::class); + + $sequence = 0; + + $request->expects($this->exactly(2)) + ->method('withHeader') + ->willReturnCallback(function (string $header, string $value) use (&$sequence, $request) { + if ($sequence === 0) { + TestCase::assertSame('Authorization', $header); + TestCase::assertSame('Bearer test-token', $value); + } elseif ($sequence === 1) { + TestCase::assertSame('User-Agent', $header); + TestCase::assertSame('neo4j-client/1.0', $value); + } else { + TestCase::fail('Unexpected header call'); + } + + ++$sequence; + + return $request; + }); + + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $this->auth->authenticateHttp($request, $uri, 'neo4j-client/1.0'); + + $this->assertSame($request, $result); + } + + public function testAuthenticateHttpWithDifferentUri(): void + { + $request = $this->createMock(RequestInterface::class); + $uri = $this->createMock(UriInterface::class); + + $request->expects($this->exactly(2)) + ->method('withHeader') + ->willReturnSelf(); + + $uri->method('getHost')->willReturn('my-neo4j-host'); + $uri->method('getPort')->willReturn(7474); + + $result = $this->auth->authenticateHttp($request, $uri, 'neo4j-client/2.0'); + + $this->assertSame($request, $result); + } + + public function testAuthenticateHttpWithHeaderReturnsNewInstance(): void + { + $initialRequest = $this->createMock(RequestInterface::class); + $modifiedRequest = $this->createMock(RequestInterface::class); + $finalRequest = $this->createMock(RequestInterface::class); + $uri = $this->createMock(UriInterface::class); + + $initialRequest->expects($this->once()) + ->method('withHeader') + ->with('Authorization', 'Bearer test-token') + ->willReturn($modifiedRequest); + + $modifiedRequest->expects($this->once()) + ->method('withHeader') + ->with('User-Agent', 'neo4j-client/1.0') + ->willReturn($finalRequest); + + $uri->method('getHost')->willReturn('localhost'); + $uri->method('getPort')->willReturn(7687); + + $result = $this->auth->authenticateHttp($initialRequest, $uri, 'neo4j-client/1.0'); + + $this->assertSame($finalRequest, $result); + } +} From d07f8543d8ddede06e081219dac93c34714c9133 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Fri, 2 May 2025 11:31:49 +0530 Subject: [PATCH 7/8] fix: Neo4j 4 Cluster tests --- .github/workflows/integration-test-cluster-neo4j-4.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test-cluster-neo4j-4.yml b/.github/workflows/integration-test-cluster-neo4j-4.yml index 3c42c6d9..e2a164f8 100644 --- a/.github/workflows/integration-test-cluster-neo4j-4.yml +++ b/.github/workflows/integration-test-cluster-neo4j-4.yml @@ -21,7 +21,7 @@ jobs: - name: Populate .env run: | echo "PHP_VERSION=${{ matrix.php }}" > .env - echo "CONNECTION=neo4j://neo4j:testtest@server1" >> .env + echo "CONNECTION=neo4j://neo4j:testtest@core1" >> .env - uses: hoverkraft-tech/compose-action@v2.0.2 name: Start services with: From e604251dae72d858917377040980eea6aa1cef18 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Fri, 2 May 2025 14:32:11 +0530 Subject: [PATCH 8/8] test: Skip logger tests for clusters --- .env.example | 2 +- tests/Integration/Neo4jLoggerTest.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index a9644d43..c1e3042e 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -CONNECTIONS=bolt://neo4j:testtest@neo4j,http://neo4j:testtest@neo4j,bolt://neo4j:testtest@core1 +PHP_VERSION=8.1 diff --git a/tests/Integration/Neo4jLoggerTest.php b/tests/Integration/Neo4jLoggerTest.php index 0168bbc9..aa8f75c5 100644 --- a/tests/Integration/Neo4jLoggerTest.php +++ b/tests/Integration/Neo4jLoggerTest.php @@ -24,10 +24,14 @@ class Neo4jLoggerTest extends EnvironmentAwareIntegrationTest { public function testLogger(): void { - if ($this->getUri()->getScheme() === 'http') { + if (str_contains($this->getUri()->getScheme(), 'http')) { self::markTestSkipped('This test is not applicable for the HTTP driver'); } + if (str_contains($this->getUri()->getScheme(), 'neo4j')) { + self::markTestSkipped('This test is not applicable clusters'); + } + // Close connections so that we can test the logger logging // during authentication while acquiring a new connection $this->driver->closeConnections();