AbstractQuery::protect()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 6
ccs 0
cts 1
cp 0
crap 6
rs 10
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
The property parts does not seem to exist on Nip\Database\Query\Condition\Condition.
Loading history...
176
                $this->parts['where'] = $this->parts['where']->and_($this->getCondition($string, $values));
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

176
                $this->parts['where'] = $this->parts['where']->and_($this->/** @scrutinizer ignore-call */ getCondition($string, $values));

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.

Loading history...
177
            } else {
178 6
                $this->parts['where'] = $this->getCondition($string, $values);
179
            }
180
        }
181
182
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Nip\Database\Query\Condition\Condition which is incompatible with the documented return type Nip\Database\Query\AbstractQuery.
Loading history...
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
introduced by
The condition is_object($string) is always false.
Loading history...
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
Bug introduced by
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 ignore-type  annotation

252
            $this->parts['limit'] .= ',' . /** @scrutinizer ignore-type */ $offset;
Loading history...
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
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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
introduced by
The condition $this->parts[$name] === null is always true.
Loading history...
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
introduced by
The condition is_array($this->parts['table']) is always false.
Loading history...
Bug introduced by
$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 ignore-type  annotation

449
        if (!is_array($this->parts['table']) && count(/** @scrutinizer ignore-type */ $this->parts['table']) < 1) {
Loading history...
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
introduced by
The condition is_array($this->parts['order']) is always false.
Loading history...
477
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
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