Passed
Push — master ( 1638bc...46c53c )
by Agel_Nash
02:33
created

IlluminateDriver::saveAffectedRows()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 2
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;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
248
        try {
249 25
            $result = $this->prepare($sql);
250 25
            $this->execute($result);
251
252 25
            if ($this->saveAffectedRows($result) === 0 && $this->isResult($result) && ! $this->isSelectQuery($sql)) {
253 25
                $result = true;
254
            }
255 3
        } catch (\Exception $exception) {
256 3
            $this->lastError = $this->isResult($result) ? $result->errorInfo() : [];
257 3
            $code = $this->isResult($result) ? $result->errorCode() : '';
258 3
            $this->lastErrorNo = $this->isResult($result) ? (empty($code) ? $exception->getCode() : $code) : '';
259 3
            throw (new Exceptions\QueryException($exception->getMessage(), $exception->getCode()))
260 3
                ->setQuery($sql);
261
        }
262
263 25
        return $result;
264
    }
265
266
    /**
267
     * @param PDOStatement $result
268
     * {@inheritDoc}
269
     */
270 13
    public function getRecordCount($result)
271
    {
272 13
        return $this->isResult($result) ? $result->rowCount() : 0;
273
    }
274
275
    /**
276
     * @param PDOStatement $result
277
     * {@inheritDoc}
278
     */
279 11
    public function getRow($result, $mode = 'assoc')
280
    {
281
        switch ($mode) {
282 11
            case 'assoc':
283 6
                $out = $result->fetch(\PDO::FETCH_ASSOC);
284 6
                break;
285 7
            case 'num':
286 4
                $out = $result->fetch(\PDO::FETCH_NUM);
287 4
                break;
288 3
            case 'object':
289 1
                $out = $result->fetchObject();
290 1
                break;
291 2
            case 'both':
292 1
                $out = $result->fetch(\PDO::FETCH_BOTH);
293 1
                break;
294
            default:
295 1
                throw new Exceptions\UnknownFetchTypeException(
296 1
                    "Unknown get type ($mode) specified for fetchRow - must be empty, 'assoc', 'num', 'object' or 'both'."
297
                );
298
        }
299
300 11
        return $out;
301
    }
302
303
    /**
304
     * {@inheritDoc}
305
     */
306 4
    public function getVersion()
307
    {
308 4
        return $this->getConnect()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION);
309
    }
310
311
    /**
312
     * {@inheritDoc}
313
     */
314 3
    public function getInsertId()
315
    {
316 3
        return $this->getConnect()->getPdo()->lastInsertId();
317
    }
318
319
    /**
320
     * {@inheritDoc}
321
     */
322 25
    public function getAffectedRows()
323
    {
324 25
        return $this->affectedRows;
325
    }
326
327
    /**
328
     * @param PDOStatement|bool $result
329
     * @return int
330
     */
331 25
    protected function saveAffectedRows($result)
332
    {
333 25
        $this->affectedRows = \is_bool($result) ? 0 : $result->rowCount();
334 25
        return $this->getAffectedRows();
335
    }
336
337
    /**
338
     * @param string $sql
339
     * @return PDOStatement|bool
340
     */
341 25
    public function prepare($sql)
342
    {
343 25
        $pdo = $this->getConnect()->getPdo();
344 25
        $result = $pdo->prepare(
345 25
            $sql,
346
            [
347 25
                \PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL,
348
349
            ]
350
        );
351
352 25
        if ($this->isResult($result)) {
353 25
            $result->setFetchMode(\PDO::FETCH_ASSOC);
354
        }
355
356 25
        return $result;
357
    }
358
359
    /**
360
     * @param PDOStatement|bool $result
361
     * @return bool
362
     */
363 25
    public function execute($result)
364
    {
365 25
        if ($this->isResult($result) && $result->execute() === false) {
366
            $result = false;
367
        }
368
369 25
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type PDOStatement which is incompatible with the documented return type boolean.
Loading history...
370
    }
371
372
    /**
373
     * @param DispatcherContract|null $dispatcher
374
     * @return bool
375
     */
376 28
    public function useEloquent(DispatcherContract $dispatcher = null)
377
    {
378 28
        $out = false;
379 28
        if ($dispatcher === null) {
380 28
            $dispatcher = $this->getCapsule()->getEventDispatcher();
381
        }
382
383 28
        if ($dispatcher === null && class_exists(Dispatcher::class)) {
384 4
            $dispatcher = new Dispatcher(new Container);
385
        }
386
387 28
        if ($dispatcher !== null) {
388 28
            $this->getCapsule()->setEventDispatcher($dispatcher);
389
390 28
            $out = true;
391
        }
392
393 28
        $this->getCapsule()->bootEloquent();
394
395 28
        return $out;
396
    }
397
398
    /**
399
     * @return Capsule
400
     */
401 29
    public function getCapsule()
402
    {
403 29
        return $this->capsule;
404
    }
405
406
    /**
407
     * @param $name
408
     * @return bool
409
     */
410 29
    public function hasConnectionName($name)
411
    {
412 29
        $connections = $this->getCapsule()->getDatabaseManager()->getConnections();
413 29
        return isset($connections[$name]);
414
    }
415
416
    /**
417
     * @param string $query
418
     * @return bool
419
     */
420 25
    protected function isSelectQuery($query)
421
    {
422 25
        return 0 === mb_stripos(trim($query), 'SELECT');
423
    }
424
}
425