1 | <?php |
||||
2 | /** |
||||
3 | * This file is part of the O2System Framework package. |
||||
4 | * |
||||
5 | * For the full copyright and license information, please view the LICENSE |
||||
6 | * file that was distributed with this source code. |
||||
7 | * |
||||
8 | * @author Steeve Andrian Salim |
||||
9 | * @copyright Copyright (c) Steeve Andrian Salim |
||||
10 | */ |
||||
11 | |||||
12 | // ------------------------------------------------------------------------ |
||||
13 | |||||
14 | namespace O2System\Database\Sql\Abstracts; |
||||
15 | |||||
16 | // ------------------------------------------------------------------------ |
||||
17 | |||||
18 | use O2System\Database\DataObjects\Result; |
||||
19 | use O2System\Database\Sql\DataStructures\Query; |
||||
20 | use O2System\Spl\Exceptions\RuntimeException; |
||||
21 | |||||
22 | /** |
||||
23 | * Class AbstractQueryBuilder |
||||
24 | * |
||||
25 | * @package O2System\Database\Sql\Abstracts |
||||
26 | */ |
||||
27 | abstract class AbstractQueryBuilder |
||||
28 | { |
||||
29 | /** |
||||
30 | * AbstractQueryBuilder::testMode |
||||
31 | * |
||||
32 | * If true, no queries will actually be |
||||
33 | * ran against the database. |
||||
34 | * |
||||
35 | * @var bool |
||||
36 | */ |
||||
37 | public $testMode = false; |
||||
38 | |||||
39 | /** |
||||
40 | * AbstractQueryBuilder::testMode |
||||
41 | * |
||||
42 | * If true, no queries will actually be |
||||
43 | * ran against the database. |
||||
44 | * |
||||
45 | * @var bool |
||||
46 | */ |
||||
47 | public $cacheMode = false; |
||||
48 | |||||
49 | /** |
||||
50 | * AbstractQueryBuilder::$conn |
||||
51 | * |
||||
52 | * Query Builder database connection instance. |
||||
53 | * |
||||
54 | * @var AbstractConnection |
||||
55 | */ |
||||
56 | protected $conn; |
||||
57 | |||||
58 | /** |
||||
59 | * AbstractQueryBuilder::$builderCache |
||||
60 | * |
||||
61 | * Query builder cache instance. |
||||
62 | * |
||||
63 | * @var QueryBuilderCache |
||||
0 ignored issues
–
show
|
|||||
64 | */ |
||||
65 | protected $builderCache; |
||||
66 | |||||
67 | /** |
||||
68 | * AbstractQueryBuilder::$arrayObjectConversionMethod |
||||
69 | * |
||||
70 | * Query Builder insert, update array object value conversion method. |
||||
71 | * |
||||
72 | * @var string |
||||
73 | */ |
||||
74 | protected $arrayObjectConversionMethod = 'json_encode'; |
||||
75 | |||||
76 | /** |
||||
77 | * AbstractQueryBuilder::$SqlRandomKeywords |
||||
78 | * |
||||
79 | * ORDER BY random keyword list. |
||||
80 | * |
||||
81 | * @var array |
||||
82 | */ |
||||
83 | protected $SqlOrderByRandomKeywords = ['RAND()', 'RAND(%d)']; |
||||
84 | |||||
85 | /** |
||||
86 | * AbstractQueryBuilder::isSubQuery |
||||
87 | * |
||||
88 | * Is Sub Query instance flag. |
||||
89 | * |
||||
90 | * @var bool |
||||
91 | */ |
||||
92 | protected $isSubQuery = false; |
||||
93 | |||||
94 | // ------------------------------------------------------------------------ |
||||
95 | |||||
96 | /** |
||||
97 | * AbstractQueryBuilder::__construct. |
||||
98 | * |
||||
99 | * @param \O2System\Database\Sql\Abstracts\AbstractConnection $conn |
||||
100 | */ |
||||
101 | public function __construct(AbstractConnection &$conn) |
||||
102 | { |
||||
103 | $this->conn =& $conn; |
||||
104 | $this->builderCache = new Query\BuilderCache(); |
||||
0 ignored issues
–
show
It seems like
new O2System\Database\Sq...es\Query\BuilderCache() of type O2System\Database\Sql\Da...ures\Query\BuilderCache is incompatible with the declared type O2System\Database\Sql\Abstracts\QueryBuilderCache of property $builderCache .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||||
105 | $this->cacheMode = $this->conn->getConfig('cacheEnable'); |
||||
0 ignored issues
–
show
It seems like
$this->conn->getConfig('cacheEnable') can also be of type array . However, the property $cacheMode is declared as type boolean . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||||
106 | } |
||||
107 | |||||
108 | // ------------------------------------------------------------------------ |
||||
109 | |||||
110 | /** |
||||
111 | * AbstractQueryBuilder::cache |
||||
112 | * |
||||
113 | * @param boolean $mode |
||||
114 | * |
||||
115 | * @return static |
||||
116 | */ |
||||
117 | public function cache($mode = true) |
||||
118 | { |
||||
119 | $this->cacheEnable = (bool)$mode; |
||||
0 ignored issues
–
show
|
|||||
120 | |||||
121 | return $this; |
||||
122 | } |
||||
123 | |||||
124 | // ------------------------------------------------------------------------ |
||||
125 | |||||
126 | /** |
||||
127 | * AbstractQueryBuilder::distinct |
||||
128 | * |
||||
129 | * Sets a flag which tells the query string compiler to add DISTINCT |
||||
130 | * keyword on SELECT statement |
||||
131 | * |
||||
132 | * @param bool $distinct |
||||
133 | * |
||||
134 | * @return static |
||||
135 | */ |
||||
136 | public function distinct($distinct = true) |
||||
137 | { |
||||
138 | $this->builderCache->distinct = is_bool($distinct) |
||||
0 ignored issues
–
show
|
|||||
139 | ? $distinct |
||||
140 | : true; |
||||
141 | |||||
142 | return $this; |
||||
143 | } |
||||
144 | |||||
145 | // ------------------------------------------------------------------------ |
||||
146 | |||||
147 | /** |
||||
148 | * AbstractQueryBuilder::into |
||||
149 | * |
||||
150 | * Add SELECT INTO Sql statement portions into Query Builder. |
||||
151 | * |
||||
152 | * @param string $table Table name |
||||
153 | * @param string|null $database Other database name |
||||
154 | * |
||||
155 | * @return static |
||||
156 | */ |
||||
157 | public function into($table, $database = null) |
||||
158 | { |
||||
159 | $this->builderCache->into = $this->conn->protectIdentifiers( |
||||
160 | $table |
||||
161 | ) . empty($database) |
||||
162 | ? '' |
||||
163 | : ' IN ' . $this->conn->escape($database); |
||||
164 | |||||
165 | return $this; |
||||
166 | } |
||||
167 | |||||
168 | //-------------------------------------------------------------------- |
||||
169 | |||||
170 | /** |
||||
171 | * AbstractQueryBuilder::union |
||||
172 | * |
||||
173 | * Add SELECT UNION Sql statement portions into Query Builder. |
||||
174 | * |
||||
175 | * @param \O2System\Database\Sql\Abstracts\AbstractQueryBuilder $select |
||||
176 | * @param bool $isUnionAll |
||||
177 | * |
||||
178 | * @return static |
||||
179 | */ |
||||
180 | public function union(AbstractQueryBuilder $select, $isUnionAll = false) |
||||
181 | { |
||||
182 | $this->builderCache->store(($isUnionAll |
||||
183 | ? 'union_all' |
||||
184 | : 'union'), $select->getSqlStatement()); |
||||
185 | |||||
186 | return $this; |
||||
187 | } |
||||
188 | |||||
189 | // ------------------------------------------------------------------------ |
||||
190 | |||||
191 | /** |
||||
192 | * AbstractQueryBuilder::getSqlStatement |
||||
193 | * |
||||
194 | * Gets Sql statement. |
||||
195 | * |
||||
196 | * @param bool $reset If sets TRUE the Query Builder cache will be reset. |
||||
197 | * |
||||
198 | * @return string |
||||
199 | */ |
||||
200 | public function getSqlStatement($reset = true) |
||||
201 | { |
||||
202 | $sqlStatementsSequence = [ |
||||
203 | 'Select', |
||||
204 | 'Union', |
||||
205 | 'Into', |
||||
206 | 'From', |
||||
207 | 'Join', |
||||
208 | 'Where', |
||||
209 | 'Having', |
||||
210 | 'GroupBy', |
||||
211 | 'OrderBy', |
||||
212 | 'Limit', |
||||
213 | ]; |
||||
214 | |||||
215 | if (empty($this->builderCache->getStatement())) { |
||||
216 | $sqlStatement = []; |
||||
217 | foreach ($sqlStatementsSequence as $compileMethod) { |
||||
218 | $sqlStatement[] = trim(call_user_func([&$this, 'compile' . $compileMethod . 'Statement'])); |
||||
219 | } |
||||
220 | |||||
221 | $sqlStatement = implode(PHP_EOL, array_filter($sqlStatement)); |
||||
222 | |||||
223 | if ($reset) { |
||||
224 | $this->builderCache->reset(); |
||||
225 | |||||
226 | return $sqlStatement; |
||||
227 | } |
||||
228 | |||||
229 | $this->builderCache->setStatement($sqlStatement); |
||||
230 | } |
||||
231 | |||||
232 | return $this->builderCache->getStatement(); |
||||
233 | } |
||||
234 | |||||
235 | // ------------------------------------------------------------------------ |
||||
236 | |||||
237 | /** |
||||
238 | * AbstractQueryBuilder::first |
||||
239 | * |
||||
240 | * Add SELECT FIRST(field) AS alias statement |
||||
241 | * |
||||
242 | * @param string $field Input name |
||||
243 | * @param string $alias Input alias |
||||
244 | * |
||||
245 | * @return static |
||||
246 | */ |
||||
247 | public function first($field, $alias = '') |
||||
248 | { |
||||
249 | return $this->prepareAggregateStatement($field, $alias, 'FIRST'); |
||||
250 | } |
||||
251 | |||||
252 | // ------------------------------------------------------------------------ |
||||
253 | |||||
254 | /** |
||||
255 | * AbstractQueryBuilder::prepareAggregateStatement |
||||
256 | * |
||||
257 | * Prepare string of Sql Aggregate Functions statement |
||||
258 | * |
||||
259 | * @param string $field Input name |
||||
260 | * @param string $alias Input alias |
||||
261 | * @param string $type AVG|COUNT|FIRST|LAST|MAX|MIN|SUM |
||||
262 | * |
||||
263 | * @return static |
||||
264 | */ |
||||
265 | protected function prepareAggregateStatement($field = '', $alias = '', $type = '') |
||||
266 | { |
||||
267 | $SqlAggregateFunctions = [ |
||||
268 | 'AVG' => 'AVG(%s)', // Returns the average value |
||||
269 | 'COUNT' => 'COUNT(%s)', // Returns the number of rows |
||||
270 | 'FIRST' => 'FIRST(%s)', // Returns the first value |
||||
271 | 'LAST' => 'LAST(%s)', // Returns the largest value |
||||
272 | 'MAX' => 'MAX(%s)', // Returns the largest value |
||||
273 | 'MIN' => 'MIN(%s)', // Returns the smallest value |
||||
274 | 'SUM' => 'SUM(%s)' // Returns the sum |
||||
275 | ]; |
||||
276 | |||||
277 | if ($field !== '*' && $this->conn->protectIdentifiers) { |
||||
278 | $field = $this->conn->protectIdentifiers($field); |
||||
279 | } |
||||
280 | |||||
281 | $alias = empty($alias) |
||||
282 | ? strtolower($type) . '_' . $field |
||||
283 | : $alias; |
||||
284 | $sqlStatement = sprintf($SqlAggregateFunctions[ $type ], $field) |
||||
285 | . ' AS ' |
||||
286 | . $this->conn->escapeIdentifiers($alias); |
||||
287 | |||||
288 | $this->select($sqlStatement); |
||||
289 | |||||
290 | return $this; |
||||
291 | } |
||||
292 | |||||
293 | // ------------------------------------------------------------------------ |
||||
294 | |||||
295 | /** |
||||
296 | * AbstractQueryBuilder::select |
||||
297 | * |
||||
298 | * Add SELECT Sql statement portions into Query Builder. |
||||
299 | * |
||||
300 | * @param string|array $field String of field name |
||||
301 | * Array list of string field names |
||||
302 | * Array list of static |
||||
303 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
304 | * |
||||
305 | * @return static |
||||
306 | */ |
||||
307 | public function select($field = '*', $escape = null) |
||||
308 | { |
||||
309 | // If the escape value was not set, we will base it on the global setting |
||||
310 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
311 | |||||
312 | if (is_string($field)) { |
||||
313 | $field = str_replace(' as ', ' AS ', $field); |
||||
314 | |||||
315 | if (strpos($field, '+') !== false || strpos($field, '(') !== false) { |
||||
316 | $field = [$field]; |
||||
317 | } else { |
||||
318 | $field = explode(',', $field); |
||||
319 | } |
||||
320 | |||||
321 | foreach ($field as $name) { |
||||
322 | $name = trim($name); |
||||
323 | |||||
324 | $this->builderCache->select[] = $name; |
||||
325 | $this->builderCache->noEscape[] = $escape; |
||||
326 | } |
||||
327 | } elseif (is_array($field)) { |
||||
0 ignored issues
–
show
|
|||||
328 | foreach ($field as $fieldName => $fieldAlias) { |
||||
329 | if (is_numeric($fieldName)) { |
||||
330 | $fieldName = $fieldAlias; |
||||
331 | } elseif (is_string($fieldName)) { |
||||
332 | if (is_string($fieldAlias)) { |
||||
333 | $fieldName = $fieldName . ' AS ' . $fieldAlias; |
||||
334 | } elseif (is_array($fieldAlias)) { |
||||
335 | $countFieldAlias = count($fieldAlias); |
||||
336 | |||||
337 | for ($i = 0; $i < $countFieldAlias; $i++) { |
||||
338 | if ($i == 0) { |
||||
339 | $fieldAlias[ $i ] = $fieldAlias[ $i ] . "'+"; |
||||
340 | } elseif ($i == ($countFieldAlias - 1)) { |
||||
341 | $fieldAlias[ $i ] = "'+" . $fieldAlias[ $i ]; |
||||
342 | } else { |
||||
343 | $fieldAlias[ $i ] = "'+" . $fieldAlias[ $i ] . "'+"; |
||||
344 | } |
||||
345 | } |
||||
346 | |||||
347 | $fieldName = implode(', ', $fieldAlias) . ' AS ' . $fieldName; |
||||
348 | } elseif ($fieldAlias instanceof AbstractQueryBuilder) { |
||||
349 | $fieldName = '( ' . $fieldAlias->getSqlStatement() . ' ) AS ' . $fieldName; |
||||
350 | } |
||||
351 | } |
||||
352 | |||||
353 | $this->select($fieldName, $escape); |
||||
354 | } |
||||
355 | } |
||||
356 | |||||
357 | return $this; |
||||
358 | } |
||||
359 | |||||
360 | //-------------------------------------------------------------------- |
||||
361 | |||||
362 | /** |
||||
363 | * AbstractQueryBuilder::last |
||||
364 | * |
||||
365 | * Add SELECT LAST(field) AS alias statement |
||||
366 | * |
||||
367 | * @param string $field Input name |
||||
368 | * @param string $alias Input alias |
||||
369 | * |
||||
370 | * @return static |
||||
371 | */ |
||||
372 | public function last($field, $alias = '') |
||||
373 | { |
||||
374 | return $this->prepareAggregateStatement($field, $alias, 'LAST'); |
||||
375 | } |
||||
376 | |||||
377 | // ------------------------------------------------------------------------ |
||||
378 | |||||
379 | /** |
||||
380 | * AbstractQueryBuilder::avg |
||||
381 | * |
||||
382 | * Add SELECT AVG(field) AS alias statement |
||||
383 | * |
||||
384 | * @param string $field Input name |
||||
385 | * @param string $alias Input alias |
||||
386 | * |
||||
387 | * @return static |
||||
388 | */ |
||||
389 | public function avg($field, $alias = '') |
||||
390 | { |
||||
391 | return $this->prepareAggregateStatement($field, $alias, 'AVG'); |
||||
392 | } |
||||
393 | |||||
394 | // ------------------------------------------------------------------------ |
||||
395 | |||||
396 | /** |
||||
397 | * AbstractQueryBuilder::max |
||||
398 | * |
||||
399 | * Add SELECT MAX(field) AS alias statement |
||||
400 | * |
||||
401 | * @param string $field Input name |
||||
402 | * @param string $alias Input alias |
||||
403 | * |
||||
404 | * @return static |
||||
405 | */ |
||||
406 | public function max($field, $alias = '') |
||||
407 | { |
||||
408 | return $this->prepareAggregateStatement($field, $alias, 'MAX'); |
||||
409 | } |
||||
410 | |||||
411 | // ------------------------------------------------------------------------ |
||||
412 | |||||
413 | /** |
||||
414 | * AbstractQueryBuilder::min |
||||
415 | * |
||||
416 | * Add SELECT MIN(field) AS alias statement |
||||
417 | * |
||||
418 | * @param string $field Input name |
||||
419 | * @param string $alias Input alias |
||||
420 | * |
||||
421 | * @return static |
||||
422 | */ |
||||
423 | public function min($field, $alias = '') |
||||
424 | { |
||||
425 | return $this->prepareAggregateStatement($field, $alias, 'MIN'); |
||||
426 | } |
||||
427 | |||||
428 | // ------------------------------------------------------------------------ |
||||
429 | |||||
430 | /** |
||||
431 | * AbstractQueryBuilder::sum |
||||
432 | * |
||||
433 | * Add SELECT SUM(field) AS alias statement |
||||
434 | * |
||||
435 | * @param string $field Input name |
||||
436 | * @param string $alias Input alias |
||||
437 | * |
||||
438 | * @return static |
||||
439 | */ |
||||
440 | public function sum($field, $alias = '') |
||||
441 | { |
||||
442 | return $this->prepareAggregateStatement($field, $alias, 'SUM'); |
||||
443 | } |
||||
444 | |||||
445 | // ------------------------------------------------------------------------ |
||||
446 | |||||
447 | /** |
||||
448 | * AbstractQueryBuilder::ucase |
||||
449 | * |
||||
450 | * Add SELECT UCASE(field) AS alias statement |
||||
451 | * |
||||
452 | * @see http://www.w3schools.com/Sql/Sql_func_ucase.asp |
||||
453 | * |
||||
454 | * @param string $field Input name |
||||
455 | * @param string $alias Input alias |
||||
456 | * |
||||
457 | * @return static |
||||
458 | */ |
||||
459 | public function ucase($field, $alias = '') |
||||
460 | { |
||||
461 | return $this->prepareScalarStatement($field, $alias, 'UCASE'); |
||||
462 | } |
||||
463 | |||||
464 | // ------------------------------------------------------------------------ |
||||
465 | |||||
466 | /** |
||||
467 | * AbstractQueryBuilder::prepareScalarStatement |
||||
468 | * |
||||
469 | * Prepare string of Sql Scalar Functions statement |
||||
470 | * |
||||
471 | * @param string $field Input name |
||||
472 | * @param string $alias Input alias |
||||
473 | * @param string $type UCASE|LCASE|MID|LEN|ROUND|FORMAT |
||||
474 | * |
||||
475 | * @return static |
||||
476 | */ |
||||
477 | protected function prepareScalarStatement($field = '', $alias = '', $type = '') |
||||
478 | { |
||||
479 | $SqlScalarFunctions = [ |
||||
480 | 'UCASE' => 'UCASE(%s)', // Converts a field to uppercase |
||||
481 | 'LCASE' => 'LCASE(%s)', // Converts a field to lowercase |
||||
482 | 'LENGTH' => 'LENGTH(%s)', // Returns the length of a text field |
||||
483 | ]; |
||||
484 | |||||
485 | $alias = $alias === '' |
||||
486 | ? strtolower($type) . '_' . $field |
||||
487 | : $alias; |
||||
488 | |||||
489 | if ($field !== '*' && $this->conn->protectIdentifiers) { |
||||
490 | $field = $this->conn->protectIdentifiers($field, true); |
||||
491 | } |
||||
492 | |||||
493 | $this->select( |
||||
494 | sprintf( |
||||
495 | $SqlScalarFunctions[ $type ], |
||||
496 | $field, |
||||
497 | $this->conn->escapeIdentifiers($alias) |
||||
498 | ) |
||||
499 | ); |
||||
500 | |||||
501 | return $this; |
||||
502 | } |
||||
503 | |||||
504 | // ------------------------------------------------------------------------ |
||||
505 | |||||
506 | /** |
||||
507 | * AbstractQueryBuilder::lcase |
||||
508 | * |
||||
509 | * Add SELECT LCASE(field) AS alias statement |
||||
510 | * |
||||
511 | * @see http://www.w3schools.com/Sql/Sql_func_lcase.asp |
||||
512 | * |
||||
513 | * @param string $field Input name |
||||
514 | * @param string $alias Input alias |
||||
515 | * |
||||
516 | * @return static |
||||
517 | */ |
||||
518 | public function lcase($field, $alias = '') |
||||
519 | { |
||||
520 | return $this->prepareScalarStatement($field, $alias, 'LCASE'); |
||||
521 | } |
||||
522 | |||||
523 | // ------------------------------------------------------------------------ |
||||
524 | |||||
525 | /** |
||||
526 | * AbstractQueryBuilder::mid |
||||
527 | * |
||||
528 | * Add SELECT MID(field) AS alias statement |
||||
529 | * |
||||
530 | * @see http://www.w3schools.com/Sql/Sql_func_mid.asp |
||||
531 | * |
||||
532 | * @param string $field Required. The field to extract characters from |
||||
533 | * @param int $start Required. Specifies the starting position (starts at 1) |
||||
534 | * @param null|int $length Optional. The number of characters to return. If omitted, the MID() function |
||||
535 | * returns the rest of the text |
||||
536 | * @param string $alias Input alias |
||||
537 | * |
||||
538 | * @return static |
||||
539 | */ |
||||
540 | public function mid($field, $start = 1, $length = null, $alias = '') |
||||
541 | { |
||||
542 | if ($this->conn->protectIdentifiers) { |
||||
543 | $field = $this->conn->protectIdentifiers($field, true); |
||||
544 | } |
||||
545 | |||||
546 | $fields = [ |
||||
547 | $field, |
||||
548 | $start, |
||||
549 | ]; |
||||
550 | |||||
551 | if (isset($length)) { |
||||
552 | array_push($fields, intval($length)); |
||||
553 | } |
||||
554 | |||||
555 | $this->select( |
||||
556 | sprintf( |
||||
557 | 'MID(%s)', // Extract characters from a text field |
||||
558 | implode(',', $fields) |
||||
559 | ) |
||||
560 | . ' AS ' |
||||
561 | . $this->conn->escapeIdentifiers( |
||||
562 | empty($alias) |
||||
563 | ? 'mid_' . $field |
||||
564 | : $alias |
||||
565 | ) |
||||
566 | ); |
||||
567 | |||||
568 | return $this; |
||||
569 | } |
||||
570 | |||||
571 | // ------------------------------------------------------------------------ |
||||
572 | |||||
573 | /** |
||||
574 | * AbstractQueryBuilder::len |
||||
575 | * |
||||
576 | * Add SELECT LEN(field) AS alias statement |
||||
577 | * |
||||
578 | * @see http://www.w3schools.com/Sql/Sql_func_len.asp |
||||
579 | * |
||||
580 | * @param string $field Input name |
||||
581 | * @param string $alias Input alias |
||||
582 | * |
||||
583 | * @return static |
||||
584 | */ |
||||
585 | public function len($field, $alias = '') |
||||
586 | { |
||||
587 | return $this->prepareScalarStatement($field, $alias, 'LENGTH'); |
||||
588 | } |
||||
589 | |||||
590 | // ------------------------------------------------------------------------ |
||||
591 | |||||
592 | /** |
||||
593 | * AbstractQueryBuilder::round |
||||
594 | * |
||||
595 | * Add SELECT ROUND(field) AS alias statement |
||||
596 | * |
||||
597 | * @see http://www.w3schools.com/Sql/Sql_func_round.asp |
||||
598 | * |
||||
599 | * @param string $field Required. The field to round. |
||||
600 | * @param int $decimals Required. Specifies the number of decimals to be returned. |
||||
601 | * @param string $alias Input alias |
||||
602 | * |
||||
603 | * @return static |
||||
604 | */ |
||||
605 | public function round($field, $decimals = 0, $alias = '') |
||||
606 | { |
||||
607 | $this->select( |
||||
608 | sprintf( |
||||
609 | 'ROUND(%s, %s)', // Rounds a numeric field to the number of decimals specified |
||||
610 | $field, |
||||
611 | $decimals |
||||
612 | ) |
||||
613 | . ' AS ' |
||||
614 | . $this->conn->escapeIdentifiers( |
||||
615 | empty($alias) |
||||
616 | ? 'mid_' . $field |
||||
617 | : $alias |
||||
618 | ) |
||||
619 | ); |
||||
620 | |||||
621 | return $this; |
||||
622 | } |
||||
623 | |||||
624 | // ------------------------------------------------------------------------ |
||||
625 | |||||
626 | /** |
||||
627 | * AbstractQueryBuilder::format |
||||
628 | * |
||||
629 | * Add SELECT FORMAT(field, format) AS alias statement |
||||
630 | * |
||||
631 | * @see http://www.w3schools.com/Sql/Sql_func_format.asp |
||||
632 | * |
||||
633 | * @param string $field Input name. |
||||
634 | * @param string $format Input format. |
||||
635 | * @param string $alias Input alias. |
||||
636 | * |
||||
637 | * @return static |
||||
638 | */ |
||||
639 | public function format($field, $format, $alias = '') |
||||
640 | { |
||||
641 | $this->select( |
||||
642 | sprintf( |
||||
643 | 'FORMAT(%s, %s)', // Formats how a field is to be displayed |
||||
644 | $field, |
||||
645 | $format |
||||
646 | ) |
||||
647 | . ' AS ' |
||||
648 | . $this->conn->escapeIdentifiers( |
||||
649 | empty($alias) |
||||
650 | ? 'mid_' . $field |
||||
651 | : $alias |
||||
652 | ) |
||||
653 | ); |
||||
654 | |||||
655 | return $this; |
||||
656 | } |
||||
657 | |||||
658 | // ------------------------------------------------------------------------ |
||||
659 | |||||
660 | /** |
||||
661 | * AbstractQueryBuilder::now |
||||
662 | * |
||||
663 | * Add / Create SELECT NOW() Sql statement. |
||||
664 | * |
||||
665 | * @return static |
||||
666 | */ |
||||
667 | public function now() |
||||
668 | { |
||||
669 | $this->select('NOW()'); // Returns the current date and time |
||||
670 | |||||
671 | return $this; |
||||
672 | } |
||||
673 | |||||
674 | // ------------------------------------------------------------------------ |
||||
675 | |||||
676 | /** |
||||
677 | * AbstractQueryBuilder::extract |
||||
678 | * |
||||
679 | * Add / Create SELECT EXTRACT(unit FROM field) AS alias Sql statement |
||||
680 | * |
||||
681 | * @see http://www.w3schools.com/Sql/func_extract.asp |
||||
682 | * |
||||
683 | * @param string $field Input name |
||||
684 | * @param string $unit UPPERCASE unit value |
||||
685 | * @param string $alias Alias field name. |
||||
686 | * |
||||
687 | * @return static|string |
||||
688 | */ |
||||
689 | public function dateExtract($field, $unit, $alias = '') |
||||
690 | { |
||||
691 | $unit = strtoupper($unit); |
||||
692 | |||||
693 | if (in_array($unit, $this->getDateTypes())) { |
||||
694 | |||||
695 | $fieldName = $field; |
||||
696 | $fieldAlias = $alias; |
||||
697 | |||||
698 | if (is_array($field)) { |
||||
0 ignored issues
–
show
|
|||||
699 | $fieldName = key($field); |
||||
700 | $fieldAlias = $field[ $fieldName ]; |
||||
701 | } elseif (strpos($field, ' AS ') !== false) { |
||||
702 | $xField = explode(' AS ', $field); |
||||
703 | $xField = array_map('trim', $xField); |
||||
704 | |||||
705 | @list($fieldName, $fieldAlias) = $xField; |
||||
706 | } elseif (strpos($field, ' as ') !== false) { |
||||
707 | $xField = explode(' as ', $field); |
||||
708 | $xField = array_map('trim', $xField); |
||||
709 | |||||
710 | @list($fieldName, $fieldAlias) = $xField; |
||||
711 | } |
||||
712 | |||||
713 | if (strpos($fieldName, '.') !== false AND empty($fieldAlias)) { |
||||
714 | $xFieldName = explode('.', $fieldName); |
||||
715 | $xFieldName = array_map('trim', $xFieldName); |
||||
716 | |||||
717 | $fieldAlias = end($xFieldName); |
||||
718 | } |
||||
719 | |||||
720 | $sqlStatement = sprintf( |
||||
721 | 'EXTRACT(%s FROM %s)', // Returns a single part of a date/time |
||||
722 | $unit, |
||||
723 | $this->conn->protectIdentifiers($fieldName) |
||||
724 | ) . ' AS ' . $this->conn->escapeIdentifiers($fieldAlias); |
||||
0 ignored issues
–
show
Are you sure
$this->conn->escapeIdentifiers($fieldAlias) of type array|mixed can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
725 | |||||
726 | $this->select($sqlStatement); |
||||
727 | } |
||||
728 | |||||
729 | return $this; |
||||
730 | } |
||||
731 | |||||
732 | // ------------------------------------------------------------------------ |
||||
733 | |||||
734 | /** |
||||
735 | * AbstractQueryBuilder::getDateTypes |
||||
736 | * |
||||
737 | * Gets Generic Sql Date Types. |
||||
738 | * |
||||
739 | * @return array |
||||
740 | */ |
||||
741 | protected function getDateTypes() |
||||
742 | { |
||||
743 | return [ |
||||
744 | 'MICROSECOND', |
||||
745 | 'SECOND', |
||||
746 | 'MINUTE', |
||||
747 | 'HOUR', |
||||
748 | 'DAY', |
||||
749 | 'WEEK', |
||||
750 | 'MONTH', |
||||
751 | 'QUARTER', |
||||
752 | 'YEAR', |
||||
753 | 'SECOND_MICROSECOND', |
||||
754 | 'MINUTE_MICROSECOND', |
||||
755 | 'MINUTE_SECOND', |
||||
756 | 'HOUR_MICROSECOND', |
||||
757 | 'HOUR_SECOND', |
||||
758 | 'HOUR_MINUTE', |
||||
759 | 'DAY_MICROSECOND', |
||||
760 | 'DAY_SECOND', |
||||
761 | 'DAY_MINUTE', |
||||
762 | 'DAY_HOUR', |
||||
763 | 'YEAR_MONTH', |
||||
764 | ]; |
||||
765 | } |
||||
766 | |||||
767 | // ------------------------------------------------------------------------ |
||||
768 | |||||
769 | /** |
||||
770 | * AbstractQueryBuilder::date |
||||
771 | * |
||||
772 | * Add / Create SELECT DATE(field) AS alias Sql statement |
||||
773 | * |
||||
774 | * @see http://www.w3schools.com/Sql/func_date.asp |
||||
775 | * |
||||
776 | * @param string $field Input name |
||||
777 | * @param string $alias Input name alias |
||||
778 | * |
||||
779 | * @return static|string |
||||
780 | */ |
||||
781 | public function date($field, $alias = '') |
||||
782 | { |
||||
783 | $this->select( |
||||
784 | sprintf( |
||||
785 | 'DATE(%s)', // Extracts the date part of a date or date/time expression |
||||
786 | $field |
||||
787 | ) |
||||
788 | . ' AS ' |
||||
789 | . $this->conn->escapeIdentifiers( |
||||
790 | empty($alias) |
||||
791 | ? 'mid_' . $field |
||||
792 | : $alias |
||||
793 | ) |
||||
794 | ); |
||||
795 | |||||
796 | return $this; |
||||
797 | } |
||||
798 | |||||
799 | // ------------------------------------------------------------------------ |
||||
800 | |||||
801 | /** |
||||
802 | * AbstractQueryBuilder::dateAdd |
||||
803 | * |
||||
804 | * Add / Create SELECT DATE_ADD(field, INTERVAL expression type) AS alias Sql statement |
||||
805 | * |
||||
806 | * @see http://www.w3schools.com/Sql/func_date.asp |
||||
807 | * |
||||
808 | * @param string $field Input name |
||||
809 | * @param string $interval Number of interval expression |
||||
810 | * @param string $alias Input alias |
||||
811 | * |
||||
812 | * @return string|static |
||||
813 | */ |
||||
814 | public function dateAdd($field, $interval, $alias = '') |
||||
815 | { |
||||
816 | if ($this->hasDateType($interval)) { |
||||
817 | |||||
818 | $this->select( |
||||
819 | sprintf( |
||||
820 | 'DATE_ADD(%s, INTERVAL %s)', // Adds a specified time interval to a date |
||||
821 | $field, |
||||
822 | $interval |
||||
823 | ) |
||||
824 | . ' AS ' |
||||
825 | . $this->conn->escapeIdentifiers( |
||||
826 | empty($alias) |
||||
827 | ? 'date_add_' . $field |
||||
828 | : $alias |
||||
829 | ) |
||||
830 | ); |
||||
831 | |||||
832 | } |
||||
833 | |||||
834 | return $this; |
||||
835 | } |
||||
836 | |||||
837 | //-------------------------------------------------------------------- |
||||
838 | |||||
839 | /** |
||||
840 | * AbstractQueryBuilder::hasDateType |
||||
841 | * |
||||
842 | * Validate whether the string has an Sql Date unit type |
||||
843 | * |
||||
844 | * @param $string |
||||
845 | * |
||||
846 | * @return bool |
||||
847 | */ |
||||
848 | protected function hasDateType($string) |
||||
849 | { |
||||
850 | return (bool)preg_match( |
||||
851 | '/(' . implode('|\s', $this->getDateTypes()) . '\s*\(|\s)/i', |
||||
852 | trim($string) |
||||
853 | ); |
||||
854 | } |
||||
855 | |||||
856 | //-------------------------------------------------------------------- |
||||
857 | |||||
858 | /** |
||||
859 | * AbstractQueryBuilder::dateSub |
||||
860 | * |
||||
861 | * Add / Create SELECT DATE_SUB(field, INTERVAL expression type) AS alias Sql statement |
||||
862 | * |
||||
863 | * @see http://www.w3schools.com/Sql/func_date.asp |
||||
864 | * |
||||
865 | * @param string $field Input name |
||||
866 | * @param string $interval Number of interval expression |
||||
867 | * @param string $alias Input alias |
||||
868 | * |
||||
869 | * @return static|string |
||||
870 | */ |
||||
871 | public function dateSub($field, $interval, $alias = '') |
||||
872 | { |
||||
873 | $this->select( |
||||
874 | sprintf( |
||||
875 | 'DATE_SUB(%s, INTERVAL %s)', // Subtracts a specified time interval from a date |
||||
876 | $field, |
||||
877 | $interval |
||||
878 | ) |
||||
879 | . ' AS ' |
||||
880 | . $this->conn->escapeIdentifiers( |
||||
881 | empty($alias) |
||||
882 | ? 'date_sub_' . $field |
||||
883 | : $alias |
||||
884 | ) |
||||
885 | ); |
||||
886 | |||||
887 | return $this; |
||||
888 | } |
||||
889 | |||||
890 | //-------------------------------------------------------------------- |
||||
891 | |||||
892 | /** |
||||
893 | * AbstractQueryBuilder::dateDiff |
||||
894 | * |
||||
895 | * Add / Create SELECT DATEDIFF(datetime_start, datetime_end) AS alias Sql statement |
||||
896 | * |
||||
897 | * @see http://www.w3schools.com/Sql/func_datediff_mySql.asp |
||||
898 | * |
||||
899 | * @param array $fields [datetime_start => datetime_end] |
||||
900 | * @param string|null $alias Input alias |
||||
901 | * |
||||
902 | * @return static|string |
||||
903 | */ |
||||
904 | public function dateDiff(array $fields, $alias) |
||||
905 | { |
||||
906 | $dateTimeStart = key($fields); |
||||
907 | $dateTimeEnd = $fields[ $dateTimeStart ]; |
||||
908 | |||||
909 | if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $dateTimeStart)) { |
||||
910 | $dateTimeStart = $this->conn->escape($dateTimeStart); |
||||
911 | } elseif ($this->conn->protectIdentifiers) { |
||||
912 | $dateTimeStart = $this->conn->protectIdentifiers($dateTimeStart); |
||||
913 | } |
||||
914 | |||||
915 | if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $dateTimeEnd)) { |
||||
916 | $dateTimeEnd = $this->conn->escape($dateTimeEnd); |
||||
917 | } elseif ($this->conn->protectIdentifiers) { |
||||
918 | $dateTimeEnd = $this->conn->protectIdentifiers($dateTimeEnd); |
||||
919 | } |
||||
920 | |||||
921 | $this->select( |
||||
922 | sprintf( |
||||
923 | 'DATEDIFF(%s, %s)', // Returns the number of days between two dates |
||||
924 | $dateTimeStart, |
||||
925 | $dateTimeEnd |
||||
926 | ) |
||||
927 | . ' AS ' |
||||
928 | . $this->conn->escapeIdentifiers($alias) |
||||
929 | ); |
||||
930 | |||||
931 | return $this; |
||||
932 | } |
||||
933 | |||||
934 | //-------------------------------------------------------------------- |
||||
935 | |||||
936 | /** |
||||
937 | * AbstractQueryBuilder::table |
||||
938 | * |
||||
939 | * Used for modifier query builder process (insert, update, replace, delete). |
||||
940 | * |
||||
941 | * @param string $table Table name. |
||||
942 | * |
||||
943 | * @return static |
||||
944 | */ |
||||
945 | public function table($table) |
||||
946 | { |
||||
947 | return $this->from($table, true); |
||||
948 | } |
||||
949 | |||||
950 | //-------------------------------------------------------------------- |
||||
951 | |||||
952 | /** |
||||
953 | * AbstractQueryBuilder::from |
||||
954 | * |
||||
955 | * Generates FROM Sql statement portions into Query Builder. |
||||
956 | * |
||||
957 | * @param string|array $table |
||||
958 | * @param bool $overwrite Should we remove the first table existing? |
||||
959 | * |
||||
960 | * @return static |
||||
961 | */ |
||||
962 | public function from($table, $overwrite = false) |
||||
963 | { |
||||
964 | if ($overwrite === true) { |
||||
965 | $this->builderCache->from = []; |
||||
966 | $this->builderCache->aliasedTables = []; |
||||
967 | } |
||||
968 | |||||
969 | if (is_string($table)) { |
||||
970 | $table = explode(',', $table); |
||||
971 | |||||
972 | foreach ($table as $name) { |
||||
973 | $name = trim($name); |
||||
974 | |||||
975 | // Extract any aliases that might exist. We use this information |
||||
976 | // in the protectIdentifiers to know whether to add a table prefix |
||||
977 | $this->trackAliases($name); |
||||
978 | |||||
979 | $this->builderCache->from[] = $this->conn->protectIdentifiers($name, true, null, false); |
||||
980 | } |
||||
981 | } elseif (is_array($table)) { |
||||
0 ignored issues
–
show
|
|||||
982 | foreach ($table as $alias => $name) { |
||||
983 | $name = trim($name) . ' AS ' . trim($alias); |
||||
984 | |||||
985 | // Extract any aliases that might exist. We use this information |
||||
986 | // in the protectIdentifiers to know whether to add a table prefix |
||||
987 | $this->trackAliases($name); |
||||
988 | |||||
989 | $this->builderCache->from[] = $this->conn->protectIdentifiers($name, true, null, false); |
||||
990 | } |
||||
991 | } |
||||
992 | |||||
993 | return $this; |
||||
994 | } |
||||
995 | |||||
996 | //-------------------------------------------------------------------- |
||||
997 | |||||
998 | /** |
||||
999 | * AbstractQueryBuilder::trackAliases |
||||
1000 | * |
||||
1001 | * Used to track Sql statements written with aliased tables. |
||||
1002 | * |
||||
1003 | * @param string|array $table Inspected table name. |
||||
1004 | * |
||||
1005 | * @return void |
||||
1006 | */ |
||||
1007 | protected function trackAliases($table) |
||||
1008 | { |
||||
1009 | if (is_array($table)) { |
||||
1010 | foreach ($table as $name) { |
||||
1011 | $this->trackAliases($name); |
||||
1012 | } |
||||
1013 | |||||
1014 | return; |
||||
1015 | } |
||||
1016 | |||||
1017 | // Does the string contain a comma? If so, we need to separate |
||||
1018 | // the string into discreet statements |
||||
1019 | if (strpos($table, ',') !== false) { |
||||
1020 | $this->trackAliases(explode(',', $table)); |
||||
1021 | |||||
1022 | return; |
||||
1023 | } |
||||
1024 | |||||
1025 | // if a table alias is used we can recognize it by a space |
||||
1026 | if (strpos($table, ' ') !== false) { |
||||
1027 | // if the alias is written with the AS keyword, remove it |
||||
1028 | //$table = preg_replace('/\s+AS\s+/i', ' ', $table); |
||||
1029 | |||||
1030 | // Grab the alias |
||||
1031 | //$table = trim(strrchr($table, ' ')); |
||||
1032 | $table = str_replace([' AS ', ' as '], '.', $table); |
||||
1033 | |||||
1034 | // Store the alias, if it doesn't already exist |
||||
1035 | if ( ! in_array($table, $this->builderCache->aliasedTables)) { |
||||
1036 | $this->builderCache->aliasedTables[] = $table; |
||||
1037 | } |
||||
1038 | } |
||||
1039 | } |
||||
1040 | |||||
1041 | //-------------------------------------------------------------------- |
||||
1042 | |||||
1043 | /** |
||||
1044 | * AbstractQueryBuilder::getAliasedTables |
||||
1045 | * |
||||
1046 | * Returns list of tracked aliased tables. |
||||
1047 | * |
||||
1048 | * @return array |
||||
1049 | */ |
||||
1050 | public function getAliasedTables() |
||||
1051 | { |
||||
1052 | if (empty($this->builderCache->aliasedTables)) { |
||||
1053 | return []; |
||||
1054 | } |
||||
1055 | |||||
1056 | return $this->builderCache->aliasedTables; |
||||
1057 | } |
||||
1058 | |||||
1059 | // -------------------------------------------------------------------- |
||||
1060 | |||||
1061 | /** |
||||
1062 | * AbstractQueryBuilder::join |
||||
1063 | * |
||||
1064 | * Add JOIN Sql statement portions into Query Builder. |
||||
1065 | * |
||||
1066 | * @param string $table Table name |
||||
1067 | * @param null $condition Join conditions: table.column = other_table.column |
||||
0 ignored issues
–
show
|
|||||
1068 | * @param string $type UPPERCASE join type LEFT|LEFT_OUTER|RIGHT|RIGHT_OUTER|INNER|OUTER|FULL|JOIN |
||||
1069 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1070 | * |
||||
1071 | * @return static |
||||
1072 | */ |
||||
1073 | public function join($table, $condition = null, $type = 'LEFT', $escape = null) |
||||
1074 | { |
||||
1075 | if ($type !== '') { |
||||
1076 | $type = strtoupper(trim($type)); |
||||
1077 | |||||
1078 | if ( ! in_array($type, ['LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'], true)) { |
||||
1079 | $type = ''; |
||||
1080 | } else { |
||||
1081 | $type .= ' '; |
||||
1082 | } |
||||
1083 | } |
||||
1084 | |||||
1085 | // Extract any aliases that might exist. We use this information |
||||
1086 | // in the protectIdentifiers to know whether to add a table prefix |
||||
1087 | $this->trackAliases($table); |
||||
1088 | |||||
1089 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
1090 | |||||
1091 | if ( ! $this->hasOperator($condition)) { |
||||
1092 | $condition = ' USING (' . ($escape |
||||
0 ignored issues
–
show
Are you sure
$escape ? $this->conn->e...condition) : $condition of type array|mixed|null can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1093 | ? $this->conn->escapeIdentifiers($condition) |
||||
1094 | : $condition) . ')'; |
||||
1095 | } elseif ($escape === false) { |
||||
1096 | $condition = ' ON ' . $condition; |
||||
1097 | } else { |
||||
1098 | // Split multiple conditions |
||||
1099 | if (preg_match_all('/\sAND\s|\sOR\s/i', $condition, $joints, PREG_OFFSET_CAPTURE)) { |
||||
1100 | $conditions = []; |
||||
1101 | $joints = $joints[ 0 ]; |
||||
1102 | array_unshift($joints, ['', 0]); |
||||
1103 | |||||
1104 | for ($i = count($joints) - 1, $pos = strlen($condition); $i >= 0; $i--) { |
||||
1105 | $joints[ $i ][ 1 ] += strlen($joints[ $i ][ 0 ]); // offset |
||||
1106 | $conditions[ $i ] = substr($condition, $joints[ $i ][ 1 ], $pos - $joints[ $i ][ 1 ]); |
||||
1107 | $pos = $joints[ $i ][ 1 ] - strlen($joints[ $i ][ 0 ]); |
||||
1108 | $joints[ $i ] = $joints[ $i ][ 0 ]; |
||||
1109 | } |
||||
1110 | } else { |
||||
1111 | $conditions = [$condition]; |
||||
1112 | $joints = ['']; |
||||
1113 | } |
||||
1114 | |||||
1115 | $condition = ' ON '; |
||||
1116 | for ($i = 0, $c = count($conditions); $i < $c; $i++) { |
||||
1117 | $operator = $this->getOperator($conditions[ $i ]); |
||||
1118 | $condition .= $joints[ $i ]; |
||||
1119 | $condition .= preg_match( |
||||
1120 | "/(\(*)?([\[\]\w\.'-]+)" . preg_quote($operator) . "(.*)/i", |
||||
1121 | $conditions[ $i ], |
||||
1122 | $match |
||||
1123 | ) |
||||
1124 | ? $match[ 1 ] . $this->conn->protectIdentifiers( |
||||
1125 | $match[ 2 ] |
||||
1126 | ) . $operator . $this->conn->protectIdentifiers($match[ 3 ]) |
||||
1127 | : $conditions[ $i ]; |
||||
1128 | } |
||||
1129 | } |
||||
1130 | |||||
1131 | // Do we want to escape the table name? |
||||
1132 | if ($escape === true) { |
||||
1133 | $table = $this->conn->protectIdentifiers($table, true, null, false); |
||||
1134 | } |
||||
1135 | |||||
1136 | // Assemble the JOIN statement |
||||
1137 | $this->builderCache->join[] = $type . 'JOIN ' . $table . $condition; |
||||
1138 | |||||
1139 | return $this; |
||||
1140 | } |
||||
1141 | |||||
1142 | //-------------------------------------------------------------------- |
||||
1143 | |||||
1144 | /** |
||||
1145 | * AbstractQueryBuilder::hasOperator |
||||
1146 | * |
||||
1147 | * Tests whether the string has an Sql operator |
||||
1148 | * |
||||
1149 | * @param string |
||||
1150 | * |
||||
1151 | * @return bool |
||||
1152 | */ |
||||
1153 | protected function hasOperator($string) |
||||
1154 | { |
||||
1155 | return (bool)preg_match( |
||||
1156 | '/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', |
||||
1157 | trim($string) |
||||
1158 | ); |
||||
1159 | } |
||||
1160 | |||||
1161 | //-------------------------------------------------------------------- |
||||
1162 | |||||
1163 | /** |
||||
1164 | * AbstractQueryBuilder::getOperator |
||||
1165 | * |
||||
1166 | * Returns the Sql string operator |
||||
1167 | * |
||||
1168 | * @param string |
||||
1169 | * |
||||
1170 | * @return string |
||||
1171 | */ |
||||
1172 | protected function getOperator($string) |
||||
1173 | { |
||||
1174 | static $operator; |
||||
1175 | |||||
1176 | if (empty($operator)) { |
||||
1177 | |||||
1178 | $likeEscapeString = ($this->conn->getConfig('likeEscapeString') !== '') |
||||
1179 | ? '\s+' . preg_quote( |
||||
1180 | trim( |
||||
1181 | sprintf( |
||||
1182 | $this->conn->getConfig('likeEscapeString'), |
||||
1183 | $this->conn->getConfig('likeEscapeCharacter') |
||||
1184 | ) |
||||
1185 | ), |
||||
1186 | '/' |
||||
1187 | ) |
||||
1188 | : ''; |
||||
1189 | |||||
1190 | $operator = [ |
||||
1191 | '\s*(?:<|>|!)?=\s*', // =, <=, >=, != |
||||
1192 | '\s*<>?\s*', // <, <> |
||||
1193 | '\s*>\s*', // > |
||||
1194 | '\s+IS NULL', // IS NULL |
||||
1195 | '\s+IS NOT NULL', // IS NOT NULL |
||||
1196 | '\s+EXISTS\s*\(.*\)', // EXISTS(Sql) |
||||
1197 | '\s+NOT EXISTS\s*\(.*\)', // NOT EXISTS(Sql) |
||||
1198 | '\s+BETWEEN\s+', // BETWEEN value AND value |
||||
1199 | '\s+IN\s*\(.*\)', // IN(list) |
||||
1200 | '\s+NOT IN\s*\(.*\)', // NOT IN (list) |
||||
1201 | '\s+LIKE\s+\S.*(' . $likeEscapeString . ')?', // LIKE 'expr'[ ESCAPE '%s'] |
||||
1202 | '\s+NOT LIKE\s+\S.*(' . $likeEscapeString . ')?' // NOT LIKE 'expr'[ ESCAPE '%s'] |
||||
1203 | ]; |
||||
1204 | } |
||||
1205 | |||||
1206 | return preg_match('/' . implode('|', $operator) . '/i', $string, $match) |
||||
0 ignored issues
–
show
The expression
return preg_match('/' . ...ch) ? $match[0] : false could also return false which is incompatible with the documented return type string . Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled. ![]() |
|||||
1207 | ? $match[ 0 ] |
||||
1208 | : false; |
||||
1209 | } |
||||
1210 | |||||
1211 | //-------------------------------------------------------------------- |
||||
1212 | |||||
1213 | /** |
||||
1214 | * AbstractQueryBuilder::orWhere |
||||
1215 | * |
||||
1216 | * Add OR WHERE Sql statement portions into Query Builder |
||||
1217 | * |
||||
1218 | * @param string|array $field Input name, array of [field => value] (grouped where) |
||||
1219 | * @param null|string $value Input criteria or UPPERCASE grouped type AND|OR |
||||
1220 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1221 | * |
||||
1222 | * @return static |
||||
1223 | */ |
||||
1224 | public function orWhere($field, $value = null, $escape = null) |
||||
1225 | { |
||||
1226 | return $this->prepareWhereStatement($field, $value, 'OR ', $escape, 'where'); |
||||
1227 | } |
||||
1228 | |||||
1229 | //-------------------------------------------------------------------- |
||||
1230 | |||||
1231 | /** |
||||
1232 | * AbstractQueryBuilder::prepareWhereHavingStatement |
||||
1233 | * |
||||
1234 | * Add WHERE, HAVING Sql statement portion into Query Builder. |
||||
1235 | * |
||||
1236 | * @used-by where() |
||||
1237 | * @used-by orWhere() |
||||
1238 | * @used-by having() |
||||
1239 | * @used-by orHaving() |
||||
1240 | * |
||||
1241 | * @param string $cacheKey 'QBWhere' or 'QBHaving' |
||||
1242 | * @param mixed $field |
||||
1243 | * @param mixed $value |
||||
1244 | * @param string $type |
||||
1245 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1246 | * |
||||
1247 | * @return static |
||||
1248 | */ |
||||
1249 | protected function prepareWhereStatement($field, $value = null, $type = 'AND ', $escape = null, $cacheKey) |
||||
1250 | { |
||||
1251 | if ( ! is_array($field)) { |
||||
1252 | $field = [$field => $value]; |
||||
1253 | } |
||||
1254 | |||||
1255 | // If the escape value was not set will base it on the global setting |
||||
1256 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
1257 | |||||
1258 | foreach ($field as $fieldName => $fieldValue) { |
||||
1259 | if ($fieldValue !== null) { |
||||
1260 | $operator = $this->getOperator($fieldName); |
||||
1261 | $fieldName = trim(str_replace($operator, '', $fieldName)); |
||||
1262 | |||||
1263 | $fieldBind = $this->bind($fieldName, $fieldValue); |
||||
1264 | |||||
1265 | if (empty($operator) && ! in_array($cacheKey, ['between', 'notBetween'])) { |
||||
1266 | $fieldName .= ' ='; |
||||
1267 | } else { |
||||
1268 | $fieldName .= $operator; |
||||
1269 | } |
||||
1270 | } elseif ( ! $this->hasOperator($fieldName)) { |
||||
1271 | // value appears not to have been set, assign the test to IS NULL |
||||
1272 | $fieldName .= ' IS NULL'; |
||||
1273 | } elseif (preg_match('/\s*(!?=|<>|IS(?:\s+NOT)?)\s*$/i', $fieldName, $match, PREG_OFFSET_CAPTURE)) { |
||||
1274 | $fieldName = substr( |
||||
1275 | $fieldName, |
||||
1276 | 0, |
||||
1277 | $match[ 0 ][ 1 ] |
||||
1278 | ) . ($match[ 1 ][ 0 ] === '=' |
||||
1279 | ? ' IS NULL' |
||||
1280 | : ' IS NOT NULL'); |
||||
1281 | } elseif ($fieldValue instanceof AbstractQueryBuilder) { |
||||
1282 | $fieldValue = $fieldValue->getSqlStatement(); |
||||
1283 | } |
||||
1284 | |||||
1285 | $fieldValue = ! is_null($fieldValue) |
||||
1286 | ? ' :' . $fieldBind |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1287 | : $fieldValue; |
||||
1288 | |||||
1289 | if ($cacheKey === 'having') { |
||||
1290 | $prefix = (count($this->builderCache->having) === 0) |
||||
1291 | ? $this->getBracketType('') |
||||
1292 | : $this->getBracketType($type); |
||||
1293 | |||||
1294 | $this->builderCache->having[] = [ |
||||
1295 | 'condition' => $prefix . $fieldName . $fieldValue, |
||||
1296 | 'escape' => $escape, |
||||
1297 | ]; |
||||
1298 | } else { |
||||
1299 | $prefix = (count($this->builderCache->where) === 0) |
||||
1300 | ? $this->getBracketType('') |
||||
1301 | : $this->getBracketType($type); |
||||
1302 | |||||
1303 | if ($cacheKey === 'between') { |
||||
1304 | $condition = $prefix . $fieldName . ' BETWEEN' . $fieldValue; |
||||
1305 | } elseif ($cacheKey === 'notBetween') { |
||||
1306 | $condition = $prefix . $fieldName . ' NOT BETWEEN' . $fieldValue; |
||||
1307 | } else { |
||||
1308 | $condition = $prefix . $fieldName . $fieldValue; |
||||
1309 | } |
||||
1310 | |||||
1311 | $this->builderCache->where[] = [ |
||||
1312 | 'condition' => $condition, |
||||
1313 | 'escape' => $escape, |
||||
1314 | ]; |
||||
1315 | } |
||||
1316 | } |
||||
1317 | |||||
1318 | return $this; |
||||
1319 | } |
||||
1320 | |||||
1321 | //-------------------------------------------------------------------- |
||||
1322 | |||||
1323 | /** |
||||
1324 | * AbstractQueryBuilder::binds |
||||
1325 | * |
||||
1326 | * @param $field |
||||
1327 | * @param $value |
||||
1328 | * |
||||
1329 | * @return string |
||||
1330 | */ |
||||
1331 | public function bind($field, $value) |
||||
1332 | { |
||||
1333 | if ( ! array_key_exists($field, $this->builderCache->binds)) { |
||||
1334 | $this->builderCache->binds[ $field ] = $value; |
||||
1335 | |||||
1336 | return $field; |
||||
1337 | } |
||||
1338 | |||||
1339 | $count = 0; |
||||
1340 | |||||
1341 | while (array_key_exists($field . '_' . $count, $this->builderCache->binds)) { |
||||
1342 | ++$count; |
||||
1343 | } |
||||
1344 | |||||
1345 | $this->builderCache->binds[ $field . '_' . $count ] = $value; |
||||
1346 | |||||
1347 | return $field . '_' . $count; |
||||
1348 | } |
||||
1349 | |||||
1350 | //-------------------------------------------------------------------- |
||||
1351 | |||||
1352 | /** |
||||
1353 | * AbstractQueryBuilder::getBracketType |
||||
1354 | * |
||||
1355 | * @used-by bracketOpen() |
||||
1356 | * @used-by prepareLikeStatement() |
||||
1357 | * @used-by whereHaving() |
||||
1358 | * @used-by prepareWhereInStatement() |
||||
1359 | * |
||||
1360 | * @param string $type |
||||
1361 | * |
||||
1362 | * @return string |
||||
1363 | */ |
||||
1364 | protected function getBracketType($type) |
||||
1365 | { |
||||
1366 | if ($this->builderCache->bracketOpen) { |
||||
1367 | $type = ''; |
||||
1368 | $this->builderCache->bracketOpen = false; |
||||
1369 | } |
||||
1370 | |||||
1371 | return $type; |
||||
1372 | } |
||||
1373 | |||||
1374 | //-------------------------------------------------------------------- |
||||
1375 | |||||
1376 | /** |
||||
1377 | * AbstractQueryBuilder::having |
||||
1378 | * |
||||
1379 | * Separates multiple calls with 'AND'. |
||||
1380 | * |
||||
1381 | * @param string $field |
||||
1382 | * @param string $value |
||||
1383 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1384 | * |
||||
1385 | * @return static |
||||
1386 | */ |
||||
1387 | public function having($field, $value = null, $escape = null) |
||||
1388 | { |
||||
1389 | return $this->prepareWhereStatement($field, $value, 'AND ', $escape, 'having'); |
||||
1390 | } |
||||
1391 | |||||
1392 | //-------------------------------------------------------------------- |
||||
1393 | |||||
1394 | /** |
||||
1395 | * AbstractQueryBuilder::orHaving |
||||
1396 | * |
||||
1397 | * Separates multiple calls with 'OR'. |
||||
1398 | * |
||||
1399 | * @param string $field |
||||
1400 | * @param string $value |
||||
1401 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1402 | * |
||||
1403 | * @return static |
||||
1404 | */ |
||||
1405 | public function orHaving($field, $value = null, $escape = null) |
||||
1406 | { |
||||
1407 | return $this->prepareWhereStatement($field, $value, 'OR ', $escape, 'having'); |
||||
1408 | } |
||||
1409 | |||||
1410 | //-------------------------------------------------------------------- |
||||
1411 | |||||
1412 | /** |
||||
1413 | * AbstractQueryBuilder::whereBetween |
||||
1414 | * |
||||
1415 | * Add WHERE BETWEEN Sql statement portions into Query Builder |
||||
1416 | * |
||||
1417 | * @param string $field Input name |
||||
1418 | * @param array $values Array of between values |
||||
1419 | * |
||||
1420 | * @return static |
||||
1421 | */ |
||||
1422 | public function whereBetween($field, array $values = [], $escape = null) |
||||
1423 | { |
||||
1424 | return $this->prepareWhereStatement($field, implode(' AND ', $values), 'AND ', $escape, 'between'); |
||||
1425 | } |
||||
1426 | |||||
1427 | //-------------------------------------------------------------------- |
||||
1428 | |||||
1429 | /** |
||||
1430 | * AbstractQueryBuilder::orWhereBetween |
||||
1431 | * |
||||
1432 | * Add OR WHERE BETWEEN Sql statement portions into Query Builder |
||||
1433 | * |
||||
1434 | * @param string $field Input name |
||||
1435 | * @param array $values Array of between values |
||||
1436 | * |
||||
1437 | * @return static |
||||
1438 | */ |
||||
1439 | public function orWhereBetween($field, array $values = [], $escape = null) |
||||
1440 | { |
||||
1441 | return $this->prepareWhereStatement($field, implode(' AND ', $values), 'OR ', $escape, 'between'); |
||||
1442 | } |
||||
1443 | |||||
1444 | //-------------------------------------------------------------------- |
||||
1445 | |||||
1446 | /** |
||||
1447 | * AbstractQueryBuilder::whereNotBetween |
||||
1448 | * |
||||
1449 | * Add WHERE NOT BETWEEN Sql statement portions into Query Builder |
||||
1450 | * |
||||
1451 | * @param string $field Input name |
||||
1452 | * @param array $values Array of between values |
||||
1453 | * |
||||
1454 | * @return static |
||||
1455 | */ |
||||
1456 | public function whereNotBetween($field, array $values = [], $escape = null) |
||||
1457 | { |
||||
1458 | return $this->prepareWhereStatement($field, implode(' AND ', $values), 'OR ', $escape, 'notBetween'); |
||||
1459 | } |
||||
1460 | |||||
1461 | //-------------------------------------------------------------------- |
||||
1462 | |||||
1463 | /** |
||||
1464 | * AbstractQueryBuilder::whereIn |
||||
1465 | * |
||||
1466 | * Add WHERE IN Sql statement portions into Query Builder |
||||
1467 | * |
||||
1468 | * @param string $field Input name |
||||
1469 | * @param array $values Array of values criteria |
||||
1470 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1471 | * |
||||
1472 | * @return static |
||||
1473 | */ |
||||
1474 | public function whereIn($field, $values = [], $escape = null) |
||||
1475 | { |
||||
1476 | return $this->prepareWhereInStatement($field, $values, false, 'AND ', $escape); |
||||
1477 | } |
||||
1478 | |||||
1479 | //-------------------------------------------------------------------- |
||||
1480 | |||||
1481 | /** |
||||
1482 | * AbstractQueryBuilder::prepareWhereInStatement |
||||
1483 | * |
||||
1484 | * Internal WHERE IN |
||||
1485 | * |
||||
1486 | * @used-by WhereIn() |
||||
1487 | * @used-by orWhereIn() |
||||
1488 | * @used-by whereNotIn() |
||||
1489 | * @used-by orWhereNotIn() |
||||
1490 | * |
||||
1491 | * @param string $field The field to search |
||||
1492 | * @param array $values The values searched on |
||||
1493 | * @param bool $not If the statement would be IN or NOT IN |
||||
1494 | * @param string $type AND|OR |
||||
1495 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1496 | * |
||||
1497 | * @return static |
||||
1498 | */ |
||||
1499 | protected function prepareWhereInStatement( |
||||
1500 | $field = null, |
||||
1501 | $values = null, |
||||
1502 | $not = false, |
||||
1503 | $type = 'AND ', |
||||
1504 | $escape = null |
||||
1505 | ) { |
||||
1506 | if ($field === null OR $values === null) { |
||||
1507 | return $this; |
||||
1508 | } |
||||
1509 | |||||
1510 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
1511 | |||||
1512 | $fieldKey = $field; |
||||
1513 | |||||
1514 | if (is_string($values) || is_numeric($values)) { |
||||
0 ignored issues
–
show
|
|||||
1515 | $values = [$values]; |
||||
1516 | } |
||||
1517 | |||||
1518 | $not = ($not) |
||||
1519 | ? ' NOT' |
||||
1520 | : ''; |
||||
1521 | |||||
1522 | $prefix = (count($this->builderCache->where) === 0) |
||||
1523 | ? $this->getBracketType('') |
||||
1524 | : $this->getBracketType($type); |
||||
1525 | |||||
1526 | if (is_array($values)) { |
||||
0 ignored issues
–
show
|
|||||
1527 | $fieldValue = array_values($values); |
||||
1528 | $fieldBind = $this->bind($fieldKey, $fieldValue); |
||||
1529 | |||||
1530 | if ($escape === true) { |
||||
1531 | $fieldKey = $this->conn->protectIdentifiers($field); |
||||
1532 | } |
||||
1533 | |||||
1534 | $whereIn = [ |
||||
1535 | 'condition' => $prefix . $fieldKey . $not . ' IN (:' . $fieldBind . ')', |
||||
1536 | 'escape' => false, |
||||
1537 | ]; |
||||
1538 | |||||
1539 | } elseif ($values instanceof AbstractQueryBuilder) { |
||||
1540 | |||||
1541 | if ($escape === true) { |
||||
1542 | $fieldKey = $this->conn->protectIdentifiers($field); |
||||
1543 | } |
||||
1544 | |||||
1545 | $importBindsPattern = []; |
||||
1546 | $importBindsReplacement = []; |
||||
1547 | foreach ($values->builderCache->binds as $bindKey => $bindValue) { |
||||
1548 | $importBindKey = $this->bind($bindKey, $bindValue); |
||||
1549 | |||||
1550 | $importBindsPattern[] = ':' . $bindKey; |
||||
1551 | $importBindsReplacement[] = ':' . $importBindKey; |
||||
1552 | } |
||||
1553 | |||||
1554 | $sqlStatement = $values->getSqlStatement(); |
||||
1555 | $sqlStatement = str_replace($importBindsPattern, $importBindsReplacement, $sqlStatement); |
||||
1556 | |||||
1557 | $whereIn = [ |
||||
1558 | 'condition' => $prefix . $fieldKey . $not . ' IN (' . $sqlStatement . ')', |
||||
1559 | 'escape' => false, |
||||
1560 | ]; |
||||
1561 | } |
||||
1562 | |||||
1563 | if (isset($whereIn)) { |
||||
1564 | $this->builderCache->where[] = $whereIn; |
||||
1565 | } |
||||
1566 | |||||
1567 | return $this; |
||||
1568 | } |
||||
1569 | |||||
1570 | //-------------------------------------------------------------------- |
||||
1571 | |||||
1572 | /** |
||||
1573 | * AbstractQueryBuilder::orWhereIn |
||||
1574 | * |
||||
1575 | * Add OR WHERE IN Sql statement portions into Query Builder |
||||
1576 | * |
||||
1577 | * @param string $field Input name |
||||
1578 | * @param array $values Array of values criteria |
||||
1579 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1580 | * |
||||
1581 | * @return static |
||||
1582 | */ |
||||
1583 | public function orWhereIn($field, $values = [], $escape = null) |
||||
1584 | { |
||||
1585 | return $this->prepareWhereInStatement($field, $values, false, 'OR ', $escape); |
||||
1586 | } |
||||
1587 | |||||
1588 | //-------------------------------------------------------------------- |
||||
1589 | |||||
1590 | /** |
||||
1591 | * AbstractQueryBuilder::whereNotIn |
||||
1592 | * |
||||
1593 | * Add WHERE NOT IN Sql statement portions into Query Builder |
||||
1594 | * |
||||
1595 | * @param string $field Input name |
||||
1596 | * @param array $values Array of values criteria |
||||
1597 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1598 | * |
||||
1599 | * @return static |
||||
1600 | */ |
||||
1601 | public function whereNotIn($field, $values = [], $escape = null) |
||||
1602 | { |
||||
1603 | return $this->prepareWhereInStatement($field, $values, true, 'AND ', $escape); |
||||
1604 | } |
||||
1605 | |||||
1606 | //-------------------------------------------------------------------- |
||||
1607 | |||||
1608 | /** |
||||
1609 | * AbstractQueryBuilder::orWhereNotIn |
||||
1610 | * |
||||
1611 | * Add OR WHERE NOT IN Sql statement portions into Query Builder |
||||
1612 | * |
||||
1613 | * @param string $field Input name |
||||
1614 | * @param array $values Array of values criteria |
||||
1615 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1616 | * |
||||
1617 | * @return static |
||||
1618 | */ |
||||
1619 | public function orWhereNotIn($field, $values = [], $escape = null) |
||||
1620 | { |
||||
1621 | return $this->prepareWhereInStatement($field, $values, true, 'OR ', $escape); |
||||
1622 | } |
||||
1623 | |||||
1624 | //-------------------------------------------------------------------- |
||||
1625 | |||||
1626 | /** |
||||
1627 | * AbstractQueryBuilder::orWhereNotBetween |
||||
1628 | * |
||||
1629 | * Add OR WHERE NOT BETWEEN Sql statement portions into Query Builder |
||||
1630 | * |
||||
1631 | * @param string $field Input name |
||||
1632 | * @param array $values Array of between values |
||||
1633 | * |
||||
1634 | * @return static |
||||
1635 | */ |
||||
1636 | public function orWhereNotBetween($field, array $values = [], $escape = null) |
||||
1637 | { |
||||
1638 | return $this->prepareWhereStatement($field, implode(' OR ', $values), 'OR ', $escape, 'notBetween'); |
||||
1639 | } |
||||
1640 | |||||
1641 | //-------------------------------------------------------------------- |
||||
1642 | |||||
1643 | /** |
||||
1644 | * AbstractQueryBuilder::like |
||||
1645 | * |
||||
1646 | * Generates a %LIKE% Sql statement portions of the query. |
||||
1647 | * Separates multiple calls with 'AND'. |
||||
1648 | * |
||||
1649 | * @param string $field Input name |
||||
1650 | * @param string $match Input criteria match |
||||
1651 | * @param string $wildcard UPPERCASE positions of wildcard character BOTH|BEFORE|AFTER |
||||
1652 | * @param bool $caseSensitive Whether perform case sensitive LIKE or not |
||||
1653 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1654 | * |
||||
1655 | * @return static |
||||
1656 | */ |
||||
1657 | public function like($field, $match = '', $wildcard = 'BOTH', $caseSensitive = true, $escape = null) |
||||
1658 | { |
||||
1659 | return $this->prepareLikeStatement($field, $match, 'AND ', $wildcard, '', $caseSensitive, $escape); |
||||
1660 | } |
||||
1661 | |||||
1662 | //-------------------------------------------------------------------- |
||||
1663 | |||||
1664 | /** |
||||
1665 | * Internal LIKE |
||||
1666 | * |
||||
1667 | * @used-by like() |
||||
1668 | * @used-by orLike() |
||||
1669 | * @used-by notLike() |
||||
1670 | * @used-by orNotLike() |
||||
1671 | * |
||||
1672 | * @param mixed $field |
||||
1673 | * @param string $match |
||||
1674 | * @param string $type |
||||
1675 | * @param string $side |
||||
1676 | * @param string $not |
||||
1677 | * @param bool $caseSensitive IF true, will force a case-insensitive search |
||||
1678 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1679 | * |
||||
1680 | * @return static |
||||
1681 | */ |
||||
1682 | protected function prepareLikeStatement( |
||||
1683 | $field, |
||||
1684 | $match = '', |
||||
1685 | $type = 'AND ', |
||||
1686 | $side = 'both', |
||||
1687 | $not = '', |
||||
1688 | $escape = null, |
||||
1689 | $caseSensitive = false |
||||
1690 | ) { |
||||
1691 | if ( ! is_array($field)) { |
||||
1692 | $field = [$field => $match]; |
||||
1693 | } |
||||
1694 | |||||
1695 | $escape = is_bool($escape) |
||||
1696 | ? $escape |
||||
1697 | : $this->conn->protectIdentifiers; |
||||
1698 | |||||
1699 | // lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh) |
||||
1700 | $side = strtolower($side); |
||||
1701 | |||||
1702 | foreach ($field as $fieldName => $fieldValue) { |
||||
1703 | $prefix = (count($this->builderCache->where) === 0) |
||||
1704 | ? $this->getBracketType('') |
||||
1705 | : $this->getBracketType($type); |
||||
1706 | |||||
1707 | if ($caseSensitive === true) { |
||||
1708 | $fieldValue = strtolower($fieldValue); |
||||
1709 | } |
||||
1710 | |||||
1711 | if ($side === 'none') { |
||||
1712 | $bind = $this->bind($fieldName, $fieldValue); |
||||
1713 | } elseif ($side === 'before') { |
||||
1714 | $bind = $this->bind($fieldName, "%$fieldValue"); |
||||
1715 | } elseif ($side === 'after') { |
||||
1716 | $bind = $this->bind($fieldName, "$fieldValue%"); |
||||
1717 | } else { |
||||
1718 | $bind = $this->bind($fieldName, "%$fieldValue%"); |
||||
1719 | } |
||||
1720 | |||||
1721 | $likeStatement = $this->platformPrepareLikeStatement($prefix, $fieldName, $not, $bind, $caseSensitive); |
||||
1722 | |||||
1723 | // some platforms require an escape sequence definition for LIKE wildcards |
||||
1724 | if ($escape === true && $this->conn->getConfig('likeEscapeString') !== '') { |
||||
1725 | $likeStatement .= sprintf( |
||||
1726 | $this->conn->getConfig('likeEscapeString'), |
||||
1727 | $this->conn->getConfig('likeEscapeCharacter') |
||||
1728 | ); |
||||
1729 | } |
||||
1730 | |||||
1731 | $this->builderCache->where[] = ['condition' => $likeStatement, 'escape' => $escape]; |
||||
1732 | } |
||||
1733 | |||||
1734 | return $this; |
||||
1735 | } |
||||
1736 | |||||
1737 | //-------------------------------------------------------------------- |
||||
1738 | |||||
1739 | /** |
||||
1740 | * AbstractQueryBuilder::platformPrepareLikeStatement |
||||
1741 | * |
||||
1742 | * Platform independent LIKE statement builder. |
||||
1743 | * |
||||
1744 | * @param string|null $prefix |
||||
1745 | * @param string $column |
||||
1746 | * @param string|null $not |
||||
1747 | * @param string $bind |
||||
1748 | * @param bool $caseSensitive |
||||
1749 | * |
||||
1750 | * @return string |
||||
1751 | */ |
||||
1752 | abstract protected function platformPrepareLikeStatement( |
||||
1753 | $prefix = null, |
||||
1754 | $column, |
||||
1755 | $not = null, |
||||
1756 | $bind, |
||||
1757 | $caseSensitive = false |
||||
1758 | ); |
||||
1759 | |||||
1760 | //-------------------------------------------------------------------- |
||||
1761 | |||||
1762 | /** |
||||
1763 | * AbstractQueryBuilder::orLike |
||||
1764 | * |
||||
1765 | * Add OR LIKE Sql statement portions into Query Builder |
||||
1766 | * |
||||
1767 | * @param string $field Input name |
||||
1768 | * @param string $match Input criteria match |
||||
1769 | * @param string $wildcard UPPERCASE positions of wildcard character BOTH|BEFORE|AFTER |
||||
1770 | * @param bool $caseSensitive Whether perform case sensitive LIKE or not |
||||
1771 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1772 | * |
||||
1773 | * @return static |
||||
1774 | */ |
||||
1775 | public function orLike($field, $match = '', $wildcard = 'BOTH', $caseSensitive = true, $escape = null) |
||||
1776 | { |
||||
1777 | return $this->prepareLikeStatement($field, $match, 'OR ', $wildcard, '', $caseSensitive, $escape); |
||||
1778 | } |
||||
1779 | |||||
1780 | //-------------------------------------------------------------------- |
||||
1781 | |||||
1782 | /** |
||||
1783 | * AbstractQueryBuilder::notLike |
||||
1784 | * |
||||
1785 | * Add NOT LIKE Sql statement portions into Query Builder |
||||
1786 | * |
||||
1787 | * @param string $field Input name |
||||
1788 | * @param string $match Input criteria match |
||||
1789 | * @param string $wildcard UPPERCASE positions of wildcard character BOTH|BEFORE|AFTER |
||||
1790 | * @param bool $caseSensitive Whether perform case sensitive LIKE or not |
||||
1791 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1792 | * |
||||
1793 | * @return static |
||||
1794 | */ |
||||
1795 | public function notLike($field, $match = '', $wildcard = 'BOTH', $caseSensitive = true, $escape = null) |
||||
1796 | { |
||||
1797 | return $this->prepareLikeStatement($field, $match, 'AND ', $wildcard, 'NOT', $caseSensitive, $escape); |
||||
1798 | } |
||||
1799 | |||||
1800 | //-------------------------------------------------------------------- |
||||
1801 | |||||
1802 | /** |
||||
1803 | * AbstractQueryBuilder::orNotLike |
||||
1804 | * |
||||
1805 | * Add OR NOT LIKE Sql statement portions into Query Builder |
||||
1806 | * |
||||
1807 | * @param string $field Input name |
||||
1808 | * @param string $match Input criteria match |
||||
1809 | * @param string $wildcard UPPERCASE positions of wildcard character BOTH|BEFORE|AFTER |
||||
1810 | * @param bool $caseSensitive Whether perform case sensitive LIKE or not |
||||
1811 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1812 | * |
||||
1813 | * @return static |
||||
1814 | */ |
||||
1815 | public function orNotLike($field, $match = '', $wildcard = 'BOTH', $caseSensitive = true, $escape = null) |
||||
1816 | { |
||||
1817 | return $this->prepareLikeStatement($field, $match, 'OR ', $wildcard, 'NOT', $caseSensitive, $escape); |
||||
1818 | } |
||||
1819 | |||||
1820 | //-------------------------------------------------------------------- |
||||
1821 | |||||
1822 | /** |
||||
1823 | * AbstractQueryBuilder::groupBy |
||||
1824 | * |
||||
1825 | * Add GROUP BY Sql statement into Query Builder. |
||||
1826 | * |
||||
1827 | * @param string $field |
||||
1828 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1829 | * |
||||
1830 | * @return $this |
||||
1831 | */ |
||||
1832 | public function groupBy($field, $escape = null) |
||||
1833 | { |
||||
1834 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
1835 | |||||
1836 | if (is_string($field)) { |
||||
0 ignored issues
–
show
|
|||||
1837 | $field = ($escape === true) |
||||
1838 | ? explode(',', $field) |
||||
1839 | : [$field]; |
||||
1840 | } |
||||
1841 | |||||
1842 | foreach ($field as $fieldName) { |
||||
1843 | $fieldName = trim($fieldName); |
||||
1844 | |||||
1845 | if ($fieldName !== '') { |
||||
1846 | $fieldName = ['field' => $fieldName, 'escape' => $escape]; |
||||
1847 | |||||
1848 | $this->builderCache->groupBy[] = $fieldName; |
||||
1849 | } |
||||
1850 | } |
||||
1851 | |||||
1852 | return $this; |
||||
1853 | } |
||||
1854 | |||||
1855 | //-------------------------------------------------------------------- |
||||
1856 | |||||
1857 | /** |
||||
1858 | * AbstractQueryBuilder::orderBy |
||||
1859 | * |
||||
1860 | * Add ORDER BY Sql statement portions into Query Builder. |
||||
1861 | * |
||||
1862 | * @param string|array $fields |
||||
1863 | * @param string $direction |
||||
1864 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
1865 | * |
||||
1866 | * @return $this |
||||
1867 | */ |
||||
1868 | public function orderBy($fields, $direction = 'ASC', $escape = null) |
||||
1869 | { |
||||
1870 | $orderBy = []; |
||||
1871 | $direction = strtoupper(trim($direction)); |
||||
1872 | |||||
1873 | if ($direction === 'RANDOM') { |
||||
1874 | $direction = ''; |
||||
1875 | |||||
1876 | // Do we have a seed value? |
||||
1877 | $fields = ctype_digit((string)$fields) |
||||
1878 | ? sprintf($this->SqlOrderByRandomKeywords[ 1 ], $fields) |
||||
0 ignored issues
–
show
It seems like
$fields can also be of type array ; however, parameter $args of sprintf() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1879 | : $this->SqlOrderByRandomKeywords[ 0 ]; |
||||
1880 | } elseif (empty($fields)) { |
||||
1881 | return $this; |
||||
1882 | } elseif ($direction !== '') { |
||||
1883 | $direction = in_array($direction, ['ASC', 'DESC'], true) |
||||
1884 | ? ' ' . $direction |
||||
1885 | : ''; |
||||
1886 | } |
||||
1887 | |||||
1888 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
1889 | |||||
1890 | if ($escape === false) { |
||||
1891 | $orderBy[] = ['field' => $fields, 'direction' => $direction, 'escape' => false]; |
||||
1892 | } elseif (is_array($fields)) { |
||||
1893 | foreach ($fields as $field_name => $field_direction) { |
||||
1894 | $field_direction = is_numeric($field_name) ? $direction : $field_direction; |
||||
1895 | $orderBy[] = ['field' => trim($field_name), 'direction' => $field_direction, 'escape' => true]; |
||||
1896 | } |
||||
1897 | } else { |
||||
1898 | foreach (explode(',', $fields) as $fields) { |
||||
0 ignored issues
–
show
|
|||||
1899 | $orderBy[] = ($direction === '' |
||||
1900 | && preg_match( |
||||
1901 | '/\s+(ASC|DESC)$/i', |
||||
1902 | rtrim($fields), |
||||
1903 | $match, |
||||
1904 | PREG_OFFSET_CAPTURE |
||||
1905 | )) |
||||
1906 | ? [ |
||||
1907 | 'field' => ltrim(substr($fields, 0, $match[ 0 ][ 1 ])), |
||||
1908 | 'direction' => ' ' . $match[ 1 ][ 0 ], |
||||
1909 | 'escape' => true, |
||||
1910 | ] |
||||
1911 | : ['field' => trim($fields), 'direction' => $direction, 'escape' => true]; |
||||
1912 | } |
||||
1913 | } |
||||
1914 | |||||
1915 | $this->builderCache->orderBy = array_merge($this->builderCache->orderBy, $orderBy); |
||||
1916 | |||||
1917 | return $this; |
||||
1918 | } |
||||
1919 | |||||
1920 | // -------------------------------------------------------------------- |
||||
1921 | |||||
1922 | /** |
||||
1923 | * AbstractQueryBuilder::page |
||||
1924 | * |
||||
1925 | * Add Set LIMIT, OFFSET Sql statement by page number and entries. |
||||
1926 | * |
||||
1927 | * @param int $page Page number |
||||
1928 | * @param null $limit Num entries of each page |
||||
0 ignored issues
–
show
|
|||||
1929 | * |
||||
1930 | * @return static |
||||
1931 | */ |
||||
1932 | public function page($page = 1, $limit = null) |
||||
1933 | { |
||||
1934 | $page = (int)intval($page); |
||||
1935 | |||||
1936 | $limit = (int)(isset($limit) |
||||
1937 | ? $limit |
||||
1938 | : ($this->builderCache->limit === false |
||||
1939 | ? 5 |
||||
1940 | : $this->builderCache->limit |
||||
1941 | ) |
||||
1942 | ); |
||||
1943 | |||||
1944 | $offset = ($page - 1) * $limit; |
||||
1945 | |||||
1946 | $this->limit($limit, $offset); |
||||
1947 | |||||
1948 | return $this; |
||||
1949 | } |
||||
1950 | |||||
1951 | //-------------------------------------------------------------------- |
||||
1952 | |||||
1953 | /** |
||||
1954 | * AbstractQueryBuilder::limit |
||||
1955 | * |
||||
1956 | * Add LIMIT,OFFSET Sql statement into Query Builder. |
||||
1957 | * |
||||
1958 | * @param int $limit LIMIT value |
||||
1959 | * @param int $offset OFFSET value |
||||
1960 | * |
||||
1961 | * @return static |
||||
1962 | */ |
||||
1963 | public function limit($limit, $offset = null) |
||||
1964 | { |
||||
1965 | if ( ! is_null($limit)) { |
||||
0 ignored issues
–
show
|
|||||
1966 | $this->builderCache->limit = (int)$limit; |
||||
1967 | } |
||||
1968 | |||||
1969 | $this->offset($offset); |
||||
1970 | |||||
1971 | return $this; |
||||
1972 | } |
||||
1973 | |||||
1974 | //-------------------------------------------------------------------- |
||||
1975 | |||||
1976 | /** |
||||
1977 | * AbstractQueryBuilder::offset |
||||
1978 | * |
||||
1979 | * Add OFFSET Sql statement into Query Builder. |
||||
1980 | * |
||||
1981 | * @param int $offset OFFSET value |
||||
1982 | * |
||||
1983 | * @return static |
||||
1984 | */ |
||||
1985 | public function offset($offset) |
||||
1986 | { |
||||
1987 | if ( ! empty($offset)) { |
||||
1988 | $this->builderCache->offset = (int)$offset; |
||||
1989 | } |
||||
1990 | |||||
1991 | return $this; |
||||
1992 | } |
||||
1993 | |||||
1994 | //-------------------------------------------------------------------- |
||||
1995 | |||||
1996 | /** |
||||
1997 | * AbstractQueryBuilder::get |
||||
1998 | * |
||||
1999 | * Perform execution of Sql Query Builder and run ConnectionInterface::query() |
||||
2000 | * |
||||
2001 | * @param null|int $limit |
||||
2002 | * @param null|int $offset |
||||
2003 | * |
||||
2004 | * @return Result |
||||
2005 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2006 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2007 | */ |
||||
2008 | public function get($limit = null, $offset = null) |
||||
2009 | { |
||||
2010 | if ( ! empty($limit)) { |
||||
2011 | $this->limit($limit, $offset); |
||||
2012 | } |
||||
2013 | |||||
2014 | if ($this->cacheMode) { |
||||
2015 | $this->conn->cache($this->cacheMode); |
||||
2016 | } |
||||
2017 | |||||
2018 | $result = $this->testMode |
||||
2019 | ? $this->getSqlStatement(false) |
||||
2020 | : $this->conn->query($this->getSqlStatement(false), $this->builderCache->binds); |
||||
2021 | |||||
2022 | if ($result) { |
||||
2023 | $result->setNumPerPage($this->builderCache->limit); |
||||
2024 | $result->setNumFounds($this->countAllResults(false)); |
||||
2025 | $result->setNumTotal($this->countAll(false)); |
||||
2026 | } |
||||
2027 | |||||
2028 | $this->builderCache->reset(); |
||||
2029 | |||||
2030 | $this->cacheMode = $this->conn->getConfig('cacheEnable'); |
||||
0 ignored issues
–
show
It seems like
$this->conn->getConfig('cacheEnable') can also be of type array . However, the property $cacheMode is declared as type boolean . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||||
2031 | |||||
2032 | return $result; |
||||
0 ignored issues
–
show
|
|||||
2033 | } |
||||
2034 | |||||
2035 | //-------------------------------------------------------------------- |
||||
2036 | |||||
2037 | /** |
||||
2038 | * AbstractQueryBuilder::countAllResult |
||||
2039 | * |
||||
2040 | * Perform execution of count all result from Query Builder along with WHERE, LIKE, HAVING, GROUP BY, and LIMIT Sql |
||||
2041 | * statement. |
||||
2042 | * |
||||
2043 | * @return int |
||||
2044 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2045 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2046 | */ |
||||
2047 | public function countAllResults($reset = true) |
||||
2048 | { |
||||
2049 | // save previous |
||||
2050 | $previousSelect = $this->builderCache->select; |
||||
2051 | $previousLimit = $this->builderCache->limit; |
||||
2052 | $previousOffset = $this->builderCache->offset; |
||||
2053 | |||||
2054 | // add select counter |
||||
2055 | array_unshift($this->builderCache->select, 'COUNT(*) AS numrows'); |
||||
2056 | |||||
2057 | // generate Sql statement |
||||
2058 | $sqlStatement = $this->getSqlStatement(); |
||||
2059 | |||||
2060 | // restore previous select |
||||
2061 | $this->builderCache->select = $previousSelect; |
||||
2062 | $this->builderCache->limit = $previousLimit; |
||||
2063 | $this->builderCache->offset = $previousOffset; |
||||
2064 | |||||
2065 | if ($this->testMode) { |
||||
2066 | return $sqlStatement; |
||||
0 ignored issues
–
show
|
|||||
2067 | } elseif ($this->isSubQuery) { |
||||
2068 | $statement = new Query\Statement(); |
||||
2069 | $statement->setSqlStatement($sqlStatement, $this->builderCache->binds); |
||||
2070 | $statement->setSqlFinalStatement($this->conn->compileSqlBinds($sqlStatement, |
||||
2071 | $this->builderCache->binds)); |
||||
2072 | |||||
2073 | $sqlStatement = $statement->getSqlFinalStatement(); |
||||
2074 | |||||
2075 | return '( ' . $sqlStatement . ' )'; |
||||
0 ignored issues
–
show
|
|||||
2076 | } |
||||
2077 | |||||
2078 | $numrows = 0; |
||||
2079 | if($result = $this->conn->query($sqlStatement, $this->builderCache->binds)) { |
||||
2080 | if($result->count()) { |
||||
2081 | $numrows = $result->first()->numrows; |
||||
0 ignored issues
–
show
The property
numrows does not exist on O2System\Database\DataObjects\Result\Row . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
2082 | } |
||||
2083 | } |
||||
2084 | |||||
2085 | if($reset) { |
||||
2086 | $this->builderCache->reset(); |
||||
2087 | } |
||||
2088 | |||||
2089 | return $numrows; |
||||
2090 | } |
||||
2091 | |||||
2092 | //-------------------------------------------------------------------- |
||||
2093 | |||||
2094 | /** |
||||
2095 | * AbstractQueryBuilder::getWhere |
||||
2096 | * |
||||
2097 | * Perform execution of Sql Query Builder and run ConnectionInterface::query() |
||||
2098 | * |
||||
2099 | * @param array $where |
||||
2100 | * @param null|int $limit |
||||
2101 | * @param null|int $offset |
||||
2102 | * |
||||
2103 | * @return Result |
||||
2104 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2105 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2106 | */ |
||||
2107 | public function getWhere(array $where = [], $limit = null, $offset = null) |
||||
2108 | { |
||||
2109 | $this->where($where); |
||||
2110 | |||||
2111 | if ( ! empty($limit)) { |
||||
2112 | $this->limit($limit, $offset); |
||||
2113 | } |
||||
2114 | |||||
2115 | if ($this->cacheMode) { |
||||
2116 | $this->conn->cache($this->cacheMode); |
||||
2117 | } |
||||
2118 | |||||
2119 | $result = $this->testMode |
||||
2120 | ? $this->getSqlStatement() |
||||
2121 | : $this->conn->query($this->getSqlStatement(false), $this->builderCache->binds); |
||||
2122 | |||||
2123 | if ($result) { |
||||
2124 | $result->setNumPerPage($this->builderCache->limit); |
||||
2125 | $result->setNumFounds($this->countAllResults(false)); |
||||
2126 | $result->setNumTotal($this->countAll(false)); |
||||
2127 | } |
||||
2128 | |||||
2129 | $this->builderCache->reset(); |
||||
2130 | |||||
2131 | $this->cacheMode = $this->conn->getConfig('cacheEnable'); |
||||
0 ignored issues
–
show
It seems like
$this->conn->getConfig('cacheEnable') can also be of type array . However, the property $cacheMode is declared as type boolean . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||||
2132 | |||||
2133 | return $result; |
||||
0 ignored issues
–
show
|
|||||
2134 | } |
||||
2135 | |||||
2136 | //-------------------------------------------------------------------- |
||||
2137 | |||||
2138 | /** |
||||
2139 | * AbstractQueryBuilder::where |
||||
2140 | * |
||||
2141 | * Add WHERE Sql statement portions into Query Builder |
||||
2142 | * |
||||
2143 | * @param string|array $field Input name, array of [field => value] (grouped where) |
||||
2144 | * @param null|string $value Input criteria or UPPERCASE grouped type AND|OR |
||||
2145 | * @param null|bool $escape Whether not to try to escape identifiers |
||||
2146 | * |
||||
2147 | * @return static |
||||
2148 | */ |
||||
2149 | public function where($field, $value = null, $escape = null) |
||||
2150 | { |
||||
2151 | return $this->prepareWhereStatement($field, $value, 'AND ', $escape, 'where'); |
||||
2152 | } |
||||
2153 | |||||
2154 | //-------------------------------------------------------------------- |
||||
2155 | |||||
2156 | /** |
||||
2157 | * AbstractQueryBuilder::countAll |
||||
2158 | * |
||||
2159 | * Perform execution of count all records of a table. |
||||
2160 | * |
||||
2161 | * @access public |
||||
2162 | * @return int |
||||
2163 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2164 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2165 | */ |
||||
2166 | public function countAll($reset = true) |
||||
2167 | { |
||||
2168 | // save previous cache |
||||
2169 | $previousQueryBuilderCache = clone $this->builderCache; |
||||
2170 | |||||
2171 | // reset cache |
||||
2172 | $this->builderCache->reset(); |
||||
2173 | |||||
2174 | $this->builderCache->from = $previousQueryBuilderCache->from; |
||||
2175 | |||||
2176 | $this->count('*', 'numrows'); |
||||
2177 | |||||
2178 | // generate Sql statement |
||||
2179 | $sqlStatement = $this->getSqlStatement(); |
||||
2180 | |||||
2181 | // restore previous cache |
||||
2182 | $this->builderCache = $previousQueryBuilderCache; |
||||
2183 | unset($previousQueryBuilderCache); |
||||
2184 | |||||
2185 | if ($this->testMode) { |
||||
2186 | return $sqlStatement; |
||||
0 ignored issues
–
show
|
|||||
2187 | } elseif ($this->isSubQuery) { |
||||
2188 | $statement = new Query\Statement(); |
||||
2189 | $statement->setSqlStatement($sqlStatement, $this->builderCache->binds); |
||||
2190 | $statement->setSqlFinalStatement($this->conn->compileSqlBinds($sqlStatement, |
||||
2191 | $this->builderCache->binds)); |
||||
2192 | |||||
2193 | $sqlStatement = $statement->getSqlFinalStatement(); |
||||
2194 | |||||
2195 | return '( ' . $sqlStatement . ' )'; |
||||
0 ignored issues
–
show
|
|||||
2196 | } |
||||
2197 | |||||
2198 | $numrows = 0; |
||||
2199 | if($result = $this->conn->query($sqlStatement, $this->builderCache->binds)) { |
||||
2200 | if($result->count()) { |
||||
2201 | $numrows = $result->first()->numrows; |
||||
0 ignored issues
–
show
The property
numrows does not exist on O2System\Database\DataObjects\Result\Row . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
2202 | } |
||||
2203 | } |
||||
2204 | |||||
2205 | if($reset) { |
||||
2206 | $this->builderCache->reset(); |
||||
2207 | } |
||||
2208 | |||||
2209 | return $numrows; |
||||
2210 | } |
||||
2211 | |||||
2212 | //-------------------------------------------------------------------- |
||||
2213 | |||||
2214 | /** |
||||
2215 | * AbstractQueryBuilder::count |
||||
2216 | * |
||||
2217 | * Add SELECT COUNT(field) AS alias statement |
||||
2218 | * |
||||
2219 | * @param string $field Input name |
||||
2220 | * @param string $alias Input alias |
||||
2221 | * |
||||
2222 | * @return static |
||||
2223 | */ |
||||
2224 | public function count($field, $alias = '') |
||||
2225 | { |
||||
2226 | return $this->prepareAggregateStatement($field, $alias, 'COUNT'); |
||||
2227 | } |
||||
2228 | |||||
2229 | //-------------------------------------------------------------------- |
||||
2230 | |||||
2231 | /** |
||||
2232 | * AbstractQueryBuilder::orBracketOpen |
||||
2233 | * |
||||
2234 | * Starts a query group, but ORs the group |
||||
2235 | * |
||||
2236 | * @return static |
||||
2237 | */ |
||||
2238 | public function orBracketOpen() |
||||
2239 | { |
||||
2240 | return $this->bracketOpen('', 'OR '); |
||||
2241 | } |
||||
2242 | |||||
2243 | //-------------------------------------------------------------------- |
||||
2244 | |||||
2245 | /** |
||||
2246 | * AbstractQueryBuilder::bracketOpen |
||||
2247 | * |
||||
2248 | * Starts a query group. |
||||
2249 | * |
||||
2250 | * @param string $not (Internal use only) |
||||
2251 | * @param string $type (Internal use only) |
||||
2252 | * |
||||
2253 | * @return static |
||||
2254 | */ |
||||
2255 | public function bracketOpen($not = '', $type = 'AND ') |
||||
2256 | { |
||||
2257 | $type = $this->getBracketType($type); |
||||
2258 | |||||
2259 | $this->builderCache->bracketOpen = true; |
||||
2260 | $prefix = count($this->builderCache->where) === 0 |
||||
2261 | ? '' |
||||
2262 | : $type; |
||||
2263 | $where = [ |
||||
2264 | 'condition' => $prefix . $not . str_repeat(' ', ++$this->builderCache->bracketCount) . ' (', |
||||
2265 | 'escape' => false, |
||||
2266 | ]; |
||||
2267 | |||||
2268 | $this->builderCache->where[] = $where; |
||||
2269 | |||||
2270 | return $this; |
||||
2271 | } |
||||
2272 | |||||
2273 | //-------------------------------------------------------------------- |
||||
2274 | |||||
2275 | /** |
||||
2276 | * AbstractQueryBuilder::notBracketOpen |
||||
2277 | * |
||||
2278 | * Starts a query group, but NOTs the group |
||||
2279 | * |
||||
2280 | * @return static |
||||
2281 | */ |
||||
2282 | public function notBracketOpen() |
||||
2283 | { |
||||
2284 | return $this->bracketOpen('NOT ', 'AND '); |
||||
2285 | } |
||||
2286 | |||||
2287 | //-------------------------------------------------------------------- |
||||
2288 | |||||
2289 | /** |
||||
2290 | * AbstractQueryBuilder::orNotBracketOpen |
||||
2291 | * |
||||
2292 | * Starts a query group, but OR NOTs the group |
||||
2293 | * |
||||
2294 | * @return static |
||||
2295 | */ |
||||
2296 | public function orNotBracketOpen() |
||||
2297 | { |
||||
2298 | return $this->bracketOpen('NOT ', 'OR '); |
||||
2299 | } |
||||
2300 | |||||
2301 | //-------------------------------------------------------------------- |
||||
2302 | |||||
2303 | /** |
||||
2304 | * AbstractQueryBuilder::bracketClose |
||||
2305 | * |
||||
2306 | * Ends a query group |
||||
2307 | * |
||||
2308 | * @return static |
||||
2309 | */ |
||||
2310 | public function bracketClose() |
||||
2311 | { |
||||
2312 | $this->builderCache->bracketOpen = false; |
||||
2313 | |||||
2314 | $where = [ |
||||
2315 | 'condition' => str_repeat(' ', $this->builderCache->bracketCount--) . ')', |
||||
2316 | 'escape' => false, |
||||
2317 | ]; |
||||
2318 | |||||
2319 | $this->builderCache->where[] = $where; |
||||
2320 | |||||
2321 | return $this; |
||||
2322 | } |
||||
2323 | |||||
2324 | //-------------------------------------------------------------------- |
||||
2325 | |||||
2326 | /** |
||||
2327 | * AbstractQueryBuilder::insert |
||||
2328 | * |
||||
2329 | * Execute INSERT Sql Query |
||||
2330 | * |
||||
2331 | * @param array $sets An associative array of set values. |
||||
2332 | * sets[][field => value] |
||||
2333 | * @param bool $escape Whether to escape values and identifiers |
||||
2334 | * |
||||
2335 | * @return bool |
||||
2336 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2337 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2338 | */ |
||||
2339 | public function insert(array $sets, $escape = null) |
||||
2340 | { |
||||
2341 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2342 | |||||
2343 | $this->set($sets, null, $escape); |
||||
2344 | |||||
2345 | $result = false; |
||||
2346 | |||||
2347 | if (count($this->builderCache->sets)) { |
||||
2348 | $sqlStatement = $this->platformInsertStatement( |
||||
2349 | $this->conn->protectIdentifiers( |
||||
2350 | $this->builderCache->from[ 0 ], |
||||
2351 | true, |
||||
2352 | $escape, |
||||
2353 | false |
||||
2354 | ), |
||||
2355 | array_keys($this->builderCache->sets), |
||||
2356 | array_values($this->builderCache->sets) |
||||
2357 | ); |
||||
2358 | |||||
2359 | if ($this->testMode) { |
||||
2360 | return $sqlStatement; |
||||
0 ignored issues
–
show
|
|||||
2361 | } |
||||
2362 | |||||
2363 | $result = $this->conn->query($sqlStatement, $this->builderCache->binds); |
||||
2364 | } |
||||
2365 | |||||
2366 | $this->builderCache->resetModifier(); |
||||
2367 | |||||
2368 | return $result; |
||||
0 ignored issues
–
show
|
|||||
2369 | } |
||||
2370 | |||||
2371 | //-------------------------------------------------------------------- |
||||
2372 | |||||
2373 | /** |
||||
2374 | * AbstractQueryBuilder::set |
||||
2375 | * |
||||
2376 | * Allows key/value pairs to be set for inserting or updating |
||||
2377 | * |
||||
2378 | * @param string|array $field |
||||
2379 | * @param mixed $value |
||||
2380 | * @param null $escape |
||||
0 ignored issues
–
show
|
|||||
2381 | * |
||||
2382 | * @return static|array |
||||
2383 | */ |
||||
2384 | public function set($field, $value = '', $escape = null) |
||||
2385 | { |
||||
2386 | $field = $this->objectToArray($field); |
||||
2387 | |||||
2388 | if ( ! is_array($field)) { |
||||
0 ignored issues
–
show
|
|||||
2389 | $field = [$field => $value]; |
||||
2390 | } |
||||
2391 | |||||
2392 | $escape = is_bool($escape) |
||||
0 ignored issues
–
show
|
|||||
2393 | ? $escape |
||||
2394 | : $this->conn->protectIdentifiers; |
||||
2395 | |||||
2396 | foreach ($field as $key => $value) { |
||||
2397 | if ($key === 'birthday' || $key === 'date') { |
||||
2398 | if (is_array($value)) { |
||||
2399 | $value = $value[ 'year' ] . '-' . $value[ 'month' ] . '-' . $value[ 'date' ]; |
||||
2400 | } elseif (is_object($value)) { |
||||
2401 | $value = $value->year . '-' . $value->month . '-' . $value->date; |
||||
2402 | } |
||||
2403 | } elseif (is_array($value) || is_object($value)) { |
||||
2404 | $value = call_user_func_array($this->arrayObjectConversionMethod, [$value]); |
||||
2405 | } |
||||
2406 | |||||
2407 | $this->builderCache->binds[ $key ] = $value; |
||||
2408 | $this->builderCache->sets[ $this->conn->protectIdentifiers($key, false, |
||||
2409 | $escape) ] = ':' . $key; |
||||
2410 | } |
||||
2411 | |||||
2412 | return $this; |
||||
2413 | } |
||||
2414 | |||||
2415 | //-------------------------------------------------------------------- |
||||
2416 | |||||
2417 | /** |
||||
2418 | * AbstractQueryBuilder::objectToArray |
||||
2419 | * |
||||
2420 | * Takes an object as input and converts the class variables to array key/vals |
||||
2421 | * |
||||
2422 | * @param mixed $object |
||||
2423 | * |
||||
2424 | * @return array |
||||
2425 | */ |
||||
2426 | protected function objectToArray($object) |
||||
2427 | { |
||||
2428 | if ( ! is_object($object)) { |
||||
2429 | return $object; |
||||
2430 | } |
||||
2431 | |||||
2432 | $array = []; |
||||
2433 | foreach (get_object_vars($object) as $key => $value) { |
||||
2434 | // There are some built in keys we need to ignore for this conversion |
||||
2435 | if ( ! is_object($value) && ! is_array($value) && $key !== '_parent_name') { |
||||
2436 | $array[ $key ] = $value; |
||||
2437 | } |
||||
2438 | } |
||||
2439 | |||||
2440 | return $array; |
||||
2441 | } |
||||
2442 | |||||
2443 | //-------------------------------------------------------------------- |
||||
2444 | |||||
2445 | /** |
||||
2446 | * AbstractQueryBuilder::platformInsertStatement |
||||
2447 | * |
||||
2448 | * Generates a platform-specific insert string from the supplied data. |
||||
2449 | * |
||||
2450 | * @param string $table Table name. |
||||
2451 | * @param array $keys Insert keys. |
||||
2452 | * @param array $values Insert values. |
||||
2453 | * |
||||
2454 | * @return string |
||||
2455 | */ |
||||
2456 | abstract protected function platformInsertStatement($table, array $keys, array $values); |
||||
2457 | |||||
2458 | //-------------------------------------------------------------------- |
||||
2459 | |||||
2460 | /** |
||||
2461 | * AbstractQueryBuilder::insertBatch |
||||
2462 | * |
||||
2463 | * Execute INSERT batch Sql Query |
||||
2464 | * |
||||
2465 | * @param array $sets An associative array of set values. |
||||
2466 | * sets[][field => value] |
||||
2467 | * @param int $batchSize Maximum batch size |
||||
2468 | * @param bool $escape Whether to escape values and identifiers |
||||
2469 | * |
||||
2470 | * @return bool |
||||
2471 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2472 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2473 | */ |
||||
2474 | public function insertBatch(array $sets, $batchSize = 1000, $escape = null) |
||||
2475 | { |
||||
2476 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2477 | |||||
2478 | $this->setInsertReplaceBatch($sets); |
||||
2479 | |||||
2480 | // Batch this baby |
||||
2481 | $affectedRows = 0; |
||||
2482 | for ($i = 0, $total = count($sets); $i < $total; $i += $batchSize) { |
||||
2483 | $Sql = $this->platformInsertBatchStatement( |
||||
2484 | $this->conn->protectIdentifiers($this->builderCache->from[ 0 ], true, $escape, false), |
||||
2485 | $this->builderCache->keys, |
||||
2486 | array_slice($this->builderCache->sets, $i, $batchSize) |
||||
2487 | ); |
||||
2488 | |||||
2489 | if ($this->testMode) { |
||||
2490 | ++$affectedRows; |
||||
2491 | } else { |
||||
2492 | $this->conn->query($Sql, $this->builderCache->binds); |
||||
2493 | $affectedRows += $this->conn->getAffectedRows(); |
||||
2494 | } |
||||
2495 | } |
||||
2496 | |||||
2497 | if ( ! $this->testMode) { |
||||
2498 | $this->builderCache->resetModifier(); |
||||
2499 | } |
||||
2500 | |||||
2501 | return $affectedRows; |
||||
0 ignored issues
–
show
|
|||||
2502 | } |
||||
2503 | |||||
2504 | //-------------------------------------------------------------------- |
||||
2505 | |||||
2506 | /** |
||||
2507 | * AbstractQueryBuilder::setInsertBatch |
||||
2508 | * |
||||
2509 | * The "setInsertBatch" function. Allows key/value pairs to be set for batch inserts |
||||
2510 | * |
||||
2511 | * @param mixed $field |
||||
2512 | * @param string $value |
||||
2513 | * @param bool $escape Whether to escape values and identifiers |
||||
2514 | * |
||||
2515 | * @return void |
||||
2516 | */ |
||||
2517 | protected function setInsertReplaceBatch($field, $value = '', $escape = null) |
||||
2518 | { |
||||
2519 | $field = $this->batchObjectToArray($field); |
||||
2520 | |||||
2521 | if ( ! is_array($field)) { |
||||
0 ignored issues
–
show
|
|||||
2522 | $field = [$field => $value]; |
||||
2523 | } |
||||
2524 | |||||
2525 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2526 | |||||
2527 | $rowKeys = array_keys($this->objectToArray(current($field))); |
||||
2528 | sort($rowKeys); |
||||
2529 | |||||
2530 | foreach ($field as $row) { |
||||
2531 | $row = $this->objectToArray($row); |
||||
2532 | if (count(array_diff($rowKeys, array_keys($row))) > 0 |
||||
2533 | || count( |
||||
2534 | array_diff(array_keys($row), $rowKeys) |
||||
2535 | ) > 0 |
||||
2536 | ) { |
||||
2537 | // batch function above returns an error on an empty array |
||||
2538 | $this->builderCache->sets[] = []; |
||||
2539 | |||||
2540 | return; |
||||
2541 | } |
||||
2542 | |||||
2543 | ksort($row); // puts $row in the same order as our keys |
||||
2544 | |||||
2545 | $clean = []; |
||||
2546 | foreach ($row as $key => $value) { |
||||
2547 | $clean[] = ':' . $this->bind($key, $value); |
||||
2548 | } |
||||
2549 | |||||
2550 | $row = $clean; |
||||
2551 | |||||
2552 | $this->builderCache->sets[] = '(' . implode(',', $row) . ')'; |
||||
2553 | } |
||||
2554 | |||||
2555 | foreach ($rowKeys as $rowKey) { |
||||
2556 | $this->builderCache->keys[] = $this->conn->protectIdentifiers($rowKey, false, $escape); |
||||
2557 | } |
||||
2558 | } |
||||
2559 | |||||
2560 | //-------------------------------------------------------------------- |
||||
2561 | |||||
2562 | /** |
||||
2563 | * Object to Array |
||||
2564 | * |
||||
2565 | * Takes an object as input and converts the class variables to array key/vals |
||||
2566 | * |
||||
2567 | * @param object |
||||
2568 | * |
||||
2569 | * @return array |
||||
2570 | */ |
||||
2571 | protected function batchObjectToArray($object) |
||||
2572 | { |
||||
2573 | if ( ! is_object($object)) { |
||||
2574 | return $object; |
||||
2575 | } |
||||
2576 | |||||
2577 | $array = []; |
||||
2578 | $out = get_object_vars($object); |
||||
2579 | $fields = array_keys($out); |
||||
2580 | |||||
2581 | foreach ($fields as $field) { |
||||
2582 | // There are some built in keys we need to ignore for this conversion |
||||
2583 | if ($field !== '_parent_name') { |
||||
2584 | $i = 0; |
||||
2585 | foreach ($out[ $field ] as $data) { |
||||
2586 | $array[ $i++ ][ $field ] = $data; |
||||
2587 | } |
||||
2588 | } |
||||
2589 | } |
||||
2590 | |||||
2591 | return $array; |
||||
2592 | } |
||||
2593 | |||||
2594 | //-------------------------------------------------------------------- |
||||
2595 | |||||
2596 | /** |
||||
2597 | * AbstractQueryBuilder::platformInsertBatchStatement |
||||
2598 | * |
||||
2599 | * @param string $table |
||||
2600 | * @param array $keys |
||||
2601 | * @param array $values |
||||
2602 | * |
||||
2603 | * @return mixed |
||||
2604 | */ |
||||
2605 | abstract protected function platformInsertBatchStatement($table, array $keys, array $values); |
||||
2606 | |||||
2607 | //-------------------------------------------------------------------- |
||||
2608 | |||||
2609 | /** |
||||
2610 | * AbstractQueryBuilder::replace |
||||
2611 | * |
||||
2612 | * Compiles an replace into string and runs the query |
||||
2613 | * |
||||
2614 | * @param array $sets An associative array of set values. |
||||
2615 | * sets[][field => value] |
||||
2616 | * @param bool $escape Whether to escape values and identifiers |
||||
2617 | * |
||||
2618 | * @return bool |
||||
2619 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2620 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2621 | */ |
||||
2622 | public function replace(array $sets, $escape = null) |
||||
2623 | { |
||||
2624 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2625 | |||||
2626 | $this->set($sets, null, $escape); |
||||
2627 | |||||
2628 | if (count($this->builderCache->sets)) { |
||||
2629 | $sqlStatement = $this->platformReplaceStatement( |
||||
2630 | $this->conn->protectIdentifiers( |
||||
2631 | $this->builderCache->from[ 0 ], |
||||
2632 | true, |
||||
2633 | $escape, |
||||
2634 | false |
||||
2635 | ), |
||||
2636 | array_keys($this->builderCache->sets), |
||||
2637 | array_values($this->builderCache->sets) |
||||
2638 | ); |
||||
2639 | |||||
2640 | if ($this->testMode) { |
||||
2641 | return $sqlStatement; |
||||
0 ignored issues
–
show
|
|||||
2642 | } |
||||
2643 | |||||
2644 | $sqlBinds = $this->builderCache->binds; |
||||
2645 | $this->builderCache->resetModifier(); |
||||
2646 | |||||
2647 | return $this->conn->query($sqlStatement, $sqlBinds); |
||||
0 ignored issues
–
show
|
|||||
2648 | } |
||||
2649 | |||||
2650 | return false; |
||||
2651 | } |
||||
2652 | |||||
2653 | //-------------------------------------------------------------------- |
||||
2654 | |||||
2655 | /** |
||||
2656 | * AbstractQueryBuilder::platformReplaceStatement |
||||
2657 | * |
||||
2658 | * Generates a platform-specific update string from the supplied data. |
||||
2659 | * |
||||
2660 | * @param string $table Table name. |
||||
2661 | * @param array $keys Insert keys. |
||||
2662 | * @param array $values Insert values. |
||||
2663 | * |
||||
2664 | * @return string |
||||
2665 | */ |
||||
2666 | abstract protected function platformReplaceStatement($table, array $keys, array $values); |
||||
2667 | |||||
2668 | //-------------------------------------------------------------------- |
||||
2669 | |||||
2670 | /** |
||||
2671 | * AbstractQueryBuilder::replaceBatch |
||||
2672 | * |
||||
2673 | * Execute REPLACE batch Sql Query |
||||
2674 | * |
||||
2675 | * @param array $sets An associative array of set values. |
||||
2676 | * sets[][field => value] |
||||
2677 | * @param int $batchSize Maximum batch size |
||||
2678 | * @param bool $escape Whether to escape values and identifiers |
||||
2679 | * |
||||
2680 | * @return bool |
||||
2681 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2682 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2683 | */ |
||||
2684 | public function replaceBatch(array $sets, $batchSize = 1000, $escape = null) |
||||
2685 | { |
||||
2686 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2687 | |||||
2688 | $this->setInsertReplaceBatch($sets); |
||||
2689 | |||||
2690 | // Batch this baby |
||||
2691 | $affectedRows = 0; |
||||
2692 | for ($i = 0, $total = count($sets); $i < $total; $i += $batchSize) { |
||||
2693 | $Sql = $this->platformReplaceStatement( |
||||
2694 | $this->conn->protectIdentifiers($this->builderCache->from[ 0 ], true, $escape, false), |
||||
2695 | $this->builderCache->keys, |
||||
2696 | array_slice($this->builderCache->sets, $i, $batchSize) |
||||
2697 | ); |
||||
2698 | |||||
2699 | if ($this->testMode) { |
||||
2700 | ++$affectedRows; |
||||
2701 | } else { |
||||
2702 | $this->conn->query($Sql, $this->builderCache->binds); |
||||
2703 | $affectedRows += $this->conn->getAffectedRows(); |
||||
2704 | } |
||||
2705 | } |
||||
2706 | |||||
2707 | if ( ! $this->testMode) { |
||||
2708 | $this->builderCache->resetModifier(); |
||||
2709 | } |
||||
2710 | |||||
2711 | return $affectedRows; |
||||
0 ignored issues
–
show
|
|||||
2712 | } |
||||
2713 | |||||
2714 | //-------------------------------------------------------------------- |
||||
2715 | |||||
2716 | /** |
||||
2717 | * AbstractQueryBuilder::update |
||||
2718 | * |
||||
2719 | * Compiles an update string and runs the query. |
||||
2720 | * |
||||
2721 | * @param array $sets An associative array of set values. |
||||
2722 | * sets[][field => value] |
||||
2723 | * @param array $where WHERE [field => match] |
||||
2724 | * @param bool $escape Whether to escape values and identifiers |
||||
2725 | * |
||||
2726 | * @return bool |
||||
2727 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2728 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2729 | */ |
||||
2730 | public function update(array $sets, array $where = [], $escape = null) |
||||
2731 | { |
||||
2732 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2733 | |||||
2734 | $this->set($sets, null, $escape); |
||||
2735 | $this->where($where); |
||||
2736 | |||||
2737 | if (count($this->builderCache->sets) && count($this->builderCache->from)) { |
||||
2738 | $sqlStatement = $this->platformUpdateStatement( |
||||
2739 | $this->conn->protectIdentifiers( |
||||
2740 | $this->builderCache->from[ 0 ], |
||||
2741 | true, |
||||
2742 | $escape, |
||||
2743 | false |
||||
2744 | ), |
||||
2745 | $this->builderCache->sets |
||||
2746 | ); |
||||
2747 | |||||
2748 | if ($this->testMode) { |
||||
2749 | return $sqlStatement; |
||||
0 ignored issues
–
show
|
|||||
2750 | } |
||||
2751 | |||||
2752 | $sqlBinds = $this->builderCache->binds; |
||||
2753 | $this->builderCache->resetModifier(); |
||||
2754 | |||||
2755 | return $this->conn->query($sqlStatement, $sqlBinds); |
||||
0 ignored issues
–
show
|
|||||
2756 | } |
||||
2757 | |||||
2758 | return false; |
||||
2759 | } |
||||
2760 | |||||
2761 | //-------------------------------------------------------------------- |
||||
2762 | |||||
2763 | /** |
||||
2764 | * AbstractQueryBuilder::platformUpdateStatement |
||||
2765 | * |
||||
2766 | * Generates a platform-specific update string from the supplied data. |
||||
2767 | * |
||||
2768 | * @param string $table Table name. |
||||
2769 | * @param array $sets An associative array of set values. |
||||
2770 | * sets[][field => value] |
||||
2771 | * |
||||
2772 | * @return string |
||||
2773 | */ |
||||
2774 | abstract protected function platformUpdateStatement($table, array $sets); |
||||
2775 | |||||
2776 | //-------------------------------------------------------------------- |
||||
2777 | |||||
2778 | /** |
||||
2779 | * AbstractQueryBuilder::updateBatch |
||||
2780 | * |
||||
2781 | * Execute UPDATE batch Sql Query |
||||
2782 | * |
||||
2783 | * @param array $sets Array of data sets[][field => value] |
||||
2784 | * @param string $index Index field |
||||
2785 | * @param int $batchSize Maximum batch size |
||||
2786 | * @param bool $escape Whether to escape values and identifiers |
||||
2787 | * |
||||
2788 | * @return bool |
||||
2789 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2790 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2791 | */ |
||||
2792 | public function updateBatch(array $sets, $index = null, $batchSize = 1000, $escape = null) |
||||
2793 | { |
||||
2794 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2795 | |||||
2796 | $this->setUpdateBatch($sets, $index); |
||||
2797 | |||||
2798 | $affectedRows = 0; |
||||
2799 | for ($i = 0, $total = count($this->builderCache->sets); $i < $total; $i += $batchSize) { |
||||
2800 | $sql = $this->platformUpdateBatchStatement( |
||||
2801 | $this->builderCache->from[ 0 ], |
||||
2802 | array_slice($this->builderCache->sets, $i, $batchSize), |
||||
2803 | $this->conn->protectIdentifiers($index, false, $escape, false) |
||||
2804 | ); |
||||
2805 | |||||
2806 | if ($this->testMode) { |
||||
2807 | ++$affectedRows; |
||||
2808 | } else { |
||||
2809 | $this->conn->query($sql, $this->builderCache->binds); |
||||
2810 | $affectedRows += $this->conn->getAffectedRows(); |
||||
2811 | } |
||||
2812 | |||||
2813 | $this->builderCache[ 'where' ] = []; |
||||
2814 | } |
||||
2815 | |||||
2816 | if ( ! $this->testMode) { |
||||
2817 | $this->builderCache->resetModifier(); |
||||
2818 | } |
||||
2819 | |||||
2820 | return $affectedRows; |
||||
0 ignored issues
–
show
|
|||||
2821 | } |
||||
2822 | |||||
2823 | //-------------------------------------------------------------------- |
||||
2824 | |||||
2825 | /** |
||||
2826 | * AbstractQueryBuilder::setUpdateBatch |
||||
2827 | * |
||||
2828 | * The "setUpdateBatch" function. Allows key/value pairs to be set for batch updating |
||||
2829 | * |
||||
2830 | * @param mixed $sets |
||||
2831 | * @param string $index |
||||
2832 | * @param bool $escape |
||||
2833 | * |
||||
2834 | * @return static |
||||
2835 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2836 | */ |
||||
2837 | protected function setUpdateBatch(array $sets, $index = '', $escape = null) |
||||
2838 | { |
||||
2839 | $sets = $this->batchObjectToArray($sets); |
||||
2840 | |||||
2841 | if ( ! is_array($sets)) { |
||||
0 ignored issues
–
show
|
|||||
2842 | // @todo error |
||||
2843 | } |
||||
2844 | |||||
2845 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2846 | |||||
2847 | foreach ($sets as $set) { |
||||
2848 | $indexSet = false; |
||||
2849 | $row = []; |
||||
2850 | foreach ($set as $key => $value) { |
||||
2851 | if ($key === $index) { |
||||
2852 | $indexSet = true; |
||||
2853 | } |
||||
2854 | |||||
2855 | $bind = $this->bind($key, $value); |
||||
2856 | |||||
2857 | $row[ $this->conn->protectIdentifiers($key, false, $escape) ] = ':' . $bind; |
||||
2858 | } |
||||
2859 | |||||
2860 | if ($indexSet === false) { |
||||
2861 | // 'One or more rows submitted for batch updating is missing the specified index.' |
||||
2862 | throw new RuntimeException('E_DATABASE_BATCH_UPDATE_MISSING_INDEX'); |
||||
2863 | } |
||||
2864 | |||||
2865 | $this->builderCache->sets[] = $row; |
||||
2866 | } |
||||
2867 | |||||
2868 | return $this; |
||||
2869 | } |
||||
2870 | |||||
2871 | /** |
||||
2872 | * AbstractQueryBuilder::platformUpdateBatchStatement |
||||
2873 | * |
||||
2874 | * Generates a platform-specific batch update string from the supplied data. |
||||
2875 | * |
||||
2876 | * @param string $table Table name |
||||
2877 | * @param array $values Update data |
||||
2878 | * @param string $index WHERE key |
||||
2879 | * |
||||
2880 | * @return string |
||||
2881 | */ |
||||
2882 | abstract protected function platformUpdateBatchStatement($table, $values, $index); |
||||
2883 | |||||
2884 | //-------------------------------------------------------------------- |
||||
2885 | |||||
2886 | /** |
||||
2887 | * AbstractQueryBuilder::delete |
||||
2888 | * |
||||
2889 | * Compiles a delete string and runs the query |
||||
2890 | * |
||||
2891 | * @param array $where Where clause. |
||||
2892 | * @param int $limit Limit clause. |
||||
2893 | * |
||||
2894 | * @return string |
||||
2895 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2896 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2897 | */ |
||||
2898 | public function delete($where = [], $limit = null) |
||||
2899 | { |
||||
2900 | $this->where($where); |
||||
2901 | |||||
2902 | if (isset($limit)) { |
||||
2903 | $this->limit($limit); |
||||
2904 | } |
||||
2905 | |||||
2906 | $sqlStatement = $this->platformDeleteStatement( |
||||
2907 | $this->conn->protectIdentifiers( |
||||
2908 | $this->builderCache->from[ 0 ], |
||||
2909 | true, |
||||
2910 | $this->conn->protectIdentifiers, |
||||
2911 | false |
||||
2912 | ) |
||||
2913 | ); |
||||
2914 | |||||
2915 | if ($this->testMode) { |
||||
2916 | return $sqlStatement; |
||||
2917 | } |
||||
2918 | |||||
2919 | $sqlBinds = $this->builderCache->binds; |
||||
2920 | $this->builderCache->resetModifier(); |
||||
2921 | |||||
2922 | return $this->conn->query($sqlStatement, $sqlBinds); |
||||
0 ignored issues
–
show
|
|||||
2923 | } |
||||
2924 | |||||
2925 | //-------------------------------------------------------------------- |
||||
2926 | |||||
2927 | /** |
||||
2928 | * AbstractQueryBuilder::platformDeleteStatement |
||||
2929 | * |
||||
2930 | * Generates a platform-specific delete string from the supplied data |
||||
2931 | * |
||||
2932 | * @param string $table The table name. |
||||
2933 | * |
||||
2934 | * @return string |
||||
2935 | */ |
||||
2936 | abstract protected function platformDeleteStatement($table); |
||||
2937 | |||||
2938 | //-------------------------------------------------------------------- |
||||
2939 | |||||
2940 | /** |
||||
2941 | * AbstractQueryBuilder::flush |
||||
2942 | * |
||||
2943 | * Compiles a truncate string and runs the query |
||||
2944 | * If the database does not support the truncate() command |
||||
2945 | * This function maps to "DELETE FROM table" |
||||
2946 | * |
||||
2947 | * @param bool $escape Whether to table identifiers |
||||
2948 | * |
||||
2949 | * @return bool TRUE on success, FALSE on failure |
||||
2950 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2951 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2952 | */ |
||||
2953 | public function flush($table, $escape = null) |
||||
2954 | { |
||||
2955 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2956 | |||||
2957 | if ($escape) { |
||||
2958 | $table = $this->conn->protectIdentifiers($table, true, true); |
||||
2959 | } |
||||
2960 | |||||
2961 | $sqlStatement = $this->platformDeleteStatement($table); |
||||
2962 | |||||
2963 | if ($this->testMode === true) { |
||||
2964 | return $sqlStatement; |
||||
0 ignored issues
–
show
|
|||||
2965 | } |
||||
2966 | |||||
2967 | $this->builderCache->resetModifier(); |
||||
2968 | |||||
2969 | return $this->conn->query($sqlStatement); |
||||
0 ignored issues
–
show
|
|||||
2970 | } |
||||
2971 | |||||
2972 | //-------------------------------------------------------------------- |
||||
2973 | |||||
2974 | /** |
||||
2975 | * AbstractQueryBuilder::truncate |
||||
2976 | * |
||||
2977 | * Compiles a truncate string and runs the query |
||||
2978 | * If the database does not support the truncate() command |
||||
2979 | * This function maps to "DELETE FROM table" |
||||
2980 | * |
||||
2981 | * @param bool $escape Whether to table identifiers |
||||
2982 | * |
||||
2983 | * @return bool TRUE on success, FALSE on failure |
||||
2984 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||
2985 | * @throws \Psr\Cache\InvalidArgumentException |
||||
2986 | */ |
||||
2987 | public function truncate($table, $escape = null) |
||||
2988 | { |
||||
2989 | is_bool($escape) || $escape = $this->conn->protectIdentifiers; |
||||
2990 | |||||
2991 | if ($escape) { |
||||
2992 | $table = $this->conn->protectIdentifiers($table, true, true); |
||||
2993 | } |
||||
2994 | |||||
2995 | $sqlStatement = $this->platformTruncateStatement($table); |
||||
2996 | |||||
2997 | if ($this->testMode === true) { |
||||
2998 | return $sqlStatement; |
||||
0 ignored issues
–
show
|
|||||
2999 | } |
||||
3000 | |||||
3001 | $this->builderCache->resetModifier(); |
||||
3002 | |||||
3003 | return $this->conn->query($sqlStatement); |
||||
0 ignored issues
–
show
|
|||||
3004 | } |
||||
3005 | |||||
3006 | //-------------------------------------------------------------------- |
||||
3007 | |||||
3008 | /** |
||||
3009 | * AbstractQueryBuilder::platformTruncateStatement |
||||
3010 | * |
||||
3011 | * Generates a platform-specific truncate statement. |
||||
3012 | * |
||||
3013 | * @param string the table name |
||||
0 ignored issues
–
show
The type
O2System\Database\Sql\Abstracts\the was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
3014 | * |
||||
3015 | * @return string |
||||
3016 | */ |
||||
3017 | abstract protected function platformTruncateStatement($table); |
||||
3018 | |||||
3019 | //-------------------------------------------------------------------- |
||||
3020 | |||||
3021 | /** |
||||
3022 | * AbstractQueryBuilder::binds |
||||
3023 | * |
||||
3024 | * @param array $binds |
||||
3025 | * |
||||
3026 | * @return static |
||||
3027 | */ |
||||
3028 | public function binds(array $binds) |
||||
3029 | { |
||||
3030 | foreach ($binds as $field => $value) { |
||||
3031 | $this->bind($field, $value); |
||||
3032 | } |
||||
3033 | |||||
3034 | return $this; |
||||
3035 | } |
||||
3036 | |||||
3037 | //-------------------------------------------------------------------- |
||||
3038 | |||||
3039 | /** |
||||
3040 | * AbstractQueryBuilder::subQuery |
||||
3041 | * |
||||
3042 | * Performs Query Builder sub query mode. |
||||
3043 | * |
||||
3044 | * @return \O2System\Database\Sql\Abstracts\AbstractQueryBuilder |
||||
3045 | */ |
||||
3046 | public function subQuery() |
||||
3047 | { |
||||
3048 | $subQuery = clone $this; |
||||
3049 | $subQuery->builderCache = new Query\BuilderCache(); |
||||
0 ignored issues
–
show
It seems like
new O2System\Database\Sq...es\Query\BuilderCache() of type O2System\Database\Sql\Da...ures\Query\BuilderCache is incompatible with the declared type O2System\Database\Sql\Abstracts\QueryBuilderCache of property $builderCache .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||||
3050 | |||||
3051 | $subQuery->isSubQuery = true; |
||||
3052 | |||||
3053 | return $subQuery; |
||||
3054 | } |
||||
3055 | |||||
3056 | // ------------------------------------------------------------------------ |
||||
3057 | |||||
3058 | /** |
||||
3059 | * AbstractQueryBuilder::compileSelectStatement |
||||
3060 | * |
||||
3061 | * Compile the SELECT statement |
||||
3062 | * |
||||
3063 | * Generates a query string based on which functions were used. |
||||
3064 | * Should not be called directly. |
||||
3065 | * |
||||
3066 | * @param bool $selectOverride |
||||
3067 | * |
||||
3068 | * @return string |
||||
3069 | */ |
||||
3070 | protected function compileSelectStatement($selectOverride = false) |
||||
3071 | { |
||||
3072 | // Write the "select" portion of the query |
||||
3073 | if ($selectOverride !== false) { |
||||
3074 | $sqlStatement = $selectOverride; |
||||
3075 | } else { |
||||
3076 | $sqlStatement = ( ! $this->builderCache->distinct) |
||||
3077 | ? 'SELECT %s' |
||||
3078 | : 'SELECT DISTINCT %s'; |
||||
3079 | |||||
3080 | if (count($this->builderCache->select) === 0) { |
||||
3081 | $SqlSelectStatement = "*"; |
||||
3082 | } else { |
||||
3083 | // Cycle through the "select" portion of the query and prep each column name. |
||||
3084 | // The reason we protect identifiers here rather than in the select() function |
||||
3085 | // is because until the user calls the from() function we don't know if there are aliases |
||||
3086 | foreach ($this->builderCache->select as $selectKey => $selectField) { |
||||
3087 | $noEscape = isset($this->builderCache->noEscape [ $selectKey ]) |
||||
3088 | ? $this->builderCache->noEscape [ $selectKey ] |
||||
3089 | : null; |
||||
3090 | $this->builderCache->select [ $selectKey ] = $this->conn->protectIdentifiers( |
||||
3091 | $selectField, |
||||
3092 | false, |
||||
3093 | $noEscape |
||||
3094 | ); |
||||
3095 | } |
||||
3096 | |||||
3097 | $SqlSelectStatement = implode(", \n\t", $this->builderCache->select); |
||||
3098 | } |
||||
3099 | |||||
3100 | $sqlStatement = sprintf($sqlStatement, $SqlSelectStatement); |
||||
3101 | } |
||||
3102 | |||||
3103 | return trim($sqlStatement); |
||||
0 ignored issues
–
show
It seems like
$sqlStatement can also be of type true ; however, parameter $str of trim() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
3104 | } |
||||
3105 | |||||
3106 | //-------------------------------------------------------------------- |
||||
3107 | |||||
3108 | /** |
||||
3109 | * AbstractQueryBuilder::compileIntoStatement |
||||
3110 | * |
||||
3111 | * @return string |
||||
3112 | */ |
||||
3113 | protected function compileUnionStatement() |
||||
3114 | { |
||||
3115 | $sqlStatement = ''; |
||||
3116 | |||||
3117 | if (count($this->builderCache->union)) { |
||||
3118 | foreach ($this->builderCache->union as $union) { |
||||
3119 | $sqlStatement .= "\n UNION \n" . $union; |
||||
3120 | } |
||||
3121 | } |
||||
3122 | |||||
3123 | if (count($this->builderCache->unionAll)) { |
||||
3124 | foreach ($this->builderCache->unionAll as $union) { |
||||
3125 | $sqlStatement .= "\n UNION ALL \n" . $union; |
||||
3126 | } |
||||
3127 | } |
||||
3128 | |||||
3129 | return trim($sqlStatement); |
||||
3130 | } |
||||
3131 | |||||
3132 | //-------------------------------------------------------------------- |
||||
3133 | |||||
3134 | /** |
||||
3135 | * AbstractQueryBuilder::compileIntoStatement |
||||
3136 | * |
||||
3137 | * @return string |
||||
3138 | */ |
||||
3139 | protected function compileIntoStatement() |
||||
3140 | { |
||||
3141 | return "\n" . $this->builderCache->into; |
||||
3142 | } |
||||
3143 | |||||
3144 | //-------------------------------------------------------------------- |
||||
3145 | |||||
3146 | /** |
||||
3147 | * AbstractQueryBuilder::compileFromStatement |
||||
3148 | * |
||||
3149 | * @return string |
||||
3150 | */ |
||||
3151 | protected function compileFromStatement() |
||||
3152 | { |
||||
3153 | if (count($this->builderCache->from) > 0) { |
||||
3154 | return "\n" . sprintf( |
||||
3155 | 'FROM %s', |
||||
3156 | implode(',', array_unique($this->builderCache->from)) |
||||
3157 | ); |
||||
3158 | } |
||||
3159 | } |
||||
3160 | |||||
3161 | //-------------------------------------------------------------------- |
||||
3162 | |||||
3163 | /** |
||||
3164 | * AbstractQueryBuilder::compileJoinStatement |
||||
3165 | * |
||||
3166 | * @return string |
||||
3167 | */ |
||||
3168 | protected function compileJoinStatement() |
||||
3169 | { |
||||
3170 | if (count($this->builderCache->join) > 0) { |
||||
3171 | return "\n" . implode("\n", $this->builderCache->join); |
||||
3172 | } |
||||
3173 | } |
||||
3174 | |||||
3175 | //-------------------------------------------------------------------- |
||||
3176 | |||||
3177 | /** |
||||
3178 | * AbstractQueryBuilder::compileWhereStatement |
||||
3179 | * |
||||
3180 | * @return string |
||||
3181 | */ |
||||
3182 | protected function compileWhereStatement() |
||||
3183 | { |
||||
3184 | return $this->compileWhereHavingStatement('where'); |
||||
3185 | } |
||||
3186 | |||||
3187 | //-------------------------------------------------------------------- |
||||
3188 | |||||
3189 | /** |
||||
3190 | * AbstractQueryBuilder::compileWhereHavingStatement |
||||
3191 | * |
||||
3192 | * Compile WHERE, HAVING statements |
||||
3193 | * |
||||
3194 | * Escapes identifiers in WHERE and HAVING statements at execution time. |
||||
3195 | * |
||||
3196 | * Required so that aliases are tracked properly, regardless of whether |
||||
3197 | * where(), orWhere(), having(), orHaving are called prior to from(), |
||||
3198 | * join() and prefixTable is added only if needed. |
||||
3199 | * |
||||
3200 | * @param string $cacheKey 'QBWhere' or 'QBHaving' |
||||
3201 | * |
||||
3202 | * @return string Sql statement |
||||
3203 | */ |
||||
3204 | protected function compileWhereHavingStatement($cacheKey) |
||||
3205 | { |
||||
3206 | if (count($this->builderCache->{$cacheKey}) > 0) { |
||||
3207 | for ($i = 0, $c = count($this->builderCache->{$cacheKey}); $i < $c; $i++) { |
||||
3208 | // Is this condition already compiled? |
||||
3209 | if (is_string($this->builderCache->{$cacheKey}[ $i ])) { |
||||
3210 | continue; |
||||
3211 | } elseif ($this->builderCache->{$cacheKey}[ $i ][ 'escape' ] === false) { |
||||
3212 | $this->builderCache->{$cacheKey}[ $i ] |
||||
3213 | = $this->builderCache->{$cacheKey}[ $i ][ 'condition' ]; |
||||
3214 | continue; |
||||
3215 | } |
||||
3216 | |||||
3217 | // Split multiple conditions |
||||
3218 | $conditions = preg_split( |
||||
3219 | '/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i', |
||||
3220 | $this->builderCache->{$cacheKey}[ $i ][ 'condition' ], |
||||
3221 | -1, |
||||
3222 | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY |
||||
3223 | ); |
||||
3224 | |||||
3225 | for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++) { |
||||
0 ignored issues
–
show
It seems like
$conditions can also be of type false ; however, parameter $var of count() does only seem to accept Countable|array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
3226 | if (($op = $this->getOperator($conditions[ $ci ])) === false |
||||
3227 | OR |
||||
3228 | ! preg_match( |
||||
3229 | '/^(\(?)(.*)(' . preg_quote($op, '/') . ')\s*(.*(?<!\)))?(\)?)$/i', |
||||
3230 | $conditions[ $ci ], |
||||
3231 | $matches |
||||
3232 | ) |
||||
3233 | ) { |
||||
3234 | continue; |
||||
3235 | } |
||||
3236 | |||||
3237 | // $matches = array( |
||||
3238 | // 0 => '(test <= foo)', /* the whole thing */ |
||||
3239 | // 1 => '(', /* optional */ |
||||
3240 | // 2 => 'test', /* the field name */ |
||||
3241 | // 3 => ' <= ', /* $op */ |
||||
3242 | // 4 => 'foo', /* optional, if $op is e.g. 'IS NULL' */ |
||||
3243 | // 5 => ')' /* optional */ |
||||
3244 | // ); |
||||
3245 | |||||
3246 | if ( ! empty($matches[ 4 ])) { |
||||
3247 | //$this->isLiteral($matches[4]) OR $matches[4] = $this->protectIdentifiers(trim($matches[4])); |
||||
3248 | $matches[ 4 ] = ' ' . $matches[ 4 ]; |
||||
3249 | } |
||||
3250 | |||||
3251 | $conditions[ $ci ] = $matches[ 1 ] . $this->conn->protectIdentifiers(trim($matches[ 2 ])) |
||||
3252 | . ' ' . trim($matches[ 3 ]) . $matches[ 4 ] . $matches[ 5 ]; |
||||
3253 | } |
||||
3254 | |||||
3255 | $this->builderCache->{$cacheKey}[ $i ] = implode('', $conditions); |
||||
0 ignored issues
–
show
It seems like
$conditions can also be of type false ; however, parameter $pieces of implode() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
3256 | } |
||||
3257 | |||||
3258 | if ($cacheKey === 'having') { |
||||
3259 | return "\n" . sprintf( |
||||
3260 | 'HAVING %s', |
||||
3261 | implode("\n", $this->builderCache->having) |
||||
3262 | ); |
||||
3263 | } |
||||
3264 | |||||
3265 | return "\n" . sprintf( |
||||
3266 | 'WHERE %s', |
||||
3267 | implode("\n", $this->builderCache->{$cacheKey}) |
||||
3268 | ); |
||||
3269 | } |
||||
3270 | |||||
3271 | return ''; |
||||
3272 | } |
||||
3273 | |||||
3274 | //-------------------------------------------------------------------- |
||||
3275 | |||||
3276 | /** |
||||
3277 | * AbstractQueryBuilder::compileGroupByStatement |
||||
3278 | * |
||||
3279 | * Compile GROUP BY |
||||
3280 | * |
||||
3281 | * Escapes identifiers in GROUP BY statements at execution time. |
||||
3282 | * |
||||
3283 | * Required so that aliases are tracked properly, regardless of wether |
||||
3284 | * groupBy() is called prior to from(), join() and prefixTable is added |
||||
3285 | * only if needed. |
||||
3286 | * |
||||
3287 | * @return string Sql statement |
||||
3288 | */ |
||||
3289 | protected function compileGroupByStatement() |
||||
3290 | { |
||||
3291 | if (count($this->builderCache->groupBy) > 0) { |
||||
3292 | for ($i = 0, $c = count($this->builderCache->groupBy); $i < $c; $i++) { |
||||
3293 | // Is it already compiled? |
||||
3294 | if (is_string($this->builderCache->groupBy[ $i ])) { |
||||
3295 | continue; |
||||
3296 | } |
||||
3297 | |||||
3298 | $this->builderCache->groupBy[ $i ] = ($this->builderCache->groupBy[ $i ][ 'escape' ] |
||||
3299 | === false OR |
||||
3300 | $this->isLiteral( |
||||
3301 | $this->builderCache->groupBy[ $i ][ 'field' ] |
||||
3302 | )) |
||||
3303 | ? $this->builderCache->groupBy[ $i ][ 'field' ] |
||||
3304 | : $this->conn->protectIdentifiers($this->builderCache->groupBy[ $i ][ 'field' ]); |
||||
3305 | } |
||||
3306 | |||||
3307 | return "\n" . sprintf( |
||||
3308 | 'GROUP BY %s', |
||||
3309 | implode(', ', $this->builderCache->groupBy) |
||||
3310 | ); |
||||
3311 | } |
||||
3312 | |||||
3313 | return ''; |
||||
3314 | } |
||||
3315 | |||||
3316 | //-------------------------------------------------------------------- |
||||
3317 | |||||
3318 | /** |
||||
3319 | * AbstractQueryBuilder::isLiteral |
||||
3320 | * |
||||
3321 | * Determines if a string represents a literal value or a field name |
||||
3322 | * |
||||
3323 | * @param string $string |
||||
3324 | * |
||||
3325 | * @return bool |
||||
3326 | */ |
||||
3327 | protected function isLiteral($string) |
||||
3328 | { |
||||
3329 | $string = trim($string); |
||||
3330 | |||||
3331 | if (empty($string) || ctype_digit($string) || (string)(float)$string === $string |
||||
3332 | || in_array( |
||||
3333 | strtoupper($string), |
||||
3334 | ['TRUE', 'FALSE'], |
||||
3335 | true |
||||
3336 | ) |
||||
3337 | ) { |
||||
3338 | return true; |
||||
3339 | } |
||||
3340 | |||||
3341 | static $stringArray; |
||||
3342 | |||||
3343 | if (empty($stringArray)) { |
||||
3344 | $stringArray = ($this->conn->getConfig('escapeCharacter') !== '"') |
||||
3345 | ? ['"', "'"] |
||||
3346 | : ["'"]; |
||||
3347 | } |
||||
3348 | |||||
3349 | return in_array($string[ 0 ], $stringArray, true); |
||||
3350 | } |
||||
3351 | |||||
3352 | //-------------------------------------------------------------------- |
||||
3353 | |||||
3354 | /** |
||||
3355 | * AbstractQueryBuilder::compileHavingStatement |
||||
3356 | * |
||||
3357 | * @return string |
||||
3358 | */ |
||||
3359 | protected function compileHavingStatement() |
||||
3360 | { |
||||
3361 | return $this->compileWhereHavingStatement('having'); |
||||
3362 | } |
||||
3363 | |||||
3364 | //-------------------------------------------------------------------- |
||||
3365 | |||||
3366 | /** |
||||
3367 | * AbstractQueryBuilder::compileHavingStatement |
||||
3368 | * |
||||
3369 | * @return string |
||||
3370 | */ |
||||
3371 | protected function compileBetweenStatement() |
||||
3372 | { |
||||
3373 | return $this->compileWhereHavingStatement('between'); |
||||
3374 | } |
||||
3375 | |||||
3376 | //-------------------------------------------------------------------- |
||||
3377 | |||||
3378 | /** |
||||
3379 | * AbstractQueryBuilder::compileHavingStatement |
||||
3380 | * |
||||
3381 | * @return string |
||||
3382 | */ |
||||
3383 | protected function compileNotBetweenStatement() |
||||
3384 | { |
||||
3385 | return $this->compileWhereHavingStatement('notBetween'); |
||||
3386 | } |
||||
3387 | |||||
3388 | //-------------------------------------------------------------------- |
||||
3389 | |||||
3390 | /** |
||||
3391 | * AbstractQueryBuilder::compileOrderByStatement |
||||
3392 | * |
||||
3393 | * Compile ORDER BY |
||||
3394 | * |
||||
3395 | * Escapes identifiers in ORDER BY statements at execution time. |
||||
3396 | * |
||||
3397 | * Required so that aliases are tracked properly, regardless of wether |
||||
3398 | * orderBy() is called prior to from(), join() and prefixTable is added |
||||
3399 | * only if needed. |
||||
3400 | * |
||||
3401 | * @return string Sql statement |
||||
3402 | */ |
||||
3403 | protected function compileOrderByStatement() |
||||
3404 | { |
||||
3405 | if (is_array($this->builderCache->orderBy) && count($this->builderCache->orderBy) > 0) { |
||||
3406 | for ($i = 0, $c = count($this->builderCache->orderBy); $i < $c; $i++) { |
||||
3407 | if ($this->builderCache->orderBy[ $i ][ 'escape' ] !== false |
||||
3408 | && ! $this->isLiteral( |
||||
3409 | $this->builderCache->orderBy[ $i ][ 'field' ] |
||||
3410 | ) |
||||
3411 | ) { |
||||
3412 | $this->builderCache->orderBy[ $i ][ 'field' ] = $this->conn->protectIdentifiers( |
||||
3413 | $this->builderCache->orderBy[ $i ][ 'field' ] |
||||
3414 | ); |
||||
3415 | } |
||||
3416 | |||||
3417 | $this->builderCache->orderBy[ $i ] = $this->builderCache->orderBy[ $i ][ 'field' ] |
||||
3418 | . $this->builderCache->orderBy[ $i ][ 'direction' ]; |
||||
3419 | } |
||||
3420 | |||||
3421 | return $this->builderCache->orderBy = "\n" . sprintf( |
||||
3422 | 'ORDER BY %s', |
||||
3423 | implode(', ', $this->builderCache->orderBy) |
||||
3424 | ); |
||||
3425 | } elseif (is_string($this->builderCache->orderBy)) { |
||||
3426 | return $this->builderCache->orderBy; |
||||
3427 | } |
||||
3428 | |||||
3429 | return ''; |
||||
3430 | } |
||||
3431 | |||||
3432 | //-------------------------------------------------------------------- |
||||
3433 | |||||
3434 | /** |
||||
3435 | * AbstractQueryBuilder::compileLimitStatement |
||||
3436 | * |
||||
3437 | * @return string |
||||
3438 | */ |
||||
3439 | protected function compileLimitStatement() |
||||
3440 | { |
||||
3441 | if ($this->builderCache->limit) { |
||||
3442 | if ($this->builderCache->offset) { |
||||
3443 | return sprintf( |
||||
3444 | 'LIMIT %s OFFSET %s', |
||||
3445 | $this->builderCache->limit, |
||||
3446 | $this->builderCache->offset |
||||
3447 | ); |
||||
3448 | } |
||||
3449 | |||||
3450 | return "\n" . sprintf( |
||||
3451 | 'LIMIT %s', |
||||
3452 | $this->builderCache->limit |
||||
3453 | ); |
||||
3454 | } |
||||
3455 | } |
||||
3456 | } |
||||
3457 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths