1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* This file is part of the Tarantool Client package. |
7
|
|
|
* |
8
|
|
|
* (c) Eugene Leonovich <[email protected]> |
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 Tarantool\Client; |
15
|
|
|
|
16
|
|
|
use Tarantool\Client\Connection\StreamConnection; |
17
|
|
|
use Tarantool\Client\Exception\RequestFailed; |
18
|
|
|
use Tarantool\Client\Handler\DefaultHandler; |
19
|
|
|
use Tarantool\Client\Handler\Handler; |
20
|
|
|
use Tarantool\Client\Handler\MiddlewareHandler; |
21
|
|
|
use Tarantool\Client\Middleware\AuthMiddleware; |
22
|
|
|
use Tarantool\Client\Middleware\Middleware; |
23
|
|
|
use Tarantool\Client\Middleware\RetryMiddleware; |
24
|
|
|
use Tarantool\Client\Packer\PurePacker; |
25
|
|
|
use Tarantool\Client\Request\Call; |
26
|
|
|
use Tarantool\Client\Request\Evaluate; |
27
|
|
|
use Tarantool\Client\Request\Execute; |
28
|
|
|
use Tarantool\Client\Request\Ping; |
29
|
55 |
|
use Tarantool\Client\Schema\Index; |
30
|
|
|
use Tarantool\Client\Schema\Space; |
31
|
55 |
|
|
32
|
55 |
|
final class Client |
33
|
55 |
|
{ |
34
|
|
|
private $handler; |
35
|
3 |
|
private $spaces = []; |
36
|
|
|
|
37
|
3 |
|
public function __construct(Handler $handler) |
38
|
|
|
{ |
39
|
|
|
$this->handler = $handler; |
40
|
2 |
|
} |
41
|
|
|
|
42
|
2 |
|
public static function fromDefaults() : self |
43
|
|
|
{ |
44
|
|
|
return new self(new DefaultHandler( |
45
|
54 |
|
StreamConnection::createTcp(), |
46
|
|
|
new PurePacker() |
47
|
54 |
|
)); |
48
|
|
|
} |
49
|
33 |
|
|
50
|
2 |
|
public static function fromOptions(array $options) : self |
51
|
|
|
{ |
52
|
33 |
|
$connectionOptions = []; |
53
|
|
|
if (isset($options['connect_timeout'])) { |
54
|
51 |
|
$connectionOptions['connect_timeout'] = $options['connect_timeout']; |
55
|
|
|
} |
56
|
51 |
|
if (isset($options['socket_timeout'])) { |
57
|
51 |
|
$connectionOptions['socket_timeout'] = $options['socket_timeout']; |
58
|
51 |
|
} |
59
|
|
|
if (isset($options['tcp_nodelay'])) { |
60
|
14 |
|
$connectionOptions['tcp_nodelay'] = $options['tcp_nodelay']; |
61
|
|
|
} |
62
|
14 |
|
|
63
|
|
|
$connection = StreamConnection::create($options['uri'] ?? StreamConnection::DEFAULT_URI, $connectionOptions); |
64
|
|
|
$handler = new DefaultHandler($connection, new PurePacker()); |
65
|
14 |
|
|
66
|
|
|
if (isset($options['username'])) { |
67
|
14 |
|
$handler = new MiddlewareHandler( |
68
|
12 |
|
new AuthMiddleware($options['username'], $options['password'] ?? ''), |
69
|
|
|
$handler |
70
|
|
|
); |
71
|
14 |
|
} |
72
|
14 |
|
if (isset($options['max_retries'])) { |
73
|
|
|
$handler = new MiddlewareHandler( |
74
|
10 |
|
RetryMiddleware::linear($options['max_retries']), |
75
|
10 |
|
$handler |
76
|
|
|
); |
77
|
10 |
|
} |
78
|
|
|
|
79
|
10 |
|
return new self($handler); |
80
|
|
|
} |
81
|
|
|
|
82
|
10 |
|
public static function fromDsn(string $dsn) : self |
83
|
|
|
{ |
84
|
10 |
|
$dsn = Dsn::parse($dsn); |
85
|
|
|
|
86
|
10 |
|
$connectionOptions = []; |
87
|
|
|
if (null !== $timeout = $dsn->getInt('connect_timeout')) { |
88
|
|
|
$connectionOptions['connect_timeout'] = $timeout; |
89
|
|
|
} |
90
|
|
|
if (null !== $timeout = $dsn->getInt('socket_timeout')) { |
91
|
|
|
$connectionOptions['socket_timeout'] = $timeout; |
92
|
|
|
} |
93
|
|
|
if (null !== $tcpNoDelay = $dsn->getBool('socket_timeout')) { |
94
|
64 |
|
$connectionOptions['tcp_nodelay'] = $tcpNoDelay; |
95
|
|
|
} |
96
|
64 |
|
|
97
|
58 |
|
$connection = $dsn->isTcp() |
98
|
|
|
? StreamConnection::createTcp($dsn->getConnectionUri(), $connectionOptions) |
99
|
|
|
: StreamConnection::createUds($dsn->getConnectionUri(), $connectionOptions); |
100
|
16 |
|
|
101
|
10 |
|
$handler = new DefaultHandler($connection, new PurePacker()); |
102
|
|
|
|
103
|
|
|
if ($username = $dsn->getUsername()) { |
104
|
14 |
|
$handler = new MiddlewareHandler( |
105
|
|
|
new AuthMiddleware($username, $dsn->getPassword() ?? ''), |
106
|
12 |
|
$handler |
107
|
|
|
); |
108
|
|
|
} |
109
|
8 |
|
if ($maxRetries = $dsn->getInt('max_retries')) { |
110
|
|
|
$handler = new MiddlewareHandler( |
111
|
8 |
|
RetryMiddleware::linear($maxRetries), |
112
|
|
|
$handler |
113
|
8 |
|
); |
114
|
|
|
} |
115
|
|
|
|
116
|
26 |
|
return new self($handler); |
117
|
|
|
} |
118
|
26 |
|
|
119
|
|
|
public function withMiddleware(Middleware $middleware, Middleware ...$middlewares) : self |
120
|
26 |
|
{ |
121
|
|
|
$new = clone $this; |
122
|
|
|
$new->handler = new MiddlewareHandler($middleware, $this->handler); |
123
|
12 |
|
|
124
|
|
|
if ($middlewares) { |
|
|
|
|
125
|
12 |
|
$new->handler = MiddlewareHandler::create($new->handler, $middlewares); |
126
|
12 |
|
} |
127
|
|
|
|
128
|
115 |
|
return $new; |
129
|
|
|
} |
130
|
115 |
|
|
131
|
30 |
|
public function getHandler() : Handler |
132
|
|
|
{ |
133
|
|
|
return $this->handler; |
134
|
113 |
|
} |
135
|
113 |
|
|
136
|
|
|
public function ping() : void |
137
|
109 |
|
{ |
138
|
|
|
$this->handler->handle(new Ping()); |
139
|
|
|
} |
140
|
14 |
|
|
141
|
|
|
public function getSpace(string $spaceName) : Space |
142
|
14 |
|
{ |
143
|
14 |
|
if (isset($this->spaces[$spaceName])) { |
144
|
14 |
|
return $this->spaces[$spaceName]; |
145
|
|
|
} |
146
|
14 |
|
|
147
|
3 |
|
$spaceId = $this->getSpaceIdByName($spaceName); |
148
|
|
|
|
149
|
|
|
return $this->spaces[$spaceName] = $this->spaces[$spaceId] = new Space($this->handler, $spaceId); |
150
|
12 |
|
} |
151
|
|
|
|
152
|
|
|
public function getSpaceById(int $spaceId) : Space |
153
|
|
|
{ |
154
|
|
|
if (isset($this->spaces[$spaceId])) { |
155
|
|
|
return $this->spaces[$spaceId]; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
return $this->spaces[$spaceId] = new Space($this->handler, $spaceId); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
public function call(string $funcName, ...$args) : array |
162
|
|
|
{ |
163
|
|
|
$request = new Call($funcName, $args); |
164
|
|
|
|
165
|
|
|
return $this->handler->handle($request)->getBodyField(IProto::DATA); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
public function executeQuery(string $sql, ...$params) : SqlQueryResult |
169
|
|
|
{ |
170
|
|
|
$request = new Execute($sql, $params); |
171
|
|
|
$response = $this->handler->handle($request); |
172
|
|
|
|
173
|
|
|
return new SqlQueryResult( |
174
|
|
|
$response->getBodyField(IProto::DATA), |
175
|
|
|
$response->getBodyField(IProto::METADATA) |
176
|
|
|
); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
public function executeUpdate(string $sql, ...$params) : SqlUpdateResult |
180
|
|
|
{ |
181
|
|
|
$request = new Execute($sql, $params); |
182
|
|
|
|
183
|
|
|
return new SqlUpdateResult( |
184
|
|
|
$this->handler->handle($request)->getBodyField(IProto::SQL_INFO) |
185
|
|
|
); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
public function evaluate(string $expr, ...$args) : array |
189
|
|
|
{ |
190
|
|
|
$request = new Evaluate($expr, $args); |
191
|
|
|
|
192
|
|
|
return $this->handler->handle($request)->getBodyField(IProto::DATA); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
public function flushSpaces() : void |
196
|
|
|
{ |
197
|
|
|
$this->spaces = []; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
private function getSpaceIdByName(string $spaceName) : int |
201
|
|
|
{ |
202
|
|
|
$schema = $this->getSpaceById(Space::VSPACE_ID); |
203
|
|
|
$data = $schema->select([$spaceName], Index::SPACE_NAME); |
204
|
|
|
|
205
|
|
|
if ([] === $data) { |
206
|
|
|
throw RequestFailed::unknownSpace($spaceName); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
return $data[0][0]; |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.