Passed
Push — master ( 8b3112...1638bc )
by Agel_Nash
02:38
created

AbstractDatabase::insertFrom()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 8.1515

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 13
cts 15
cp 0.8667
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 15
nc 10
nop 6
crap 8.1515
1
<?php namespace AgelxNash\Modx\Evo\Database;
2
3
abstract class AbstractDatabase implements Interfaces\DatabaseInterface, Interfaces\DebugInterface
4
{
5
    use Traits\DebugTrait,
6
        Traits\SupportTrait,
7
        Traits\ConfigTrait;
8
9
    /**
10
     * @var Interfaces\DriverInterface
11
     */
12
    protected $driver;
13
14
    /**
15
     * @var int
16
     */
17
    protected $safeLoopCount = 1000;
18
19
    /**
20
     * {@inheritDoc}
21
     */
22 2
    public function getLastError()
23
    {
24 2
        return $this->getDriver()->getLastError();
25
    }
26
27
    /**
28
     * {@inheritDoc}
29
     */
30 6
    public function getLastErrorNo()
31
    {
32 6
        return (string)$this->getDriver()->getLastErrorNo();
33
    }
34
35
    /**
36
     * {@inheritDoc}
37
     */
38 53
    public function connect()
39
    {
40 53
        $tStart = microtime(true);
41
42 53
        $out = $this->getDriver()->getConnect();
43
44 52
        $totalTime = microtime(true) - $tStart;
45 52
        if ($this->isDebug()) {
46 52
            $this->setConnectionTime($totalTime);
47
        }
48 52
        $this->setCharset(
49 52
            $this->getConfig('charset'),
50 52
            $this->getConfig('method')
51
        );
52
53 52
        return $out;
54
    }
55
56
    /**
57
     * {@inheritDoc}
58
     */
59 4
    public function disconnect()
60
    {
61 4
        $this->getDriver()->disconnect();
62
63 4
        $this->setConnectionTime(0);
64 4
        $this->flushExecutedQuery();
65
66 4
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type AgelxNash\Modx\Evo\Database\AbstractDatabase which is incompatible with the return type mandated by AgelxNash\Modx\Evo\Datab...Interface::disconnect() of AgelxNash\Modx\Evo\Datab...erfaces\DriverInterface.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
67
    }
68
69
    /**
70
     * {@inheritDoc}
71
     */
72 13
    public function isResult($result)
73
    {
74 13
        return $this->getDriver()->isResult($result);
75
    }
76
77
    /**
78
     * {@inheritDoc}
79
     */
80 2
    public function numFields($result)
81
    {
82 2
        return $this->getDriver()->numFields($result);
83
    }
84
85
    /**
86
     * {@inheritDoc}
87
     */
88 2
    public function fieldName($result, $col = 0)
89
    {
90 2
        return $this->getDriver()->fieldName($result, $col);
91
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96 52
    public function setCharset($charset, $method = null)
97
    {
98 52
        return $this->getDriver()->setCharset($charset, $method);
99
    }
100
101
    /**
102
     * {@inheritDoc}
103
     */
104 2
    public function selectDb($name)
105
    {
106 2
        $tStart = microtime(true);
107
108 2
        $result = $this->getDriver()->selectDb($name);
109
110 2
        $this->addQueriesTime(microtime(true) - $tStart);
111
112 2
        return $result;
113
    }
114
115
    /**
116
     * {@inheritDoc}
117
     * @throws Exceptions\TooManyLoopsException
118
     */
119 2
    public function escape($data, $safeCount = 0)
120
    {
121 2
        $safeCount++;
122 2
        if ($this->safeLoopCount < $safeCount) {
123 2
            throw new Exceptions\TooManyLoopsException("Too many loops '{$safeCount}'");
124
        }
125 2
        if (\is_array($data)) {
126 2
            if (\count($data) === 0) {
127 2
                $data = '';
128
            } else {
129 2
                foreach ($data as $i => $v) {
130 2
                    $data[$i] = $this->escape($v, $safeCount);
131
                }
132
            }
133
        } else {
134 2
            $data = $this->getDriver()->escape($data);
135
        }
136
137 2
        return $data;
138
    }
139
140
    /**
141
     * @param string|array $sql
142
     * {@inheritDoc}
143
     */
144 39
    public function query($sql)
145
    {
146 39
        $tStart = microtime(true);
147 39
        if (\is_array($sql)) {
148 2
            $sql = implode("\n", $sql);
149
        }
150 39
        $this->setLastQuery($sql);
151
152 39
        $result = $this->getDriver()->query(
153 39
            $this->getLastQuery()
154
        );
155
156 37
        if ($result === false) {
157
            /**
158
             * @TODO: NOT WORK?
159
             */
160
            $this->checkLastError($this->getLastQuery());
161
        } else {
162 37
            $tend = microtime(true);
163 37
            $totalTime = $tend - $tStart;
164 37
            $this->addQueriesTime($totalTime);
165 37
            if ($this->isDebug()) {
166 37
                $this->collectQuery(
167 37
                    $result,
168 37
                    $this->getLastQuery(),
169 37
                    $totalTime
170
                );
171
            }
172
        }
173
174 37
        return $result;
175
    }
176
177
    /**
178
     * {@inheritDoc}
179
     */
180 27
    public function getRecordCount($result)
181
    {
182 27
        return $this->getDriver()->getRecordCount($result);
183
    }
184
185
    /**
186
     * {@inheritDoc}
187
     */
188 7
    public function getRow($result, $mode = 'assoc')
189
    {
190 7
        if (\is_scalar($result)) {
191 3
            $result = $this->query($result);
192
        }
193
194 7
        return $this->getDriver()->getRow($result, $mode);
195
    }
196
197
    /**
198
     * {@inheritDoc}
199
     */
200 6
    public function getVersion()
201
    {
202 6
        return $this->getDriver()->getVersion();
203
    }
204
205
    /**
206
     * {@inheritDoc}
207
     */
208 6
    public function getInsertId()
209
    {
210 6
        return $this->convertValue(
211 6
            $this->getDriver()->getInsertId()
212
        );
213
    }
214
215
    /**
216
     * {@inheritDoc}
217
     */
218 16
    public function getAffectedRows()
219
    {
220 16
        return $this->getDriver()->getAffectedRows();
221
    }
222
223
    /**
224
     * {@inheritDoc}
225
     */
226 6
    public function getColumn($name, $result)
227
    {
228 6
        if (\is_scalar($result)) {
229 6
            $result = $this->query($result);
230
        }
231
232 6
        return $this->getDriver()->getColumn($name, $result);
233
    }
234
235
    /**
236
     * {@inheritDoc}
237
     */
238 2
    public function getColumnNames($result)
239
    {
240 2
        if (\is_scalar($result)) {
241 2
            $result = $this->query($result);
242
        }
243 2
        return $this->getDriver()->getColumnNames($result);
244
    }
245
246
    /**
247
     * {@inheritDoc}
248
     */
249 8
    public function getValue($result)
250
    {
251 8
        if (\is_scalar($result)) {
252 8
            $result = $this->query($result);
253
        }
254
255 8
        return $this->convertValue(
256 8
            $this->getDriver()->getValue($result)
257
        );
258
    }
259
260
    /**
261
     * {@inheritDoc}
262
     */
263 2
    public function getTableMetaData($table)
264
    {
265 2
        $metadata = [];
266 2
        if (! empty($table)) {
267 2
            $sql = 'SHOW FIELDS FROM ' . $table;
268 2
            $result = $this->query($sql);
269 2
            $metadata = $this->getDriver()->getTableMetaData($result);
270
        }
271
272 2
        return $metadata;
273
    }
274
275
    /**
276
     * {@inheritDoc}
277
     */
278 58
    public function getDriver()
279
    {
280 58
        return $this->driver;
281
    }
282
283
    /**
284
     * {@inheritDoc}
285
     */
286 80
    public function setDriver($driver)
287
    {
288 80
        if (! \in_array(Interfaces\DriverInterface::class, class_implements($driver), true)) {
289 1
            throw new Exceptions\DriverException(
290 1
                $driver . ' should implements the ' . Interfaces\DriverInterface::class
291
            );
292
        }
293
294 80
        if (is_scalar($driver)) {
295 76
            $this->driver = new $driver($this->getConfig());
296
        } else {
297 4
            $this->driver = $driver;
298 4
            $this->config = array_merge($this->config, $driver->getConfig());
299
        }
300
301 80
        return $this->driver;
302
    }
303
304
    /**
305
     * {@inheritDoc}
306
     */
307 2
    public function makeArray($result, $index = false)
308
    {
309 2
        $rsArray = [];
310 2
        $iterator = 0;
311 2
        while ($row = $this->getRow($result)) {
312 2
            $returnIndex = $index !== false && isset($row[$index]) ? $row[$index] : $iterator;
313 2
            $rsArray[$returnIndex] = $row;
314 2
            $iterator++;
315
        }
316
317 2
        return $rsArray;
318
    }
319
320
    /**
321
     * {@inheritDoc}
322
     */
323 2
    public function delete($table, $where = '', $orderBy = '', $limit = '')
324
    {
325 2
        $table = $this->prepareFrom($table);
326 2
        $where = $this->prepareWhere($where);
327 2
        $orderBy = $this->prepareOrder($orderBy);
328 2
        $limit = $this->prepareLimit($limit);
329
330 2
        $result = $this->query("DELETE FROM {$table} {$where} {$orderBy} {$limit}");
331 2
        return $this->isResult($result) ? true : $result;
332
    }
333
334
    /**
335
     * {@inheritDoc}
336
     */
337 6
    public function select($fields, $tables, $where = '', $orderBy = '', $limit = '')
338
    {
339 6
        $fields = $this->prepareFields($fields);
340 6
        $tables = $this->prepareFrom($tables, true);
341 6
        $where = $this->prepareWhere($where);
342 6
        $orderBy = $this->prepareOrder($orderBy);
343 6
        $limit = $this->prepareLimit($limit);
344
345 6
        return $this->query("SELECT {$fields} FROM {$tables} {$where} {$orderBy} {$limit}");
346
    }
347
348
    /**
349
     * {@inheritDoc}
350
     */
351 4
    public function update($values, $table, $where = '')
352
    {
353 4
        $table = $this->prepareFrom($table);
354 4
        $values = $this->prepareValuesSet($values);
355 4
        if (mb_strtoupper(mb_substr($values, 0, 4)) !== 'SET ') {
356 4
            $values = 'SET ' . $values;
357
        }
358 4
        $where = $this->prepareWhere($where);
359
360 4
        $result = $this->query("UPDATE {$table} {$values} {$where}");
361 4
        return $this->isResult($result) ? true : $result;
362
    }
363
364
    /**
365
     * {@inheritDoc}
366
     */
367 9
    public function insert(
368
        $fields,
369
        $table,
370
        $fromFields = '*',
371
        $fromTable = '',
372
        $where = '',
373
        $limit = ''
374
    ) {
375 9
        $lid = null;
376
377 9
        if (empty($fromTable)) {
378 7
            $table = $this->prepareFrom($table);
379 7
            $useFields = \is_array($fields) ? $this->prepareValues($fields) : $fields;
380
381 7
            if (empty($useFields) || ! \is_scalar($useFields) || $useFields === '*') {
382 1
                throw (new Exceptions\InvalidFieldException('Invalid insert fields'))
383 1
                    ->setData($fields);
384
            }
385
386 6
            $this->query("INSERT INTO {$table} {$useFields}");
387
        } else {
388 2
            $lid = $this->insertFrom($fields, $table, $fromFields, $fromTable, $where, $limit);
389
        }
390
391 8
        if ($lid === null && ($lid = $this->getInsertId()) === false) {
392
            throw new Exceptions\GetDataException("Couldn't get last insert key!");
393
        }
394
395 8
        return $this->convertValue($lid);
396
    }
397
398
    /**
399
     * @param string|array $fields
400
     * @param string $table
401
     * @param string|array $fromFields
402
     * @param string $fromTable
403
     * @param string|array $where
404
     * @param string $limit
405
     * @return mixed
406
     * @throws Exceptions\InvalidFieldException
407
     * @throws Exceptions\TableNotDefinedException
408
     */
409 2
    public function insertFrom(
410
        $fields,
411
        $table,
412
        $fromFields = '*',
413
        $fromTable = '',
414
        $where = '',
415
        $limit = ''
416
    ) {
417 2
        $table = $this->prepareFrom($table);
418 2
        $lid = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $lid is dead and can be removed.
Loading history...
419 2
        $useFields = \is_array($fields) ? $this->prepareFields($fields, true) : $fields;
420
421 2
        if (empty($useFields) || ! \is_scalar($useFields) || $useFields === '*') {
422
            throw (new Exceptions\InvalidFieldException('Invalid insert fields'))
423
                ->setData($fields);
0 ignored issues
show
Bug introduced by
It seems like $fields can also be of type array; however, parameter $data of AgelxNash\Modx\Evo\Datab...eldException::setData() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

423
                ->setData(/** @scrutinizer ignore-type */ $fields);
Loading history...
424
        }
425
426 2
        if (empty($fromFields) || $fromFields === '*') {
427 2
            $fromFields = $this->prepareFields($fields, true);
428
        } else {
429 2
            $fromFields = $this->prepareFields($fromFields, true);
430
        }
431
432 2
        $where = $this->prepareWhere($where);
433 2
        $limit = $this->prepareLimit($limit);
434
435 2
        $lid = $this->query(
436 2
            "INSERT INTO {$table} ({$useFields}) SELECT {$fromFields} FROM {$fromTable} {$where} {$limit}"
437
        );
438
439 2
        return $this->isResult($lid) ? true : $lid;
440
    }
441
442
    /**
443
     * {@inheritDoc}
444
     */
445 2
    public function save($fields, $table, $where = '')
446
    {
447 2
        if ($where === '') {
448 2
            $mode = 'insert';
449
        } else {
450 2
            $result = $this->select('*', $table, $where);
451
452 2
            if ($this->getRecordCount($result) === 0) {
453 2
                $mode = 'insert';
454
            } else {
455 2
                $mode = 'update';
456
            }
457
        }
458
459 2
        return ($mode === 'insert') ? $this->insert($fields, $table) : $this->update($fields, $table, $where);
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465 2
    public function optimize($table)
466
    {
467 2
        $result = $this->query('OPTIMIZE TABLE ' . $table);
468 2
        if ($result !== false) {
469 2
            $result = $this->alterTable($table);
470
        }
471
472 2
        return $this->isResult($result) ? true : $result;
473
    }
474
475
    /**
476
     * {@inheritDoc}
477
     */
478 4
    public function alterTable($table)
479
    {
480 4
        $result = $this->query('ALTER TABLE ' . $table);
481
482 4
        return $this->isResult($result) ? true : $result;
483
    }
484
485
    /**
486
     * {@inheritDoc}
487
     */
488 2
    public function truncate($table)
489
    {
490 2
        $result = $this->query('TRUNCATE ' . $table);
491
492 2
        return $this->isResult($result) ? $this->getValue($result) : $result;
493
    }
494
495
    /**
496
     * {@inheritDoc}
497
     */
498 55
    public function getTableName($table, $escape = true)
499
    {
500 55
        if (empty($table)) {
501 2
            throw new Exceptions\TableNotDefinedException($table);
502
        }
503
504 55
        $out = $this->getConfig('prefix') . $table;
505
506 55
        return $escape ? '`' . $out . '`' : $out;
507
    }
508
509
    /**
510
     * {@inheritDoc}
511
     */
512 53
    public function getFullTableName($table)
513
    {
514 53
        if (empty($table)) {
515 1
            throw new Exceptions\TableNotDefinedException($table);
516
        }
517
518 53
        return implode('.', [
519 53
            '`' . $this->getConfig('database') . '`',
520 53
            $this->getTableName($table)
521
        ]);
522
    }
523
}
524