Completed
Push — master ( 776132...4b8bb5 )
by Alexey
13:36
created

Query::query()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 14
nc 8
nop 1
dl 0
loc 22
rs 8.6737
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
    public function __construct($instance = null) {
35
        if (!$instance) {
36
            $this->curInstance = \App::$cur->db->connection;
37
        } else {
38
            $this->curInstance = $instance;
39
        }
40
    }
41
42
    public function insert($table, $data) {
43
        $this->operation = 'INSERT';
44
        $this->table = $table;
45
        $this->cols = $data;
46
        $this->query();
47
        return $this->curInstance->pdo->lastInsertId();
48
    }
49
50
    public function select($table) {
51
        $this->operation = 'SELECT';
52
        $this->table = $table;
53
        return $this->query();
54
    }
55
56
    public function update($table, $data) {
57
        $this->operation = 'UPDATE';
58
        $this->table = $table;
59
        $this->cols = $data;
60
        $result = $this->query();
61
        return $result->pdoResult->rowCount();
62
    }
63
64
    public function delete($table) {
65
        $this->operation = 'DELETE';
66
        $this->table = $table;
67
        $result = $this->query();
68
        return $result->pdoResult->rowCount();
69
    }
70
71
    public function createTable($table_name, $cols, $indexes = []) {
72
        $this->operation = 'CREATE TABLE';
73
        $this->table = $table_name;
74
        $this->cols = $cols;
75
        $this->indexes = $indexes;
76
        return $this->query();
77
    }
78
79
    public function cols($cols) {
80
        if (is_array($cols)) {
81
            $this->cols = array_merge($this->cols, array_values($cols));
82
        } else {
83
            $this->cols[] = $cols;
84
        }
85
    }
86
87
    public function join($table, $where = false, $type = 'LEFT', $alias = '') {
88
        if (is_array($table)) {
89
            foreach ($table as $item) {
90
                if (!is_array($item)) {
91
                    call_user_func_array(array($this, 'join'), $table);
92
                    break;
93
                } else {
94
                    $this->join($item);
95
                }
96
            }
97
        } else {
98
            $this->join[] = [$table, $where, $type, $alias];
99
        }
100
    }
101
102
    public function where($where = '', $value = '', $operation = false, $concatenation = 'AND') {
103
        if (!is_array($where)) {
104
            $this->where[] = [$where, $value, $operation, $concatenation];
105
        } else {
106
            $this->where[] = $where;
107
        }
108
    }
109
110
    public function having($where = '', $value = '', $operation = false, $concatenation = 'AND') {
111
        if (!is_array($where)) {
112
            $this->having[] = [$where, $value, $operation, $concatenation];
113
        } else {
114
            $this->having[] = $where;
115
        }
116
    }
117
118
    public function group($colname) {
119
        $this->group[] = $colname;
120
    }
121
122
    public function order($order, $type = 'ASC') {
123
124
125
        if (!is_array($order)) {
126
            $this->order[] = "{$order} {$type}";
127
        } else {
128
            foreach ($order as $item) {
129
                            if (!is_array($item)) {
130
                    call_user_func_array(array($this, 'order'), $order);
131
            }
132
                    break;
133
                } else {
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

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