This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Childish\query; |
||
3 | |||
4 | use Childish\support\Tools; |
||
5 | use Childish\support\Collection; |
||
6 | |||
7 | /** |
||
8 | * Grammar |
||
9 | * |
||
10 | * @author Pu ShaoWei <[email protected]> |
||
11 | * @date 2017/12/7 |
||
12 | * @package Childish |
||
13 | * @version 1.0 |
||
14 | */ |
||
15 | abstract class Grammar |
||
16 | { |
||
17 | /** |
||
18 | * The grammar table prefix. |
||
19 | * |
||
20 | * @var string |
||
21 | */ |
||
22 | protected $tablePrefix = ''; |
||
23 | |||
24 | /** |
||
25 | * The components that make up a select clause. |
||
26 | * |
||
27 | * @var array |
||
28 | */ |
||
29 | protected $selectComponents = [ |
||
30 | 'aggregate', |
||
31 | 'columns', |
||
32 | 'from', |
||
33 | 'joins', |
||
34 | 'wheres', |
||
35 | 'groups', |
||
36 | 'havings', |
||
37 | 'orders', |
||
38 | 'limit', |
||
39 | 'offset', |
||
40 | 'unions', |
||
41 | 'lock', |
||
42 | ]; |
||
43 | |||
44 | /** |
||
45 | * Wrap an array of values. |
||
46 | * |
||
47 | * @param array $values |
||
48 | * @return array |
||
49 | */ |
||
50 | public function wrapArray(array $values) |
||
51 | { |
||
52 | return array_map([$this, 'wrap'], $values); |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * Wrap a table in keyword identifiers. |
||
57 | * |
||
58 | * @param string $table |
||
59 | * @return string |
||
60 | */ |
||
61 | public function wrapTable($table) |
||
62 | { |
||
63 | return $this->wrap($this->tablePrefix . $table, true); |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * Wrap a value in keyword identifiers. |
||
68 | * |
||
69 | * @param string $value |
||
70 | * @param bool $prefixAlias |
||
71 | * @return string |
||
72 | */ |
||
73 | public function wrap($value, $prefixAlias = false) |
||
74 | { |
||
75 | // If the value being wrapped has a column alias we will need to separate out |
||
76 | // the pieces so we can wrap each of the segments of the expression on it |
||
77 | // own, and then joins them both back together with the "as" connector. |
||
78 | if (strpos(strtolower($value), ' as ') !== false) { |
||
79 | return $this->wrapAliasedValue($value, $prefixAlias); |
||
80 | } |
||
81 | |||
82 | return $this->wrapSegments(explode('.', $value)); |
||
83 | } |
||
84 | |||
85 | /** |
||
86 | * Wrap a value that has an alias. |
||
87 | * |
||
88 | * @param string $value |
||
89 | * @param bool $prefixAlias |
||
90 | * @return string |
||
91 | */ |
||
92 | protected function wrapAliasedValue($value, $prefixAlias = false) |
||
93 | { |
||
94 | $segments = preg_split('/\s+as\s+/i', $value); |
||
95 | |||
96 | // If we are wrapping a table we need to prefix the alias with the table prefix |
||
97 | // as well in order to generate proper syntax. If this is a column of course |
||
98 | // no prefix is necessary. The condition will be true when from wrapTable. |
||
99 | if ($prefixAlias) { |
||
100 | $segments[1] = $this->tablePrefix . $segments[1]; |
||
101 | } |
||
102 | |||
103 | return $this->wrap( |
||
104 | $segments[0]) . ' as ' . $this->wrapValue($segments[1] |
||
105 | ); |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Wrap the given value segments. |
||
110 | * |
||
111 | * @param array $segments |
||
112 | * @return string |
||
113 | */ |
||
114 | protected function wrapSegments($segments) |
||
115 | { |
||
116 | return (new Collection($segments))->map(function ($segment, $key) use ($segments) { |
||
117 | return $key == 0 && count($segments) > 1 |
||
118 | ? $this->wrapTable($segment) |
||
119 | : $this->wrapValue($segment); |
||
120 | })->implode('.'); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Wrap a single string in keyword identifiers. |
||
125 | * |
||
126 | * @param string $value |
||
127 | * @return string |
||
128 | */ |
||
129 | protected function wrapValue($value) |
||
130 | { |
||
131 | if ($value !== '*') { |
||
132 | return '"' . str_replace('"', '""', $value) . '"'; |
||
133 | } |
||
134 | |||
135 | return $value; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Convert an array of column names into a delimited string. |
||
140 | * |
||
141 | * @param array $columns |
||
142 | * @return string |
||
143 | */ |
||
144 | public function columnize(array $columns) |
||
145 | { |
||
146 | return implode(', ', array_map([$this, 'wrap'], $columns)); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Create query parameter place-holders for an array. |
||
151 | * |
||
152 | * @param array $values |
||
153 | * @return string |
||
154 | */ |
||
155 | public function parameterize(array $values) |
||
156 | { |
||
157 | return implode(', ', array_fill(0, count($values), '?')); |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * Get the format for database stored dates. |
||
162 | * |
||
163 | * @return string |
||
164 | */ |
||
165 | public function getDateFormat() |
||
166 | { |
||
167 | return 'Y-m-d H:i:s'; |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Get the grammar's table prefix. |
||
172 | * |
||
173 | * @return string |
||
174 | */ |
||
175 | public function getTablePrefix() |
||
176 | { |
||
177 | return $this->tablePrefix; |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Set the grammar's table prefix. |
||
182 | * |
||
183 | * @param string $prefix |
||
184 | * @return $this |
||
185 | */ |
||
186 | public function setTablePrefix($prefix) |
||
187 | { |
||
188 | $this->tablePrefix = $prefix; |
||
189 | |||
190 | return $this; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * Compile a select query into SQL. |
||
195 | * |
||
196 | * @param \Childish\query\Builder $query |
||
197 | * @return string |
||
198 | */ |
||
199 | public function compileSelect(Builder $query) |
||
200 | { |
||
201 | // If the query does not have any columns set, we'll set the columns to the |
||
202 | // * character to just get all of the columns from the database. Then we |
||
203 | // can build the query and concatenate all the pieces together as one. |
||
204 | $original = $query->columns; |
||
205 | |||
206 | if (is_null($query->columns)) { |
||
207 | $query->columns = ['*']; |
||
208 | } |
||
209 | |||
210 | // To compile the query, we'll spin through each component of the query and |
||
211 | // see if that component exists. If it does we'll just call the compiler |
||
212 | // function for the component which is responsible for making the SQL. |
||
213 | $sql = trim($this->concatenate( |
||
214 | $this->compileComponents($query)) |
||
215 | ); |
||
216 | |||
217 | $query->columns = $original; |
||
218 | |||
219 | return $sql; |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Compile the components necessary for a select clause. |
||
224 | * |
||
225 | * @param \Childish\query\Builder $query |
||
226 | * @return array |
||
227 | */ |
||
228 | protected function compileComponents(Builder $query) |
||
229 | { |
||
230 | $sql = []; |
||
231 | |||
232 | foreach ($this->selectComponents as $component) { |
||
233 | // To compile the query, we'll spin through each component of the query and |
||
234 | // see if that component exists. If it does we'll just call the compiler |
||
235 | // function for the component which is responsible for making the SQL. |
||
236 | if (!is_null($query->$component)) { |
||
237 | $method = 'compile' . ucfirst($component); |
||
238 | |||
239 | $sql[$component] = $this->$method($query, $query->$component); |
||
240 | } |
||
241 | } |
||
242 | |||
243 | return $sql; |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Compile an aggregated select clause. |
||
248 | * |
||
249 | * @param \Childish\query\Builder $query |
||
250 | * @param array $aggregate |
||
251 | * @return string |
||
252 | */ |
||
253 | protected function compileAggregate(Builder $query, $aggregate) |
||
254 | { |
||
255 | $column = $this->columnize($aggregate['columns']); |
||
256 | |||
257 | // If the query has a "distinct" constraint and we're not asking for all columns |
||
258 | // we need to prepend "distinct" onto the column name so that the query takes |
||
259 | // it into account when it performs the aggregating operations on the data. |
||
260 | if ($query->distinct && $column !== '*') { |
||
261 | $column = 'distinct ' . $column; |
||
262 | } |
||
263 | |||
264 | return 'select ' . $aggregate['function'] . '(' . $column . ') as aggregate'; |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * Compile the "select *" portion of the query. |
||
269 | * |
||
270 | * @param \Childish\query\Builder $query |
||
271 | * @param array $columns |
||
272 | * @return string|null |
||
273 | */ |
||
274 | protected function compileColumns(Builder $query, $columns) |
||
275 | { |
||
276 | // If the query is actually performing an aggregating select, we will let that |
||
277 | // compiler handle the building of the select clauses, as it will need some |
||
278 | // more syntax that is best handled by that function to keep things neat. |
||
279 | if (!is_null($query->aggregate)) { |
||
280 | return; |
||
281 | } |
||
282 | |||
283 | $select = $query->distinct ? 'select distinct ' : 'select '; |
||
284 | |||
285 | return $select . $this->columnize($columns); |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Compile the "from" portion of the query. |
||
290 | * |
||
291 | * @param \Childish\query\Builder $query |
||
292 | * @param string $table |
||
293 | * @return string |
||
294 | */ |
||
295 | protected function compileFrom(Builder $query, $table) |
||
0 ignored issues
–
show
|
|||
296 | { |
||
297 | return 'from ' . $this->wrapTable($table); |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Compile the "join" portions of the query. |
||
302 | * |
||
303 | * @param \Childish\query\Builder $query |
||
304 | * @param array $joins |
||
305 | * @return string |
||
306 | */ |
||
307 | protected function compileJoins(Builder $query, $joins) |
||
308 | { |
||
309 | return collect($joins)->map(function ($join) use ($query) { |
||
310 | $table = $this->wrapTable($join->table); |
||
311 | |||
312 | return trim("{$join->type} join {$table} {$this->compileWheres($join)}"); |
||
313 | })->implode(' '); |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Compile the "where" portions of the query. |
||
318 | * |
||
319 | * @param \Childish\query\Builder $query |
||
320 | * @return string |
||
321 | */ |
||
322 | protected function compileWheres(Builder $query) |
||
323 | { |
||
324 | // Each type of where clauses has its own compiler function which is responsible |
||
325 | // for actually creating the where clauses SQL. This helps keep the code nice |
||
326 | // and maintainable since each clause has a very small method that it uses. |
||
327 | if (is_null($query->wheres)) { |
||
328 | return ''; |
||
329 | } |
||
330 | |||
331 | // If we actually have some where clauses, we will strip off the first boolean |
||
332 | // operator, which is added by the query builders for convenience so we can |
||
333 | // avoid checking for the first clauses in each of the compilers methods. |
||
334 | if (count($sql = $this->compileWheresToArray($query)) > 0) { |
||
335 | return $this->concatenateWhereClauses($query, $sql); |
||
336 | } |
||
337 | |||
338 | return ''; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Get an array of all the where clauses for the query. |
||
343 | * |
||
344 | * @param \Childish\query\Builder $query |
||
345 | * @return array |
||
346 | */ |
||
347 | protected function compileWheresToArray($query) |
||
348 | { |
||
349 | return (new Collection($query->wheres))->map(function ($where) use ($query) { |
||
350 | return $where['boolean'] . ' ' . $this->{"where{$where['type']}"}($query, $where); |
||
351 | })->all(); |
||
352 | } |
||
353 | |||
354 | /** |
||
355 | * Format the where clause statements into one string. |
||
356 | * |
||
357 | * @param \Childish\query\Builder $query |
||
358 | * @param array $sql |
||
359 | * @return string |
||
360 | */ |
||
361 | protected function concatenateWhereClauses($query, $sql) |
||
362 | { |
||
363 | $conjunction = $query instanceof JoinClause ? 'on' : 'where'; |
||
0 ignored issues
–
show
The class
Childish\query\JoinClause does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() |
|||
364 | |||
365 | return $conjunction . ' ' . $this->removeLeadingBoolean(implode(' ', $sql)); |
||
366 | } |
||
367 | |||
368 | /** |
||
369 | * Compile a raw where clause. |
||
370 | * |
||
371 | * @param \Childish\query\Builder $query |
||
372 | * @param array $where |
||
373 | * @return string |
||
374 | */ |
||
375 | protected function whereRaw(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
376 | { |
||
377 | return $where['sql']; |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * Compile a basic where clause. |
||
382 | * |
||
383 | * @param \Childish\query\Builder $query |
||
384 | * @param array $where |
||
385 | * @return string |
||
386 | */ |
||
387 | protected function whereBasic(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
388 | { |
||
389 | return $this->wrap($where['column']) . ' ' . $where['operator'] . ' ' . '?'; |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * Compile a "where in" clause. |
||
394 | * |
||
395 | * @param \Childish\query\Builder $query |
||
396 | * @param array $where |
||
397 | * @return string |
||
398 | */ |
||
399 | protected function whereIn(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
400 | { |
||
401 | if (!empty($where['values'])) { |
||
402 | return $this->wrap($where['column']) . ' in (' . $this->parameterize($where['values']) . ')'; |
||
403 | } |
||
404 | |||
405 | return '0 = 1'; |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Compile a "where not in" clause. |
||
410 | * |
||
411 | * @param \Childish\query\Builder $query |
||
412 | * @param array $where |
||
413 | * @return string |
||
414 | */ |
||
415 | protected function whereNotIn(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
416 | { |
||
417 | if (!empty($where['values'])) { |
||
418 | return $this->wrap($where['column']) . ' not in (' . $this->parameterize($where['values']) . ')'; |
||
419 | } |
||
420 | |||
421 | return '1 = 1'; |
||
422 | } |
||
423 | |||
424 | /** |
||
425 | * Compile a where in sub-select clause. |
||
426 | * |
||
427 | * @param \Childish\query\Builder $query |
||
428 | * @param array $where |
||
429 | * @return string |
||
430 | */ |
||
431 | protected function whereInSub(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
432 | { |
||
433 | return $this->wrap($where['column']) . ' in (' . $this->compileSelect($where['query']) . ')'; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Compile a where not in sub-select clause. |
||
438 | * |
||
439 | * @param \Childish\query\Builder $query |
||
440 | * @param array $where |
||
441 | * @return string |
||
442 | */ |
||
443 | protected function whereNotInSub(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
444 | { |
||
445 | return $this->wrap($where['column']) . ' not in (' . $this->compileSelect($where['query']) . ')'; |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Compile a "where null" clause. |
||
450 | * |
||
451 | * @param \Childish\query\Builder $query |
||
452 | * @param array $where |
||
453 | * @return string |
||
454 | */ |
||
455 | protected function whereNull(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
456 | { |
||
457 | return $this->wrap($where['column']) . ' is null'; |
||
458 | } |
||
459 | |||
460 | /** |
||
461 | * Compile a "where not null" clause. |
||
462 | * |
||
463 | * @param \Childish\query\Builder $query |
||
464 | * @param array $where |
||
465 | * @return string |
||
466 | */ |
||
467 | protected function whereNotNull(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
468 | { |
||
469 | return $this->wrap($where['column']) . ' is not null'; |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * Compile a "between" where clause. |
||
474 | * |
||
475 | * @param \Childish\query\Builder $query |
||
476 | * @param array $where |
||
477 | * @return string |
||
478 | */ |
||
479 | protected function whereBetween(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
480 | { |
||
481 | $between = $where['not'] ? 'not between' : 'between'; |
||
482 | |||
483 | return $this->wrap($where['column']) . ' ' . $between . ' ? and ?'; |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * Compile a "where date" clause. |
||
488 | * |
||
489 | * @param \Childish\query\Builder $query |
||
490 | * @param array $where |
||
491 | * @return string |
||
492 | */ |
||
493 | protected function whereDate(Builder $query, $where) |
||
494 | { |
||
495 | return $this->dateBasedWhere('date', $query, $where); |
||
496 | } |
||
497 | |||
498 | /** |
||
499 | * Compile a "where time" clause. |
||
500 | * |
||
501 | * @param \Childish\query\Builder $query |
||
502 | * @param array $where |
||
503 | * @return string |
||
504 | */ |
||
505 | protected function whereTime(Builder $query, $where) |
||
506 | { |
||
507 | return $this->dateBasedWhere('time', $query, $where); |
||
508 | } |
||
509 | |||
510 | /** |
||
511 | * Compile a "where day" clause. |
||
512 | * |
||
513 | * @param \Childish\query\Builder $query |
||
514 | * @param array $where |
||
515 | * @return string |
||
516 | */ |
||
517 | protected function whereDay(Builder $query, $where) |
||
518 | { |
||
519 | return $this->dateBasedWhere('day', $query, $where); |
||
520 | } |
||
521 | |||
522 | /** |
||
523 | * Compile a "where month" clause. |
||
524 | * |
||
525 | * @param \Childish\query\Builder $query |
||
526 | * @param array $where |
||
527 | * @return string |
||
528 | */ |
||
529 | protected function whereMonth(Builder $query, $where) |
||
530 | { |
||
531 | return $this->dateBasedWhere('month', $query, $where); |
||
532 | } |
||
533 | |||
534 | /** |
||
535 | * Compile a "where year" clause. |
||
536 | * |
||
537 | * @param \Childish\query\Builder $query |
||
538 | * @param array $where |
||
539 | * @return string |
||
540 | */ |
||
541 | protected function whereYear(Builder $query, $where) |
||
542 | { |
||
543 | return $this->dateBasedWhere('year', $query, $where); |
||
544 | } |
||
545 | |||
546 | /** |
||
547 | * Compile a date based where clause. |
||
548 | * |
||
549 | * @param string $type |
||
550 | * @param \Childish\query\Builder $query |
||
551 | * @param array $where |
||
552 | * @return string |
||
553 | */ |
||
554 | protected function dateBasedWhere($type, Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
555 | { |
||
556 | return $type . '(' . $this->wrap($where['column']) . ') ' . $where['operator'] . ' ' . '?'; |
||
557 | } |
||
558 | |||
559 | /** |
||
560 | * Compile a where clause comparing two columns.. |
||
561 | * |
||
562 | * @param \Childish\query\Builder $query |
||
563 | * @param array $where |
||
564 | * @return string |
||
565 | */ |
||
566 | protected function whereColumn(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
567 | { |
||
568 | return $this->wrap($where['first']) . ' ' . $where['operator'] . ' ' . $this->wrap($where['second']); |
||
569 | } |
||
570 | |||
571 | /** |
||
572 | * Compile a nested where clause. |
||
573 | * |
||
574 | * @param \Childish\query\Builder $query |
||
575 | * @param array $where |
||
576 | * @return string |
||
577 | */ |
||
578 | protected function whereNested(Builder $query, $where) |
||
579 | { |
||
580 | // Here we will calculate what portion of the string we need to remove. If this |
||
581 | // is a join clause query, we need to remove the "on" portion of the SQL and |
||
582 | // if it is a normal query we need to take the leading "where" of queries. |
||
583 | $offset = $query instanceof JoinClause ? 3 : 6; |
||
0 ignored issues
–
show
The class
Childish\query\JoinClause does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() |
|||
584 | |||
585 | return '(' . substr($this->compileWheres($where['query']), $offset) . ')'; |
||
586 | } |
||
587 | |||
588 | /** |
||
589 | * Compile a where condition with a sub-select. |
||
590 | * |
||
591 | * @param \Childish\query\Builder $query |
||
592 | * @param array $where |
||
593 | * @return string |
||
594 | */ |
||
595 | protected function whereSub(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
596 | { |
||
597 | $select = $this->compileSelect($where['query']); |
||
598 | |||
599 | return $this->wrap($where['column']) . ' ' . $where['operator'] . " ($select)"; |
||
600 | } |
||
601 | |||
602 | /** |
||
603 | * Compile a where exists clause. |
||
604 | * |
||
605 | * @param \Childish\query\Builder $query |
||
606 | * @param array $where |
||
607 | * @return string |
||
608 | */ |
||
609 | protected function whereExists(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
610 | { |
||
611 | return 'exists (' . $this->compileSelect($where['query']) . ')'; |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * Compile a where exists clause. |
||
616 | * |
||
617 | * @param \Childish\query\Builder $query |
||
618 | * @param array $where |
||
619 | * @return string |
||
620 | */ |
||
621 | protected function whereNotExists(Builder $query, $where) |
||
0 ignored issues
–
show
|
|||
622 | { |
||
623 | return 'not exists (' . $this->compileSelect($where['query']) . ')'; |
||
624 | } |
||
625 | |||
626 | /** |
||
627 | * Compile the "group by" portions of the query. |
||
628 | * |
||
629 | * @param \Childish\query\Builder $query |
||
630 | * @param array $groups |
||
631 | * @return string |
||
632 | */ |
||
633 | protected function compileGroups(Builder $query, $groups) |
||
0 ignored issues
–
show
|
|||
634 | { |
||
635 | return 'group by ' . $this->columnize($groups); |
||
636 | } |
||
637 | |||
638 | /** |
||
639 | * Compile the "having" portions of the query. |
||
640 | * |
||
641 | * @param \Childish\query\Builder $query |
||
642 | * @param array $havings |
||
643 | * @return string |
||
644 | */ |
||
645 | protected function compileHavings(Builder $query, $havings) |
||
0 ignored issues
–
show
|
|||
646 | { |
||
647 | $sql = implode(' ', array_map([$this, 'compileHaving'], $havings)); |
||
648 | |||
649 | return 'having ' . $this->removeLeadingBoolean($sql); |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Compile a single having clause. |
||
654 | * |
||
655 | * @param array $having |
||
656 | * @return string |
||
657 | */ |
||
658 | protected function compileHaving(array $having) |
||
659 | { |
||
660 | // If the having clause is "raw", we can just return the clause straight away |
||
661 | // without doing any more processing on it. Otherwise, we will compile the |
||
662 | // clause into SQL based on the components that make it up from builder. |
||
663 | if ($having['type'] === 'Raw') { |
||
664 | return $having['boolean'] . ' ' . $having['sql']; |
||
665 | } |
||
666 | |||
667 | return $this->compileBasicHaving($having); |
||
668 | } |
||
669 | |||
670 | /** |
||
671 | * Compile a basic having clause. |
||
672 | * |
||
673 | * @param array $having |
||
674 | * @return string |
||
675 | */ |
||
676 | protected function compileBasicHaving($having) |
||
677 | { |
||
678 | $column = $this->wrap($having['column']); |
||
679 | |||
680 | |||
681 | return $having['boolean'] . ' ' . $column . ' ' . $having['operator'] . ' ' . '?'; |
||
682 | } |
||
683 | |||
684 | /** |
||
685 | * Compile the "order by" portions of the query. |
||
686 | * |
||
687 | * @param \Childish\query\Builder $query |
||
688 | * @param array $orders |
||
689 | * @return string |
||
690 | */ |
||
691 | protected function compileOrders(Builder $query, $orders) |
||
692 | { |
||
693 | if (!empty($orders)) { |
||
694 | return 'order by ' . implode(', ', $this->compileOrdersToArray($query, $orders)); |
||
695 | } |
||
696 | |||
697 | return ''; |
||
698 | } |
||
699 | |||
700 | /** |
||
701 | * Compile the query orders to an array. |
||
702 | * |
||
703 | * @param \Childish\query\Builder |
||
704 | * @param array $orders |
||
705 | * @return array |
||
706 | */ |
||
707 | protected function compileOrdersToArray(Builder $query, $orders) |
||
0 ignored issues
–
show
|
|||
708 | { |
||
709 | return array_map(function ($order) { |
||
710 | return !isset($order['sql']) |
||
711 | ? $this->wrap($order['column']) . ' ' . $order['direction'] |
||
712 | : $order['sql']; |
||
713 | }, $orders); |
||
714 | } |
||
715 | |||
716 | /** |
||
717 | * Compile the random statement into SQL. |
||
718 | * |
||
719 | * @param string $seed |
||
720 | * @return string |
||
721 | */ |
||
722 | public function compileRandom($seed) |
||
723 | { |
||
724 | return 'RANDOM()'; |
||
725 | } |
||
726 | |||
727 | /** |
||
728 | * Compile the "limit" portions of the query. |
||
729 | * |
||
730 | * @param \Childish\query\Builder $query |
||
731 | * @param int $limit |
||
732 | * @return string |
||
733 | */ |
||
734 | protected function compileLimit(Builder $query, $limit) |
||
0 ignored issues
–
show
|
|||
735 | { |
||
736 | return 'limit ' . (int)$limit; |
||
737 | } |
||
738 | |||
739 | /** |
||
740 | * Compile the "offset" portions of the query. |
||
741 | * |
||
742 | * @param \Childish\query\Builder $query |
||
743 | * @param int $offset |
||
744 | * @return string |
||
745 | */ |
||
746 | protected function compileOffset(Builder $query, $offset) |
||
0 ignored issues
–
show
|
|||
747 | { |
||
748 | return 'offset ' . (int)$offset; |
||
749 | } |
||
750 | |||
751 | /** |
||
752 | * Compile the "union" queries attached to the main query. |
||
753 | * |
||
754 | * @param \Childish\query\Builder $query |
||
755 | * @return string |
||
756 | */ |
||
757 | protected function compileUnions(Builder $query) |
||
758 | { |
||
759 | $sql = ''; |
||
760 | |||
761 | foreach ($query->unions as $union) { |
||
762 | $sql .= $this->compileUnion($union); |
||
763 | } |
||
764 | |||
765 | if (!empty($query->unionOrders)) { |
||
766 | $sql .= ' ' . $this->compileOrders($query, $query->unionOrders); |
||
767 | } |
||
768 | |||
769 | if (isset($query->unionLimit)) { |
||
770 | $sql .= ' ' . $this->compileLimit($query, $query->unionLimit); |
||
771 | } |
||
772 | |||
773 | if (isset($query->unionOffset)) { |
||
774 | $sql .= ' ' . $this->compileOffset($query, $query->unionOffset); |
||
775 | } |
||
776 | |||
777 | return ltrim($sql); |
||
778 | } |
||
779 | |||
780 | /** |
||
781 | * Compile a single union statement. |
||
782 | * |
||
783 | * @param array $union |
||
784 | * @return string |
||
785 | */ |
||
786 | protected function compileUnion(array $union) |
||
787 | { |
||
788 | $conjuction = $union['all'] ? ' union all ' : ' union '; |
||
789 | |||
790 | return $conjuction . $union['query']->toSql(); |
||
791 | } |
||
792 | |||
793 | /** |
||
794 | * Compile an exists statement into SQL. |
||
795 | * |
||
796 | * @param \Childish\query\Builder $query |
||
797 | * @return string |
||
798 | */ |
||
799 | public function compileExists(Builder $query) |
||
800 | { |
||
801 | $select = $this->compileSelect($query); |
||
802 | |||
803 | return "select exists({$select}) as {$this->wrap('exists')}"; |
||
804 | } |
||
805 | |||
806 | /** |
||
807 | * Compile an insert statement into SQL. |
||
808 | * |
||
809 | * @param \Childish\query\Builder $query |
||
810 | * @param array $values |
||
811 | * @return string |
||
812 | */ |
||
813 | public function compileInsert(Builder $query, array $values) |
||
814 | { |
||
815 | // Essentially we will force every insert to be treated as a batch insert which |
||
816 | // simply makes creating the SQL easier for us since we can utilize the same |
||
817 | // basic routine regardless of an amount of records given to us to insert. |
||
818 | $table = $this->wrapTable($query->from); |
||
819 | |||
820 | if (!is_array(reset($values))) { |
||
821 | $values = [$values]; |
||
822 | } |
||
823 | |||
824 | $columns = $this->columnize(array_keys(reset($values))); |
||
825 | |||
826 | // We need to build a list of parameter place-holders of values that are bound |
||
827 | // to the query. Each insert should have the exact same amount of parameter |
||
828 | // bindings so we will loop through the record and parameterize them all. |
||
829 | |||
830 | $parameters = (new Collection($values))->map(function ($record) { |
||
831 | return '(' . $this->parameterize($record) . ')'; |
||
832 | })->implode(', '); |
||
833 | |||
834 | return "insert into $table ($columns) values $parameters"; |
||
835 | } |
||
836 | |||
837 | /** |
||
838 | * Compile an insert and get ID statement into SQL. |
||
839 | * |
||
840 | * @param \Childish\query\Builder $query |
||
841 | * @param array $values |
||
842 | * @param string $sequence |
||
843 | * @return string |
||
844 | */ |
||
845 | public function compileInsertGetId(Builder $query, $values, $sequence) |
||
0 ignored issues
–
show
|
|||
846 | { |
||
847 | return $this->compileInsert($query, $values); |
||
848 | } |
||
849 | |||
850 | /** |
||
851 | * Compile an update statement into SQL. |
||
852 | * |
||
853 | * @param \Childish\query\Builder $query |
||
854 | * @param array $values |
||
855 | * @return string |
||
856 | */ |
||
857 | public function compileUpdate(Builder $query, $values) |
||
858 | { |
||
859 | $table = $this->wrapTable($query->from); |
||
860 | |||
861 | // Each one of the columns in the update statements needs to be wrapped in the |
||
862 | // keyword identifiers, also a place-holder needs to be created for each of |
||
863 | // the values in the list of bindings so we can make the sets statements. |
||
864 | $columns = (new Collection($values))->map(function ($value, $key) { |
||
865 | return $this->wrap($key) . ' = ?' ; |
||
866 | })->implode(', '); |
||
867 | |||
868 | // If the query has any "join" clauses, we will setup the joins on the builder |
||
869 | // and compile them so we can attach them to this update, as update queries |
||
870 | // can get join statements to attach to other tables when they're needed. |
||
871 | $joins = ''; |
||
872 | |||
873 | if (isset($query->joins)) { |
||
874 | $joins = ' ' . $this->compileJoins($query, $query->joins); |
||
875 | } |
||
876 | |||
877 | // Of course, update queries may also be constrained by where clauses so we'll |
||
878 | // need to compile the where clauses and attach it to the query so only the |
||
879 | // intended records are updated by the SQL statements we generate to run. |
||
880 | $wheres = $this->compileWheres($query); |
||
881 | |||
882 | return trim("update {$table}{$joins} set $columns $wheres"); |
||
883 | } |
||
884 | |||
885 | /** |
||
886 | * Prepare the bindings for an update statement. |
||
887 | * |
||
888 | * @param array $bindings |
||
889 | * @param array $values |
||
890 | * @return array |
||
891 | */ |
||
892 | public function prepareBindingsForUpdate(array $bindings, array $values) |
||
893 | { |
||
894 | $cleanBindings = Tools::except($bindings, ['join', 'select']); |
||
895 | |||
896 | return array_values( |
||
897 | array_merge($bindings['join'], $values, Tools::flatten($cleanBindings)) |
||
898 | ); |
||
899 | } |
||
900 | |||
901 | /** |
||
902 | * Compile a delete statement into SQL. |
||
903 | * |
||
904 | * @param \Childish\query\Builder $query |
||
905 | * @return string |
||
906 | */ |
||
907 | public function compileDelete(Builder $query) |
||
908 | { |
||
909 | $wheres = is_array($query->wheres) ? $this->compileWheres($query) : ''; |
||
910 | |||
911 | return trim("delete from {$this->wrapTable($query->from)} $wheres"); |
||
912 | } |
||
913 | |||
914 | /** |
||
915 | * Compile a truncate table statement into SQL. |
||
916 | * |
||
917 | * @param \Childish\query\Builder $query |
||
918 | * @return array |
||
919 | */ |
||
920 | public function compileTruncate(Builder $query) |
||
921 | { |
||
922 | return ['truncate ' . $this->wrapTable($query->from) => []]; |
||
923 | } |
||
924 | |||
925 | /** |
||
926 | * Compile the lock into SQL. |
||
927 | * |
||
928 | * @param \Childish\query\Builder $query |
||
929 | * @param bool|string $value |
||
930 | * @return string |
||
931 | */ |
||
932 | protected function compileLock(Builder $query, $value) |
||
0 ignored issues
–
show
|
|||
933 | { |
||
934 | return is_string($value) ? $value : ''; |
||
935 | } |
||
936 | |||
937 | /** |
||
938 | * Determine if the grammar supports savepoints. |
||
939 | * |
||
940 | * @return bool |
||
941 | */ |
||
942 | public function supportsSavepoints() |
||
943 | { |
||
944 | return true; |
||
945 | } |
||
946 | |||
947 | /** |
||
948 | * Compile the SQL statement to define a savepoint. |
||
949 | * |
||
950 | * @param string $name |
||
951 | * @return string |
||
952 | */ |
||
953 | public function compileSavepoint($name) |
||
954 | { |
||
955 | return 'SAVEPOINT ' . $name; |
||
956 | } |
||
957 | |||
958 | /** |
||
959 | * Compile the SQL statement to execute a savepoint rollback. |
||
960 | * |
||
961 | * @param string $name |
||
962 | * @return string |
||
963 | */ |
||
964 | public function compileSavepointRollBack($name) |
||
965 | { |
||
966 | return 'ROLLBACK TO SAVEPOINT ' . $name; |
||
967 | } |
||
968 | |||
969 | /** |
||
970 | * Concatenate an array of segments, removing empties. |
||
971 | * |
||
972 | * @param array $segments |
||
973 | * @return string |
||
974 | */ |
||
975 | protected function concatenate($segments) |
||
976 | { |
||
977 | return implode(' ', array_filter($segments, function ($value) { |
||
978 | return (string)$value !== ''; |
||
979 | })); |
||
980 | } |
||
981 | |||
982 | /** |
||
983 | * Remove the leading boolean from a statement. |
||
984 | * |
||
985 | * @param string $value |
||
986 | * @return string |
||
987 | */ |
||
988 | protected function removeLeadingBoolean($value) |
||
989 | { |
||
990 | return preg_replace('/and |or /i', '', $value, 1); |
||
991 | } |
||
992 | |||
993 | /** |
||
994 | * Get the grammar specific operators. |
||
995 | * |
||
996 | * @return array |
||
997 | */ |
||
998 | public function getOperators() |
||
999 | { |
||
1000 | return $this->operators; |
||
0 ignored issues
–
show
The property
operators does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
1001 | } |
||
1002 | } |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.