Skip to content

Commit c511ba1

Browse files
committed
first working version of full result format
1 parent 5162453 commit c511ba1

31 files changed

+1159
-139
lines changed

src/Bolt/BoltConnectionPool.php

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,54 @@
1313

1414
namespace Laudis\Neo4j\Bolt;
1515

16+
use Bolt\Bolt;
1617
use Bolt\connection\StreamSocket;
1718
use Exception;
1819
use function explode;
1920
use Laudis\Neo4j\Common\TransactionHelper;
2021
use Laudis\Neo4j\Contracts\AuthenticateInterface;
22+
use Laudis\Neo4j\Contracts\ConnectionInterface;
2123
use Laudis\Neo4j\Contracts\ConnectionPoolInterface;
22-
use Laudis\Neo4j\Enum\AccessMode;
24+
use Laudis\Neo4j\Databags\SessionConfiguration;
2325
use Psr\Http\Message\UriInterface;
2426
use function str_starts_with;
2527

2628
/**
27-
* @implements ConnectionPoolInterface<StreamSocket>
29+
* @implements ConnectionPoolInterface<Bolt>
2830
*/
2931
final class BoltConnectionPool implements ConnectionPoolInterface
3032
{
3133
/**
3234
* @throws Exception
3335
*/
34-
public function acquire(UriInterface $uri, AccessMode $mode, AuthenticateInterface $authenticate, float $socketTimeout): StreamSocket
35-
{
36+
public function acquire(
37+
UriInterface $uri,
38+
AuthenticateInterface $authenticate,
39+
float $socketTimeout,
40+
string $userAgent,
41+
SessionConfiguration $config
42+
): ConnectionInterface {
3643
$host = $uri->getHost();
3744
$socket = new StreamSocket($host, $uri->getPort() ?? 7687, $socketTimeout);
3845

46+
$this->configureSsl($uri, $host, $socket);
47+
48+
return TransactionHelper::connectionFromSocket($socket, $uri, $userAgent, $authenticate, $config);
49+
}
50+
51+
/**
52+
* @param UriInterface $uri
53+
* @param string $host
54+
* @param StreamSocket $socket
55+
*/
56+
private function configureSsl(UriInterface $uri, string $host, StreamSocket $socket): void
57+
{
3958
$scheme = $uri->getScheme();
4059
$explosion = explode('+', $scheme, 2);
4160
$sslConfig = $explosion[1] ?? '';
4261

4362
if (str_starts_with('s', $sslConfig)) {
4463
TransactionHelper::enableSsl($host, $sslConfig, $socket);
4564
}
46-
47-
return $socket;
4865
}
4966
}

src/Bolt/BoltUnmanagedTransaction.php

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Ds\Vector;
1919
use Exception;
2020
use Laudis\Neo4j\Common\TransactionHelper;
21+
use Laudis\Neo4j\Contracts\ConnectionInterface;
2122
use Laudis\Neo4j\Contracts\FormatterInterface;
2223
use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface;
2324
use Laudis\Neo4j\Databags\Neo4jError;
@@ -26,6 +27,7 @@
2627
use Laudis\Neo4j\ParameterHelper;
2728
use Laudis\Neo4j\Types\CypherList;
2829
use Throwable;
30+
use function microtime;
2931

3032
/**
3133
* @template T
@@ -37,17 +39,19 @@
3739
final class BoltUnmanagedTransaction implements UnmanagedTransactionInterface
3840
{
3941
private FormatterInterface $formatter;
40-
private Bolt $bolt;
42+
/** @var ConnectionInterface<Bolt> */
43+
private ConnectionInterface $connection;
4144
private string $database;
4245
private bool $finished = false;
4346

4447
/**
45-
* @param FormatterInterface<T> $formatter
48+
* @param FormatterInterface<T> $formatter
49+
* @param ConnectionInterface<Bolt> $connection
4650
*/
47-
public function __construct(string $database, FormatterInterface $formatter, Bolt $bolt)
51+
public function __construct(string $database, FormatterInterface $formatter, ConnectionInterface $connection)
4852
{
4953
$this->formatter = $formatter;
50-
$this->bolt = $bolt;
54+
$this->connection = $connection;
5155
$this->database = $database;
5256
}
5357

@@ -60,7 +64,7 @@ public function commit(iterable $statements = []): CypherList
6064
}
6165

6266
try {
63-
$this->bolt->commit();
67+
$this->getBolt()->commit();
6468
$this->finished = true;
6569
} catch (Exception $e) {
6670
$code = TransactionHelper::extractCode($e);
@@ -77,7 +81,7 @@ public function rollback(): void
7781
}
7882

7983
try {
80-
$this->bolt->rollback();
84+
$this->connection->getImplementation()->rollback();
8185
$this->finished = true;
8286
} catch (Exception $e) {
8387
$code = TransactionHelper::extractCode($e) ?? '';
@@ -112,20 +116,38 @@ public function runStatements(iterable $statements): CypherList
112116
$extra = ['db' => $this->database];
113117
$parameters = ParameterHelper::formatParameters($statement->getParameters());
114118
try {
119+
$start = microtime(true);
115120
/** @var BoltMeta $meta */
116-
$meta = $this->bolt->run($statement->getText(), $parameters->toArray(), $extra);
121+
$meta = $this->getBolt()->run($statement->getText(), $parameters->toArray(), $extra);
122+
$run = microtime(true);
117123
/** @var array<array> $results */
118-
$results = $this->bolt->pullAll();
124+
$results = $this->getBolt()->pullAll();
125+
$end = microtime(true);
119126
} catch (Throwable $e) {
120127
if ($e instanceof MessageException) {
121128
$code = TransactionHelper::extractCode($e) ?? '';
122129
throw new Neo4jException(new Vector([new Neo4jError($code, $e->getMessage())]), $e);
123130
}
124131
throw $e;
125132
}
126-
$tbr->push($this->formatter->formatBoltResult($meta, $results, $this->bolt));
133+
$tbr->push($this->formatter->formatBoltResult(
134+
$meta,
135+
$results,
136+
$this->connection,
137+
$run - $start,
138+
$end - $start,
139+
$statement
140+
));
127141
}
128142

129143
return new CypherList($tbr);
130144
}
145+
146+
/**
147+
* @return Bolt|mixed
148+
*/
149+
private function getBolt()
150+
{
151+
return $this->connection->getImplementation();
152+
}
131153
}

src/Bolt/Session.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
namespace Laudis\Neo4j\Bolt;
1515

1616
use Bolt\Bolt;
17-
use Bolt\connection\StreamSocket;
1817
use Ds\Vector;
1918
use Exception;
2019
use Laudis\Neo4j\Common\TransactionHelper;
2120
use Laudis\Neo4j\Contracts\AuthenticateInterface;
21+
use Laudis\Neo4j\Contracts\ConnectionInterface;
2222
use Laudis\Neo4j\Contracts\ConnectionPoolInterface;
2323
use Laudis\Neo4j\Contracts\FormatterInterface;
2424
use Laudis\Neo4j\Contracts\SessionInterface;
@@ -41,7 +41,7 @@
4141
final class Session implements SessionInterface
4242
{
4343
private SessionConfiguration $config;
44-
/** @var ConnectionPoolInterface<StreamSocket> */
44+
/** @var ConnectionPoolInterface<Bolt> */
4545
private ConnectionPoolInterface $pool;
4646
/** @var FormatterInterface<T> */
4747
private FormatterInterface $formatter;
@@ -51,8 +51,8 @@ final class Session implements SessionInterface
5151
private float $socketTimeout;
5252

5353
/**
54-
* @param FormatterInterface<T> $formatter
55-
* @param ConnectionPoolInterface<StreamSocket> $pool
54+
* @param FormatterInterface<T> $formatter
55+
* @param ConnectionPoolInterface<Bolt> $pool
5656
*/
5757
public function __construct(
5858
SessionConfiguration $config,
@@ -137,26 +137,28 @@ private function beginInstantTransaction(SessionConfiguration $config): Transact
137137
return new BoltUnmanagedTransaction(
138138
$this->config->getDatabase(),
139139
$this->formatter,
140-
$this->acquireBolt(TransactionConfiguration::default(), $config)
140+
$this->acquireConnection(TransactionConfiguration::default(), $config)
141141
);
142142
}
143143

144-
private function acquireBolt(TransactionConfiguration $config, SessionConfiguration $sessionConfig): Bolt
144+
/**
145+
* @throws Exception
146+
*
147+
* @return ConnectionInterface<Bolt>
148+
*/
149+
private function acquireConnection(TransactionConfiguration $config, SessionConfiguration $sessionConfig): ConnectionInterface
145150
{
146151
$timeout = max($this->socketTimeout, $config->getTimeout());
147152

148-
$bolt = new Bolt($this->pool->acquire($this->uri, $sessionConfig->getAccessMode(), $this->auth, $timeout));
149-
$this->auth->authenticateBolt($bolt, $this->uri, $this->userAgent);
150-
151-
return $bolt;
153+
return $this->pool->acquire($this->uri, $this->auth, $timeout, $this->userAgent, $sessionConfig);
152154
}
153155

154156
private function startTransaction(TransactionConfiguration $config, SessionConfiguration $sessionConfig): UnmanagedTransactionInterface
155157
{
156158
try {
157-
$bolt = $this->acquireBolt($config, $sessionConfig);
159+
$bolt = $this->acquireConnection($config, $sessionConfig);
158160

159-
$begin = $bolt->begin(['db' => $this->config->getDatabase()]);
161+
$begin = $bolt->getImplementation()->begin(['db' => $this->config->getDatabase()]);
160162

161163
if (!$begin) {
162164
throw new Neo4jException(new Vector([new Neo4jError('', 'Cannot open new transaction')]));

src/Client.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
use Laudis\Neo4j\Databags\TransactionConfiguration;
3131
use Laudis\Neo4j\Enum\AccessMode;
3232
use Laudis\Neo4j\Types\CypherList;
33+
use function microtime;
34+
use const PHP_EOL;
3335
use Psr\Http\Message\UriInterface;
3436
use function sprintf;
3537

src/Common/Connection.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Laudis\Neo4j\Common;
5+
6+
use Laudis\Neo4j\Contracts\ConnectionInterface;
7+
use Laudis\Neo4j\Databags\DatabaseInfo;
8+
use Laudis\Neo4j\Enum\AccessMode;
9+
use Laudis\Neo4j\Enum\ConnectionProtocol;
10+
use Psr\Http\Message\UriInterface;
11+
12+
/**
13+
* @template T
14+
* @implements ConnectionInterface<T>
15+
*/
16+
final class Connection implements ConnectionInterface
17+
{
18+
/** @var T */
19+
private $socket;
20+
private string $serverAgent;
21+
private UriInterface $serverAddress;
22+
private string $serverVersion;
23+
private ConnectionProtocol $protocol;
24+
private AccessMode $accessMode;
25+
private DatabaseInfo $databaseInfo;
26+
27+
/**
28+
* @param T $socket
29+
*/
30+
public function __construct(
31+
$socket,
32+
string $serverAgent,
33+
UriInterface $serverAddress,
34+
string $serverVersion,
35+
ConnectionProtocol $protocol,
36+
AccessMode $accessMode,
37+
DatabaseInfo $databaseInfo
38+
) {
39+
$this->socket = $socket;
40+
$this->serverAgent = $serverAgent;
41+
$this->serverAddress = $serverAddress;
42+
$this->serverVersion = $serverVersion;
43+
$this->protocol = $protocol;
44+
$this->accessMode = $accessMode;
45+
$this->databaseInfo = $databaseInfo;
46+
}
47+
48+
public function getImplementation()
49+
{
50+
return $this->socket;
51+
}
52+
53+
public function getServerAgent(): string
54+
{
55+
return $this->serverAgent;
56+
}
57+
58+
public function getServerAddress(): UriInterface
59+
{
60+
return $this->serverAddress;
61+
}
62+
63+
public function getServerVersion(): string
64+
{
65+
return $this->serverVersion;
66+
}
67+
68+
public function getProtocol(): ConnectionProtocol
69+
{
70+
return $this->protocol;
71+
}
72+
73+
public function getAccessMode(): AccessMode
74+
{
75+
return $this->accessMode;
76+
}
77+
78+
public function getDatabaseInfo(): DatabaseInfo
79+
{
80+
return $this->databaseInfo;
81+
}
82+
}

src/Common/TransactionHelper.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,23 @@
1313

1414
namespace Laudis\Neo4j\Common;
1515

16+
use Bolt\Bolt;
1617
use Bolt\connection\StreamSocket;
1718
use const FILTER_VALIDATE_IP;
1819
use function filter_var;
20+
use Laudis\Neo4j\Contracts\AuthenticateInterface;
21+
use Laudis\Neo4j\Contracts\ConnectionInterface;
1922
use Laudis\Neo4j\Contracts\TransactionInterface;
2023
use Laudis\Neo4j\Contracts\UnmanagedTransactionInterface;
24+
use Laudis\Neo4j\Databags\DatabaseInfo;
25+
use Laudis\Neo4j\Databags\SessionConfiguration;
2126
use Laudis\Neo4j\Databags\TransactionConfiguration;
27+
use Laudis\Neo4j\Enum\ConnectionProtocol;
2228
use Laudis\Neo4j\Exception\Neo4jException;
2329
use function microtime;
2430
use function preg_match;
2531
use const PREG_OFFSET_CAPTURE;
32+
use Psr\Http\Message\UriInterface;
2633
use Throwable;
2734

2835
final class TransactionHelper
@@ -63,7 +70,6 @@ public static function enableSsl(string $host, string $sslConfig, StreamSocket $
6370
{
6471
$options = [
6572
'verify_peer' => true,
66-
// 'verify_peer_name' => false,
6773
'peer_name' => $host,
6874
];
6975
if (!filter_var($host, FILTER_VALIDATE_IP)) {
@@ -77,6 +83,41 @@ public static function enableSsl(string $host, string $sslConfig, StreamSocket $
7783
}
7884
}
7985

86+
/**
87+
* @return ConnectionInterface<Bolt>
88+
*/
89+
public static function connectionFromSocket(
90+
StreamSocket $socket,
91+
UriInterface $uri,
92+
string $userAgent,
93+
AuthenticateInterface $authenticate,
94+
SessionConfiguration $config
95+
): ConnectionInterface {
96+
$bolt = new Bolt($socket);
97+
$authenticate->authenticateBolt($bolt, $uri, $userAgent);
98+
99+
/** @var array{'name': 0, 'version': 1, 'edition': 2} $fields */
100+
$fields = array_flip($bolt->run(<<<'CYPHER'
101+
CALL dbms.components()
102+
YIELD name, versions, edition
103+
UNWIND versions AS version
104+
RETURN name, version, edition
105+
CYPHER)['fields']);
106+
107+
/** @var array{0: array{0: string, 1: string, 2: string}} $results */
108+
$results = $bolt->pullAll();
109+
110+
return new Connection(
111+
$bolt,
112+
$results[0][$fields['name']].'-'.$results[0][$fields['edition']].'/'.$results[0][$fields['version']],
113+
$uri,
114+
$results[0][$fields['version']],
115+
ConnectionProtocol::determineBoltVersion($bolt),
116+
$config->getAccessMode(),
117+
new DatabaseInfo($config->getDatabase())
118+
);
119+
}
120+
80121
public static function extractCode(Throwable $throwable): ?string
81122
{
82123
$message = $throwable->getMessage();

0 commit comments

Comments
 (0)