Skip to content

Commit 5e94937

Browse files
authored
Allow forcing socket type via driver configuration (#284)
* allow forcing socket type via driver configuration * refactor: Use SocketType enum instead of string for socket configuration
1 parent 79cdb30 commit 5e94937

File tree

6 files changed

+117
-6
lines changed

6 files changed

+117
-6
lines changed

src/Bolt/ConnectionPool.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public static function create(
5353
): self {
5454
return new self(
5555
$semaphore,
56-
BoltFactory::create($conf->getLogger()),
56+
BoltFactory::create($conf->getLogger(), $conf->getSocketType()),
5757
new ConnectionRequestData(
5858
$uri->getHost(),
5959
$uri,

src/Bolt/SystemWideConnectionFactory.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use function extension_loaded;
1717

1818
use Laudis\Neo4j\Contracts\BasicConnectionFactoryInterface;
19+
use Laudis\Neo4j\Enum\SocketType;
1920

2021
/**
2122
* Singleton connection factory based on the installed extensions.
@@ -35,8 +36,17 @@ private function __construct(
3536
/**
3637
* @psalm-suppress InvalidNullableReturnType
3738
*/
38-
public static function getInstance(): SystemWideConnectionFactory
39+
public static function getInstance(?SocketType $preferredSocket = null): SystemWideConnectionFactory
3940
{
41+
// If a specific socket type is requested, create a new instance without caching
42+
if ($preferredSocket === SocketType::SOCKETS() && extension_loaded('sockets')) {
43+
return new self(new SocketConnectionFactory(new StreamConnectionFactory()));
44+
}
45+
46+
if ($preferredSocket === SocketType::STREAM()) {
47+
return new self(new StreamConnectionFactory());
48+
}
49+
4050
if (self::$instance === null) {
4151
$factory = new StreamConnectionFactory();
4252
if (extension_loaded('sockets')) {

src/BoltFactory.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Laudis\Neo4j\Databags\SessionConfiguration;
2828
use Laudis\Neo4j\Databags\TransactionConfiguration;
2929
use Laudis\Neo4j\Enum\ConnectionProtocol;
30+
use Laudis\Neo4j\Enum\SocketType;
3031

3132
/**
3233
* Small wrapper around the bolt library to easily guarantee only bolt version 3 and up will be created and authenticated.
@@ -44,9 +45,9 @@ public function __construct(
4445
) {
4546
}
4647

47-
public static function create(?Neo4jLogger $logger): self
48+
public static function create(?Neo4jLogger $logger, ?SocketType $socketType = null): self
4849
{
49-
return new self(SystemWideConnectionFactory::getInstance(), new ProtocolFactory(), new SslConfigurationFactory(), $logger);
50+
return new self(SystemWideConnectionFactory::getInstance($socketType), new ProtocolFactory(), new SslConfigurationFactory(), $logger);
5051
}
5152

5253
public function createConnection(ConnectionRequestData $data, SessionConfiguration $sessionConfig): BoltConnection

src/Databags/DriverConfiguration.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Laudis\Neo4j\Common\Neo4jLogger;
2525
use Laudis\Neo4j\Common\SemaphoreFactory;
2626
use Laudis\Neo4j\Contracts\SemaphoreFactoryInterface;
27+
use Laudis\Neo4j\Enum\SocketType;
2728
use Psr\Log\LoggerInterface;
2829
use Psr\Log\LogLevel;
2930
use Psr\SimpleCache\CacheInterface;
@@ -44,11 +45,13 @@ final class DriverConfiguration
4445
/** @var callable():(SemaphoreFactoryInterface|null)|SemaphoreFactoryInterface|null */
4546
private $semaphoreFactory;
4647
private ?Neo4jLogger $logger;
48+
private ?SocketType $socketType;
4749

4850
/**
4951
* @param callable():(CacheInterface|null)|CacheInterface|null $cache
5052
* @param callable():(SemaphoreFactoryInterface|null)|SemaphoreFactoryInterface|null $semaphore
51-
* @param string|null $logLevel The log level to use. If null, LogLevel::INFO is used.
53+
* @param string|null $logLevel The log level to use. If null, LogLevel::INFO is used.
54+
* @param SocketType|null $socketType the socket type to use (SocketType::SOCKETS(), SocketType::STREAM(), or null for auto-detect)
5255
*
5356
* @psalm-external-mutation-free
5457
*/
@@ -61,6 +64,7 @@ public function __construct(
6164
callable|SemaphoreFactoryInterface|null $semaphore,
6265
?string $logLevel,
6366
?LoggerInterface $logger,
67+
?SocketType $socketType = null,
6468
) {
6569
$this->cache = $cache;
6670
$this->semaphoreFactory = $semaphore;
@@ -69,6 +73,7 @@ public function __construct(
6973
} else {
7074
$this->logger = null;
7175
}
76+
$this->socketType = $socketType;
7277
}
7378

7479
/**
@@ -83,6 +88,7 @@ public static function create(
8388
SemaphoreFactoryInterface $semaphore,
8489
?string $logLevel,
8590
?LoggerInterface $logger,
91+
?SocketType $socketType = null,
8692
): self {
8793
return new self(
8894
$userAgent,
@@ -92,7 +98,8 @@ public static function create(
9298
$acquireConnectionTimeout,
9399
$semaphore,
94100
$logLevel,
95-
$logger
101+
$logger,
102+
$socketType
96103
);
97104
}
98105

@@ -261,4 +268,23 @@ public function withLogger(?string $logLevel, ?LoggerInterface $logger): self
261268

262269
return $tbr;
263270
}
271+
272+
/**
273+
* @psalm-immutable
274+
*/
275+
public function getSocketType(): ?SocketType
276+
{
277+
return $this->socketType;
278+
}
279+
280+
/**
281+
* @psalm-immutable
282+
*/
283+
public function withSocketType(?SocketType $socketType): self
284+
{
285+
$tbr = clone $this;
286+
$tbr->socketType = $socketType;
287+
288+
return $tbr;
289+
}
264290
}

src/Enum/SocketType.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Neo4j PHP Client and Driver package.
7+
*
8+
* (c) Nagels <https://nagels.tech>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Laudis\Neo4j\Enum;
15+
16+
use JsonSerializable;
17+
use Laudis\TypedEnum\TypedEnum;
18+
19+
/**
20+
* Defines the socket type to use for connections.
21+
*
22+
* @method static self SOCKETS()
23+
* @method static self STREAM()
24+
*
25+
* @extends TypedEnum<string>
26+
*
27+
* @psalm-immutable
28+
*
29+
* @psalm-suppress MutableDependency
30+
*/
31+
final class SocketType extends TypedEnum implements JsonSerializable
32+
{
33+
private const SOCKETS = 'sockets';
34+
private const STREAM = 'stream';
35+
36+
public function jsonSerialize(): string
37+
{
38+
return $this->getValue();
39+
}
40+
}

tests/Unit/BoltFactoryTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
use Laudis\Neo4j\Bolt\Connection;
2121
use Laudis\Neo4j\Bolt\ProtocolFactory;
2222
use Laudis\Neo4j\Bolt\SslConfigurationFactory;
23+
use Laudis\Neo4j\Bolt\SystemWideConnectionFactory;
2324
use Laudis\Neo4j\BoltFactory;
2425
use Laudis\Neo4j\Common\Uri;
2526
use Laudis\Neo4j\Contracts\AuthenticateInterface;
2627
use Laudis\Neo4j\Contracts\BasicConnectionFactoryInterface;
2728
use Laudis\Neo4j\Databags\ConnectionRequestData;
29+
use Laudis\Neo4j\Databags\DriverConfiguration;
2830
use Laudis\Neo4j\Databags\SessionConfiguration;
2931
use Laudis\Neo4j\Databags\SslConfiguration;
32+
use Laudis\Neo4j\Enum\SocketType;
3033
use PHPUnit\Framework\TestCase;
3134

3235
final class BoltFactoryTest extends TestCase
@@ -73,4 +76,35 @@ public function testCreateBasic(): void
7376
self::assertInstanceOf(Connection::class,
7477
$connection->getImplementation()[1]);
7578
}
79+
80+
public function testSystemWideConnectionFactoryStreamOverride(): void
81+
{
82+
$factory = SystemWideConnectionFactory::getInstance(SocketType::STREAM());
83+
self::assertInstanceOf(SystemWideConnectionFactory::class, $factory);
84+
}
85+
86+
public function testSystemWideConnectionFactorySocketOverride(): void
87+
{
88+
if (!extension_loaded('sockets')) {
89+
self::markTestSkipped('sockets extension not loaded');
90+
}
91+
92+
$factory = SystemWideConnectionFactory::getInstance(SocketType::SOCKETS());
93+
self::assertInstanceOf(SystemWideConnectionFactory::class, $factory);
94+
}
95+
96+
public function testDriverConfigurationWithSocketType(): void
97+
{
98+
$socketType = SocketType::STREAM();
99+
$config = DriverConfiguration::default()
100+
->withSocketType($socketType);
101+
102+
self::assertEquals($socketType, $config->getSocketType());
103+
}
104+
105+
public function testBoltFactoryWithSocketTypeOverride(): void
106+
{
107+
$factory = BoltFactory::create(null, SocketType::STREAM());
108+
self::assertInstanceOf(BoltFactory::class, $factory);
109+
}
76110
}

0 commit comments

Comments
 (0)