Passed
Push — master ( 35c865...a45e07 )
by Agel_Nash
03:00
created

IlluminateDriver   F

Complexity

Total Complexity 73

Size/Duplication

Total Lines 474
Duplicated Lines 0 %

Test Coverage

Coverage 89.29%

Importance

Changes 0
Metric Value
wmc 73
dl 0
loc 474
ccs 150
cts 168
cp 0.8929
rs 2.459
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 39 5
B useEloquent() 0 20 5
A isResult() 0 3 1
B getRow() 0 22 5
A getInsertId() 0 3 1
A getAffectedRows() 0 3 1
A getConnect() 0 10 3
A isConnected() 0 3 2
A disconnect() 0 9 2
A escape() 0 9 2
A getCapsule() 0 3 1
A connect() 0 23 3
D query() 0 27 11
A getConfig() 0 3 1
A setCharset() 0 8 2
A getVersion() 0 3 1
A hasConnectionName() 0 4 1
A selectDb() 0 5 1
A dataSeek() 0 3 1
A getColumnNames() 0 12 3
A fieldName() 0 4 3
A getValue() 0 10 3
A getColumn() 0 13 4
A getTableMetaData() 0 12 3
A numFields() 0 3 2
A getLastErrorNo() 0 5 2
A getLastError() 0 5 2
A getRecordCount() 0 3 2

How to fix   Complexity   

Complex Class

Complex classes like IlluminateDriver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use IlluminateDriver, and based on these observations, apply Extract Interface, too.

1
<?php namespace AgelxNash\Modx\Evo\Database\Drivers;
2
3
use AgelxNash\Modx\Evo\Database\Interfaces\DriverInterface;
4
use AgelxNash\Modx\Evo\Database\Exceptions;
5
use Illuminate\Database\Capsule\Manager as Capsule;
6
use Illuminate\Database\Connection;
7
use PDOStatement;
8
use Illuminate\Events\Dispatcher;
9
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
10
use Illuminate\Container\Container;
11
use ReflectionClass;
12
use PDO;
13
14
class IlluminateDriver implements DriverInterface
15
{
16
    /**
17
     * @var Connection
18
     */
19
    protected $conn;
20
21
    /**
22
     * @var string
23
     */
24
    protected $connection = 'default';
25
26
    /**
27
     * @var Capsule
28
     */
29
    protected $capsule;
30
31
    private $affectedRows = 0;
32
33
    /**
34
     * @var array
35
     */
36
    protected $config;
37
38
    /**
39
     * @var array
40
     */
41
    public $lastError;
42
43
    /**
44
     * @var string
45
     */
46
    public $lastErrorNo;
47
48
    protected $driver = 'mysql';
49
50
    /**
51
     * {@inheritDoc}
52
     */
53 23
    public function __construct(array $config = [], $connection = 'default')
54
    {
55 23
        $reflection = new ReflectionClass(Capsule::class);
56 23
        $property = $reflection->getProperty('instance');
57 23
        $property->setAccessible(true);
58
        /**
59
         * @var Capsule|null $capsule
60
         */
61 23
        $capsule = $property->getValue(Capsule::class);
0 ignored issues
show
Bug introduced by
Illuminate\Database\Capsule\Manager::class of type string is incompatible with the type object expected by parameter $object of ReflectionProperty::getValue(). ( Ignorable by Annotation )

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

61
        $capsule = $property->getValue(/** @scrutinizer ignore-type */ Capsule::class);
Loading history...
62 23
        if ($capsule === null) {
63 1
            $this->capsule = new Capsule;
64
65 1
            $this->getCapsule()->setAsGlobal();
66
        } else {
67 22
            $this->capsule = $capsule;
68
        }
69
70 23
        if ($this->hasConnectionName($connection)) {
71 22
            if (empty($config)) {
72 2
                $config = $this->getCapsule()->getConnection($connection)->getConfig();
73 2
                unset($config['name'], $config['driver']);
74
            } else {
75 20
                $diff = array_diff_assoc(
76 20
                    array_merge(['driver' => $this->driver], $config),
77 20
                    $this->getCapsule()->getConnection($connection)->getConfig()
78
                );
79 20
                if (array_intersect(['driver', 'host', 'database', 'password', 'username'], array_keys($diff))) {
80 1
                    throw new Exceptions\ConnectException(
81 1
                        sprintf('The connection name "%s" is already used', $connection)
82
                    );
83
                }
84
            }
85
        }
86
87 22
        $this->connection = $connection;
88
89 22
        $this->useEloquent();
90
91 22
        $this->config = $config;
92 22
    }
93
94
    /**
95
     * @param DispatcherContract|null $dispatcher
96
     * @return bool
97
     */
98 22
    public function useEloquent(DispatcherContract $dispatcher = null)
99
    {
100 22
        $out = false;
101 22
        if ($dispatcher === null) {
102 22
            $dispatcher = $this->getCapsule()->getEventDispatcher();
103
        }
104
105 22
        if ($dispatcher === null && class_exists(Dispatcher::class)) {
106 3
            $dispatcher = new Dispatcher(new Container);
107
        }
108
109 22
        if ($dispatcher !== null) {
110 22
            $this->getCapsule()->setEventDispatcher($dispatcher);
111
112 22
            $out = true;
113
        }
114
115 22
        $this->getCapsule()->bootEloquent();
116
117 22
        return $out;
118
    }
119
120
    /**
121
     * @return array
122
     */
123 3
    public function getConfig()
124
    {
125 3
        return $this->config;
126
    }
127
128
    /**
129
     * @return Capsule
130
     */
131 23
    public function getCapsule()
132
    {
133 23
        return $this->capsule;
134
    }
135
136
    /**
137
     * @return Connection
138
     * @throws Exceptions\Exception
139
     */
140 22
    public function getConnect()
141
    {
142 22
        if (! $this->isConnected()) {
143 22
            $this->connect();
144 22
            if (! $this->conn->getPdo() instanceof PDO) {
0 ignored issues
show
introduced by
$this->conn->getPdo() is always a sub-type of PDO.
Loading history...
Bug introduced by
The method getPdo() does not exist on null. ( Ignorable by Annotation )

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

144
            if (! $this->conn->/** @scrutinizer ignore-call */ getPdo() instanceof PDO) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
145 1
                $this->conn->reconnect();
146
            }
147
        }
148
149 22
        return $this->conn;
150
    }
151
152
    /**
153
     * @param $name
154
     * @return bool
155
     */
156 23
    public function hasConnectionName($name)
157
    {
158 23
        $connections = $this->getCapsule()->getDatabaseManager()->getConnections();
159 23
        return isset($connections[$name]);
160
    }
161
162
    /**
163
     * @return Connection
164
     * @throws Exceptions\Exception
165
     */
166 22
    public function connect()
167
    {
168
        try {
169 22
            if (! $this->hasConnectionName($this->connection)) {
170 1
                $this->getCapsule()->addConnection([
171 1
                    'driver'    => $this->driver,
172 1
                    'host'      => $this->config['host'],
173 1
                    'database'  => $this->config['database'],
174 1
                    'username'  => $this->config['username'],
175 1
                    'password'  => $this->config['password'],
176 1
                    'charset'   => $this->config['charset'],
177 1
                    'collation' => $this->config['collation'],
178 1
                    'prefix'    => $this->config['prefix'],
179 1
                ], $this->connection);
180
            }
181
182 22
            $this->conn = $this->getCapsule()->getConnection($this->connection);
183
        } catch (\Exception $exception) {
184
            $this->conn = null;
185
            throw new Exceptions\ConnectException($exception->getMessage(), $exception->getCode());
186
        }
187
188 22
        return $this->conn;
189
    }
190
191
    /**
192
     * {@inheritDoc}
193
     */
194 1
    public function disconnect()
195
    {
196 1
        if ($this->isConnected()) {
197 1
            $this->conn->disconnect();
198
        }
199
200 1
        $this->conn = null;
201
202 1
        return $this;
203
    }
204
205
    /**
206
     * @return bool
207
     */
208 22
    public function isConnected()
209
    {
210 22
        return ($this->conn instanceof Connection && $this->conn->getDatabaseName());
211
    }
212
213
    /**
214
     * @param $data
215
     * @return mixed
216
     * @throws Exceptions\Exception
217
     */
218 1
    public function escape($data)
219
    {
220
        /**
221
         * It's not secure
222
         * But need for backward compatibility
223
         */
224
225 1
        $quote = $this->getConnect()->getPdo()->quote($data);
226 1
        return strpos($quote, '\'') === 0 ? mb_substr($quote, 1, -1) : $quote;
227
    }
228
229
    /**
230
     * @return mixed
231
     * @throws Exceptions\Exception
232
     */
233 3
    public function getInsertId()
234
    {
235 3
        return $this->getConnect()->getPdo()->lastInsertId();
236
    }
237
238
    /**
239
     * @return int
240
     * @throws Exceptions\Exception
241
     */
242 8
    public function getAffectedRows()
243
    {
244 8
        return $this->affectedRows;
245
    }
246
247
    /**
248
     * @return string
249
     * @throws Exceptions\Exception
250
     */
251 4
    public function getVersion()
252
    {
253 4
        return $this->getConnect()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
254
        //return $this->getConnect()->server_info;
255
    }
256
257
    /**
258
     * @param PDOStatement $result
259
     * @return int
260
     */
261 9
    public function getRecordCount($result)
262
    {
263 9
        return $this->isResult($result) ? $result->rowCount() : 0;
264
    }
265
266
    /**
267
     * {@inheritDoc}
268
     */
269 19
    public function setCharset($charset, $collation, $method = null)
270
    {
271 19
        if ($method === null) {
272
            $method = $this->config['method'];
273
        }
274 19
        $query = $method . ' ' . $charset . ' COLLATE ' . $collation;
275
276 19
        return (bool)$this->query($query);
277
    }
278
279
    /**
280
     * @param $result
281
     * @return bool
282
     */
283 19
    public function isResult($result)
284
    {
285 19
        return $result instanceof PDOStatement;
286
    }
287
288
    /**
289
     * @param PDOStatement $result
290
     * @return int
291
     */
292 1
    public function numFields($result)
293
    {
294 1
        return $this->isResult($result) ? $result->columnCount() : 0;
295
    }
296
297
    /**
298
     * @param PDOStatement $result
299
     * @param int $col
300
     * @return string|null
301
     */
302 1
    public function fieldName($result, $col = 0)
303
    {
304 1
        $field = $this->isResult($result) ? $result->getColumnMeta($col) : [];
305 1
        return isset($field['name']) ? $field['name'] : null;
306
    }
307
308
    /**
309
     * @param string $name
310
     * @return bool
311
     * @throws Exceptions\Exception
312
     */
313
    public function selectDb($name)
314
    {
315
        $this->getConnect()->setDatabaseName($name);
316
317
        return true;
318
    }
319
320
    /**
321
     * @param PDOStatement $result
322
     * @param string $mode
323
     * @return array|mixed|object|\stdClass
324
     * @throws Exceptions\Exception
325
     */
326 7
    public function getRow($result, $mode = 'assoc')
327
    {
328
        switch ($mode) {
329 7
            case 'assoc':
330 5
                $out = $result->fetch(\PDO::FETCH_ASSOC);
331 5
                break;
332 3
            case 'num':
333 3
                $out = $result->fetch(\PDO::FETCH_NUM);
334 3
                break;
335
            case 'object':
336
                $out = $result->fetchObject();
337
                break;
338
            case 'both':
339
                $out = $result->fetch(\PDO::FETCH_BOTH);
340
                break;
341
            default:
342
                throw new Exceptions\UnknownFetchTypeException(
343
                    "Unknown get type ($mode) specified for fetchRow - must be empty, 'assoc', 'num' or 'both'."
344
                );
345
        }
346
347 7
        return $out;
348
    }
349
350
    /**
351
     * @param string $query
352
     * @return mixed
353
     * @throws Exceptions\Exception
354
     */
355 19
    public function query($query)
356
    {
357 19
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
358
        try {
359 19
            $result = $this->getConnect()->getPdo()->prepare(
360 19
                $query,
361
                [
362 19
                    \PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL
363
                ]
364
            );
365 19
            $result->setFetchMode(\PDO::FETCH_ASSOC);
366 19
            if ($result->execute() === false) {
367
                $result = false;
368
            }
369 19
            $this->affectedRows = \is_bool($result) ? 0 : $result->rowCount();
370 19
            if ($this->affectedRows === 0 && $this->isResult($result) && 0 !== mb_stripos(trim($query), 'SELECT')) {
371 19
                $result = true;
372
            }
373 2
        } catch (\Exception $exception) {
374 2
            $this->lastError = $this->isResult($result) ? $result->errorInfo() : [];
375 2
            $code = $this->isResult($result) ? $result->errorCode() : '';
376 2
            $this->lastErrorNo = $this->isResult($result) ? (empty($code) ? $exception->getCode() : $code) : '';
377 2
            throw (new Exceptions\QueryException($exception->getMessage(), $exception->getCode()))
378 2
                ->setQuery($query);
379
        }
380
381 19
        return $result;
382
    }
383
384
    /**
385
     * @param string $name
386
     * @param $result
387
     * @return array
388
     * @throws Exceptions\Exception
389
     */
390 3
    public function getColumn($name, $result)
391
    {
392 3
        $col = [];
393
394 3
        if ($this->isResult($result)) {
395 3
            while ($row = $this->getRow($result)) {
396 3
                if (array_key_exists($name, $row)) {
0 ignored issues
show
Bug introduced by
It seems like $row can also be of type object and stdClass; however, parameter $search of array_key_exists() does only seem to accept array, 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

396
                if (array_key_exists($name, /** @scrutinizer ignore-type */ $row)) {
Loading history...
397 3
                    $col[] = $row[$name];
398
                }
399
            }
400
        }
401
402 3
        return $col;
403
    }
404
405
    /**
406
     * @param $result
407
     * @return array
408
     */
409 1
    public function getColumnNames($result)
410
    {
411 1
        $names = [];
412
413 1
        if ($this->isResult($result)) {
414 1
            $limit = $this->numFields($result);
415 1
            for ($i = 0; $i < $limit; $i++) {
416 1
                $names[] = $this->fieldName($result, $i);
417
            }
418
        }
419
420 1
        return $names;
421
    }
422
423
    /**
424
     * @param $result
425
     * @return bool|mixed
426
     * @throws Exceptions\Exception
427
     */
428 3
    public function getValue($result)
429
    {
430 3
        $out = false;
431
432 3
        if ($this->isResult($result)) {
433 3
            $result = $this->getRow($result, 'num');
434 3
            $out = isset($result[0]) ? $result[0] : false;
435
        }
436
437 3
        return $out;
438
    }
439
440
    /**
441
     * @param $result
442
     * @return array|mixed
443
     * @throws Exceptions\Exception
444
     */
445 1
    public function getTableMetaData($result)
446
    {
447 1
        $out = [];
448
449 1
        if ($this->isResult($result)) {
450 1
            while ($row = $this->getRow($result)) {
451 1
                $fieldName = $row['Field'];
452 1
                $out[$fieldName] = $row;
453
            }
454
        }
455
456 1
        return $out;
457
    }
458
459
    /**
460
     * @return string|null
461
     * @throws Exceptions\Exception
462
     */
463 1
    public function getLastError()
464
    {
465 1
        $pdo = $this->getConnect()->getPdo();
466 1
        $error = $pdo->errorInfo();
467 1
        return (string)(isset($error[2]) ? $error[2] : $this->lastError[2]);
468
    }
469
470
    /**
471
     * @return string|null
472
     * @throws Exceptions\Exception
473
     */
474 1
    public function getLastErrorNo()
475
    {
476 1
        $pdo = $this->getConnect()->getPdo();
477 1
        $error = $pdo->errorInfo();
478 1
        return (string)(isset($error[1]) ? $error[1] : $this->lastErrorNo);
479
    }
480
481
    /**
482
     * {@inheritDoc}
483
     * @param \PDOStatement $result
484
     */
485
    public function dataSeek(&$result, $position)
486
    {
487
        throw new Exceptions\DriverException('No implemented');
488
    }
489
}
490