These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace LaravelFreelancerNL\Aranguent; |
||
4 | |||
5 | use ArangoDBClient\CollectionHandler as ArangoCollectionHandler; |
||
6 | use ArangoDBClient\Connection as ArangoConnection; |
||
7 | use ArangoDBClient\ConnectionOptions as ArangoConnectionOptions; |
||
8 | use ArangoDBClient\DocumentHandler as ArangoDocumentHandler; |
||
9 | use ArangoDBClient\Exception; |
||
10 | use ArangoDBClient\GraphHandler as ArangoGraphHandler; |
||
11 | use ArangoDBClient\Statement; |
||
12 | use ArangoDBClient\UserHandler as ArangoUserHandler; |
||
13 | use ArangoDBClient\ViewHandler as ArangoViewHandler; |
||
14 | use Illuminate\Database\Connection as IlluminateConnection; |
||
15 | use Iterator; |
||
16 | use LaravelFreelancerNL\Aranguent\Concerns\DetectsDeadlocks; |
||
17 | use LaravelFreelancerNL\Aranguent\Concerns\DetectsLostConnections; |
||
18 | use LaravelFreelancerNL\Aranguent\Concerns\ManagesTransactions; |
||
19 | use LaravelFreelancerNL\Aranguent\Query\Builder as QueryBuilder; |
||
20 | use LaravelFreelancerNL\Aranguent\Query\Grammar as QueryGrammar; |
||
21 | use LaravelFreelancerNL\Aranguent\Query\Processor; |
||
22 | use LaravelFreelancerNL\Aranguent\Schema\Builder as SchemaBuilder; |
||
23 | use LaravelFreelancerNL\FluentAQL\QueryBuilder as FluentAQL; |
||
24 | |||
25 | class Connection extends IlluminateConnection |
||
26 | { |
||
27 | use DetectsDeadlocks, |
||
28 | DetectsLostConnections, |
||
29 | ManagesTransactions; |
||
30 | |||
31 | /** |
||
32 | * {@inheritdoc} |
||
33 | * |
||
34 | * @var array |
||
35 | */ |
||
36 | protected $defaultConfig = [ |
||
37 | ArangoConnectionOptions::OPTION_ENDPOINT => 'tcp://localhost:8529', |
||
38 | ArangoConnectionOptions::OPTION_CONNECTION => 'Keep-Alive', |
||
39 | ArangoConnectionOptions::OPTION_AUTH_USER => null, |
||
40 | ArangoConnectionOptions::OPTION_AUTH_PASSWD => null, |
||
41 | 'tablePrefix' => '', |
||
42 | ]; |
||
43 | |||
44 | protected $config; |
||
45 | |||
46 | protected $arangoConnection; |
||
47 | |||
48 | protected $readArangoConnection; |
||
49 | |||
50 | protected $reconnector; |
||
51 | |||
52 | protected $database; |
||
53 | |||
54 | protected $schemaGrammar; |
||
55 | |||
56 | protected $queryGrammar; |
||
57 | |||
58 | protected $pretending; |
||
59 | |||
60 | protected $recordsModified; |
||
61 | |||
62 | protected $loggingQueries; |
||
63 | |||
64 | protected $queryLog; |
||
65 | |||
66 | protected $collectionHandler; |
||
67 | |||
68 | protected $viewHandler; |
||
69 | |||
70 | protected $documentHandler; |
||
71 | |||
72 | protected $graphHandler; |
||
73 | |||
74 | protected $userHandler; |
||
75 | |||
76 | /** |
||
77 | * The ArangoDB driver name. |
||
78 | * |
||
79 | * @var string |
||
80 | */ |
||
81 | protected $driverName = 'arangodb'; |
||
82 | |||
83 | /** |
||
84 | * Connection constructor. |
||
85 | * |
||
86 | * @param array $config |
||
87 | * @throws Exception |
||
88 | */ |
||
89 | public function __construct($config = []) |
||
90 | { |
||
91 | $this->config = array_merge($this->defaultConfig, $config); |
||
92 | |||
93 | if (isset($this->config ['database'])) { |
||
94 | $this->database = $this->config ['database']; |
||
95 | } |
||
96 | |||
97 | $this->tablePrefix = $this->config['tablePrefix']; |
||
98 | |||
99 | // activate and set the database client connection |
||
100 | $this->arangoConnection = new ArangoConnection($this->config); |
||
101 | |||
102 | // We need to initialize a query grammar and the query post processors |
||
103 | // which are both very important parts of the database abstractions |
||
104 | // so we initialize these to their default values while starting. |
||
105 | $this->useDefaultQueryGrammar(); |
||
106 | |||
107 | $this->useDefaultPostProcessor(); |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Get a schema builder instance for the connection. |
||
112 | * |
||
113 | * @return \LaravelFreelancerNL\Aranguent\Schema\Builder |
||
114 | */ |
||
115 | public function getSchemaBuilder() |
||
116 | { |
||
117 | if (is_null($this->schemaGrammar)) { |
||
118 | $this->useDefaultSchemaGrammar(); |
||
119 | } |
||
120 | |||
121 | return new SchemaBuilder($this); |
||
0 ignored issues
–
show
|
|||
122 | } |
||
123 | |||
124 | /** |
||
125 | * Get the default query grammar instance. |
||
126 | * |
||
127 | * @return QueryGrammar |
||
128 | */ |
||
129 | protected function getDefaultQueryGrammar() |
||
130 | { |
||
131 | return new QueryGrammar; |
||
0 ignored issues
–
show
The return type of
return new \LaravelFreel...nguent\Query\Grammar(); (LaravelFreelancerNL\Aranguent\Query\Grammar ) is incompatible with the return type of the parent method Illuminate\Database\Conn...:getDefaultQueryGrammar of type Illuminate\Database\Query\Grammars\Grammar .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Get the default post processor instance. |
||
136 | * |
||
137 | * @return Processor |
||
138 | */ |
||
139 | protected function getDefaultPostProcessor() |
||
140 | { |
||
141 | return new Processor; |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * Run a select statement against the database and returns a generator. |
||
146 | * ($useReadPdo is a dummy to adhere to the interface). |
||
147 | * |
||
148 | * @param string $query |
||
149 | * @param array $bindings |
||
150 | * @param bool $useReadPdo |
||
151 | * @param array|null $transactionCollections |
||
152 | * @return Iterator|null |
||
153 | * @throws Exception |
||
154 | */ |
||
155 | public function cursor($query, $bindings = [], $useReadPdo = null, $transactionCollections = null) |
||
156 | { |
||
157 | return $this->run($query, $bindings, function ($query, $bindings) use ($transactionCollections) { |
||
158 | if ($this->pretending()) { |
||
159 | return []; |
||
160 | } |
||
161 | if ($this->transactionLevel() > 0) { |
||
162 | $this->addQueryToTransaction($query, $bindings, $transactionCollections); |
||
163 | |||
164 | return []; |
||
165 | } |
||
166 | |||
167 | $statement = $this->newArangoStatement($query, $bindings); |
||
168 | |||
169 | return $statement->execute(); |
||
170 | }); |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * Execute an AQL statement and return the boolean result. |
||
175 | * |
||
176 | * @param string|FluentAQL $query |
||
177 | * @param array $bindings |
||
178 | * @param array|null $transactionCollections |
||
179 | * @return bool |
||
180 | */ |
||
181 | public function statement($query, $bindings = [], $transactionCollections = null) |
||
182 | { |
||
183 | if ($query instanceof FluentAQL) { |
||
184 | $bindings = $query->binds; |
||
185 | $transactionCollections = $query->collections; |
||
186 | $query = $query->query; |
||
187 | } |
||
188 | |||
189 | return $this->run($query, $bindings, function ($query, $bindings) use ($transactionCollections) { |
||
190 | if ($this->pretending()) { |
||
191 | return true; |
||
192 | } |
||
193 | if ($this->transactionLevel() > 0) { |
||
194 | $this->addQueryToTransaction($query, $bindings, $transactionCollections); |
||
195 | |||
196 | return true; |
||
197 | } |
||
198 | |||
199 | $statement = $this->newArangoStatement($query, $bindings); |
||
200 | |||
201 | $cursor = $statement->execute(); |
||
202 | |||
203 | $affectedDocumentCount = $cursor->getWritesExecuted(); |
||
204 | $this->recordsHaveBeenModified($changed = $affectedDocumentCount > 0); |
||
205 | |||
206 | return $changed; |
||
207 | }); |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * Run an AQL statement and get the number of rows affected. |
||
212 | * |
||
213 | * @param string|FluentAQL $query |
||
214 | * @param array $bindings |
||
215 | * @param array|null $transactionCollections |
||
216 | * @return int |
||
217 | */ |
||
218 | public function affectingStatement($query, $bindings = [], $transactionCollections = null) |
||
219 | { |
||
220 | if ($query instanceof FluentAQL) { |
||
221 | $bindings = $query->binds; |
||
222 | $transactionCollections = $query->collections; |
||
223 | $query = $query->query; |
||
224 | } |
||
225 | |||
226 | return $this->run($query, $bindings, function ($query, $bindings) use ($transactionCollections) { |
||
227 | if ($this->pretending()) { |
||
228 | return 0; |
||
229 | } |
||
230 | if ($this->transactionLevel() > 0) { |
||
231 | $this->addQueryToTransaction($query, $bindings, $transactionCollections); |
||
232 | |||
233 | return 0; |
||
234 | } |
||
235 | |||
236 | // For update or delete statements, we want to get the number of rows affected |
||
237 | // by the statement and return that back to the developer. We'll first need |
||
238 | // to execute the statement and get the executed writes from the extra. |
||
239 | $statement = $this->newArangoStatement($query, $bindings); |
||
240 | |||
241 | $cursor = $statement->execute(); |
||
242 | |||
243 | $affectedDocumentCount = $cursor->getWritesExecuted(); |
||
244 | |||
245 | $this->recordsHaveBeenModified($affectedDocumentCount > 0); |
||
246 | |||
247 | return $affectedDocumentCount; |
||
248 | }); |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Run a raw, unprepared query against the connection. |
||
253 | * |
||
254 | * @param string $query |
||
255 | * @return bool |
||
256 | */ |
||
257 | public function unprepared($query) |
||
258 | { |
||
259 | return $this->run($query, [], function ($query) { |
||
260 | if ($this->pretending()) { |
||
261 | return true; |
||
262 | } |
||
263 | if ($this->transactionLevel() > 0) { |
||
264 | $this->addQueryToTransaction($query); |
||
265 | |||
266 | return []; |
||
267 | } |
||
268 | |||
269 | $statement = $this->newArangoStatement($query, []); |
||
270 | |||
271 | $cursor = $statement->execute(); |
||
272 | |||
273 | $affectedDocumentCount = $cursor->getWritesExecuted(); |
||
274 | |||
275 | $change = $affectedDocumentCount > 0; |
||
276 | |||
277 | $this->recordsHaveBeenModified($change); |
||
278 | |||
279 | return $change; |
||
280 | }); |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * Returns the query execution plan. The query will not be executed. |
||
285 | * |
||
286 | * @param string $query |
||
287 | * @param array $bindings |
||
288 | * @return array |
||
289 | * @throws Exception |
||
290 | */ |
||
291 | public function explain($query, $bindings = []) |
||
292 | { |
||
293 | $statement = $this->newArangoStatement($query, $bindings); |
||
294 | |||
295 | return $statement->explain(); |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Run a select statement against the database. |
||
300 | * |
||
301 | * @param string|FluentAQL $query |
||
302 | * @param array $bindings |
||
303 | * @param bool $useReadPdo |
||
304 | * @param null|array $transactionCollections |
||
305 | * @return array |
||
306 | */ |
||
307 | public function select($query, $bindings = [], $useReadPdo = true, $transactionCollections = null) |
||
308 | { |
||
309 | return $this->execute($query, $bindings, $useReadPdo, $transactionCollections); |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * Run an AQL query against the database and return the results. |
||
314 | * |
||
315 | * @param string|FluentAQL $query |
||
316 | * @param array $bindings |
||
317 | * @param bool $useReadPdo |
||
318 | * @param null|array $transactionCollections |
||
319 | * @return array |
||
320 | */ |
||
321 | public function execute($query, $bindings = [], $useReadPdo = true, $transactionCollections = null) |
||
322 | { |
||
323 | if ($query instanceof FluentAQL) { |
||
324 | $bindings = $query->binds; |
||
325 | $transactionCollections = $query->collections; |
||
0 ignored issues
–
show
$transactionCollections is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
326 | $query = $query->query; |
||
327 | } |
||
328 | |||
329 | return $this->run($query, $bindings, function ($query, $bindings, $transactionCollections = null) { |
||
330 | if ($this->pretending()) { |
||
331 | return []; |
||
332 | } |
||
333 | if ($this->transactionLevel() > 0) { |
||
334 | $this->addQueryToTransaction($query, $bindings, $transactionCollections); |
||
335 | |||
336 | return []; |
||
337 | } |
||
338 | |||
339 | $statement = $this->newArangoStatement($query, $bindings); |
||
340 | $cursor = $statement->execute(); |
||
341 | |||
342 | return $cursor->getAll(); |
||
343 | }); |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * Run an insert statement against the database. |
||
348 | * |
||
349 | * @param string|FluentAQL $query |
||
350 | * @param array $bindings |
||
351 | * @param array|null $transactionCollections |
||
352 | * @return bool |
||
353 | */ |
||
354 | public function insert($query, $bindings = [], $transactionCollections = null) |
||
355 | { |
||
356 | return $this->statement($query, $bindings, $transactionCollections); |
||
357 | } |
||
358 | |||
359 | /** |
||
360 | * Run an update statement against the database. |
||
361 | * |
||
362 | * @param string|FluentAQL $query |
||
363 | * @param array $bindings |
||
364 | * @param array|null $transactionCollections |
||
365 | * @return int |
||
366 | */ |
||
367 | public function update($query, $bindings = [], $transactionCollections = null) |
||
368 | { |
||
369 | return $this->affectingStatement($query, $bindings, $transactionCollections); |
||
370 | } |
||
371 | |||
372 | /** |
||
373 | * Run a delete statement against the database. |
||
374 | * |
||
375 | * @param string $query |
||
376 | * @param array $bindings |
||
377 | * @param array|null $transactionCollections |
||
378 | * @return int |
||
379 | */ |
||
380 | public function delete($query, $bindings = [], $transactionCollections = null) |
||
381 | { |
||
382 | return $this->affectingStatement($query, $bindings, $transactionCollections); |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * Get a new query builder instance. |
||
387 | * |
||
388 | * @return QueryBuilder |
||
389 | */ |
||
390 | public function query() |
||
391 | { |
||
392 | return new QueryBuilder( |
||
393 | $this, $this->getQueryGrammar(), $this->getPostProcessor() |
||
394 | ); |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * Get the collection prefix for the connection. |
||
399 | * |
||
400 | * @return string |
||
401 | */ |
||
402 | public function getTablePrefix() |
||
403 | { |
||
404 | return $this->tablePrefix; |
||
405 | } |
||
406 | |||
407 | /** |
||
408 | * Disconnect from the underlying ArangoDB connection. |
||
409 | * |
||
410 | * @return void |
||
411 | */ |
||
412 | public function disconnect() |
||
413 | { |
||
414 | $this->transactions = 0; |
||
415 | |||
416 | $this->arangoConnection = null; |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Reconnect to the database if a Arango connection is missing. |
||
421 | * |
||
422 | * @return void |
||
423 | */ |
||
424 | protected function reconnectIfMissingConnection() |
||
425 | { |
||
426 | if (is_null($this->arangoConnection)) { |
||
427 | $this->reconnect(); |
||
428 | } |
||
429 | } |
||
430 | |||
431 | public function getArangoConnection() |
||
432 | { |
||
433 | return $this->arangoConnection; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * @param $query |
||
438 | * @param $bindings |
||
439 | * @param $connection |
||
440 | * @return Statement |
||
441 | * @throws Exception |
||
442 | */ |
||
443 | public function newArangoStatement($query, $bindings): Statement |
||
444 | { |
||
445 | $statement = new Statement($this->arangoConnection, ['query' => $query, 'bindVars' => $bindings]); |
||
446 | $statement->setDocumentClass(Document::class); |
||
447 | |||
448 | return $statement; |
||
449 | } |
||
450 | |||
451 | public function getCollectionHandler() |
||
452 | { |
||
453 | if (! isset($this->collectionHandler)) { |
||
454 | $this->collectionHandler = new ArangoCollectionHandler($this->arangoConnection); |
||
455 | $this->collectionHandler->setDocumentClass(Document::class); |
||
456 | } |
||
457 | |||
458 | return $this->collectionHandler; |
||
459 | } |
||
460 | |||
461 | public function getDocumentHandler() |
||
462 | { |
||
463 | if (! isset($this->documentHandler)) { |
||
464 | $this->documentHandler = new ArangoDocumentHandler($this->arangoConnection); |
||
465 | $this->documentHandler->setDocumentClass(Document::class); |
||
466 | } |
||
467 | |||
468 | return $this->documentHandler; |
||
469 | } |
||
470 | |||
471 | public function getUserHandler() |
||
472 | { |
||
473 | if (! isset($this->userHandler)) { |
||
474 | $this->userHandler = new ArangoUserHandler($this->arangoConnection); |
||
475 | $this->userHandler->setDocumentClass(Document::class); |
||
476 | } |
||
477 | |||
478 | return $this->userHandler; |
||
479 | } |
||
480 | |||
481 | public function getGraphHandler() |
||
482 | { |
||
483 | if (! isset($this->graphHandler)) { |
||
484 | $this->graphHandler = new ArangoGraphHandler($this->arangoConnection); |
||
485 | $this->graphHandler->setDocumentClass(Document::class); |
||
486 | } |
||
487 | |||
488 | return $this->graphHandler; |
||
489 | } |
||
490 | |||
491 | public function getViewHandler() |
||
492 | { |
||
493 | if (! isset($this->viewHandler)) { |
||
494 | $this->viewHandler = new ArangoViewHandler($this->arangoConnection); |
||
495 | $this->viewHandler->setDocumentClass(Document::class); |
||
496 | } |
||
497 | |||
498 | return $this->viewHandler; |
||
499 | } |
||
500 | |||
501 | public function setDatabaseName($database) |
||
502 | { |
||
503 | $this->database = $database; |
||
504 | $this->arangoConnection->setDatabase($database); |
||
505 | } |
||
506 | |||
507 | public function getDatabaseName() |
||
508 | { |
||
509 | return $this->database; |
||
510 | } |
||
511 | } |
||
512 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.