Test Failed
Branch v5 (12d602)
by Alexey
04:51
created

Query::buildQuery()   F

Complexity

Conditions 24
Paths 3872

Size

Total Lines 79
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
eloc 61
nc 3872
nop 0
dl 0
loc 79
rs 2.2932
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Query class for mysql driver
5
 *
6
 * @author Alexey Krupskiy <[email protected]>
7
 * @link http://inji.ru/
8
 * @copyright 2015 Alexey Krupskiy
9
 * @license https://github.com/injitools/cms-Inji/blob/master/LICENSE
10
 */
11
12
namespace Inji\Db\Mysql;
13
14
class Query extends \Inji\InjiObject implements \Inji\Db\DriverQuery {
15
16
    public $curInstance = null;
17
    public $where = [];
18
    public $whereString = '';
19
    public $having = [];
20
    public $havingString = '';
21
    public $cols = [];
22
    public $order = null;
23
    public $join = [];
24
    public $group = [];
25
    public $limit = '';
26
    public $error = '';
27
    public $query = '';
28
    public $table = '';
29
    public $operation = '';
30
    public $indexes = [];
31
    public $params = [];
32
    public $distinct = false;
33
    public $dbOptions = [];
34
35
    /**
36
     * @param  $instance
37
     */
38
    public function __construct($instance = null) {
39
        if (!$instance) {
40
            $this->curInstance = \App::$cur->db->connection;
41
        } else {
42
            $this->curInstance = $instance;
43
        }
44
    }
45
46
    public function setDbOption($name, $value) {
47
        $this->dbOptions[$name] = $value;
48
    }
49
50
    public function setTable($tableName) {
51
        $this->table = $tableName;
52
    }
53
54
    public function insert($table, $data) {
55
        $this->operation = 'INSERT';
56
        $this->table = $table;
57
        $this->cols = $data;
58
        $this->query();
59
        return $this->curInstance->pdo->lastInsertId();
60
    }
61
62
    public function select($table = null) {
63
        $this->operation = 'SELECT';
64
        if ($table !== null) {
65
            $this->table = $table;
66
        }
67
        return $this->query();
68
    }
69
70 View Code Duplication
    public function update($table, $data) {
71
        $this->operation = 'UPDATE';
72
        $this->table = $table;
73
        $this->cols = $data;
74
        $result = $this->query();
75
        return $result->pdoResult->rowCount();
76
    }
77
78 View Code Duplication
    public function delete($table) {
79
        $this->operation = 'DELETE';
80
        $this->table = $table;
81
        $result = $this->query();
82
        return $result->pdoResult->rowCount();
83
    }
84
85
    public function createTable($table_name, $cols, $indexes = []) {
86
        $this->operation = 'CREATE TABLE';
87
        $this->table = $table_name;
88
        $this->cols = $cols;
89
        $this->indexes = $indexes;
90
        return $this->query();
91
    }
92
93
    public function cols($cols) {
94
        if (is_array($cols)) {
95
            $this->cols = array_merge($this->cols, array_values($cols));
96
        } else {
97
            $this->cols[] = $cols;
98
        }
99
    }
100
101
    public function join($table, $where = false, $type = 'LEFT', $alias = '') {
102
        if (is_array($table)) {
103 View Code Duplication
            foreach ($table as $item) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
104
                if (!is_array($item)) {
105
                    call_user_func_array(array($this, 'join'), $table);
106
                    break;
107
                } else {
108
                    $this->join($item);
109
                }
110
            }
111
        } else {
112
            $this->join[] = [$table, $where, $type, $alias];
113
        }
114
    }
115
116 View Code Duplication
    public function where($where = '', $value = '', $operation = false, $concatenation = 'AND') {
117
        if (!is_array($where)) {
118
            $this->where[] = [$where, $value, $operation, $concatenation];
119
        } else {
120
            $this->where[] = $where;
121
        }
122
    }
123
124 View Code Duplication
    public function having($where = '', $value = '', $operation = false, $concatenation = 'AND') {
125
        if (!is_array($where)) {
126
            $this->having[] = [$where, $value, $operation, $concatenation];
127
        } else {
128
            $this->having[] = $where;
129
        }
130
    }
131
132
    public function group($colname) {
133
        $this->group[] = $colname;
134
    }
135
136
    public function order($order, $type = 'ASC') {
137
138
139
        if (!is_array($order)) {
140
            $this->order[] = "{$order} {$type}";
141
        } else {
142 View Code Duplication
            foreach ($order as $item) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
143
                if (!is_array($item)) {
144
                    call_user_func_array(array($this, 'order'), $order);
145
                    break;
146
                } else {
147
                    $this->order($item);
148
                }
149
            }
150
        }
151
    }
152
153
    public function limit($start = 0, $len = 0) {
154
        $start = intval($start);
155
        $len = intval($len);
156
        $this->limit = "LIMIT {$start}";
157
        if ($len !== 0) {
158
            $this->limit .= ",{$len}";
159
        }
160
    }
161
162
    public function buildJoin($table, $where = false, $type = 'LEFT', $alias = '') {
163
        $join = '';
164
        if (is_array($table)) {
165
            $joins = func_get_args();
166
            foreach ($joins as $joinAr) {
167
                $join .= call_user_func_array([$this, 'buildJoin'], $joinAr);
168
            }
169
        } else {
170
            $join .= " {$type} JOIN {$this->curInstance->table_prefix}{$table}";
171
            if ($alias) {
172
                $join .= " AS `{$alias}`";
173
            }
174
            if ($where) {
175
                $join .= " ON {$where}";
176
            }
177
        }
178
        return $join;
179
    }
180
181
    /**
182
     * Build where string
183
     *
184
     * @param string|array $where
185
     * @param mixed $value
186
     * @param string $operation
187
     * @param string $concatenation
188
     */
189 View Code Duplication
    public function buildWhere($where = '', $value = '', $operation = '=', $concatenation = 'AND') {
190
        if (!is_array($where)) {
191
            if (empty($operation)) {
192
                $operation = '=';
193
            }
194
195
            if ($concatenation === false) {
196
                $concatenation = 'AND';
197
            } elseif ($concatenation === true) {
198
                $concatenation = '';
199
            }
200
201
            if ($this->whereString == '') {
202
                $this->whereString = ' WHERE ';
203
            }
204
205
            if (stristr($operation, 'IN') || stristr($operation, 'NOT IN')) {
206
                if (is_array($value)) {
207
                    $newValue = '';
208
                    foreach ($value as $item) {
209
                        if ($newValue) {
210
                            $newValue .= ',';
211
                        }
212
                        if (is_string($item)) {
213
                            $newValue .= '"' . $item . '"';
214
                        } else {
215
                            $newValue .= $item;
216
                        }
217
                    }
218
                    $value = '(' . $newValue . ')';
219
                } elseif (!preg_match('!\(!', $value) && !preg_match('![^0-9,\.\(\) ]!', $value)) {
220
                    $value = "({$value})";
221
                } elseif (preg_match('!\(!', $value) && preg_match('![^0-9,\.\(\) ]!', $value)) {
222
                    $value = "\"{$value}\"";
223
                }
224
            } elseif (!in_array($value, array('CURRENT_TIMESTAMP', 'NULL'))) {
225
                $this->params[] = $value;
226
                $value = "?";
227
            }
228
229
            if (substr($this->whereString, -1, 1) == '(' || substr($this->whereString, -2, 2) == 'E ') {
230
                $this->whereString .= " {$where} {$operation} {$value} ";
231
            } else {
232
                $this->whereString .= "{$concatenation} {$where} {$operation} {$value} ";
233
            }
234
        } else {
235
            $i = -1;
236
            while (isset($where[++$i])) {
237
                $item = $where[$i];
238
                if (isset($where[$i + 1]) && !isset($where[$i - 1]) && is_array($where[$i])) {
239
                    if ($this->whereString != null && substr($this->whereString, -1, 1) != '(' && $this->whereString != 'WHERE ') {
240
                        if (!isset($item[3])) {
241
                            $concatenation = 'AND';
242
                        } else {
243
                            $concatenation = $item[3];
244
                        }
245
246
                        $this->whereString .= "{$concatenation} ";
247
                    }
248
249
                    if ($this->whereString != '') {
250
                        $this->whereString .= '(';
251
                    } else {
252
                        $this->whereString = 'WHERE (';
253
                    }
254
                }
255
256
                if (!is_array($item)) {
257
                    call_user_func_array(array($this, 'buildWhere'), $where);
258
                    break;
259
                } else {
260
                    $this->buildWhere($item);
261
                }
262
                if (!isset($where[$i + 1]) && isset($where[$i - 1])) {
263
                    $this->whereString .= ') ';
264
                }
265
            }
266
        }
267
    }
268
269
    /**
270
     * Build having string
271
     *
272
     * @param string|array $where
273
     * @param mixed $value
274
     * @param string $operation
275
     * @param string $concatenation
276
     */
277 View Code Duplication
    public function buildHaving($where = '', $value = '', $operation = '=', $concatenation = 'AND') {
278
        if (!is_array($where)) {
279
            if (empty($operation)) {
280
                $operation = '=';
281
            }
282
            if ($concatenation === false) {
283
                $concatenation = 'AND';
284
            } elseif ($concatenation === true) {
285
                $concatenation = '';
286
            }
287
288
            if ($this->havingString == '') {
289
                $this->havingString = ' HAVING ';
290
            }
291
292
            if (stristr($operation, 'IN') || stristr($operation, 'NOT IN')) {
293
                if (is_array($value)) {
294
                    $newValue = '';
295
                    foreach ($value as $item) {
296
                        if ($newValue) {
297
                            $newValue .= ',';
298
                        }
299
                        if (is_string($item)) {
300
                            $newValue .= '"' . $item . '"';
301
                        } else {
302
                            $newValue .= $item;
303
                        }
304
                    }
305
                    $value = '(' . $newValue . ')';
306
                } elseif (!preg_match('!\(!', $value) && !preg_match('![^0-9,\.\(\) ]!', $value)) {
307
                    $value = "({$value})";
308
                } elseif (preg_match('!\(!', $value) && preg_match('![^0-9,\.\(\) ]!', $value)) {
309
                    $value = "\"{$value}\"";
310
                }
311
            } elseif (!in_array($value, array('CURRENT_TIMESTAMP', 'NULL'))) {
312
                $this->params[] = $value;
313
                $value = "?";
314
            }
315
            if (substr($this->havingString, -1, 1) == '(' || substr($this->havingString, -2, 2) == 'G ') {
316
                $this->havingString .= " {$where} {$operation} {$value} ";
317
            } else {
318
                $this->havingString .= "{$concatenation} {$where} {$operation} {$value} ";
319
            }
320
        } else {
321
            $i = -1;
322
            while (isset($where[++$i])) {
323
                $item = $where[$i];
324
                if (isset($where[$i + 1]) && !isset($where[$i - 1]) && is_array($where[$i])) {
325
                    if ($this->havingString != null && substr($this->havingString, -1, 1) != '(' && $this->havingString != 'HAVING ') {
326
                        if (!isset($item[3])) {
327
                            $concatenation = 'AND';
328
                        } else {
329
                            $concatenation = $item[3];
330
                        }
331
332
                        $this->havingString .= "{$concatenation} ";
333
                    }
334
335
                    if ($this->havingString != '') {
336
                        $this->havingString .= '(';
337
                    } else {
338
                        $this->havingString = 'HAVING (';
339
                    }
340
                }
341
342
                if (!is_array($item)) {
343
                    call_user_func_array(array($this, 'buildHaving'), $where);
344
                    break;
345
                } else {
346
                    $this->buildHaving($item);
347
                }
348
                if (!isset($where[$i + 1]) && isset($where[$i - 1])) {
349
                    $this->havingString .= ') ';
350
                }
351
            }
352
        }
353
    }
354
355
    public function buildQuery() {
356
        $query = $this->operation;
357
        $this->operation = strtoupper($this->operation);
358
359
        switch ($this->operation) {
360
            case 'SELECT':
361
                $query .= ' ' . ($this->distinct ? 'DISTINCT' : '');
362
                $query .= ' ' . (!$this->cols ? '*' : ((is_array($this->cols) ? implode(',', $this->cols) : $this->cols)));
363
            // no break
364
            case 'DELETE':
365
                $query .= ' FROM';
366
                break;
367
            case 'INSERT':
368
                $query .= ' INTO';
369
                break;
370
        }
371
        $query .= " `{$this->curInstance->db_name}`.`{$this->curInstance->table_prefix}{$this->table}`";
372
        if ($this->join) {
373
            $query .= $this->buildJoin($this->join);
374
        }
375
        switch ($this->operation) {
376
            case 'INSERT':
377
                $this->params = array_merge($this->params, array_values($this->cols));
378
                $colsStr = '';
379
                if ($this->cols) {
380
                    $colsStr = '`' . implode('`,`', array_keys($this->cols)) . '`';
381
                }
382
                $query .= ' (' . $colsStr . ') VALUES (' . rtrim(str_repeat('?,', count($this->cols)), ',') . ')';
383
                break;
384
            case 'CREATE TABLE':
385
                $query .= " (";
386
                foreach ($this->cols as $col_name => $param) {
387
                    if ($param == 'pk') {
388
                        $param = "int(11) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (`{$col_name}`)";
389
                    }
390
                    $query .= " `{$col_name}` {$param},";
391
                }
392
                $query = rtrim($query, ',');
393
                if ($this->indexes) {
394
                    $query .= ', ' . implode(',', $this->indexes);
395
                }
396
                $query .= ") ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_general_ci";
397
                break;
398
            case 'UPDATE':
399
                $updates = [];
400
                foreach ($this->cols as $key => $item) {
401
                    if (!in_array($item, array('CURRENT_TIMESTAMP'))) {
402
                        $this->params[] = $item;
403
                        $updates[] = "`{$key}` = ?";
404
                    } else {
405
                        $updates[] = "`{$key}` = {$item}";
406
                    }
407
                }
408
                $update = implode(',', $updates);
409
                $query .= " SET {$update}";
410
            // no break
411
            case 'SELECT':
412
            case 'DELETE':
413
                $this->buildWhere($this->where);
414
                if ($this->whereString) {
415
                    $query .= ' ' . $this->whereString;
416
                }
417
                break;
418
        }
419
        if ($this->group) {
420
            $query .= ' GROUP BY ' . implode(',', $this->group);
421
        }
422
        $this->buildHaving($this->having);
423
        if ($this->havingString) {
424
            $query .= ' ' . $this->havingString;
425
        }
426
        if ($this->order) {
427
            $query .= ' ORDER BY ' . implode(',', $this->order);
428
        }
429
        if ($this->limit) {
430
            $query .= ' ' . $this->limit;
431
        }
432
        return ['query' => $query, 'params' => $this->params];
433
    }
434
435
    /**
436
     * Execute query
437
     *
438
     * @param string|array $query
439
     * @return Result
440
     */
441
    public function query($query = []) {
442
        if (!$query) {
443
            $this->params = [];
444
            $query = $this->buildQuery();
445
        }
446
        if (is_string($query)) {
447
            $query = ['query' => $query, 'params' => $this->params];
448
        }
449
450
        $prepare = $this->curInstance->pdo->prepare($query['query']);
451
        $params = [];
0 ignored issues
show
Unused Code introduced by
$params is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
452
        $pos = 1;
453
        foreach ($query['params'] as $param) {
454
            $prepare->bindValue($pos++, $param, is_null($param) ? \PDO::PARAM_NULL : \PDO::PARAM_STR);
455
        }
456
        $key = \Inji\App::$cur->log->start('query: ' . $query['query']);
457
        $prepare->execute();
458
        \Inji\App::$cur->log->end($key);
459
        $this->curInstance->lastQuery = $query;
460
        $result = new Result();
0 ignored issues
show
Bug introduced by
The call to Result::__construct() misses a required argument $dbResult.

This check looks for function calls that miss required arguments.

Loading history...
461
        $result->pdoResult = $prepare;
462
        if ($this->curInstance->dbInstance && $this->curInstance->dbInstance->curQuery && $this->curInstance->dbInstance->curQuery === $this) {
463
            $this->curInstance->dbInstance->curQuery = null;
464
        }
465
466
        return $result;
467
    }
468
}