Passed
Push — master ( 1c0e8d...8b3112 )
by Agel_Nash
02:39
created

IlluminateDriver::getRow()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 16
cts 16
cp 1
rs 8.6737
c 0
b 0
f 0
cc 5
eloc 17
nc 5
nop 2
crap 5
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
    /**
49
     * {@inheritDoc}
50
     */
51 29
    public function __construct(array $config = [], $connection = 'default')
52
    {
53 29
        $reflection = new ReflectionClass(Capsule::class);
54 29
        $property = $reflection->getProperty('instance');
55 29
        $property->setAccessible(true);
56
        /**
57
         * @var Capsule|null $capsule
58
         */
59 29
        $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

59
        $capsule = $property->getValue(/** @scrutinizer ignore-type */ Capsule::class);
Loading history...
60 29
        if ($capsule === null) {
61 1
            $this->capsule = new Capsule;
62
63 1
            $this->getCapsule()->setAsGlobal();
64
        } else {
65 28
            $this->capsule = $capsule;
66
        }
67
68 29
        if ($this->hasConnectionName($connection)) {
69 28
            if (empty($config)) {
70 2
                $config = $this->getCapsule()->getConnection($connection)->getConfig();
71 2
                unset($config['name'], $config['driver']);
72
            } else {
73 26
                $diff = array_diff_assoc(
74 26
                    array_merge(['driver' => $this->driver], $config),
75 26
                    $this->getCapsule()->getConnection($connection)->getConfig()
76
                );
77 26
                if (array_intersect(['driver', 'host', 'database', 'password', 'username'], array_keys($diff))) {
78 2
                    throw new Exceptions\ConnectException(
79 2
                        sprintf('The connection name "%s" is already used', $connection)
80
                    );
81
                }
82
            }
83
        }
84
85 28
        $this->connection = $connection;
86
87 28
        $this->useEloquent();
88
89 28
        $this->setConfig($config);
90 28
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95 28
    public function getConnect()
96
    {
97 28
        if (! $this->isConnected()) {
98 28
            $this->connect();
99 28
            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

99
            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...
100 2
                $this->conn->reconnect();
101
            }
102
        }
103
104 28
        return $this->conn;
105
    }
106
107
    /**
108
     * @return bool
109
     */
110 28
    public function isConnected()
111
    {
112 28
        return ($this->conn instanceof Connection && $this->conn->getDatabaseName());
113
    }
114
115
    /**
116
     * {@inheritDoc}
117
     */
118 1
    public function getLastError()
119
    {
120 1
        $error = $this->getConnect()->getPdo()->errorInfo();
121 1
        return empty($error[2]) ? (empty($this->lastError[2]) ? '' : $this->lastError[2]) : $error[2];
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127 3
    public function getLastErrorNo()
128
    {
129 3
        $error = $this->getConnect()->getPdo()->errorInfo();
130 3
        $out = empty($error[0]) || $error[0] === '00000' ? $this->lastErrorNo : $error[0];
131
132 3
        return $out;
133
    }
134
135
    /**
136
     * {@inheritDoc}
137
     */
138 28
    public function connect()
139
    {
140
        try {
141 28
            if (! $this->hasConnectionName($this->connection)) {
142 1
                $this->getCapsule()->addConnection([
143 1
                    'driver'    => $this->driver,
144 1
                    'host'      => $this->getConfig('host'),
145 1
                    'database'  => $this->getConfig('database'),
146 1
                    'username'  => $this->getConfig('username'),
147 1
                    'password'  => $this->getConfig('password'),
148 1
                    'charset'   => $this->getConfig('charset'),
149 1
                    'collation' => $this->getConfig('collation'),
150 1
                    'prefix'    => $this->getConfig('prefix'),
151 1
                ], $this->connection);
152
            }
153
154 28
            $this->conn = $this->getCapsule()->getConnection($this->connection);
155
        } catch (\Exception $exception) {
156
            $this->conn = null;
157
            throw new Exceptions\ConnectException($exception->getMessage(), $exception->getCode());
158
        }
159
160 28
        return $this->conn;
161
    }
162
163
    /**
164
     * {@inheritDoc}
165
     */
166 2
    public function disconnect()
167
    {
168 2
        if ($this->isConnected()) {
169 2
            $this->conn->disconnect();
170
        }
171
172 2
        $this->conn = null;
173 2
        $this->lastErrorNo = '';
174 2
        $this->lastError = [];
175
176 2
        return $this;
177
    }
178
179
    /**
180
     * {@inheritDoc}
181
     */
182 25
    public function isResult($result)
183
    {
184 25
        return $result instanceof PDOStatement;
185
    }
186
187
    /**
188
     * @param PDOStatement $result
189
     * {@inheritDoc}
190
     */
191 1
    public function numFields($result)
192
    {
193 1
        return $this->isResult($result) ? $result->columnCount() : 0;
194
    }
195
196
    /**
197
     * @param PDOStatement $result
198
     * {@inheritDoc}
199
     */
200 1
    public function fieldName($result, $col = 0)
201
    {
202 1
        $field = $this->isResult($result) ? $result->getColumnMeta($col) : [];
203 1
        return isset($field['name']) ? $field['name'] : null;
204
    }
205
206
    /**
207
     * {@inheritDoc}
208
     */
209 25
    public function setCharset($charset, $method = null)
210
    {
211 25
        if ($method === null) {
212 1
            $method = $this->getConfig('method');
213
        }
214
215 25
        $query = $method . ' ' . $charset;
216
217 25
        return (bool)$this->query($query);
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223 1
    public function selectDb($name)
224
    {
225 1
        return $this->getConnect()->getPdo()->exec('USE ' . $name) === 0;
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231 1
    public function escape($data)
232
    {
233
        /**
234
         * It's not secure
235
         * But need for backward compatibility
236
         */
237
238 1
        $quote = $this->getConnect()->getPdo()->quote($data);
239 1
        return strpos($quote, '\'') === 0 ? mb_substr($quote, 1, -1) : $quote;
240
    }
241
242
    /**
243
     * {@inheritDoc}
244
     */
245 25
    public function query($sql)
246
    {
247 25
        $result = null;
248
        try {
249 25
            $pdo = $this->getConnect()->getPdo();
250 25
            $result = $pdo->prepare(
251 25
                $sql,
252
                [
253 25
                    \PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL,
254
255
                ]
256
            );
257 25
            $result->setFetchMode(\PDO::FETCH_ASSOC);
258 25
            if ($result->execute() === false) {
259
                $result = false;
260
            }
261 25
            $this->affectedRows = \is_bool($result) ? 0 : $result->rowCount();
262 25
            if ($this->affectedRows === 0 && $this->isResult($result) && 0 !== mb_stripos(trim($sql), 'SELECT')) {
263 25
                $result = true;
264
            }
265 3
        } catch (\Exception $exception) {
266 3
            $this->lastError = $this->isResult($result) ? $result->errorInfo() : [];
267 3
            $code = $this->isResult($result) ? $result->errorCode() : '';
268 3
            $this->lastErrorNo = $this->isResult($result) ? (empty($code) ? $exception->getCode() : $code) : '';
269 3
            throw (new Exceptions\QueryException($exception->getMessage(), $exception->getCode()))
270 3
                ->setQuery($sql);
271
        }
272
273 25
        return $result;
274
    }
275
276
    /**
277
     * @param PDOStatement $result
278
     * {@inheritDoc}
279
     */
280 13
    public function getRecordCount($result)
281
    {
282 13
        return $this->isResult($result) ? $result->rowCount() : 0;
283
    }
284
285
    /**
286
     * @param PDOStatement $result
287
     * {@inheritDoc}
288
     */
289 11
    public function getRow($result, $mode = 'assoc')
290
    {
291
        switch ($mode) {
292 11
            case 'assoc':
293 6
                $out = $result->fetch(\PDO::FETCH_ASSOC);
294 6
                break;
295 7
            case 'num':
296 4
                $out = $result->fetch(\PDO::FETCH_NUM);
297 4
                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 11
        return $out;
311
    }
312
313
    /**
314
     * {@inheritDoc}
315
     */
316 4
    public function getVersion()
317
    {
318 4
        return $this->getConnect()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
319
    }
320
321
    /**
322
     * {@inheritDoc}
323
     */
324 3
    public function getInsertId()
325
    {
326 3
        return $this->getConnect()->getPdo()->lastInsertId();
327
    }
328
329
    /**
330
     * {@inheritDoc}
331
     */
332 8
    public function getAffectedRows()
333
    {
334 8
        return $this->affectedRows;
335
    }
336
337
    /**
338
     * @param DispatcherContract|null $dispatcher
339
     * @return bool
340
     */
341 28
    public function useEloquent(DispatcherContract $dispatcher = null)
342
    {
343 28
        $out = false;
344 28
        if ($dispatcher === null) {
345 28
            $dispatcher = $this->getCapsule()->getEventDispatcher();
346
        }
347
348 28
        if ($dispatcher === null && class_exists(Dispatcher::class)) {
349 4
            $dispatcher = new Dispatcher(new Container);
350
        }
351
352 28
        if ($dispatcher !== null) {
353 28
            $this->getCapsule()->setEventDispatcher($dispatcher);
354
355 28
            $out = true;
356
        }
357
358 28
        $this->getCapsule()->bootEloquent();
359
360 28
        return $out;
361
    }
362
363
    /**
364
     * @return Capsule
365
     */
366 29
    public function getCapsule()
367
    {
368 29
        return $this->capsule;
369
    }
370
371
    /**
372
     * @param $name
373
     * @return bool
374
     */
375 29
    public function hasConnectionName($name)
376
    {
377 29
        $connections = $this->getCapsule()->getDatabaseManager()->getConnections();
378 29
        return isset($connections[$name]);
379
    }
380
}
381