Passed
Push — master ( 286385...e07415 )
by Agel_Nash
02:39
created

Database::save()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 6
nop 3
dl 0
loc 15
ccs 8
cts 8
cp 1
crap 4
rs 9.2
c 0
b 0
f 0
1
<?php namespace AgelxNash\Modx\Evo\Database;
2
3
class Database implements Interfaces\DatabaseInterface
4
{
5
    use Traits\DebugTrait,
6
        Traits\SupportTrait;
7
8
    /**
9
     * @var array
10
     */
11
    protected $config = [];
12
13
    /**
14
     * @var Drivers\MySqliDriver
15
     */
16
    protected $driver;
17
18
    /**
19
     * @var int
20
     */
21
    protected $safeLoopCount = 1000;
22
23
    /**
24
     * @param string $host
25
     * @param string $base
26
     * @param string $user
27
     * @param string $pass
28
     * @param string $prefix
29
     * @param string $charset
30
     * @param string $method
31
     * @param string $driver
32
     * @throws Exceptions\Exception
33
     */
34 38
    public function __construct(
35
        $host = '',
36
        $base = '',
37
        $user = '',
38
        $pass = '',
39
        $prefix = '',
40
        $charset = 'utf8mb4',
41
        $method = 'SET CHARACTER SET',
42
        $driver = Drivers\MySqliDriver::class
43
    ) {
44 38
        $base = trim($base, '`');
45
46 38
        $this->setConfig(compact(
47 38
            'host',
48 38
            'base',
49 38
            'user',
50 38
            'pass',
51 38
            'prefix',
52 38
            'charset',
53 38
            'method'
54
        ));
55
56 38
        if (! \in_array(Interfaces\DriverInterface::class, class_implements($driver), true)) {
57
            throw new Exceptions\Exception(
58
                $driver . ' should implements the ' . Interfaces\DriverInterface::class
59
            );
60
        }
61
62 38
        $this->driver = new $driver(
63 38
            $this->getConfig()
64
        );
65 38
    }
66
67
    /**
68
     * @param $data
69
     * @return $this
70
     */
71 38
    public function setConfig($data) : self
72
    {
73 38
        $this->config = $data;
74
75 38
        return $this;
76
    }
77
78
    /**
79
     * @param null|string $key
80
     * @return mixed
81
     */
82 38
    public function getConfig($key = null)
83
    {
84 38
        return ($key === null ? $this->config : ($this->config[$key] ?? null));
85
    }
86
87
    /**
88
     * @return mixed
89
     * @throws Exceptions\Exception
90
     */
91 2
    public function getConnect()
92
    {
93 2
        return $this->getDriver()->getConnect();
94
    }
95
96
    /**
97
     * @return Interfaces\DriverInterface
98
     * @throws Exceptions\Exception
99
     */
100 21
    public function getDriver() : Interfaces\DriverInterface
101
    {
102 21
        return $this->driver;
103
    }
104
105
    /**
106
     * @return mixed
107
     * @throws Exceptions\Exception
108
     */
109 19
    public function connect()
110
    {
111 19
        $tStart = microtime(true);
112
113 19
        $out = $this->getDriver()->connect();
114
115 18
        $totalTime = microtime(true) - $tStart;
116 18
        if ($this->isDebug()) {
117 18
            $this->connectionTime = $totalTime;
118
        }
119
120 18
        return $out;
121
    }
122
123
    /**
124
     * @return $this
125
     */
126 1
    public function disconnect() : self
127
    {
128 1
        $this->getDriver()->disconnect();
129
130 1
        $this->connectionTime = 0;
131 1
        $this->flushExecutedQuery();
132
133 1
        return $this;
134
    }
135
136
    /**
137
     * @return bool
138
     */
139 2
    public function isConnected() : bool
140
    {
141 2
        return $this->getDriver()->isConnected();
142
    }
143
144
    /**
145
     * @param string|array $data
146
     * @param int $safeCount
147
     * @return array|string
148
     * @throws Exceptions\Exception
149
     */
150 1
    public function escape($data, $safeCount = 0)
151
    {
152 1
        $safeCount++;
153 1
        if ($this->safeLoopCount < $safeCount) {
154 1
            throw new Exceptions\TooManyLoopsException("Too many loops '{$safeCount}'");
155
        }
156 1
        if (\is_array($data)) {
157 1
            if (\count($data) === 0) {
158
                $data = '';
159
            } else {
160 1
                foreach ($data as $i => $v) {
161 1
                    $data[$i] = $this->escape($v, $safeCount);
162
                }
163
            }
164
        } else {
165 1
            $data = $this->getDriver()->escape($data);
166
        }
167
168 1
        return $data;
169
    }
170
171
    /**
172
     * @param mixed $sql
173
     * @return mixed
174
     * @throws Exceptions\Exception
175
     */
176 13
    public function query($sql)
177
    {
178 13
        $tStart = microtime(true);
179 13
        if (\is_array($sql)) {
180 1
            $sql = implode("\n", $sql);
181
        }
182 13
        $this->lastQuery = $sql;
183
184 13
        $result = $this->getDriver()->query($this->getLastQuery());
185
186 13
        if ($result === false) {
187
            $this->checkLastError($this->getLastQuery());
188
        } else {
189 13
            $tend = microtime(true);
190 13
            $totalTime = $tend - $tStart;
191 13
            $this->queryTime += $totalTime;
192 13
            if ($this->isDebug()) {
193 13
                $this->collectQuery(
194 13
                    $result,
195 13
                    $this->getLastQuery(),
196 13
                    $this->executedQueries + 1,
197 13
                    $totalTime
198
                );
199
            }
200 13
            $this->executedQueries++;
201
202 13
            return $result;
203
        }
204
        return false;
205
    }
206
207
    /**
208
     * @param string $table
209
     * @param array|string $where
210
     * @param string $orderBy
211
     * @param string $limit
212
     * @return mixed
213
     * @throws Exceptions\Exception
214
     */
215 1
    public function delete($table, $where = '', $orderBy = '', $limit = '')
216
    {
217 1
        $table = $this->prepareFrom($table);
218 1
        $where = $this->prepareWhere($where);
219 1
        $orderBy = $this->prepareOrder($orderBy);
220 1
        $limit = $this->prepareOrder($limit);
221
222 1
        return $this->query("DELETE FROM {$table} {$where} {$orderBy} {$limit}");
223
    }
224
225
    /**
226
     * @param array|string $fields
227
     * @param array|string $tables
228
     * @param array|string $where
229
     * @param string $orderBy
230
     * @param string $limit
231
     * @return mixed
232
     * @throws Exceptions\Exception
233
     */
234 3
    public function select($fields, $tables, $where = '', $orderBy = '', $limit = '')
235
    {
236 3
        $fields = $this->prepareFields($fields);
237 3
        $tables = $this->prepareFrom($tables, true);
238 3
        $where = $this->prepareWhere($where);
239 3
        $orderBy = $this->prepareOrder($orderBy);
240 3
        $limit = $this->prepareLimit($limit);
241
242 3
        return $this->query("SELECT {$fields} FROM {$tables} {$where} {$orderBy} {$limit}");
243
    }
244
245
    /**
246
     * @param array|string $values
247
     * @param string $table
248
     * @param array|string $where
249
     * @return mixed
250
     * @throws Exceptions\Exception
251
     */
252 2
    public function update($values, string $table, $where = '')
253
    {
254 2
        $table = $this->prepareFrom($table);
255 2
        $values = $this->prepareValuesSet($values);
256 2
        if (mb_strtoupper(mb_substr($values, 0, 4)) !== 'SET ') {
257 2
            $values = 'SET ' . $values;
258
        }
259 2
        $where = $this->prepareWhere($where);
260
261 2
        return $this->query("UPDATE {$table} {$values} {$where}");
262
    }
263
264
    /**
265
     * @param array|string $fields
266
     * @param string $table
267
     * @param array|string $fromFields
268
     * @param string $fromTable
269
     * @param array|string $where
270
     * @param string $limit
271
     * @return mixed
272
     * @throws Exceptions\Exception
273
     */
274 4
    public function insert(
275
        $fields,
276
        string $table,
277
        $fromFields = '*',
278
        string $fromTable = '',
279
        $where = '',
280
        string $limit = ''
281
    ) {
282 4
        $table = $this->prepareFrom($table);
283
284 4
        $useFields = null;
285 4
        $lid = null;
286
287 4
        if (\is_array($fields)) {
288 4
            $useFields = empty($fromTable) ?
289 3
                $this->prepareValues($fields) :
290 4
                $this->prepareFields($fields, true);
291
        } else {
292 2
            $useFields = $fields;
293
        }
294
295 4
        if (empty($useFields) || ! \is_scalar($useFields) || ($useFields === '*' && ! empty($fromTable))) {
296
            throw (new Exceptions\InvalidFieldException('Invalid insert fields'))
297
                ->setData($fields);
298
        }
299
300 4
        if (empty($fromTable)) {
301 3
            $this->query("INSERT INTO {$table} {$useFields}");
302
        } else {
303 1
            if (empty($fromFields) || $fromFields === '*') {
304 1
                $fromFields = $this->prepareFields($fields, true);
305
            } else {
306 1
                $fromFields = $this->prepareFields($fromFields, true);
307
            }
308
309 1
            $where = $this->prepareWhere($where);
310 1
            $limit = $this->prepareLimit($limit);
311
312 1
            $lid = $this->query(
313 1
                "INSERT INTO {$table} ({$useFields}) SELECT {$fromFields} FROM {$fromTable} {$where} {$limit}"
314
            );
315
        }
316
317 4
        if ($lid === null && ($lid = $this->getInsertId()) === false) {
318
            throw new Exceptions\GetDataException("Couldn't get last insert key!");
319
        }
320
321 4
        return $this->convertValue($lid);
322
    }
323
324
    /**
325
     * @param string|array $fields
326
     * @param string $table
327
     * @param array|string $where
328
     * @return mixed
329
     * @throws Exceptions\Exception
330
     */
331 1
    public function save($fields, string $table, $where = '')
332
    {
333 1
        if ($where === '') {
334 1
            $mode = 'insert';
335
        } else {
336 1
            $result = $this->select('*', $table, $where);
337
338 1
            if ($this->getRecordCount($result) === 0) {
339 1
                $mode = 'insert';
340
            } else {
341 1
                $mode = 'update';
342
            }
343
        }
344
345 1
        return ($mode === 'insert') ? $this->insert($fields, $table) : $this->update($fields, $table, $where);
346
    }
347
348
    /**
349
     * @param $result
350
     * @return bool
351
     */
352 1
    public function isResult($result) : bool
353
    {
354 1
        return $this->getDriver()->isResult($result);
355
    }
356
357
    /**
358
     * @param $result
359
     * @return int
360
     */
361 1
    public function numFields($result) : int
362
    {
363 1
        return $this->getDriver()->numFields($result);
364
    }
365
366
    /**
367
     * @param $result
368
     * @param int $col
369
     * @return string|null
370
     */
371 1
    public function fieldName($result, $col = 0) :? string
372
    {
373 1
        return $this->getDriver()->fieldName($result, $col);
374
    }
375
376
    /**
377
     * @param string $charset
378
     * @param string|null $method
379
     * @return bool
380
     * @throws Exceptions\Exception
381
     */
382
    public function setCharset(string $charset, $method = null) : bool
383
    {
384
        if ($method !== null) {
385
            $this->query($method . ' ' . $charset);
386
        }
387
388
        $tStart = microtime(true);
389
390
        $result = $this->getDriver()->setCharset($charset);
391
392
        $this->queryTime += microtime(true) - $tStart;
393
394
        return $result;
395
    }
396
397
    /**
398
     * @param string $name
399
     * @return bool
400
     * @throws Exceptions\Exception
401
     */
402
    public function selectDb(string $name) : bool
403
    {
404
        $tStart = microtime(true);
405
406
        $result = $this->getDriver()->selectDb($name);
407
408
        $this->queryTime += microtime(true) - $tStart;
409
410
        return $result;
411
    }
412
413
    /**
414
     * @param $result
415
     * @return int
416
     */
417 9
    public function getRecordCount($result) : int
418
    {
419 9
        return $this->getDriver()->getRecordCount($result);
420
    }
421
422
    /**
423
     * @param $result
424
     * @param string $mode
425
     * @return array|mixed|object|\stdClass
426
     * @throws Exceptions\Exception
427
     */
428 1
    public function getRow($result, $mode = 'assoc')
429
    {
430 1
        return $this->getDriver()->getRow($result, $mode);
431
    }
432
433
    /**
434
     * @param string string $name
435
     * @param mixed $result
436
     * @return array
437
     * @throws Exceptions\Exception
438
     */
439 3
    public function getColumn(string $name, $result) : array
440
    {
441 3
        if (\is_scalar($result)) {
442 2
            $result = $this->query($result);
443
        }
444
445 3
        return $this->getDriver()->getColumn($name, $result);
446
    }
447
448
    /**
449
     * @param mixed $result
450
     * @return array
451
     * @throws Exceptions\Exception
452
     */
453 1
    public function getColumnNames($result) : array
454
    {
455 1
        if (\is_scalar($result)) {
456
            $result = $this->query($result);
457
        }
458
459 1
        return $this->getDriver()->getColumnNames($result);
460
    }
461
462
    /**
463
     * @param mixed $result
464
     * @return bool|mixed
465
     * @throws Exceptions\Exception
466
     */
467 3
    public function getValue($result)
468
    {
469 3
        if (\is_scalar($result)) {
470 3
            $result = $this->query($result);
471
        }
472
473 3
        return $this->convertValue(
474 3
            $this->getDriver()->getValue($result)
475
        );
476
    }
477
478
    /**
479
     * @param string $table
480
     * @return array
481
     * @throws Exceptions\Exception
482
     */
483 1
    public function getTableMetaData(string $table) : array
484
    {
485 1
        $metadata = [];
486 1
        if (! empty($table)) {
487 1
            $sql = 'SHOW FIELDS FROM ' . $table;
488 1
            $result = $this->query($sql);
489 1
            $metadata = $this->getDriver()->getTableMetaData($result);
490
        }
491
492 1
        return $metadata;
493
    }
494
495
    /**
496
     * @param $result
497
     * @param bool $index
498
     * @return array
499
     * @throws Exceptions\Exception
500
     */
501 1
    public function makeArray($result, bool $index = false) : array
502
    {
503 1
        $rsArray = [];
504 1
        $iterator = 0;
505 1
        while ($row = $this->getRow($result)) {
506 1
            $returnIndex = $index !== false && isset($row[$index]) ? $row[$index] : $iterator;
507 1
            $rsArray[$returnIndex] = $row;
508 1
            $iterator++;
509
        }
510
511 1
        return $rsArray;
512
    }
513
514
    /**
515
     * @return string
516
     * @throws Exceptions\Exception
517
     */
518 1
    public function getVersion() : string
519
    {
520 1
        return $this->getDriver()->getVersion();
521
    }
522
523
    /**
524
     * @param string $table
525
     * @return mixed
526
     * @throws Exceptions\Exception
527
     */
528 1
    public function optimize(string $table)
529
    {
530 1
        $result = $this->query('OPTIMIZE TABLE ' . $table);
531 1
        if ($result !== false) {
532 1
            $result = $this->query('ALTER TABLE ' . $table);
533
        }
534
535 1
        return $result;
536
    }
537
538
    /**
539
     * @param string $table
540
     * @return mixed
541
     * @throws Exceptions\Exception
542
     */
543 1
    public function truncate(string $table)
544
    {
545 1
        return $this->query('TRUNCATE ' . $table);
546
    }
547
548
    /**
549
     * @return mixed
550
     * @throws Exceptions\Exception
551
     */
552 3
    public function getInsertId()
553
    {
554 3
        return $this->convertValue(
555 3
            $this->getDriver()->getInsertId()
556
        );
557
    }
558
559
    /**
560
     * @return int
561
     * @throws Exceptions\Exception
562
     */
563 7
    public function getAffectedRows() : int
564
    {
565 7
        return $this->getDriver()->getAffectedRows();
566
    }
567
}
568