1 | <?php |
||||
2 | |||||
3 | namespace Nip\Database\Query; |
||||
4 | |||||
5 | use Nip\Database\Connections\Connection; |
||||
6 | use Nip\Database\Query\Condition\Condition; |
||||
7 | use Nip\Database\Result; |
||||
8 | |||||
9 | /** |
||||
10 | * Class AbstractQuery |
||||
11 | * @package Nip\Database\Query |
||||
12 | * |
||||
13 | * @method $this setCols() setCols(array | string $cols = null) |
||||
14 | * @method $this setWhere() setWhere(array | string $cols = null) |
||||
15 | * |
||||
16 | * @method $this cols() cols(array | string $cols) |
||||
17 | * @method $this count() count(string $col, string $alias = null) |
||||
18 | * @method $this sum() sum(array | string $cols) |
||||
19 | * @method $this from() from(array | string $from) |
||||
20 | * @method $this data() data(array $data) |
||||
21 | * @method $this table() table(array | string $table) |
||||
22 | * @method $this order() order(array | string $order)\ |
||||
23 | * @method $this group() group(array | string $group, $rollup = false)\ |
||||
24 | */ |
||||
25 | abstract class AbstractQuery |
||||
26 | { |
||||
27 | /** |
||||
28 | * @var Connection |
||||
29 | */ |
||||
30 | protected $db; |
||||
31 | |||||
32 | protected $parts = [ |
||||
33 | 'where' => null, |
||||
34 | ]; |
||||
35 | |||||
36 | protected $string = null; |
||||
37 | |||||
38 | /** |
||||
39 | * @param Connection $manager |
||||
40 | * @return $this |
||||
41 | */ |
||||
42 | public function setManager(Connection $manager) |
||||
43 | 23 | { |
|||
44 | $this->db = $manager; |
||||
45 | 23 | ||||
46 | return $this; |
||||
47 | 23 | } |
|||
48 | |||||
49 | /** |
||||
50 | * @param $name |
||||
51 | * @param $arguments |
||||
52 | * @return $this |
||||
53 | */ |
||||
54 | public function __call($name, $arguments) |
||||
55 | 13 | { |
|||
56 | if (strpos($name, 'set') === 0) { |
||||
57 | 13 | $name = str_replace('set', '', $name); |
|||
58 | 1 | $name[0] = strtolower($name[0]); |
|||
59 | 1 | $this->initPart($name); |
|||
60 | 1 | } |
|||
61 | |||||
62 | foreach ($arguments as $argument) { |
||||
63 | 13 | $this->addPart($name, $argument); |
|||
64 | 13 | } |
|||
65 | |||||
66 | return $this; |
||||
67 | 13 | } |
|||
68 | |||||
69 | /** |
||||
70 | * @param $name |
||||
71 | * @return $this |
||||
72 | */ |
||||
73 | protected function initPart($name) |
||||
74 | 13 | { |
|||
75 | $this->isGenerated(false); |
||||
76 | 13 | $this->parts[$name] = []; |
|||
77 | 13 | ||||
78 | return $this; |
||||
79 | 13 | } |
|||
80 | |||||
81 | /** |
||||
82 | * @param boolean $generated |
||||
83 | * @return bool |
||||
84 | */ |
||||
85 | public function isGenerated($generated = null) |
||||
86 | 13 | { |
|||
87 | if ($generated === false) { |
||||
88 | 13 | $this->string = null; |
|||
89 | 13 | } |
|||
90 | |||||
91 | return $this->string !== null; |
||||
92 | 13 | } |
|||
93 | |||||
94 | /** |
||||
95 | * @param $name |
||||
96 | * @param $value |
||||
97 | * @return $this |
||||
98 | */ |
||||
99 | protected function addPart($name, $value) |
||||
100 | 13 | { |
|||
101 | if (!isset($this->parts[$name])) { |
||||
102 | 13 | $this->initPart($name); |
|||
103 | 13 | } |
|||
104 | |||||
105 | $this->isGenerated(false); |
||||
106 | 13 | $this->parts[$name][] = $value; |
|||
107 | 13 | ||||
108 | return $this; |
||||
109 | 13 | } |
|||
110 | |||||
111 | /** |
||||
112 | * @param $params |
||||
113 | */ |
||||
114 | public function addParams($params) |
||||
115 | { |
||||
116 | $this->checkParamSelect($params); |
||||
117 | $this->checkParamFrom($params); |
||||
118 | $this->checkParamWhere($params); |
||||
119 | $this->checkParamOrder($params); |
||||
120 | $this->checkParamGroup($params); |
||||
121 | $this->checkParamHaving($params); |
||||
122 | $this->checkParamLimit($params); |
||||
123 | } |
||||
124 | |||||
125 | /** |
||||
126 | * @param $params |
||||
127 | */ |
||||
128 | protected function checkParamSelect($params) |
||||
129 | { |
||||
130 | if (isset($params['select']) && is_array($params['select'])) { |
||||
131 | call_user_func_array([$this, 'cols'], $params['select']); |
||||
132 | } |
||||
133 | } |
||||
134 | |||||
135 | /** |
||||
136 | * @param $params |
||||
137 | */ |
||||
138 | protected function checkParamFrom($params) |
||||
139 | { |
||||
140 | if (isset($params['from']) && !empty($params['from'])) { |
||||
141 | $this->from($params['from']); |
||||
142 | } |
||||
143 | } |
||||
144 | |||||
145 | /** |
||||
146 | * @param $params |
||||
147 | */ |
||||
148 | protected function checkParamWhere($params) |
||||
149 | { |
||||
150 | if (isset($params['where']) && is_array($params['where'])) { |
||||
151 | foreach ($params['where'] as $condition) { |
||||
152 | if ($condition instanceof Condition) { |
||||
153 | $condition->setQuery($this); |
||||
154 | $this->where($condition); |
||||
155 | continue; |
||||
156 | } |
||||
157 | $condition = (array)$condition; |
||||
158 | $this->where( |
||||
159 | $condition[0], |
||||
160 | isset($condition[1]) ? $condition[1] : null |
||||
161 | ); |
||||
162 | } |
||||
163 | } |
||||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | 6 | * @param $string |
|||
168 | * @param array $values |
||||
169 | * @return $this |
||||
170 | 6 | */ |
|||
171 | 6 | public function where($string, $values = []) |
|||
172 | 2 | { |
|||
173 | /** @var Condition $this ->_parts[] */ |
||||
174 | 6 | if ($string) { |
|||
175 | if (isset($this->parts['where']) && $this->parts['where'] instanceof Condition) { |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
176 | $this->parts['where'] = $this->parts['where']->and_($this->getCondition($string, $values)); |
||||
0 ignored issues
–
show
The method
getCondition() does not exist on Nip\Database\Query\Condition\Condition .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||
177 | } else { |
||||
178 | 6 | $this->parts['where'] = $this->getCondition($string, $values); |
|||
179 | } |
||||
180 | } |
||||
181 | |||||
182 | return $this; |
||||
0 ignored issues
–
show
|
|||||
183 | } |
||||
184 | |||||
185 | /** |
||||
186 | 13 | * @param string $string |
|||
187 | * @param array $values |
||||
188 | 13 | * |
|||
189 | 13 | * @return Condition |
|||
190 | 13 | */ |
|||
191 | public function getCondition($string, $values = []) |
||||
192 | { |
||||
193 | if (!is_object($string)) { |
||||
0 ignored issues
–
show
|
|||||
194 | $condition = new Condition($string, $values); |
||||
195 | 13 | $condition->setQuery($this); |
|||
196 | } else { |
||||
197 | $condition = $string; |
||||
198 | } |
||||
199 | |||||
200 | return $condition; |
||||
201 | } |
||||
202 | |||||
203 | /** |
||||
204 | * @param $params |
||||
205 | */ |
||||
206 | protected function checkParamOrder($params) |
||||
207 | { |
||||
208 | if (isset($params['order']) && !empty($params['order'])) { |
||||
209 | call_user_func_array([$this, 'order'], $params['order']); |
||||
210 | } |
||||
211 | } |
||||
212 | |||||
213 | /** |
||||
214 | * @param $params |
||||
215 | */ |
||||
216 | protected function checkParamGroup($params) |
||||
217 | { |
||||
218 | if (isset($params['group']) && !empty($params['group'])) { |
||||
219 | call_user_func_array([$this, 'group'], [$params['group']]); |
||||
220 | } |
||||
221 | } |
||||
222 | |||||
223 | /** |
||||
224 | * @param $params |
||||
225 | */ |
||||
226 | protected function checkParamHaving($params) |
||||
227 | { |
||||
228 | if (isset($params['having']) && !empty($params['having'])) { |
||||
229 | call_user_func_array([$this, 'having'], [$params['having']]); |
||||
230 | } |
||||
231 | } |
||||
232 | |||||
233 | /** |
||||
234 | * @param $params |
||||
235 | */ |
||||
236 | protected function checkParamLimit($params) |
||||
237 | { |
||||
238 | if (isset($params['limit']) && !empty($params['limit'])) { |
||||
239 | call_user_func_array([$this, 'limit'], [$params['limit']]); |
||||
240 | } |
||||
241 | } |
||||
242 | |||||
243 | 2 | /** |
|||
244 | * @param integer $start |
||||
245 | 2 | * @param bool $offset |
|||
246 | 2 | * @return $this |
|||
247 | 1 | */ |
|||
248 | public function limit($start, $offset = false) |
||||
249 | { |
||||
250 | 2 | $this->parts['limit'] = $start; |
|||
251 | if ($offset) { |
||||
252 | $this->parts['limit'] .= ',' . $offset; |
||||
0 ignored issues
–
show
Are you sure
$offset of type true 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
![]() |
|||||
253 | } |
||||
254 | |||||
255 | return $this; |
||||
256 | } |
||||
257 | |||||
258 | 1 | /** |
|||
259 | * @param $string |
||||
260 | 1 | * @param array $values |
|||
261 | 1 | * |
|||
262 | 1 | * @return $this |
|||
263 | */ |
||||
264 | public function orWhere($string, $values = []) |
||||
265 | { |
||||
266 | if ($string) { |
||||
267 | if ($this->parts['where'] instanceof Condition) { |
||||
268 | 1 | $this->parts['where'] = $this->parts['where']->or_($this->getCondition($string, $values)); |
|||
269 | } else { |
||||
270 | $this->parts['where'] = $this->getCondition($string, $values); |
||||
271 | } |
||||
272 | } |
||||
273 | |||||
274 | return $this; |
||||
275 | } |
||||
276 | |||||
277 | /** |
||||
278 | * @param $string |
||||
279 | * @param array $values |
||||
280 | * |
||||
281 | * @return $this |
||||
282 | */ |
||||
283 | public function having($string, $values = []) |
||||
284 | { |
||||
285 | if (empty($string)) { |
||||
286 | return $this; |
||||
287 | } |
||||
288 | |||||
289 | $condition = $this->getCondition($string, $values); |
||||
290 | $having = $this->getPart('having'); |
||||
291 | |||||
292 | if ($having instanceof Condition) { |
||||
293 | $having = $having->and_($this->getCondition($string, $values)); |
||||
294 | } else { |
||||
295 | $having = $condition; |
||||
296 | } |
||||
297 | $this->parts['having'] = $having; |
||||
298 | return $this; |
||||
299 | } |
||||
300 | |||||
301 | /** |
||||
302 | * Escapes data for safe use in SQL queries |
||||
303 | 3 | * |
|||
304 | * @param string $data |
||||
305 | 3 | * @return string |
|||
306 | */ |
||||
307 | public function cleanData($data) |
||||
308 | { |
||||
309 | return $this->getManager()->getAdapter()->cleanData($data); |
||||
310 | } |
||||
311 | |||||
312 | /** |
||||
313 | * @return Connection |
||||
314 | */ |
||||
315 | public function getManager() |
||||
316 | { |
||||
317 | return $this->db; |
||||
318 | } |
||||
319 | |||||
320 | /** |
||||
321 | 2 | * @return Result |
|||
322 | */ |
||||
323 | 2 | public function execute() |
|||
324 | { |
||||
325 | return $this->getManager()->execute($this); |
||||
326 | } |
||||
327 | |||||
328 | /** |
||||
329 | 2 | * Implements magic method. |
|||
330 | * |
||||
331 | 2 | * @return string This object as a Query string. |
|||
332 | 2 | */ |
|||
333 | public function __toString() |
||||
334 | { |
||||
335 | 2 | return $this->getString(); |
|||
336 | } |
||||
337 | |||||
338 | /** |
||||
339 | * @return string |
||||
340 | */ |
||||
341 | public function getString() |
||||
342 | { |
||||
343 | if ($this->string === null) { |
||||
344 | $this->string = (string)$this->assemble(); |
||||
0 ignored issues
–
show
Are you sure the usage of
$this->assemble() targeting Nip\Database\Query\AbstractQuery::assemble() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
345 | } |
||||
346 | |||||
347 | return $this->string; |
||||
348 | } |
||||
349 | |||||
350 | /** |
||||
351 | * @return null |
||||
352 | */ |
||||
353 | abstract public function assemble(); |
||||
354 | 10 | ||||
355 | /** |
||||
356 | 10 | * @return array |
|||
357 | */ |
||||
358 | 10 | public function getParts() |
|||
359 | 6 | { |
|||
360 | return $this->parts; |
||||
361 | } |
||||
362 | 4 | ||||
363 | /** |
||||
364 | * @return null|string |
||||
365 | */ |
||||
366 | protected function assembleWhere() |
||||
367 | { |
||||
368 | 10 | $where = $this->parseWhere(); |
|||
369 | |||||
370 | 10 | if (!empty($where)) { |
|||
371 | return " WHERE $where"; |
||||
372 | } |
||||
373 | |||||
374 | return null; |
||||
375 | } |
||||
376 | 10 | ||||
377 | /** |
||||
378 | 10 | * @return string |
|||
379 | 10 | */ |
|||
380 | 1 | protected function parseWhere() |
|||
381 | { |
||||
382 | return is_object($this->parts['where']) ? (string)$this->parts['where'] : ''; |
||||
383 | 9 | } |
|||
384 | |||||
385 | /** |
||||
386 | * @return null|string |
||||
387 | */ |
||||
388 | protected function assembleLimit() |
||||
389 | { |
||||
390 | 11 | $limit = $this->getPart('limit'); |
|||
391 | if (!empty($limit)) { |
||||
392 | 11 | return " LIMIT {$this->parts['limit']}"; |
|||
393 | } |
||||
394 | |||||
395 | return null; |
||||
396 | } |
||||
397 | |||||
398 | /** |
||||
399 | 13 | * @param string $name |
|||
400 | * @return mixed|null |
||||
401 | 13 | */ |
|||
402 | 11 | public function getPart($name) |
|||
403 | { |
||||
404 | 3 | return $this->hasPart($name) ? $this->parts[$name] : null; |
|||
405 | } |
||||
406 | |||||
407 | 3 | /** |
|||
408 | 1 | * @param $name |
|||
409 | * @return bool |
||||
410 | */ |
||||
411 | 3 | public function hasPart($name) |
|||
412 | { |
||||
413 | if (!isset($this->parts[$name])) { |
||||
414 | return false; |
||||
415 | } |
||||
416 | if ($this->parts[$name] === null) { |
||||
0 ignored issues
–
show
|
|||||
417 | return false; |
||||
418 | } |
||||
419 | if (is_array($this->parts[$name]) && count($this->parts[$name]) < 1) { |
||||
420 | return false; |
||||
421 | } |
||||
422 | if (is_string($this->parts[$name]) && empty($this->parts[$name])) { |
||||
423 | return false; |
||||
424 | } |
||||
425 | |||||
426 | return true; |
||||
427 | } |
||||
428 | |||||
429 | /** |
||||
430 | 2 | * @param $name |
|||
431 | * @param $value |
||||
432 | 2 | * |
|||
433 | * @return $this |
||||
434 | */ |
||||
435 | protected function setPart($name, $value) |
||||
436 | 2 | { |
|||
437 | $this->initPart($name); |
||||
438 | $this->addPart($name, $value); |
||||
439 | |||||
440 | return $this; |
||||
441 | } |
||||
442 | 10 | ||||
443 | /** |
||||
444 | 10 | * @return string |
|||
445 | * @return mixed |
||||
446 | */ |
||||
447 | protected function getTable() |
||||
448 | 10 | { |
|||
449 | if (!is_array($this->parts['table']) && count($this->parts['table']) < 1) { |
||||
0 ignored issues
–
show
$this->parts['table'] of type null is incompatible with the type Countable|array expected by parameter $value of count() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
450 | trigger_error('No Table defined', E_USER_WARNING); |
||||
451 | } |
||||
452 | |||||
453 | return reset($this->parts['table']); |
||||
454 | } |
||||
455 | |||||
456 | 10 | /** |
|||
457 | * @return string |
||||
458 | 10 | */ |
|||
459 | 10 | protected function parseHaving() |
|||
460 | { |
||||
461 | if (isset($this->parts['having'])) { |
||||
462 | return (string)$this->parts['having']; |
||||
463 | } |
||||
464 | |||||
465 | return ''; |
||||
466 | } |
||||
467 | |||||
468 | /** |
||||
469 | * Parses ORDER BY entries. |
||||
470 | * Parses ORDER BY entries |
||||
471 | * |
||||
472 | * @return string |
||||
473 | */ |
||||
474 | protected function parseOrder() |
||||
475 | { |
||||
476 | if (!isset($this->parts['order']) || !is_array($this->parts['order']) || count($this->parts['order']) < 1) { |
||||
0 ignored issues
–
show
|
|||||
477 | return false; |
||||
0 ignored issues
–
show
|
|||||
478 | } |
||||
479 | |||||
480 | $orderParts = []; |
||||
481 | |||||
482 | foreach ($this->parts['order'] as $itemOrder) { |
||||
483 | if ($itemOrder) { |
||||
484 | if (!is_array($itemOrder)) { |
||||
485 | $itemOrder = [$itemOrder]; |
||||
486 | } |
||||
487 | |||||
488 | $column = isset($itemOrder[0]) ? $itemOrder[0] : false; |
||||
489 | 7 | $type = isset($itemOrder[1]) ? $itemOrder[1] : ''; |
|||
490 | $protected = isset($itemOrder[2]) ? $itemOrder[2] : true; |
||||
491 | 7 | ||||
492 | 7 | $column = ($protected ? $this->protect($column) : $column) . ' ' . strtoupper($type); |
|||
493 | |||||
494 | $orderParts[] = trim($column); |
||||
495 | } |
||||
496 | } |
||||
497 | |||||
498 | return implode(', ', $orderParts); |
||||
499 | } |
||||
500 | |||||
501 | /** |
||||
502 | * Adds backticks to input. |
||||
503 | * |
||||
504 | * @param string $input |
||||
505 | * |
||||
506 | * @return string |
||||
507 | */ |
||||
508 | protected function protect($input) |
||||
509 | { |
||||
510 | return strpos($input, '(') !== false ? $input : str_replace( |
||||
511 | "`*`", |
||||
512 | "*", |
||||
513 | '`' . str_replace('.', '`.`', $input) . '`' |
||||
514 | ); |
||||
515 | } |
||||
516 | |||||
517 | /** |
||||
518 | * Prefixes table names |
||||
519 | * |
||||
520 | * @param string $table |
||||
521 | * @return string |
||||
522 | */ |
||||
523 | protected function tableName($table = '') |
||||
524 | { |
||||
525 | return $this->getManager()->tableName($table); |
||||
526 | } |
||||
527 | |||||
528 | /** |
||||
529 | * Removes backticks from input |
||||
530 | * |
||||
531 | * @param string $input |
||||
532 | * @return string |
||||
533 | */ |
||||
534 | protected function cleanProtected($input) |
||||
535 | { |
||||
536 | return str_replace('`', '', $input); |
||||
537 | } |
||||
538 | } |
||||
539 |