1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file is part of the tarantool/client package. |
||
5 | * |
||
6 | * (c) Eugene Leonovich <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | declare(strict_types=1); |
||
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\AuthenticationMiddleware; |
||
22 | use Tarantool\Client\Middleware\Middleware; |
||
23 | use Tarantool\Client\Middleware\RetryMiddleware; |
||
24 | use Tarantool\Client\Packer\Packer; |
||
25 | use Tarantool\Client\Packer\PurePacker; |
||
26 | use Tarantool\Client\Request\CallRequest; |
||
27 | use Tarantool\Client\Request\EvaluateRequest; |
||
28 | use Tarantool\Client\Request\ExecuteRequest; |
||
29 | use Tarantool\Client\Request\PingRequest; |
||
30 | use Tarantool\Client\Request\PrepareRequest; |
||
31 | use Tarantool\Client\Schema\Criteria; |
||
32 | use Tarantool\Client\Schema\Space; |
||
33 | |||
34 | final class Client |
||
35 | { |
||
36 | /** @var Handler */ |
||
37 | private $handler; |
||
38 | |||
39 | /** @var array<array-key, Space> */ |
||
40 | private $spaces = []; |
||
41 | |||
42 | 979 | public function __construct(Handler $handler) |
|
43 | { |
||
44 | 979 | $this->handler = $handler; |
|
45 | } |
||
46 | |||
47 | 4 | public static function fromDefaults() : self |
|
48 | { |
||
49 | 4 | return new self(new DefaultHandler( |
|
50 | 4 | StreamConnection::createTcp(), |
|
51 | 4 | PurePacker::fromAvailableExtensions() |
|
52 | 4 | )); |
|
53 | } |
||
54 | |||
55 | 124 | public static function fromOptions(array $options, ?Packer $packer = null) : self |
|
56 | { |
||
57 | 124 | $connectionOptions = []; |
|
58 | 124 | if (isset($options['connect_timeout'])) { |
|
59 | 16 | $connectionOptions['connect_timeout'] = $options['connect_timeout']; |
|
60 | } |
||
61 | 124 | if (isset($options['socket_timeout'])) { |
|
62 | 16 | $connectionOptions['socket_timeout'] = $options['socket_timeout']; |
|
63 | } |
||
64 | 124 | if (isset($options['tcp_nodelay'])) { |
|
65 | 12 | $connectionOptions['tcp_nodelay'] = $options['tcp_nodelay']; |
|
66 | } |
||
67 | 124 | if (isset($options['persistent'])) { |
|
68 | 12 | $connectionOptions['persistent'] = $options['persistent']; |
|
69 | } |
||
70 | |||
71 | 124 | $middleware = []; |
|
72 | 124 | if (isset($options['max_retries']) && 0 !== $options['max_retries']) { |
|
73 | 24 | $middleware[] = RetryMiddleware::linear($options['max_retries']); |
|
74 | } |
||
75 | 112 | if (isset($options['username'])) { |
|
76 | 32 | $middleware[] = new AuthenticationMiddleware($options['username'], $options['password'] ?? ''); |
|
77 | } |
||
78 | |||
79 | 96 | $connection = isset($options['uri']) |
|
80 | 28 | ? StreamConnection::create($options['uri'], $connectionOptions) |
|
81 | 88 | : StreamConnection::createTcp(StreamConnection::DEFAULT_TCP_URI, $connectionOptions); |
|
82 | |||
83 | 56 | $handler = new DefaultHandler($connection, $packer ?? PurePacker::fromAvailableExtensions()); |
|
84 | |||
85 | 56 | return $middleware |
|
86 | 20 | ? new self(MiddlewareHandler::append($handler, $middleware)) |
|
87 | 56 | : new self($handler); |
|
88 | } |
||
89 | |||
90 | 120 | public static function fromDsn(string $dsn, ?Packer $packer = null) : self |
|
91 | { |
||
92 | 120 | $dsn = Dsn::parse($dsn); |
|
93 | |||
94 | 120 | $connectionOptions = []; |
|
95 | 120 | if (null !== $timeout = $dsn->getFloat('connect_timeout')) { |
|
96 | 4 | $connectionOptions['connect_timeout'] = $timeout; |
|
97 | } |
||
98 | 116 | if (null !== $timeout = $dsn->getFloat('socket_timeout')) { |
|
99 | 4 | $connectionOptions['socket_timeout'] = $timeout; |
|
100 | } |
||
101 | 112 | if (null !== $tcpNoDelay = $dsn->getBool('tcp_nodelay')) { |
|
102 | 32 | $connectionOptions['tcp_nodelay'] = $tcpNoDelay; |
|
103 | } |
||
104 | 108 | if (null !== $persistent = $dsn->getBool('persistent')) { |
|
105 | 32 | $connectionOptions['persistent'] = $persistent; |
|
106 | } |
||
107 | |||
108 | 100 | $middleware = []; |
|
109 | 100 | if ($maxRetries = $dsn->getInt('max_retries')) { |
|
110 | 4 | $middleware[] = RetryMiddleware::linear($maxRetries); |
|
111 | } |
||
112 | 92 | if ($username = $dsn->getUsername()) { |
|
113 | $middleware[] = new AuthenticationMiddleware($username, $dsn->getPassword() ?? ''); |
||
114 | } |
||
115 | |||
116 | 92 | $connection = $dsn->isTcp() |
|
117 | 92 | ? StreamConnection::createTcp($dsn->getConnectionUri(), $connectionOptions) |
|
118 | 92 | : StreamConnection::createUds($dsn->getConnectionUri(), $connectionOptions); |
|
119 | |||
120 | 92 | $handler = new DefaultHandler($connection, $packer ?? PurePacker::fromAvailableExtensions()); |
|
121 | |||
122 | 92 | return $middleware |
|
123 | 4 | ? new self(MiddlewareHandler::append($handler, $middleware)) |
|
124 | 92 | : new self($handler); |
|
125 | } |
||
126 | |||
127 | 40 | public function withMiddleware(Middleware ...$middleware) : self |
|
128 | { |
||
129 | 40 | $new = clone $this; |
|
130 | 40 | $new->handler = MiddlewareHandler::append($new->handler, $middleware); |
|
131 | |||
132 | 40 | return $new; |
|
133 | } |
||
134 | |||
135 | 4 | public function withPrependedMiddleware(Middleware ...$middleware) : self |
|
136 | { |
||
137 | 4 | $new = clone $this; |
|
138 | 4 | $new->handler = MiddlewareHandler::prepend($new->handler, $middleware); |
|
139 | |||
140 | 4 | return $new; |
|
141 | } |
||
142 | |||
143 | 146 | public function getHandler() : Handler |
|
144 | { |
||
145 | 146 | return $this->handler; |
|
146 | } |
||
147 | |||
148 | 259 | public function getSpace(string $spaceName) : Space |
|
149 | { |
||
150 | 259 | if (isset($this->spaces[$spaceName])) { |
|
151 | 4 | return $this->spaces[$spaceName]; |
|
152 | } |
||
153 | |||
154 | 259 | $spaceId = $this->getSpaceIdByName($spaceName); |
|
155 | |||
156 | 251 | return $this->spaces[$spaceName] = $this->spaces[$spaceId] = new Space($this->handler, $spaceId); |
|
157 | } |
||
158 | |||
159 | 275 | public function getSpaceById(int $spaceId) : Space |
|
160 | { |
||
161 | 275 | if (isset($this->spaces[$spaceId])) { |
|
162 | return $this->spaces[$spaceId]; |
||
163 | } |
||
164 | |||
165 | 275 | return $this->spaces[$spaceId] = new Space($this->handler, $spaceId); |
|
166 | } |
||
167 | |||
168 | 169 | public function ping() : void |
|
169 | { |
||
170 | 169 | $this->handler->handle(new PingRequest()); |
|
171 | } |
||
172 | |||
173 | /** |
||
174 | * @param mixed ...$args |
||
175 | */ |
||
176 | 49 | public function call(string $funcName, ...$args) : array |
|
177 | { |
||
178 | 49 | return $this->handler->handle(new CallRequest($funcName, $args)) |
|
179 | 49 | ->getBodyField(Keys::DATA); |
|
180 | } |
||
181 | |||
182 | /** |
||
183 | * @param mixed ...$args |
||
184 | */ |
||
185 | 418 | public function evaluate(string $expr, ...$args) : array |
|
186 | { |
||
187 | 418 | return $this->handler->handle(new EvaluateRequest($expr, $args)) |
|
188 | 418 | ->getBodyField(Keys::DATA); |
|
189 | } |
||
190 | |||
191 | /** |
||
192 | * @param mixed ...$params |
||
193 | */ |
||
194 | 12 | public function execute(string $sql, ...$params) : Response |
|
195 | { |
||
196 | 12 | return $this->handler->handle(ExecuteRequest::fromSql($sql, $params)); |
|
197 | } |
||
198 | |||
199 | /** |
||
200 | * @param mixed ...$params |
||
201 | */ |
||
202 | 45 | public function executeQuery(string $sql, ...$params) : SqlQueryResult |
|
203 | { |
||
204 | 45 | $response = $this->handler->handle(ExecuteRequest::fromSql($sql, $params)); |
|
205 | |||
206 | 45 | return new SqlQueryResult( |
|
207 | 45 | $response->getBodyField(Keys::DATA), |
|
208 | 45 | $response->getBodyField(Keys::METADATA) |
|
209 | 45 | ); |
|
210 | } |
||
211 | |||
212 | /** |
||
213 | * @param mixed ...$params |
||
214 | */ |
||
215 | 28 | public function executeUpdate(string $sql, ...$params) : SqlUpdateResult |
|
216 | { |
||
217 | 28 | $response = $this->handler->handle(ExecuteRequest::fromSql($sql, $params)); |
|
218 | |||
219 | 28 | return new SqlUpdateResult($response->getBodyField(Keys::SQL_INFO)); |
|
220 | } |
||
221 | |||
222 | 20 | public function prepare(string $sql) : PreparedStatement |
|
223 | { |
||
224 | 20 | $response = $this->handler->handle(PrepareRequest::fromSql($sql)); |
|
225 | |||
226 | 20 | return new PreparedStatement( |
|
227 | 20 | $this->handler, |
|
228 | 20 | $response->getBodyField(Keys::STMT_ID), |
|
229 | 20 | $response->getBodyField(Keys::BIND_COUNT), |
|
230 | 20 | $response->getBodyField(Keys::BIND_METADATA), |
|
231 | 20 | $response->tryGetBodyField(Keys::METADATA, []) |
|
232 | 20 | ); |
|
233 | } |
||
234 | |||
235 | 8 | public function flushSpaces() : void |
|
236 | { |
||
237 | 8 | $this->spaces = []; |
|
238 | } |
||
239 | |||
240 | 44 | public function __clone() |
|
241 | { |
||
242 | 44 | $this->spaces = []; |
|
243 | } |
||
244 | |||
245 | 259 | private function getSpaceIdByName(string $spaceName) : int |
|
246 | { |
||
247 | 259 | $schema = $this->getSpaceById(Space::VSPACE_ID); |
|
248 | 259 | $data = $schema->select(Criteria::key([$spaceName])->andIndex(Space::VSPACE_NAME_INDEX)); |
|
249 | |||
250 | 259 | if ($data) { |
|
0 ignored issues
–
show
|
|||
251 | 251 | return $data[0][0]; |
|
252 | } |
||
253 | |||
254 | 8 | throw RequestFailed::unknownSpace($spaceName); |
|
255 | } |
||
256 | } |
||
257 |
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.