Issues (4)

src/Drivers/IlluminateDriver.php (4 issues)

1
<?php namespace AgelxNash\Modx\Evo\Database\Drivers;
2
3
use AgelxNash\Modx\Evo\Database\Exceptions;
4
use Illuminate\Database\Capsule\Manager as Capsule;
5
use Illuminate\Database\Connection;
6
use PDOStatement;
7
use Illuminate\Events\Dispatcher;
8
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
9
use Illuminate\Container\Container;
10
use ReflectionClass;
11
use PDO;
12
13
/**
14
 * @property Connection $conn
15
 */
16
class IlluminateDriver extends AbstractDriver
17
{
18
    /**
19
     * @var string
20
     */
21
    protected $connection = 'default';
22
23
    /**
24
     * @var Capsule
25
     */
26
    protected $capsule;
27
28
    /**
29
     * @var int
30
     */
31
    protected $affectedRows = 0;
32
33
    /**
34
     * @var array
35
     */
36
    protected $lastError = [];
37
38
    /**
39
     * @var string
40
     */
41
    protected $lastErrorNo = '';
42
43
    /**
44
     * @var string
45
     */
46
    protected $driver = 'mysql';
47
48
    private $elapsedTimeMethod;
49
50
    /**
51
     * {@inheritDoc}
52
     * @throws \ReflectionException
53
     */
54 30
    public function __construct(array $config = [], $connection = 'default')
55
    {
56 30
        $this->capsule = new Capsule;
57 30
        $this->getCapsule()->setAsGlobal();
58
59 30
        if ($this->hasConnectionName($connection)) {
60
            if (empty($config)) {
61
                $config = $this->getCapsule()->getConnection($connection)->getConfig();
62
                unset($config['name'], $config['driver']);
63
            } else {
64
                $diff = array_diff_assoc(
65
                    array_merge(['driver' => $this->driver], $config),
66
                    $this->getCapsule()->getConnection($connection)->getConfig()
0 ignored issues
show
It seems like $this->getCapsule()->get...onnection)->getConfig() can also be of type null; however, parameter $array2 of array_diff_assoc() 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

66
                    /** @scrutinizer ignore-type */ $this->getCapsule()->getConnection($connection)->getConfig()
Loading history...
67
                );
68
                if (array_intersect(['driver', 'host', 'database', 'password', 'username'], array_keys($diff))) {
69
                    throw new Exceptions\ConnectException(
70
                        sprintf('The connection name "%s" is already used', $connection)
71
                    );
72
                }
73
            }
74
        }
75
76 30
        $this->connection = $connection;
77
78 30
        $this->useEloquent();
79
80 30
        $this->setConfig($config);
81 30
    }
82
83
    /**
84
     * Get the elapsed time since a given starting point.
85
     *
86
     * @param  int    $start
87
     * @return float
88
     */
89 28
    protected function getElapsedTime($start)
90
    {
91 28
        if ($this->elapsedTimeMethod === null) {
92 28
            $reflection = new ReflectionClass($this->getConnect());
93 28
            $this->elapsedTimeMethod = $reflection->getMethod('getElapsedTime');
94 28
            $this->elapsedTimeMethod->setAccessible(true);
95
        }
96
97 28
        return $this->elapsedTimeMethod->invoke($this->getConnect(), $start);
98
    }
99
    /**
100
     * {@inheritDoc}
101
     * @return Connection
102
     */
103 29
    public function getConnect()
104
    {
105 29
        if (! $this->isConnected()) {
106 29
            $this->connect();
107 29
            if (! $this->conn->getPdo() instanceof PDO) {
0 ignored issues
show
$this->conn->getPdo() is always a sub-type of PDO.
Loading history...
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

107
            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...
108 2
                $this->conn->reconnect();
109
            }
110
        }
111 29
        return $this->conn;
112
    }
113
114
    /**
115
     * @return bool
116
     */
117 29
    public function isConnected()
118
    {
119 29
        return ($this->conn instanceof Connection && $this->conn->getDatabaseName());
120
    }
121
122
    /**
123
     * {@inheritDoc}
124
     */
125 1
    public function getLastError()
126
    {
127 1
        $error = $this->getConnect()->getPdo()->errorInfo();
128 1
        return empty($error[2]) ? (empty($this->lastError[2]) ? '' : $this->lastError[2]) : $error[2];
129
    }
130
131
    /**
132
     * {@inheritDoc}
133
     */
134 3
    public function getLastErrorNo()
135
    {
136 3
        $error = $this->getConnect()->getPdo()->errorInfo();
137 3
        $out = empty($error[0]) || $error[0] === '00000' ? $this->lastErrorNo : $error[0];
138
139 3
        return $out;
140
    }
141
142
    /**
143
     * {@inheritDoc}
144
     */
145 29
    public function connect()
146
    {
147
        try {
148 29
            if (! $this->hasConnectionName($this->connection)) {
149 29
                $this->getCapsule()->addConnection([
150 29
                    'driver'    => $this->driver,
151 29
                    'host'      => $this->getConfig('host'),
152 29
                    'database'  => $this->getConfig('database'),
153 29
                    'username'  => $this->getConfig('username'),
154 29
                    'password'  => $this->getConfig('password'),
155 29
                    'charset'   => $this->getConfig('charset'),
156 29
                    'collation' => $this->getConfig('collation'),
157 29
                    'prefix'    => $this->getConfig('prefix'),
158 29
                ], $this->connection);
159
            }
160
161 29
            $this->conn = $this->getCapsule()->getConnection($this->connection);
162
        } catch (\Exception $exception) {
163
            $this->conn = null;
164
            throw new Exceptions\ConnectException($exception->getMessage(), $exception->getCode());
165
        }
166
167 29
        return $this->conn;
168
    }
169
170
    /**
171
     * {@inheritDoc}
172
     */
173 2
    public function disconnect()
174
    {
175 2
        if ($this->isConnected()) {
176 2
            $this->conn->disconnect();
177
        }
178
179 2
        $this->conn = null;
180 2
        $this->lastErrorNo = '';
181 2
        $this->lastError = [];
182
183 2
        return true;
184
    }
185
186
    /**
187
     * {@inheritDoc}
188
     */
189 28
    public function isResult($result)
190
    {
191 28
        return $result instanceof PDOStatement;
192
    }
193
194
    /**
195
     * @param PDOStatement $result
196
     * {@inheritDoc}
197
     */
198 1
    public function numFields($result)
199
    {
200 1
        return $this->isResult($result) ? $result->columnCount() : 0;
201
    }
202
203
    /**
204
     * @param PDOStatement $result
205
     * {@inheritDoc}
206
     */
207 1
    public function fieldName($result, $col = 0)
208
    {
209 1
        $field = $this->isResult($result) ? $result->getColumnMeta($col) : [];
210 1
        return isset($field['name']) ? $field['name'] : null;
211
    }
212
213
    /**
214
     * {@inheritDoc}
215
     */
216 28
    public function setCharset($charset, $method = null)
217
    {
218 28
        if ($method === null) {
219 1
            $method = $this->getConfig('method');
220
        }
221
222 28
        $query = $method . ' ' . $charset;
223
224 28
        return (bool)$this->query($query);
225
    }
226
227
    /**
228
     * {@inheritDoc}
229
     */
230 1
    public function selectDb($name)
231
    {
232 1
        return $this->getConnect()->getPdo()->exec('USE ' . $name) === 0;
233
    }
234
235
    /**
236
     * {@inheritDoc}
237
     */
238 1
    public function escape($data)
239
    {
240
        /**
241
         * It's not secure
242
         * But need for backward compatibility
243
         */
244
245 1
        $quote = $this->getConnect()->getPdo()->quote($data);
246 1
        return strpos($quote, '\'') === 0 ? mb_substr($quote, 1, -1) : $quote;
247
    }
248
249
    /**
250
     * {@inheritDoc}
251
     * @return bool|PDOStatement
252
     */
253 28
    public function query($sql)
254
    {
255
        try {
256 28
            $start = microtime(true);
257
258 28
            $result = $this->prepare($sql);
259 28
            $this->execute($result);
260
261 28
            if ($this->saveAffectedRows($result) === 0 && $this->isResult($result) && ! $this->isSelectQuery($sql)) {
262 28
                $result = true;
263
            }
264 28
            $this->getConnect()->logQuery($sql, [], $this->getElapsedTime($start));
265 3
        } catch (\Exception $exception) {
266 3
            $this->lastError = $this->isResult($result) ? $result->errorInfo() : [];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
267 2
            $code = $this->isResult($result) ? $result->errorCode() : '';
268 2
            $this->lastErrorNo = $this->isResult($result) ? (empty($code) ? $exception->getCode() : $code) : '';
269 2
            throw (new Exceptions\QueryException($exception->getMessage(), $exception->getCode()))
270 2
                ->setQuery($sql);
271
        }
272
273 28
        return $result;
274
    }
275
276
    /**
277
     * @param PDOStatement $result
278
     * {@inheritDoc}
279
     */
280 16
    public function getRecordCount($result)
281
    {
282 16
        return $this->isResult($result) ? $result->rowCount() : 0;
283
    }
284
285
    /**
286
     * @param PDOStatement $result
287
     * {@inheritDoc}
288
     */
289 14
    public function getRow($result, $mode = 'assoc')
290
    {
291
        switch ($mode) {
292 14
            case 'assoc':
293 6
                $out = $result->fetch(\PDO::FETCH_ASSOC);
294 6
                break;
295 10
            case 'num':
296 7
                $out = $result->fetch(\PDO::FETCH_NUM);
297 7
                break;
298 3
            case 'object':
299 1
                $out = $result->fetchObject();
300 1
                break;
301 2
            case 'both':
302 1
                $out = $result->fetch(\PDO::FETCH_BOTH);
303 1
                break;
304
            default:
305 1
                throw new Exceptions\UnknownFetchTypeException(
306 1
                    "Unknown get type ($mode) specified for fetchRow - must be empty, 'assoc', 'num', 'object' or 'both'."
307
                );
308
        }
309
310 14
        return $out;
311
    }
312
313
    /**
314
     * {@inheritDoc}
315
     */
316 2
    public function getVersion()
317
    {
318 2
        return $this->getConnect()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
319
    }
320
321
    /**
322
     * {@inheritDoc}
323
     */
324 7
    public function getInsertId()
325
    {
326 7
        return $this->getConnect()->getPdo()->lastInsertId();
327
    }
328
329
    /**
330
     * @return int
331
     */
332 28
    public function getAffectedRows()
333
    {
334 28
        return $this->affectedRows;
335
    }
336
337
    /**
338
     * @param PDOStatement|bool $result
339
     * @return int
340
     */
341 28
    protected function saveAffectedRows($result)
342
    {
343 28
        $this->affectedRows = \is_bool($result) ? 0 : $result->rowCount();
344 28
        return $this->getAffectedRows();
345
    }
346
347
    /**
348
     * @param string $sql
349
     * @return PDOStatement|bool
350
     * @throws Exceptions\ConnectException
351
     */
352 28
    public function prepare($sql)
353
    {
354 28
        $pdo = $this->getConnect()->getPdo();
355 28
        $result = $pdo->prepare(
356 28
            $sql,
357
            [
358 28
                \PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL,
359
360
            ]
361
        );
362
363 28
        if ($this->isResult($result)) {
364 28
            $result->setFetchMode(\PDO::FETCH_ASSOC);
365
        }
366
367 28
        return $result;
368
    }
369
370
    /**
371
     * @param PDOStatement|bool $result
372
     * @return bool
373
     */
374 28
    public function execute($result)
375
    {
376 28
        return $this->isResult($result) ? $result->execute() : (bool)$result;
377
    }
378
379
    /**
380
     * @param DispatcherContract|null $dispatcher
381
     * @return bool
382
     */
383 30
    public function useEloquent(DispatcherContract $dispatcher = null)
384
    {
385 30
        $out = false;
386 30
        if ($dispatcher === null) {
387 30
            $dispatcher = $this->getCapsule()->getEventDispatcher();
388
        }
389
390 30
        if ($dispatcher === null && class_exists(Dispatcher::class)) {
391 30
            $dispatcher = new Dispatcher(new Container);
392
        }
393
394 30
        if ($dispatcher !== null) {
395 30
            $this->getCapsule()->setEventDispatcher($dispatcher);
396
397 30
            $out = true;
398
        }
399
400 30
        $this->getCapsule()->bootEloquent();
401
402 30
        return $out;
403
    }
404
405
    /**
406
     * @return Capsule
407
     */
408 30
    public function getCapsule()
409
    {
410 30
        return $this->capsule;
411
    }
412
413
    /**
414
     * @param $name
415
     * @return bool
416
     */
417 30
    public function hasConnectionName($name)
418
    {
419 30
        $connections = $this->getCapsule()->getDatabaseManager()->getConnections();
420 30
        return isset($connections[$name]);
421
    }
422
423
    /**
424
     * @param string $query
425
     * @return bool
426
     */
427 28
    protected function isSelectQuery($query)
428
    {
429 28
        return 0 === mb_stripos(trim($query), 'SELECT');
430
    }
431
432
    /**
433
     * {@inheritDoc}
434
     */
435
    public function begin ($flag = 0, $name = null)
436
    {
437
        return $this->getConnect()->getPdo()->beginTransaction();
438
    }
439
440
    /**
441
     * {@inheritDoc}
442
     */
443
    public function commit ($flag = 0, $name = null)
444
    {
445
        return $this->getConnect()->getPdo()->commit();
446
    }
447
448
    /**
449
     * {@inheritDoc}
450
     */
451
    public function rollback ($flag = 0, $name = null)
452
    {
453
        return $this->getConnect()->getPdo()->rollback();
454
    }
455
}
456