Issues (99)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/connection/Connection.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Childish\connection;
3
4
use PDO;
5
use Closure;
6
use Childish\ChildishException;
7
use Childish\query\Builder;
8
use Childish\connection\traits\ManagesTransactions;
9
use Childish\query\Processor;
10
use Childish\query\QueryGrammar;
11
use Childish\support\Tools;
12
13
14
/**
15
 * Connection
16
 *
17
 * @author    Pu ShaoWei <[email protected]>
18
 * @date      2017/12/7
19
 * @package   Childish
20
 * @version   1.0
21
 */
22
class Connection implements ConnectionInterface
23
{
24
    use ManagesTransactions;
25
    /**
26
     * The active PDO connection.
27
     *
28
     * @var \PDO|\Closure
29
     */
30
    protected $pdo;
31
32
    /**
33
     * The active PDO connection used for reads.
34
     *
35
     * @var \PDO|\Closure
36
     */
37
    protected $readPdo;
38
39
    /**
40
     * The name of the connected database.
41
     *
42
     * @var string
43
     */
44
    protected $database;
45
46
    /**
47
     * The table prefix for the connection.
48
     *
49
     * @var string
50
     */
51
    protected $tablePrefix = '';
52
53
    /**
54
     * The database connection configuration options.
55
     *
56
     * @var array
57
     */
58
    protected $config = [];
59
60
    /**
61
     * The reconnector instance for the connection.
62
     *
63
     * @var callable
64
     */
65
    protected $reconnector;
66
67
    /**
68
     * The query grammar implementation.
69
     *
70
     * @var  \Childish\query\QueryGrammar
71
     */
72
    protected $queryGrammar;
73
74
75
    /**
76
     * The query post processor implementation.
77
     *
78
     * @var \childish\query\Processor
79
     */
80
    protected $postProcessor;
81
82
83
    /**
84
     * The default fetch mode of the connection.
85
     *
86
     * @var int
87
     */
88
    protected $fetchMode = PDO::FETCH_OBJ;
89
90
    /**
91
     * The number of active transactions.
92
     *
93
     * @var int
94
     */
95
    protected $transactions = 0;
96
97
    /**
98
     * Indicates if changes have been made to the database.
99
     *
100
     * @var int
101
     */
102
    protected $recordsModified = false;
103
104
    /**
105
     * All of the queries run against the connection.
106
     *
107
     * @var array
108
     */
109
    protected $queryLog = [];
110
111
    /**
112
     * Indicates whether queries are being logged.
113
     *
114
     * @var bool
115
     */
116
    protected $loggingQueries = false;
117
118
    /**
119
     * Indicates if the connection is in a "dry run".
120
     *
121
     * @var bool
122
     */
123
    protected $pretending = false;
124
125
126
    /**
127
     * The connection resolvers.
128
     *
129
     * @var array
130
     */
131
    protected static $resolvers = [];
132
133
    /**
134
     * Create a new database connection instance.
135
     *
136
     * @param  \PDO|\Closure $pdo
137
     * @param  string        $database
138
     * @param  string        $tablePrefix
139
     * @param  array         $config
140
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
141
     */
142
    public function __construct($pdo, $database = '', $tablePrefix = '', array $config = [])
143
    {
144
        $this->pdo = $pdo;
145
146
        // First we will setup the default properties. We keep track of the DB
147
        // name we are connected to since it is needed when some reflective
148
        // type commands are run such as checking whether a table exists.
149
        $this->database = $database;
150
151
        $this->tablePrefix = $tablePrefix;
152
153
        $this->config = $config;
154
155
        // We need to initialize a query grammar and the query post processors
156
        // which are both very important parts of the database abstractions
157
        // so we initialize these to their default values while starting.
158
159
        $this->useDefaultQueryGrammar();
160
161
        $this->useDefaultPostProcessor();
162
    }
163
164
    /**
165
     * Set the query post processor to the default implementation.
166
     *
167
     * @return void
168
     */
169
    public function useDefaultPostProcessor()
170
    {
171
        $this->postProcessor = $this->getDefaultPostProcessor();
172
    }
173
174
    /**
175
     * Get the default post processor instance.
176
     *
177
     * @return \Childish\query\Processor
178
     */
179
    protected function getDefaultPostProcessor()
180
    {
181
        return new Processor;
182
    }
183
184
    /**
185
     * Set the query grammar to the default implementation.
186
     *
187
     * @return void
188
     */
189
    public function useDefaultQueryGrammar()
190
    {
191
        $this->queryGrammar = $this->getDefaultQueryGrammar();
192
    }
193
194
    /**
195
     * Get the default query grammar instance.
196
     *
197
     * @return  \Childish\query\QueryGrammar
198
     */
199
    protected function getDefaultQueryGrammar()
200
    {
201
        return new QueryGrammar;
202
    }
203
204
    /**
205
     * Begin a fluent query against a database table.
206
     *
207
     * @param  string $table
208
     * @return \Childish\query\Builder
209
     */
210
    public function table($table)
211
    {
212
        return $this->query()->from($table);
213
    }
214
215
    /**
216
     * Get a new query builder instance.
217
     *
218
     * @return \Childish\query\Builder
219
     */
220
    public function query()
221
    {
222
        return new Builder(
223
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
224
        );
225
    }
226
227
    /**
228
     * Run a select statement and return a single result.
229
     *
230
     * @param  string $query
231
     * @param  array  $bindings
232
     * @param  bool   $useReadPdo
233
     * @return mixed
234
     */
235
    public function selectOne($query, $bindings = [], $useReadPdo = true)
236
    {
237
        $records = $this->select($query, $bindings, $useReadPdo);
238
239
        return array_shift($records);
240
    }
241
242
    /**
243
     * Run a select statement against the database.
244
     *
245
     * @param  string $query
246
     * @param  array  $bindings
247
     * @return array
248
     */
249
    public function selectFromWriteConnection($query, $bindings = [])
250
    {
251
        return $this->select($query, $bindings, false);
252
    }
253
254
    /**
255
     * Run a select statement against the database.
256
     *
257
     * @param  string $query
258
     * @param  array  $bindings
259
     * @param  bool   $useReadPdo
260
     * @return array
261
     */
262
    public function select($query, $bindings = [], $useReadPdo = true)
263
    {
264
        return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
265
            if ($this->pretending()) {
266
                return [];
267
            }
268
269
            // For select statements, we'll simply execute the query and return an array
270
            // of the database result set. Each element in the array will be a single
271
            // row from the database table, and will either be an array or objects.
272
            $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
273
                                              ->prepare($query));
274
275
            $this->bindValues($statement, $this->prepareBindings($bindings));
276
277
            $statement->execute();
278
279
            return $statement->fetchAll();
280
        });
281
    }
282
283
    /**
284
     * Run a select statement against the database and returns a generator.
285
     *
286
     * @param  string $query
287
     * @param  array  $bindings
288
     * @param  bool   $useReadPdo
289
     * @return \Generator
290
     */
291
    public function cursor($query, $bindings = [], $useReadPdo = true)
292
    {
293
        $statement = $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
294
            if ($this->pretending()) {
295
                return [];
296
            }
297
298
            // First we will create a statement for the query. Then, we will set the fetch
299
            // mode and prepare the bindings for the query. Once that's done we will be
300
            // ready to execute the query against the database and return the cursor.
301
            $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
302
                                              ->prepare($query));
303
304
            $this->bindValues(
305
                $statement, $this->prepareBindings($bindings)
306
            );
307
308
            // Next, we'll execute the query against the database and return the statement
309
            // so we can return the cursor. The cursor will use a PHP generator to give
310
            // back one row at a time without using a bunch of memory to render them.
311
            $statement->execute();
312
313
            return $statement;
314
        });
315
316
        while ($record = $statement->fetch()) {
317
            yield $record;
318
        }
319
    }
320
321
    /**
322
     * Configure the PDO prepared statement.
323
     *
324
     * @param  \PDOStatement $statement
325
     * @return \PDOStatement
326
     */
327
    protected function prepared($statement)
328
    {
329
        $statement->setFetchMode($this->fetchMode);
330
331
        return $statement;
332
    }
333
334
    /**
335
     * Get the PDO connection to use for a select query.
336
     *
337
     * @param  bool $useReadPdo
338
     * @return \PDO
339
     */
340
    protected function getPdoForSelect($useReadPdo = true)
341
    {
342
        return $useReadPdo ? $this->getReadPdo() : $this->getPdo();
343
    }
344
345
    /**
346
     * Run an insert statement against the database.
347
     *
348
     * @param  string $query
349
     * @param  array  $bindings
350
     * @return bool
351
     */
352
    public function insert($query, $bindings = [])
353
    {
354
        return $this->statement($query, $bindings);
355
    }
356
357
    /**
358
     * Run an update statement against the database.
359
     *
360
     * @param  string $query
361
     * @param  array  $bindings
362
     * @return int
363
     */
364
    public function update($query, $bindings = [])
365
    {
366
        return $this->affectingStatement($query, $bindings);
367
    }
368
369
    /**
370
     * Run a delete statement against the database.
371
     *
372
     * @param  string $query
373
     * @param  array  $bindings
374
     * @return int
375
     */
376
    public function delete($query, $bindings = [])
377
    {
378
        return $this->affectingStatement($query, $bindings);
379
    }
380
381
    /**
382
     * Execute an SQL statement and return the boolean result.
383
     *
384
     * @param  string $query
385
     * @param  array  $bindings
386
     * @return bool
387
     */
388
    public function statement($query, $bindings = [])
389
    {
390
        return $this->run($query, $bindings, function ($query, $bindings) {
391
            if ($this->pretending()) {
392
                return true;
393
            }
394
395
            $statement = $this->getPdo()->prepare($query);
396
397
            $this->bindValues($statement, $this->prepareBindings($bindings));
398
399
            $this->recordsHaveBeenModified();
400
401
            return $statement->execute();
402
        });
403
    }
404
405
    /**
406
     * Run an SQL statement and get the number of rows affected.
407
     *
408
     * @param  string $query
409
     * @param  array  $bindings
410
     * @return int
411
     */
412
    public function affectingStatement($query, $bindings = [])
413
    {
414
        return $this->run($query, $bindings, function ($query, $bindings) {
415
            if ($this->pretending()) {
416
                return 0;
417
            }
418
419
            // For update or delete statements, we want to get the number of rows affected
420
            // by the statement and return that back to the developer. We'll first need
421
            // to execute the statement and then we'll use PDO to fetch the affected.
422
            $statement = $this->getPdo()->prepare($query);
423
424
            $this->bindValues($statement, $this->prepareBindings($bindings));
425
426
            $statement->execute();
427
428
            $this->recordsHaveBeenModified(
429
                ($count = $statement->rowCount()) > 0
430
            );
431
432
            return $count;
433
        });
434
    }
435
436
    /**
437
     * Run a raw, unprepared query against the PDO connection.
438
     *
439
     * @param  string $query
440
     * @return bool
441
     */
442
    public function unprepared($query)
443
    {
444
        return $this->run($query, [], function ($query) {
445
            if ($this->pretending()) {
446
                return true;
447
            }
448
449
            $this->recordsHaveBeenModified(
450
                $change = ($this->getPdo()->exec($query) === false ? false : true)
451
            );
452
453
            return $change;
454
        });
455
    }
456
457
    /**
458
     * Execute the given callback in "dry run" mode.
459
     *
460
     * @param  \Closure $callback
461
     * @return array
462
     */
463
    public function pretend(Closure $callback)
464
    {
465
        return $this->withFreshQueryLog(function () use ($callback) {
466
            $this->pretending = true;
467
468
            // Basically to make the database connection "pretend", we will just return
469
            // the default values for all the query methods, then we will return an
470
            // array of queries that were "executed" within the Closure callback.
471
            $callback($this);
472
473
            $this->pretending = false;
474
475
            return $this->queryLog;
476
        });
477
    }
478
479
    /**
480
     * Execute the given callback in "dry run" mode.
481
     *
482
     * @param  \Closure $callback
483
     * @return array
484
     */
485
    protected function withFreshQueryLog($callback)
486
    {
487
        $loggingQueries = $this->loggingQueries;
488
489
        // First we will back up the value of the logging queries property and then
490
        // we'll be ready to run callbacks. This query log will also get cleared
491
        // so we will have a new log of all the queries that are executed now.
492
        $this->enableQueryLog();
493
494
        $this->queryLog = [];
495
496
        // Now we'll execute this callback and capture the result. Once it has been
497
        // executed we will restore the value of query logging and give back the
498
        // value of hte callback so the original callers can have the results.
499
        $result = $callback();
500
501
        $this->loggingQueries = $loggingQueries;
502
503
        return $result;
504
    }
505
506
    /**
507
     * Bind values to their parameters in the given statement.
508
     *
509
     * @param  \PDOStatement $statement
510
     * @param  array         $bindings
511
     * @return void
512
     */
513
    public function bindValues($statement, $bindings)
514
    {
515
        foreach ($bindings as $key => $value) {
516
            $statement->bindValue(
517
                is_string($key) ? $key : $key + 1, $value,
518
                is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
519
            );
520
        }
521
    }
522
523
    /**
524
     * Prepare the query bindings for execution.
525
     *
526
     * @param  array $bindings
527
     * @return array
528
     */
529
    public function prepareBindings(array $bindings)
530
    {
531
        foreach ($bindings as $key => $value) {
532
            // We need to transform all instances of DateTimeInterface into the actual
533
            // date string. Each query grammar maintains its own date string format
534
            // so we'll just ask the grammar for the format to get from the date.
535
            if (is_bool($value)) {
536
                $bindings[$key] = (int)$value;
537
            }
538
        }
539
540
        return $bindings;
541
    }
542
543
    /**
544
     * Run a SQL statement and log its execution context.
545
     *
546
     * @param  string   $query
547
     * @param  array    $bindings
548
     * @param  \Closure $callback
549
     * @return mixed
550
     * @throws \childish\ChildishException
551
     */
552
    protected function run($query, $bindings, Closure $callback)
553
    {
554
        $this->reconnectIfMissingConnection();
555
556
        $start = microtime(true);
557
558
        // Here we will run this query. If an exception occurs we'll determine if it was
559
        // caused by a connection that has been lost. If that is the cause, we'll try
560
        // to re-establish connection and re-run the query with a fresh connection.
561
        try {
562
            $result = $this->runQueryCallback($query, $bindings, $callback);
563
        } catch (ChildishException $e) {
564
            $result = $this->handleQueryException(
565
                $e, $query, $bindings, $callback
566
            );
567
        }
568
569
        // Once we have run the query we will calculate the time that it took to run and
570
        // then log the query, bindings, and execution time so we will report them on
571
        // the event that the developer needs them. We'll log time in milliseconds.
572
        $this->logQuery(
573
            $query, $bindings, $this->getElapsedTime($start)
574
        );
575
576
        return $result;
577
    }
578
579
    /**
580
     * Run a SQL statement.
581
     *
582
     * @param  string   $query
583
     * @param  array    $bindings
584
     * @param  \Closure $callback
585
     * @return mixed
586
     * @throws \Childish\ChildishException
587
     */
588
    protected function runQueryCallback($query, $bindings, Closure $callback)
589
    {
590
        // To execute the statement, we'll simply call the callback, which will actually
591
        // run the SQL against the PDO connection. Then we can calculate the time it
592
        // took to execute and log the query SQL, bindings and time in our memory.
593
        try {
594
595
            $result = $callback($query, $bindings);
596
        }
597
598
            // If an exception occurs when attempting to run a query, we'll format the error
599
            // message to include the bindings with SQL, which will make this exception a
600
            // lot more helpful to the developer instead of just the database's errors.
601
        catch (\Exception $e) {
602
            throw new ChildishException(
603
                $query, $this->prepareBindings($bindings), $e
604
            );
605
        }
606
607
        return $result;
608
    }
609
610
    /**
611
     * Log a query in the connection's query log.
612
     *
613
     * @param  string     $query
614
     * @param  array      $bindings
615
     * @param  float|null $time
616
     * @return void
617
     */
618
    public function logQuery($query, $bindings, $time = null)
619
    {
620
        if ($this->loggingQueries) {
621
            $this->queryLog[] = compact('query', 'bindings', 'time');
622
        }
623
    }
624
625
    /**
626
     * Get the elapsed time since a given starting point.
627
     *
628
     * @param  int $start
629
     * @return float
630
     */
631
    protected function getElapsedTime($start)
632
    {
633
        return round((microtime(true) - $start) * 1000, 2);
634
    }
635
636
    /**
637
     * Handle a query exception.
638
     *
639
     * @param  \Exception $e
640
     * @param  string     $query
641
     * @param  array      $bindings
642
     * @param  \Closure   $callback
643
     * @return mixed
644
     * @throws \Exception
645
     */
646
    protected function handleQueryException($e, $query, $bindings, Closure $callback)
647
    {
648
        if ($this->transactions >= 1) {
649
            throw $e;
650
        }
651
652
        return $this->tryAgainIfCausedByLostConnection(
653
            $e, $query, $bindings, $callback
0 ignored issues
show
$e of type object<Exception> is not a sub-type of object<Childish\ChildishException>. It seems like you assume a child class of the class Exception to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
654
        );
655
    }
656
657
    /**
658
     * Handle a query exception that occurred during query execution.
659
     *
660
     * @param \Childish\ChildishException $e
661
     * @param                             $query
662
     * @param                             $bindings
663
     * @param \Closure                    $callback
664
     * @return mixed
665
     */
666
    protected function tryAgainIfCausedByLostConnection(ChildishException $e, $query, $bindings, Closure $callback)
667
    {
668
        if ($this->causedByLostConnection($e->getPrevious())) {
669
            $this->reconnect();
670
671
            return $this->runQueryCallback($query, $bindings, $callback);
672
        }
673
674
        throw $e;
675
    }
676
677
    /**
678
     * Reconnect to the database.
679
     *
680
     * @return void
681
     * @throws \LogicException
682
     */
683
    public function reconnect()
684
    {
685
        if (is_callable($this->reconnector)) {
686
            return call_user_func($this->reconnector, $this);
687
        }
688
689
        throw new \LogicException('Lost connection and no reconnector available.');
690
    }
691
692
    /**
693
     * Reconnect to the database if a PDO connection is missing.
694
     *
695
     * @return void
696
     */
697
    protected function reconnectIfMissingConnection()
698
    {
699
        if (is_null($this->pdo)) {
700
            $this->reconnect();
701
        }
702
    }
703
704
    /**
705
     * Disconnect from the underlying PDO connection.
706
     *
707
     * @return void
708
     */
709
    public function disconnect()
710
    {
711
        $this->setPdo(null)->setReadPdo(null);
712
    }
713
714
    /**
715
     * Fire the given event if possible.
716
     *
717
     * @param  mixed $event
718
     * @return void
719
     */
720
    protected function event($event)
721
    {
722
        if (isset($this->events)) {
723
            $this->events->dispatch($event);
0 ignored issues
show
The property events does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
724
        }
725
    }
726
727
    /**
728
     * Indicate if any records have been modified.
729
     *
730
     * @param  bool $value
731
     * @return void
732
     */
733
    public function recordsHaveBeenModified($value = true)
734
    {
735
        if (!$this->recordsModified) {
736
            $this->recordsModified = $value;
0 ignored issues
show
Documentation Bug introduced by
The property $recordsModified was declared of type integer, but $value is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
737
        }
738
    }
739
740
    /**
741
     * Get the current PDO connection.
742
     *
743
     * @return \PDO
744
     */
745
    public function getPdo()
746
    {
747
        if ($this->pdo instanceof Closure) {
748
            return $this->pdo = call_user_func($this->pdo);
749
        }
750
751
        return $this->pdo;
752
    }
753
754
    /**
755
     * Get the current PDO connection used for reading.
756
     *
757
     * @return \PDO
758
     */
759
    public function getReadPdo()
760
    {
761
        if ($this->transactions > 0) {
762
            return $this->getPdo();
763
        }
764
765
        if ($this->getConfig('sticky') && $this->recordsModified) {
766
            return $this->getPdo();
767
        }
768
769
        if ($this->readPdo instanceof Closure) {
770
            return $this->readPdo = call_user_func($this->readPdo);
771
        }
772
773
        return $this->readPdo ? : $this->getPdo();
774
    }
775
776
    /**
777
     * Set the PDO connection.
778
     *
779
     * @param  \PDO|\Closure|null $pdo
780
     * @return $this
781
     */
782
    public function setPdo($pdo)
783
    {
784
        $this->transactions = 0;
785
786
        $this->pdo = $pdo;
787
788
        return $this;
789
    }
790
791
    /**
792
     * Set the PDO connection used for reading.
793
     *
794
     * @param  \PDO||\Closure|null  $pdo
0 ignored issues
show
The doc-type \PDO||\Closure|null could not be parsed: Unknown type name "|" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
795
     * @return $this
796
     */
797
    public function setReadPdo($pdo)
798
    {
799
        $this->readPdo = $pdo;
800
801
        return $this;
802
    }
803
804
    /**
805
     * Set the reconnect instance on the connection.
806
     *
807
     * @param  callable $reconnector
808
     * @return $this
809
     */
810
    public function setReconnector(callable $reconnector)
811
    {
812
        $this->reconnector = $reconnector;
813
814
        return $this;
815
    }
816
817
    /**
818
     * Get the database connection name.
819
     *
820
     * @return string|null
821
     */
822
    public function getName()
823
    {
824
        return $this->getConfig('name');
825
    }
826
827
    /**
828
     * Get an option from the configuration options.
829
     *
830
     * @param  string|null $option
831
     * @return mixed
832
     */
833
    public function getConfig($option = null)
834
    {
835
        return Tools::get($this->config, $option);
836
    }
837
838
    /**
839
     * Get the PDO driver name.
840
     *
841
     * @return string
842
     */
843
    public function getDriverName()
844
    {
845
        return $this->getConfig('driver');
846
    }
847
848
    /**
849
     * Get the query grammar used by the connection.
850
     *
851
     * @return  \Childish\query\QueryGrammar
852
     */
853
    public function getQueryGrammar()
854
    {
855
        return $this->queryGrammar;
856
    }
857
858
    /**
859
     * Set the query grammar used by the connection.
860
     *
861
     * @param   \Childish\query\QueryGrammar $grammar
862
     * @return void
863
     */
864
    public function setQueryGrammar(QueryGrammar $grammar)
865
    {
866
        $this->queryGrammar = $grammar;
867
    }
868
869
870
    /**
871
     * Get the query post processor used by the connection.
872
     *
873
     * @return \childish\query\Processor
874
     */
875
    public function getPostProcessor()
876
    {
877
        return $this->postProcessor;
878
    }
879
880
    /**
881
     * Set the query post processor used by the connection.
882
     *
883
     * @param  \childish\query\Processor $processor
884
     * @return void
885
     */
886
    public function setPostProcessor(Processor $processor)
887
    {
888
        $this->postProcessor = $processor;
889
    }
890
891
    /**
892
     * Determine if the connection in a "dry run".
893
     *
894
     * @return bool
895
     */
896
    public function pretending()
897
    {
898
        return $this->pretending === true;
899
    }
900
901
    /**
902
     * Get the connection query log.
903
     *
904
     * @return array
905
     */
906
    public function getQueryLog()
907
    {
908
        return $this->queryLog;
909
    }
910
911
    /**
912
     * Clear the query log.
913
     *
914
     * @return void
915
     */
916
    public function flushQueryLog()
917
    {
918
        $this->queryLog = [];
919
    }
920
921
    /**
922
     * Enable the query log on the connection.
923
     *
924
     * @return void
925
     */
926
    public function enableQueryLog()
927
    {
928
        $this->loggingQueries = true;
929
    }
930
931
    /**
932
     * Disable the query log on the connection.
933
     *
934
     * @return void
935
     */
936
    public function disableQueryLog()
937
    {
938
        $this->loggingQueries = false;
939
    }
940
941
    /**
942
     * Determine whether we're logging queries.
943
     *
944
     * @return bool
945
     */
946
    public function logging()
947
    {
948
        return $this->loggingQueries;
949
    }
950
951
    /**
952
     * Get the name of the connected database.
953
     *
954
     * @return string
955
     */
956
    public function getDatabaseName()
957
    {
958
        return $this->database;
959
    }
960
961
    /**
962
     * Set the name of the connected database.
963
     *
964
     * @param  string $database
965
     * @return string
966
     */
967
    public function setDatabaseName($database)
968
    {
969
        $this->database = $database;
970
    }
971
972
    /**
973
     * Get the table prefix for the connection.
974
     *
975
     * @return string
976
     */
977
    public function getTablePrefix()
978
    {
979
        return $this->tablePrefix;
980
    }
981
982
    /**
983
     * Set the table prefix in use by the connection.
984
     *
985
     * @param  string $prefix
986
     * @return void
987
     */
988
    public function setTablePrefix($prefix)
989
    {
990
        $this->tablePrefix = $prefix;
991
992
        $this->getQueryGrammar()->setTablePrefix($prefix);
993
    }
994
995
    /**
996
     * Set the table prefix and return the grammar.
997
     *
998
     * @param   \Childish\query\QueryGrammar $grammar
999
     * @return  \Childish\query\QueryGrammar
1000
     */
1001
    public function withTablePrefix(QueryGrammar $grammar)
1002
    {
1003
        $grammar->setTablePrefix($this->tablePrefix);
1004
1005
        return $grammar;
1006
    }
1007
1008
    /**
1009
     * Register a connection resolver.
1010
     *
1011
     * @param  string   $driver
1012
     * @param  \Closure $callback
1013
     * @return void
1014
     */
1015
    public static function resolverFor($driver, Closure $callback)
1016
    {
1017
        static::$resolvers[$driver] = $callback;
1018
    }
1019
1020
    /**
1021
     * Get the connection resolver for the given driver.
1022
     *
1023
     * @param  string $driver
1024
     * @return mixed
1025
     */
1026
    public static function getResolver($driver)
1027
    {
1028
        return static::$resolvers[$driver] ?? null;
1029
    }
1030
1031
    /**
1032
     * Call the given Closure with the given value then return the value.
1033
     *
1034
     * @param  mixed         $value
1035
     * @param  callable|null $callback
1036
     * @return mixed
1037
     */
1038
    public function higherOrderTap($value, $callback = null)
1039
    {
1040
        if (is_null($callback) === false) {
1041
            $callback($value);
1042
        }
1043
        return $value;
1044
    }
1045
1046
    /**
1047
     * Determine if the given exception was caused by a lost connection.
1048
     *
1049
     * @param  \Exception  $e
1050
     * @return bool
1051
     */
1052
    protected function causedByLostConnection(\Exception $e)
1053
    {
1054
        $message = $e->getMessage();
1055
1056
        return Tools::contains($message, [
1057
            'server has gone away',
1058
            'no connection to the server',
1059
            'Lost connection',
1060
            'is dead or not enabled',
1061
            'Error while sending',
1062
            'decryption failed or bad record mac',
1063
            'server closed the connection unexpectedly',
1064
            'SSL connection has been closed unexpectedly',
1065
            'Error writing data to the connection',
1066
            'Resource deadlock avoided',
1067
            'Transaction() on null',
1068
            'child connection forced to terminate due to client_idle_limit',
1069
        ]);
1070
    }
1071
}