Passed
Push — master ( 1fbb37...9e3033 )
by Alexey
07:10
created

Query::query()   C

Complexity

Conditions 8
Paths 16

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 18
nc 16
nop 1
dl 0
loc 27
ccs 0
cts 22
cp 0
crap 72
rs 5.3846
c 0
b 0
f 0
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 Db\Mysql;
13
14
class Query extends \Object {
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
34
    /**
35
     * @param  $instance
36
     */
37
    public function __construct($instance = null) {
38
        if (!$instance) {
39
            $this->curInstance = \App::$cur->db->connection;
40
        } else {
41
            $this->curInstance = $instance;
42
        }
43
    }
44
45
    public function insert($table, $data) {
46
        $this->operation = 'INSERT';
47
        $this->table = $table;
48
        $this->cols = $data;
49
        $this->query();
50
        return $this->curInstance->pdo->lastInsertId();
51
    }
52
53
    public function select($table) {
54
        $this->operation = 'SELECT';
55
        $this->table = $table;
56
        return $this->query();
57
    }
58
59 View Code Duplication
    public function update($table, $data) {
60
        $this->operation = 'UPDATE';
61
        $this->table = $table;
62
        $this->cols = $data;
63
        $result = $this->query();
64
        return $result->pdoResult->rowCount();
65
    }
66
67 View Code Duplication
    public function delete($table) {
68
        $this->operation = 'DELETE';
69
        $this->table = $table;
70
        $result = $this->query();
71
        return $result->pdoResult->rowCount();
72
    }
73
74
    public function createTable($table_name, $cols, $indexes = []) {
75
        $this->operation = 'CREATE TABLE';
76
        $this->table = $table_name;
77
        $this->cols = $cols;
78
        $this->indexes = $indexes;
79
        return $this->query();
80
    }
81
82
    public function cols($cols) {
83
        if (is_array($cols)) {
84
            $this->cols = array_merge($this->cols, array_values($cols));
85
        } else {
86
            $this->cols[] = $cols;
87
        }
88
    }
89
90
    public function join($table, $where = false, $type = 'LEFT', $alias = '') {
91
        if (is_array($table)) {
92 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...
93
                if (!is_array($item)) {
94
                    call_user_func_array(array($this, 'join'), $table);
95
                    break;
96
                } else {
97
                    $this->join($item);
98
                }
99
            }
100
        } else {
101
            $this->join[] = [$table, $where, $type, $alias];
102
        }
103
    }
104
105 View Code Duplication
    public function where($where = '', $value = '', $operation = false, $concatenation = 'AND') {
106
        if (!is_array($where)) {
107
            $this->where[] = [$where, $value, $operation, $concatenation];
108
        } else {
109
            $this->where[] = $where;
110
        }
111
    }
112
113 View Code Duplication
    public function having($where = '', $value = '', $operation = false, $concatenation = 'AND') {
114
        if (!is_array($where)) {
115
            $this->having[] = [$where, $value, $operation, $concatenation];
116
        } else {
117
            $this->having[] = $where;
118
        }
119
    }
120
121
    public function group($colname) {
122
        $this->group[] = $colname;
123
    }
124
125
    public function order($order, $type = 'ASC') {
126
127
128
        if (!is_array($order)) {
129
            $this->order[] = "{$order} {$type}";
130
        } else {
131 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...
132
                if (!is_array($item)) {
133
                    call_user_func_array(array($this, 'order'), $order);
134
                    break;
135
                } else {
136
                    $this->order($item);
137
                }
138
            }
139
        }
140
    }
141
142
    public function limit($start = 0, $len = 0) {
143
        $start = intval($start);
144
        $len = intval($len);
145
        $this->limit = "LIMIT {$start}";
146
        if ($len !== 0) {
147
            $this->limit .= ",{$len}";
148
        }
149
    }
150
151
    public function buildJoin($table, $where = false, $type = 'LEFT', $alias = '') {
152
        $join = '';
153
        if (is_array($table)) {
154
            $joins = func_get_args();
155
            foreach ($joins as $joinAr) {
156
                $join .= call_user_func_array([$this, 'buildJoin'], $joinAr);
157
            }
158
        } else {
159
            $join .= " {$type} JOIN {$this->curInstance->table_prefix}{$table}";
160
            if ($alias) {
161
                $join .= " AS `{$alias}`";
162
            }
163
            if ($where) {
164
                $join .= " ON {$where}";
165
            }
166
        }
167
        return $join;
168
    }
169
170
    /**
171
     * Build where string
172
     * 
173
     * @param string|array $where
174
     * @param mixed $value
175
     * @param string $operation
176
     * @param string $concatenation
177
     */
178 View Code Duplication
    public function buildWhere($where = '', $value = '', $operation = '=', $concatenation = 'AND') {
179
        if (!is_array($where)) {
180
            if (empty($operation)) {
181
                $operation = '=';
182
            }
183
184
            if ($concatenation === false) {
185
                $concatenation = 'AND';
186
            } elseif ($concatenation === true) {
187
                $concatenation = '';
188
            }
189
190
            if ($this->whereString == '') {
191
                $this->whereString = ' WHERE ';
192
            }
193
194
            if (stristr($operation, 'IN') || stristr($operation, 'NOT IN')) {
195
                if (is_array($value)) {
196
                    $newValue = '';
197
                    foreach ($value as $item) {
198
                        if ($newValue) {
199
                            $newValue .= ',';
200
                        }
201
                        if (is_string($item)) {
202
                            $newValue .= '"' . $item . '"';
203
                        } else {
204
                            $newValue .= $item;
205
                        }
206
                    }
207
                    $value = '(' . $newValue . ')';
208
                } elseif (!preg_match('!\(!', $value) && !preg_match('![^0-9,\.\(\) ]!', $value)) {
209
                    $value = "({$value})";
210
                } elseif (preg_match('!\(!', $value) && preg_match('![^0-9,\.\(\) ]!', $value)) {
211
                    $value = "\"{$value}\"";
212
                }
213
            } elseif (!in_array($value, array('CURRENT_TIMESTAMP', 'NULL'))) {
214
                $this->params[] = $value;
215
                $value = "?";
216
            }
217
218
            if (substr($this->whereString, -1, 1) == '(' || substr($this->whereString, -2, 2) == 'E ') {
219
                $this->whereString .= " {$where} {$operation} {$value} ";
220
            } else {
221
                $this->whereString .= "{$concatenation} {$where} {$operation} {$value} ";
222
            }
223
        } else {
224
            $i = -1;
225
            while (isset($where[++$i])) {
226
                $item = $where[$i];
227
                if (isset($where[$i + 1]) && !isset($where[$i - 1]) && is_array($where[$i])) {
228
                    if ($this->whereString != null && substr($this->whereString, -1, 1) != '(' && $this->whereString != 'WHERE ') {
229
                        if (!isset($item[3])) {
230
                            $concatenation = 'AND';
231
                        } else {
232
                            $concatenation = $item[3];
233
                        }
234
235
                        $this->whereString .= "{$concatenation} ";
236
                    }
237
238
                    if ($this->whereString != '') {
239
                        $this->whereString .= '(';
240
                    } else {
241
                        $this->whereString = 'WHERE (';
242
                    }
243
                }
244
245
                if (!is_array($item)) {
246
                    call_user_func_array(array($this, 'buildWhere'), $where);
247
                    break;
248
                } else {
249
                    $this->buildWhere($item);
250
                }
251
                if (!isset($where[$i + 1]) && isset($where[$i - 1])) {
252
                    $this->whereString .= ') ';
253
                }
254
            }
255
        }
256
    }
257
258
    /**
259
     * Build having string
260
     * 
261
     * @param string|array $where
262
     * @param mixed $value
263
     * @param string $operation
264
     * @param string $concatenation
265
     */
266 View Code Duplication
    public function buildHaving($where = '', $value = '', $operation = '=', $concatenation = 'AND') {
267
        if (!is_array($where)) {
268
            if (empty($operation)) {
269
                $operation = '=';
270
            }
271
            if ($concatenation === false) {
272
                $concatenation = 'AND';
273
            } elseif ($concatenation === true) {
274
                $concatenation = '';
275
            }
276
277
            if ($this->havingString == '') {
278
                $this->havingString = ' HAVING ';
279
            }
280
281
            if (stristr($operation, 'IN') || stristr($operation, 'NOT IN')) {
282
                if (is_array($value)) {
283
                    $newValue = '';
284
                    foreach ($value as $item) {
285
                        if ($newValue) {
286
                            $newValue .= ',';
287
                        }
288
                        if (is_string($item)) {
289
                            $newValue .= '"' . $item . '"';
290
                        } else {
291
                            $newValue .= $item;
292
                        }
293
                    }
294
                    $value = '(' . $newValue . ')';
295
                } elseif (!preg_match('!\(!', $value) && !preg_match('![^0-9,\.\(\) ]!', $value)) {
296
                    $value = "({$value})";
297
                } elseif (preg_match('!\(!', $value) && preg_match('![^0-9,\.\(\) ]!', $value)) {
298
                    $value = "\"{$value}\"";
299
                }
300
            } elseif (!in_array($value, array('CURRENT_TIMESTAMP', 'NULL'))) {
301
                $this->params[] = $value;
302
                $value = "?";
303
            }
304
            if (substr($this->havingString, -1, 1) == '(' || substr($this->havingString, -2, 2) == 'G ') {
305
                $this->havingString .= " {$where} {$operation} {$value} ";
306
            } else {
307
                $this->havingString .= "{$concatenation} {$where} {$operation} {$value} ";
308
            }
309
        } else {
310
            $i = -1;
311
            while (isset($where[++$i])) {
312
                $item = $where[$i];
313
                if (isset($where[$i + 1]) && !isset($where[$i - 1]) && is_array($where[$i])) {
314
                    if ($this->havingString != null && substr($this->havingString, -1, 1) != '(' && $this->havingString != 'HAVING ') {
315
                        if (!isset($item[3])) {
316
                            $concatenation = 'AND';
317
                        } else {
318
                            $concatenation = $item[3];
319
                        }
320
321
                        $this->havingString .= "{$concatenation} ";
322
                    }
323
324
                    if ($this->havingString != '') {
325
                        $this->havingString .= '(';
326
                    } else {
327
                        $this->havingString = 'HAVING (';
328
                    }
329
                }
330
331
                if (!is_array($item)) {
332
                    call_user_func_array(array($this, 'buildHaving'), $where);
333
                    break;
334
                } else {
335
                    $this->buildHaving($item);
336
                }
337
                if (!isset($where[$i + 1]) && isset($where[$i - 1])) {
338
                    $this->havingString .= ') ';
339
                }
340
            }
341
        }
342
    }
343
344
    public function buildQuery() {
345
        $query = $this->operation;
346
        $this->operation = strtoupper($this->operation);
347
348
        switch ($this->operation) {
349
            case 'SELECT':
350
                $query .= ' ' . ($this->distinct ? 'DISTINCT' : '');
351
                $query .= ' ' . (!$this->cols ? '*' : ((is_array($this->cols) ? implode(',', $this->cols) : $this->cols)));
352
            // no break
353
            case 'DELETE':
354
                $query .= ' FROM';
355
                break;
356
            case 'INSERT':
357
                $query .= ' INTO';
358
                break;
359
        }
360
        $query .= " `{$this->curInstance->db_name}`.`{$this->curInstance->table_prefix}{$this->table}`";
361
        if ($this->join) {
362
            $query .= $this->buildJoin($this->join);
363
        }
364
        switch ($this->operation) {
365
            case 'INSERT':
366
                $this->params = array_merge($this->params, array_values($this->cols));
367
                $colsStr = '';
368
                if ($this->cols) {
369
                    $colsStr = '`' . implode('`,`', array_keys($this->cols)) . '`';
370
                }
371
                $query .= ' (' . $colsStr . ') VALUES (' . rtrim(str_repeat('?,', count($this->cols)), ',') . ')';
372
                break;
373
            case 'CREATE TABLE':
374
                $query .= " (";
375
                foreach ($this->cols as $col_name => $param) {
376
                    if ($param == 'pk') {
377
                        $param = "int(11) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (`{$col_name}`)";
378
                    }
379
                    $query .= " `{$col_name}` {$param},";
380
                }
381
                $query = rtrim($query, ',');
382
                if ($this->indexes) {
383
                    $query .= ', ' . implode(',', $this->indexes);
384
                }
385
                $query .= ") ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_general_ci";
386
                break;
387
            case 'UPDATE':
388
                $updates = [];
389
                foreach ($this->cols as $key => $item) {
390
                    if (!in_array($item, array('CURRENT_TIMESTAMP'))) {
391
                        $this->params[] = $item;
392
                        $updates[] = "`{$key}` = ?";
393
                    } else {
394
                        $updates[] = "`{$key}` = {$item}";
395
                    }
396
                }
397
                $update = implode(',', $updates);
398
                $query .= " SET {$update}";
399
            // no break
400
            case 'SELECT':
401
            case 'DELETE':
402
                $this->buildWhere($this->where);
403
                if ($this->whereString) {
404
                    $query .= ' ' . $this->whereString;
405
                }
406
                break;
407
        }
408
        if ($this->group) {
409
            $query .= ' GROUP BY ' . implode(',', $this->group);
410
        }
411
        $this->buildHaving($this->having);
412
        if ($this->havingString) {
413
            $query .= ' ' . $this->havingString;
414
        }
415
        if ($this->order) {
416
            $query .= ' ORDER BY ' . implode(',', $this->order);
417
        }
418
        if ($this->limit) {
419
            $query .= ' ' . $this->limit;
420
        }
421
        return ['query' => $query, 'params' => $this->params];
422
    }
423
424
    /**
425
     * Execute query
426
     * 
427
     * @param string|array $query
428
     * @return \Db\Mysql\Result
429
     */
430
    public function query($query = []) {
431
        if (!$query) {
432
            $this->params = [];
433
            $query = $this->buildQuery();
434
        }
435
436
        if (is_string($query)) {
437
            $query = ['query' => $query, 'params' => $this->params];
438
        }
439
440
        $prepare = $this->curInstance->pdo->prepare($query['query']);
441
        $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...
442
        $pos = 1;
443
        foreach ($query['params'] as $param) {
444
            $prepare->bindValue($pos++, $param, is_null($param) ? \PDO::PARAM_NULL : \PDO::PARAM_STR);
445
        }
446
        $prepare->execute();
447
448
        $this->curInstance->lastQuery = $query;
449
        $result = new Result();
450
        $result->pdoResult = $prepare;
451
        if ($this->curInstance->dbInstance && $this->curInstance->dbInstance->curQuery && $this->curInstance->dbInstance->curQuery === $this) {
452
            $this->curInstance->dbInstance->curQuery = null;
453
        }
454
455
        return $result;
456
    }
457
}