1 | <?php |
||
2 | |||
3 | namespace Bdf\Prime\Query; |
||
4 | |||
5 | use Bdf\Prime\Connection\ConnectionInterface; |
||
6 | use Bdf\Prime\Connection\Result\ResultSetInterface; |
||
7 | use Bdf\Prime\Exception\PrimeException; |
||
8 | use Bdf\Prime\Query\Compiler\CompilerInterface; |
||
9 | use Bdf\Prime\Query\Compiler\Preprocessor\DefaultPreprocessor; |
||
10 | use Bdf\Prime\Query\Compiler\Preprocessor\PreprocessorInterface; |
||
11 | use Bdf\Prime\Query\Compiler\QuoteCompilerInterface; |
||
12 | use Bdf\Prime\Query\Compiler\SqlCompiler; |
||
13 | use Bdf\Prime\Query\Contract\Compilable; |
||
14 | use Bdf\Prime\Query\Contract\Paginable; |
||
15 | use Bdf\Prime\Query\Contract\ReadOperation; |
||
16 | use Bdf\Prime\Query\Contract\WriteOperation; |
||
17 | use Bdf\Prime\Query\Expression\Raw; |
||
18 | use Bdf\Prime\Query\Extension\EntityJoinTrait; |
||
19 | use Bdf\Prime\Query\Extension\LimitableTrait; |
||
20 | use Bdf\Prime\Query\Extension\LockableTrait; |
||
21 | use Bdf\Prime\Query\Extension\OrderableTrait; |
||
22 | use Bdf\Prime\Query\Extension\PaginableTrait; |
||
23 | use Bdf\Prime\Query\Extension\SimpleJoinTrait; |
||
24 | use Doctrine\DBAL\Query\Expression\CompositeExpression; |
||
25 | use Stringable; |
||
26 | |||
27 | /** |
||
28 | * Sql Query |
||
29 | * |
||
30 | * @package Bdf\Prime\Query |
||
31 | * |
||
32 | * @todo comment reset un statement (ex ecraser les orders). Prendre en compte le reset du compiler |
||
33 | * |
||
34 | * @template C as \Bdf\Prime\Connection\ConnectionInterface&\Doctrine\DBAL\Connection |
||
35 | * @template R as object|array |
||
36 | * |
||
37 | * @extends AbstractQuery<C, R> |
||
38 | * @implements SqlQueryInterface<C, R> |
||
39 | * @implements Paginable<R> |
||
40 | */ |
||
41 | class Query extends AbstractQuery implements SqlQueryInterface, Paginable, Stringable |
||
42 | { |
||
43 | use EntityJoinTrait; |
||
44 | /** @use PaginableTrait<R> */ |
||
45 | use PaginableTrait; |
||
46 | use LimitableTrait; |
||
47 | use OrderableTrait; |
||
48 | use SimpleJoinTrait; |
||
49 | use LockableTrait; |
||
50 | |||
51 | /** |
||
52 | * Initializes a new <tt>Query</tt>. |
||
53 | * |
||
54 | * @param C $connection The DBAL Connection. |
||
55 | * @param PreprocessorInterface|null $preprocessor |
||
56 | */ |
||
57 | 980 | public function __construct(ConnectionInterface $connection, PreprocessorInterface $preprocessor = null) |
|
58 | { |
||
59 | 980 | parent::__construct($connection, $preprocessor ?: new DefaultPreprocessor()); |
|
60 | |||
61 | 980 | $this->statements = [ |
|
62 | 980 | 'ignore' => null, |
|
63 | 980 | 'replace' => null, |
|
64 | 980 | 'values' => [], |
|
65 | 980 | 'columns' => [], |
|
66 | 980 | 'distinct' => null, |
|
67 | 980 | 'tables' => [], |
|
68 | 980 | 'joins' => [], |
|
69 | 980 | 'where' => [], |
|
70 | 980 | 'groups' => [], |
|
71 | 980 | 'having' => [], |
|
72 | 980 | 'orders' => [], |
|
73 | 980 | 'limit' => null, |
|
74 | 980 | 'offset' => null, |
|
75 | 980 | 'aggregate' => null, |
|
76 | 980 | 'lock' => null, |
|
77 | 980 | ]; |
|
78 | } |
||
79 | |||
80 | /** |
||
81 | * {@inheritdoc} |
||
82 | * |
||
83 | * @return CompilerInterface<Query>&QuoteCompilerInterface |
||
84 | */ |
||
85 | 925 | public function compiler(): object |
|
86 | { |
||
87 | 925 | return parent::compiler(); |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * {@inheritdoc} |
||
92 | */ |
||
93 | 823 | public function getBindings(): array |
|
94 | { |
||
95 | 823 | return $this->compiler()->getBindings($this); |
|
96 | } |
||
97 | |||
98 | /** |
||
99 | * {@inheritdoc} |
||
100 | */ |
||
101 | public function quote($value, int $type = null): string |
||
102 | { |
||
103 | return $this->connection->quote($value, $type); |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * {@inheritdoc} |
||
108 | */ |
||
109 | public function quoteIdentifier(string $column): string |
||
110 | { |
||
111 | return $this->compiler()->quoteIdentifier($this, $column); |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * {@inheritdoc} |
||
116 | */ |
||
117 | #[WriteOperation] |
||
118 | 27 | public function delete(): int |
|
119 | { |
||
120 | 27 | return $this->executeUpdate(self::TYPE_DELETE); |
|
121 | } |
||
122 | |||
123 | /** |
||
124 | * {@inheritdoc} |
||
125 | */ |
||
126 | #[WriteOperation] |
||
127 | 29 | public function update(array $data = [], array $types = []): int |
|
128 | { |
||
129 | 29 | if ($data) { |
|
0 ignored issues
–
show
|
|||
130 | 11 | $this->statements['values'] = [ |
|
131 | 11 | 'data' => $data, |
|
132 | 11 | 'types' => $types, |
|
133 | 11 | ]; |
|
134 | } |
||
135 | |||
136 | 29 | return $this->executeUpdate(self::TYPE_UPDATE); |
|
137 | } |
||
138 | |||
139 | /** |
||
140 | * {@inheritdoc} |
||
141 | */ |
||
142 | #[WriteOperation] |
||
143 | 662 | public function insert(array $data = []): int |
|
144 | { |
||
145 | 662 | if ($data) { |
|
0 ignored issues
–
show
The expression
$data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
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 ![]() |
|||
146 | 662 | $this->statements['values'] = [ |
|
147 | 662 | 'data' => $data, |
|
148 | 662 | ]; |
|
149 | } |
||
150 | |||
151 | 662 | return $this->executeUpdate(self::TYPE_INSERT); |
|
152 | } |
||
153 | |||
154 | /** |
||
155 | * {@inheritdoc} |
||
156 | */ |
||
157 | 1 | public function ignore(bool $flag = true) |
|
158 | { |
||
159 | 1 | $this->statements['ignore'] = $flag; |
|
160 | |||
161 | 1 | return $this; |
|
162 | } |
||
163 | |||
164 | /** |
||
165 | * {@inheritdoc} |
||
166 | */ |
||
167 | #[WriteOperation] |
||
168 | 2 | public function replace(array $values = []): int |
|
169 | { |
||
170 | 2 | $this->statements['replace'] = true; |
|
171 | |||
172 | 2 | return $this->insert($values); |
|
173 | } |
||
174 | |||
175 | /** |
||
176 | * Set values to insert or update |
||
177 | * |
||
178 | * @todo Remonter sur une interface ? |
||
179 | * |
||
180 | * <code> |
||
181 | * // Perform a INSERT INTO ... SELECT ... query |
||
182 | * $query |
||
183 | * ->from('users_bck') |
||
184 | * ->values($connection->builder()->from('users')) |
||
185 | * ->insert() |
||
186 | * ; |
||
187 | * |
||
188 | * // Perform a REPLACE INTO ... SELECT ... with column mapping |
||
189 | * $query |
||
190 | * ->from('users_bck') |
||
191 | * ->values( |
||
192 | * $connection->builder() |
||
193 | * ->from('users') |
||
194 | * ->select([ |
||
195 | * 'backup_name' => 'name', // Map "name" column from select table to "backup_name" column to insert table |
||
196 | * 'id' // Use "id" column without mapping other than ORM data mapping |
||
197 | * ]) |
||
198 | * ) |
||
199 | * ->replace() // Replace can also be used |
||
200 | * ; |
||
201 | * |
||
202 | * // Simple UPDATE query |
||
203 | * $query->values(['foo' => 'bar'])->update(); |
||
204 | * </code> |
||
205 | * |
||
206 | * @param QueryInterface|array $data The values, in form [column] => [value], or the SELECT query |
||
207 | * @param array $types The binding types Do not works with INSERT INTO SELECT query |
||
208 | * |
||
209 | * @return $this |
||
210 | */ |
||
211 | 27 | public function values($data = [], array $types = []) |
|
212 | { |
||
213 | 27 | $this->statements['values'] = [ |
|
214 | 27 | 'data' => $data, |
|
215 | 27 | 'type' => $types |
|
216 | 27 | ]; |
|
217 | |||
218 | 27 | return $this; |
|
219 | } |
||
220 | |||
221 | /** |
||
222 | * Executes this query as an update query |
||
223 | * |
||
224 | * @param Compilable::TYPE_* $type The query type |
||
225 | * |
||
226 | * @return int The number of updated rows |
||
227 | * |
||
228 | * @throws PrimeException |
||
229 | */ |
||
230 | 674 | protected function executeUpdate(string $type): int |
|
231 | { |
||
232 | 674 | $this->setType($type); |
|
233 | |||
234 | 674 | $result = $this->connection->execute($this); |
|
235 | |||
236 | 674 | if ($result->hasWrite()) { |
|
237 | 665 | $this->clearCacheOnWrite(); |
|
238 | } |
||
239 | |||
240 | 674 | return $result->count(); |
|
241 | } |
||
242 | |||
243 | /** |
||
244 | * {@inheritdoc} |
||
245 | */ |
||
246 | #[ReadOperation] |
||
247 | 739 | public function execute($columns = null): ResultSetInterface |
|
248 | { |
||
249 | 739 | if (!empty($columns)) { |
|
250 | 54 | $this->select($columns); |
|
251 | } |
||
252 | |||
253 | 739 | $this->setType(self::TYPE_SELECT); |
|
254 | |||
255 | 739 | return $this->executeCached(); |
|
256 | } |
||
257 | |||
258 | /** |
||
259 | * {@inheritdoc} |
||
260 | */ |
||
261 | 10 | protected function cacheKey(): ?string |
|
262 | { |
||
263 | 10 | return sha1($this->toSql().'-'.serialize($this->getBindings())); |
|
264 | } |
||
265 | |||
266 | /** |
||
267 | * {@inheritdoc} |
||
268 | */ |
||
269 | #[ReadOperation] |
||
270 | 15 | public function paginationCount(?string $column = null): int |
|
271 | { |
||
272 | 15 | $statements = $this->statements; |
|
273 | |||
274 | 15 | $this->compilerState->invalidate(['columns', 'orders']); |
|
275 | |||
276 | 15 | $this->statements['orders'] = []; |
|
277 | 15 | $this->statements['limit'] = null; |
|
278 | 15 | $this->statements['offset'] = null; |
|
279 | 15 | $this->statements['aggregate'] = ['pagination', $this->getPaginationColumns($column)]; |
|
280 | |||
281 | 15 | $count = (int)$this->execute()->current()['aggregate']; |
|
282 | |||
283 | 15 | $this->compilerState->invalidate(['columns', 'orders']); |
|
284 | 15 | $this->statements = $statements; |
|
285 | |||
286 | 15 | return $count; |
|
287 | } |
||
288 | |||
289 | /** |
||
290 | * Get the column to count for pagination |
||
291 | * @todo Voir pour count sur PK quand une entité est liée ? |
||
292 | * @todo array ? |
||
293 | * |
||
294 | * @param string|null $column |
||
295 | * |
||
296 | * @return string |
||
297 | */ |
||
298 | 15 | protected function getPaginationColumns(?string $column): string |
|
299 | { |
||
300 | 15 | if (!empty($column)) { |
|
301 | return $column; |
||
302 | } |
||
303 | |||
304 | // If distinct is on and no column are given, we use the current column |
||
305 | 15 | if ($this->statements['distinct'] && !empty($this->statements['columns'])) { |
|
306 | 3 | return $this->statements['columns'][0]['column']; |
|
307 | } |
||
308 | |||
309 | // If group by we use the columns of the group by |
||
310 | 13 | if ($this->statements['groups']) { |
|
311 | 2 | $this->statements['distinct'] = true; |
|
312 | 2 | $column = $this->statements['groups'][0]; |
|
313 | 2 | $this->statements['groups'] = []; |
|
314 | |||
315 | 2 | return $column; |
|
316 | } |
||
317 | |||
318 | 12 | return '*'; |
|
319 | } |
||
320 | |||
321 | /** |
||
322 | * {@inheritdoc} |
||
323 | */ |
||
324 | #[ReadOperation] |
||
325 | 563 | public function count(?string $column = null): int |
|
326 | { |
||
327 | 563 | return (int)$this->aggregate(__FUNCTION__, $column); |
|
328 | } |
||
329 | |||
330 | /** |
||
331 | * {@inheritdoc} |
||
332 | */ |
||
333 | #[ReadOperation] |
||
334 | 2 | public function avg(?string $column = null): float |
|
335 | { |
||
336 | 2 | return (float)$this->aggregate(__FUNCTION__, $column); |
|
337 | } |
||
338 | |||
339 | /** |
||
340 | * {@inheritdoc} |
||
341 | */ |
||
342 | #[ReadOperation] |
||
343 | 2 | public function min(?string $column = null) |
|
344 | { |
||
345 | 2 | return $this->aggregate(__FUNCTION__, $column); |
|
346 | } |
||
347 | |||
348 | /** |
||
349 | * {@inheritdoc} |
||
350 | */ |
||
351 | #[ReadOperation] |
||
352 | 3 | public function max(?string $column = null) |
|
353 | { |
||
354 | 3 | return $this->aggregate(__FUNCTION__, $column); |
|
355 | } |
||
356 | |||
357 | /** |
||
358 | * {@inheritdoc} |
||
359 | */ |
||
360 | #[ReadOperation] |
||
361 | 2 | public function sum(?string $column = null): float |
|
362 | { |
||
363 | 2 | return (float)$this->aggregate(__FUNCTION__, $column); |
|
364 | } |
||
365 | |||
366 | /** |
||
367 | * {@inheritdoc} |
||
368 | */ |
||
369 | #[ReadOperation] |
||
370 | 572 | public function aggregate(string $function, ?string $column = null) |
|
371 | { |
||
372 | 572 | $statements = $this->statements; |
|
373 | |||
374 | 572 | $this->compilerState->invalidate('columns'); |
|
375 | |||
376 | 572 | $this->statements['aggregate'] = [$function, $column ?: '*']; |
|
377 | |||
378 | 572 | $aggregate = $this->execute()->current()['aggregate']; |
|
379 | |||
380 | 572 | $this->compilerState->invalidate('columns'); |
|
381 | 572 | $this->statements = $statements; |
|
382 | |||
383 | 572 | return $aggregate; |
|
384 | } |
||
385 | |||
386 | /** |
||
387 | * {@inheritdoc} |
||
388 | */ |
||
389 | 18 | public function distinct(bool $flag = true) |
|
390 | { |
||
391 | 18 | $this->compilerState->invalidate('columns'); |
|
392 | |||
393 | 18 | $this->statements['distinct'] = $flag; |
|
394 | |||
395 | 18 | return $this; |
|
396 | } |
||
397 | |||
398 | /** |
||
399 | * {@inheritdoc} |
||
400 | * |
||
401 | * @param string|Query $from The table name, or the embedded query |
||
402 | */ |
||
403 | 946 | public function from($from, ?string $alias = null) |
|
404 | { |
||
405 | 946 | $this->compilerState->invalidate('from'); |
|
406 | |||
407 | 946 | $table = [ |
|
408 | 946 | 'table' => $from, |
|
409 | 946 | 'alias' => $alias, |
|
410 | 946 | ]; |
|
411 | 946 | $key = $alias ?: $from; |
|
412 | |||
413 | 946 | if (is_string($key)) { |
|
414 | 946 | $this->statements['tables'][$key] = $table; |
|
415 | } else { |
||
416 | $this->statements['tables'][] = $table; |
||
417 | } |
||
418 | |||
419 | 946 | return $this; |
|
420 | } |
||
421 | |||
422 | /** |
||
423 | * Change a FROM alias for a table (or previously defined alias) |
||
424 | * |
||
425 | * Usage: |
||
426 | * <code> |
||
427 | * // Change alias of the current table : FROM my_table as my_alias |
||
428 | * $query->from('my_table')->fromAlias('my_alias'); |
||
429 | * |
||
430 | * // Change alias of the foo table : FROM foo as my_alias, bar |
||
431 | * $query->from('foo')->from('bar')->fromAlias('my_alias', 'foo'); |
||
432 | * |
||
433 | * // Redefine alias of foo : FROM foo as my_alias, bar |
||
434 | * $query->from('foo', 'f')->from('bar')->fromAlias('my_alias', 'f'); |
||
435 | * </code> |
||
436 | * |
||
437 | * @param string $alias The new alias name |
||
438 | * @param string|null $table The last alias / table name. If null, will define the last table |
||
439 | * |
||
440 | * @return $this |
||
441 | */ |
||
442 | 3 | public function fromAlias(string $alias, ?string $table = null) |
|
443 | { |
||
444 | 3 | $this->compilerState->invalidate('from'); |
|
445 | |||
446 | 3 | $table = $table ?? key($this->statements['tables']); |
|
447 | |||
448 | 3 | $this->statements['tables'][$alias] = $this->statements['tables'][$table]; |
|
449 | 3 | $this->statements['tables'][$alias]['alias'] = $alias; |
|
450 | |||
451 | 3 | unset($this->statements['tables'][$table]); |
|
452 | |||
453 | 3 | return $this; |
|
454 | } |
||
455 | |||
456 | /** |
||
457 | * {@inheritdoc} |
||
458 | */ |
||
459 | 5 | public function group(string ...$columns) |
|
460 | { |
||
461 | 5 | $this->compilerState->invalidate('groups'); |
|
462 | |||
463 | 5 | $this->statements['groups'] = $columns; |
|
464 | |||
465 | 5 | return $this; |
|
466 | } |
||
467 | |||
468 | /** |
||
469 | * {@inheritdoc} |
||
470 | * |
||
471 | * @no-named-arguments |
||
472 | */ |
||
473 | public function addGroup(string ...$columns) |
||
474 | { |
||
475 | $this->compilerState->invalidate('groups'); |
||
476 | |||
477 | $this->statements['groups'] = [...$this->statements['groups'], ...$columns]; |
||
478 | |||
479 | return $this; |
||
480 | } |
||
481 | |||
482 | /** |
||
483 | * {@inheritdoc} |
||
484 | */ |
||
485 | 2 | public function having($column, $operator = null, $value = null) |
|
486 | { |
||
487 | 2 | $this->compilerState->invalidate('having'); |
|
488 | |||
489 | 2 | return $this->buildClause('having', $column, $operator, $value); |
|
490 | } |
||
491 | |||
492 | /** |
||
493 | * {@inheritdoc} |
||
494 | */ |
||
495 | 3 | public function orHaving($column, $operator = null, $value = null) |
|
496 | { |
||
497 | 3 | $this->compilerState->invalidate('having'); |
|
498 | |||
499 | 3 | return $this->buildClause('having', $column, $operator, $value, CompositeExpression::TYPE_OR); |
|
500 | } |
||
501 | |||
502 | /** |
||
503 | * {@inheritdoc} |
||
504 | */ |
||
505 | 2 | public function havingNull(string $column, string $type = CompositeExpression::TYPE_AND) |
|
506 | { |
||
507 | 2 | $this->compilerState->invalidate('having'); |
|
508 | |||
509 | 2 | return $this->buildClause('having', $column, '=', null, $type); |
|
510 | } |
||
511 | |||
512 | /** |
||
513 | * {@inheritdoc} |
||
514 | */ |
||
515 | 2 | public function havingNotNull(string $column, string $type = CompositeExpression::TYPE_AND) |
|
516 | { |
||
517 | 2 | $this->compilerState->invalidate('having'); |
|
518 | |||
519 | 2 | return $this->buildClause('having', $column, '!=', null, $type); |
|
520 | } |
||
521 | |||
522 | /** |
||
523 | * {@inheritdoc} |
||
524 | */ |
||
525 | 1 | public function orHavingNull(string $column) |
|
526 | { |
||
527 | 1 | return $this->havingNull($column, CompositeExpression::TYPE_OR); |
|
528 | } |
||
529 | |||
530 | /** |
||
531 | * {@inheritdoc} |
||
532 | */ |
||
533 | 1 | public function orHavingNotNull(string $column) |
|
534 | { |
||
535 | 1 | return $this->havingNotNull($column, CompositeExpression::TYPE_OR); |
|
536 | } |
||
537 | |||
538 | /** |
||
539 | * {@inheritdoc} |
||
540 | */ |
||
541 | 2 | public function havingRaw($raw, string $type = CompositeExpression::TYPE_AND) |
|
542 | { |
||
543 | 2 | $this->compilerState->invalidate('having'); |
|
544 | |||
545 | 2 | return $this->buildRaw('having', $raw, $type); |
|
546 | } |
||
547 | |||
548 | /** |
||
549 | * {@inheritdoc} |
||
550 | */ |
||
551 | 1 | public function orHavingRaw($raw) |
|
552 | { |
||
553 | 1 | return $this->havingRaw($raw, CompositeExpression::TYPE_OR); |
|
554 | } |
||
555 | |||
556 | /** |
||
557 | * {@inheritdoc} |
||
558 | */ |
||
559 | 6 | public function addCommand(string $command, $value) |
|
560 | { |
||
561 | switch ($command) { |
||
562 | 6 | case ':limit': |
|
563 | 4 | if (is_array($value)) { |
|
564 | $this->limit($value[0], $value[1]); |
||
565 | } else { |
||
566 | 4 | $this->limit($value); |
|
567 | } |
||
568 | 4 | break; |
|
569 | |||
570 | 4 | case ':limitPage': |
|
571 | 1 | if (is_array($value)) { |
|
572 | 1 | $this->limitPage($value[0], $value[1]); |
|
573 | } else { |
||
574 | $this->limitPage($value); |
||
575 | } |
||
576 | 1 | break; |
|
577 | |||
578 | 3 | case ':offset': |
|
579 | $this->offset($value); |
||
580 | break; |
||
581 | |||
582 | 3 | case ':order': |
|
583 | 2 | $this->order($value); |
|
584 | 2 | break; |
|
585 | |||
586 | 1 | case ':distinct': |
|
587 | 1 | $this->distinct($value); |
|
588 | 1 | break; |
|
589 | |||
590 | case ':group': |
||
591 | $this->group($value); |
||
592 | break; |
||
593 | |||
594 | case ':having': |
||
595 | $this->having($value); |
||
596 | break; |
||
597 | } |
||
598 | |||
599 | 6 | return $this; |
|
600 | } |
||
601 | |||
602 | /** |
||
603 | * {@inheritdoc} |
||
604 | */ |
||
605 | 146 | public function toSql(): string |
|
606 | { |
||
607 | 146 | return $this->compile(); |
|
608 | } |
||
609 | |||
610 | /** |
||
611 | * {@inheritdoc} |
||
612 | * |
||
613 | * @todo A reprendre: utiliser les types des bindings |
||
614 | * |
||
615 | * @return string |
||
616 | */ |
||
617 | 19 | public function toRawSql(): string |
|
618 | { |
||
619 | 19 | $keys = []; |
|
620 | 19 | $sql = $this->toSql(); |
|
621 | 19 | $values = $this->compiler()->getBindings($this); |
|
622 | |||
623 | # build a regular expression for each parameter |
||
624 | 19 | foreach ($values as $key => $value) { |
|
625 | 19 | if (is_string($key)) { |
|
626 | $keys[] = '/:' . $key . '/'; |
||
627 | } else { |
||
628 | 19 | $keys[] = '/[?]/'; |
|
629 | } |
||
630 | |||
631 | 19 | if (is_array($value)) { |
|
632 | $values[$key] = implode(',', $this->connection->quote($value)); |
||
633 | 19 | } elseif (is_null($value)) { |
|
634 | $values[$key] = 'NULL'; |
||
635 | 19 | } elseif ($value instanceof \DateTimeInterface) { |
|
636 | $values[$key] = $value->format($this->connection->platform()->grammar()->getDateTimeFormatString()); |
||
637 | 19 | } elseif (is_string($value)) { |
|
638 | 10 | $values[$key] = $this->connection->quote($value); |
|
639 | } else { |
||
640 | 17 | $values[$key] = $value; |
|
641 | } |
||
642 | } |
||
643 | |||
644 | 19 | return preg_replace($keys, $values, $sql, 1); |
|
645 | } |
||
646 | |||
647 | /** |
||
648 | * Gets a string representation of this Query which corresponds to |
||
649 | * the final SQL query being constructed. |
||
650 | * |
||
651 | * @return string The string representation of this Query. |
||
652 | */ |
||
653 | public function __toString(): string |
||
654 | { |
||
655 | return $this->toSql(); |
||
656 | } |
||
657 | } |
||
658 |
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.