1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Lenevor PHP Framework |
5
|
|
|
* |
6
|
|
|
* LICENSE |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the new BSD license that is bundled |
9
|
|
|
* with this package in the file license.md. |
10
|
|
|
* It is also available through the world-wide-web at this URL: |
11
|
|
|
* https://lenevor.com/license |
12
|
|
|
* If you did not receive a copy of the license and are unable to |
13
|
|
|
* obtain it through the world-wide-web, please send an email |
14
|
|
|
* to [email protected] so we can send you a copy immediately. |
15
|
|
|
* |
16
|
|
|
* @package Lenevor |
17
|
|
|
* @subpackage Base |
18
|
|
|
* @link https://lenevor.com |
19
|
|
|
* @copyright Copyright (c) 2019 - 2021 Alexander Campo <[email protected]> |
20
|
|
|
* @license https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md |
21
|
|
|
*/ |
22
|
|
|
|
23
|
|
|
namespace Syscodes\Database; |
24
|
|
|
|
25
|
|
|
use PDO; |
26
|
|
|
use Closure; |
27
|
|
|
use DateTime; |
28
|
|
|
use Exception; |
29
|
|
|
use PDOStatement; |
30
|
|
|
use LogicException; |
31
|
|
|
use Syscodes\Support\Str; |
32
|
|
|
use Syscodes\Collections\Arr; |
33
|
|
|
use Syscodes\Database\Query\Processor; |
34
|
|
|
use Syscodes\Database\Query\Expression; |
35
|
|
|
use Syscodes\Contracts\Events\Dispatcher; |
36
|
|
|
use Syscodes\Database\Events\QueryExecuted; |
37
|
|
|
use Syscodes\Database\Events\TransactionBegin; |
38
|
|
|
use Syscodes\Database\Events\StatementPrepared; |
39
|
|
|
use Syscodes\Database\Exceptions\QueryException; |
40
|
|
|
use Syscodes\Database\Events\TransactionRollback; |
41
|
|
|
use Syscodes\Database\Events\TransactionCommitted; |
42
|
|
|
use Syscodes\Database\Query\Builder as QueryBuilder; |
43
|
|
|
use Syscodes\Database\Query\Grammar as QueryGrammar; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Creates a database connection using PDO. |
47
|
|
|
* |
48
|
|
|
* @author Alexander Campo <[email protected]> |
49
|
|
|
*/ |
50
|
|
|
class Connection implements ConnectionInterface |
51
|
|
|
{ |
52
|
|
|
use Concerns\ManagesTransactions, |
53
|
|
|
Concerns\DetectLostConnections; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* The database connection configuration options. |
57
|
|
|
* |
58
|
|
|
* @var array $config |
59
|
|
|
*/ |
60
|
|
|
protected $config = []; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* The name of the connected database. |
64
|
|
|
* |
65
|
|
|
* @var string $database |
66
|
|
|
*/ |
67
|
|
|
protected $database; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* The event dispatcher instance. |
71
|
|
|
* |
72
|
|
|
* @var \Syscodes\Contracts\Events\Dispatcher $events |
73
|
|
|
*/ |
74
|
|
|
protected $events; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* The default fetch mode of the connection. |
78
|
|
|
* |
79
|
|
|
* @var int $fetchMode |
80
|
|
|
*/ |
81
|
|
|
protected $fetchMode = PDO::FETCH_OBJ; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* The query grammar implementation. |
85
|
|
|
* |
86
|
|
|
* @var \Syscodes\Database\Query\Grammar|string |
87
|
|
|
*/ |
88
|
|
|
protected $queryGrammar; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* All of the queries run against the connection. |
92
|
|
|
* |
93
|
|
|
* @var array $queryLog |
94
|
|
|
*/ |
95
|
|
|
protected $queryLog = []; |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Indicates whether queries are being logged. |
99
|
|
|
* |
100
|
|
|
* @var bool $loggingQueries |
101
|
|
|
*/ |
102
|
|
|
protected $loggingQueries = false; |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* The active PDO connection. |
106
|
|
|
* |
107
|
|
|
* @var \PDO $pdo |
108
|
|
|
*/ |
109
|
|
|
protected $pdo; |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* The query post processor implementation. |
113
|
|
|
* |
114
|
|
|
* @var \Syscodes\Database\Query\Processor|string $postProcessor |
115
|
|
|
*/ |
116
|
|
|
protected $postProcessor; |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Indicates if the connection is in a "dry run". |
120
|
|
|
* |
121
|
|
|
* @var bool $pretending |
122
|
|
|
*/ |
123
|
|
|
protected $pretending = false; |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* The active PDO connection used for reads. |
127
|
|
|
* |
128
|
|
|
* @var \PDO $readPdo |
129
|
|
|
*/ |
130
|
|
|
protected $readPdo; |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* The reconnector instance for the connection. |
134
|
|
|
* |
135
|
|
|
* @var callable $reconnector |
136
|
|
|
*/ |
137
|
|
|
protected $reconnector; |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* The connection resolvers. |
141
|
|
|
* |
142
|
|
|
* @var array $resolvers |
143
|
|
|
*/ |
144
|
|
|
protected static $resolvers = []; |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* The table prefix for the connection. |
148
|
|
|
* |
149
|
|
|
* @var string $tablePrefix |
150
|
|
|
*/ |
151
|
|
|
protected $tablePrefix; |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* The number of active transactions. |
155
|
|
|
* |
156
|
|
|
* @var int $transactions |
157
|
|
|
*/ |
158
|
|
|
protected $transactions = 0; |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Constructor. Create new a Database connection instance. |
162
|
|
|
* |
163
|
|
|
* @param \PDO|Closure $pdo |
164
|
|
|
* @param string $database |
165
|
|
|
* @param string $tablePrefix |
166
|
|
|
* @param array $config |
167
|
|
|
* |
168
|
|
|
* @return void |
169
|
|
|
*/ |
170
|
|
|
public function __construct($pdo, $database = '', $tablePrefix = '', array $config = []) |
171
|
|
|
{ |
172
|
|
|
$this->pdo = $pdo; |
|
|
|
|
173
|
|
|
|
174
|
|
|
$this->database = $database; |
175
|
|
|
|
176
|
|
|
$this->tablePrefix = $tablePrefix; |
177
|
|
|
|
178
|
|
|
$this->config = $config; |
179
|
|
|
|
180
|
|
|
$this->useDefaultQueryGrammar(); |
181
|
|
|
|
182
|
|
|
$this->useDefaultPostProcessor(); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Begin a fluent query against a database table. |
187
|
|
|
* |
188
|
|
|
* @param \Closure|Syscodes\Database\Query\Builder|string $table |
|
|
|
|
189
|
|
|
* @param string $as |
190
|
|
|
* |
191
|
|
|
* @return \Syscodes\Database\Query\Builder |
192
|
|
|
*/ |
193
|
|
|
public function table($table, $as = null) |
194
|
|
|
{ |
195
|
|
|
return $this->query()->from($table, $as); |
|
|
|
|
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Get a new query builder instance. |
200
|
|
|
* |
201
|
|
|
* @return \Syscodes\Database\Query\Builder |
202
|
|
|
*/ |
203
|
|
|
public function query() |
204
|
|
|
{ |
205
|
|
|
return new QueryBuilder( |
|
|
|
|
206
|
|
|
$this, $this->getQueryGrammar(), $this->getPostProcessor() |
207
|
|
|
); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Get a new raw query expression. |
212
|
|
|
* |
213
|
|
|
* @param mixed $value |
214
|
|
|
* |
215
|
|
|
* @return \Syscodes\Database\Query\Expression |
216
|
|
|
*/ |
217
|
|
|
public function raw($value) |
218
|
|
|
{ |
219
|
|
|
return new Expression($value); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Run a select statement and return a single result. |
224
|
|
|
* |
225
|
|
|
* @param string $query |
226
|
|
|
* @param array $bindings |
227
|
|
|
* @param bool $useReadPdo |
228
|
|
|
* |
229
|
|
|
* @return mixed |
230
|
|
|
*/ |
231
|
|
|
public function selectOne($query, $bindings = [], $useReadPdo = true) |
232
|
|
|
{ |
233
|
|
|
$records = $this->select($query, $bindings, $useReadPdo); |
234
|
|
|
|
235
|
|
|
return array_shift($records); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Run a select statement against the database. |
240
|
|
|
* |
241
|
|
|
* @param string $query |
242
|
|
|
* @param array $bindings |
243
|
|
|
* |
244
|
|
|
* @return array |
245
|
|
|
*/ |
246
|
|
|
public function selectFromConnection($query, $bindings) |
247
|
|
|
{ |
248
|
|
|
return $this->select($query, $bindings, false); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Run a select statement against the database. |
253
|
|
|
* |
254
|
|
|
* @param string $query |
255
|
|
|
* @param array $bindings |
256
|
|
|
* @param bool $useReadPdo |
257
|
|
|
* |
258
|
|
|
* @return array |
259
|
|
|
*/ |
260
|
|
|
public function select($query, $bindings = [], $useReadPdo = true) |
261
|
|
|
{ |
262
|
|
|
return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) { |
263
|
|
|
|
264
|
|
|
if ($this->pretending()) { |
265
|
|
|
return []; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$statement = $this->prepared( |
269
|
|
|
$this->getPdoForSelect($useReadPdo)->prepare($query) |
270
|
|
|
); |
271
|
|
|
|
272
|
|
|
$this->bindValues($statement, $this->prepareBindings($bindings)); |
273
|
|
|
|
274
|
|
|
$statement->execute(); |
275
|
|
|
|
276
|
|
|
return $statement->fetchAll(); |
277
|
|
|
}); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Run an insert statement against the database. |
282
|
|
|
* |
283
|
|
|
* @param string $query |
284
|
|
|
* @param array $bindings |
285
|
|
|
* |
286
|
|
|
* @return bool |
287
|
|
|
*/ |
288
|
|
|
public function insert($query, $bindings = []) |
289
|
|
|
{ |
290
|
|
|
$this->statement($query, $bindings); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Run an update statement against the database. |
295
|
|
|
* |
296
|
|
|
* @param string $query |
297
|
|
|
* @param array $bindings |
298
|
|
|
* |
299
|
|
|
* @return int |
300
|
|
|
*/ |
301
|
|
|
public function update($query, $bindings = []) |
302
|
|
|
{ |
303
|
|
|
return $this->affectingStatement($query, $bindings); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Run an delete statement against the database. |
308
|
|
|
* |
309
|
|
|
* @param string $query |
310
|
|
|
* @param array $bindings |
311
|
|
|
* |
312
|
|
|
* @return int |
313
|
|
|
*/ |
314
|
|
|
public function delete($query, $bindings = []) |
315
|
|
|
{ |
316
|
|
|
return $this->affectingStatement($query, $bindings); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Execute an SQL statement and return the boolean result. |
321
|
|
|
* |
322
|
|
|
* @param string $query |
323
|
|
|
* @param array $bindings |
324
|
|
|
* |
325
|
|
|
* @return bool |
326
|
|
|
*/ |
327
|
|
|
public function statement($query, $bindings = []) |
328
|
|
|
{ |
329
|
|
|
return $this->run($query, $bindings, function ($query, $bindings) { |
330
|
|
|
if ($this->pretending()) { |
331
|
|
|
return true; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
$statement = $this->getPdo()->prepare($query); |
335
|
|
|
|
336
|
|
|
$this->bindValues($statement, $this->prepareBindings($bindings)); |
337
|
|
|
|
338
|
|
|
return $statement->execute(); |
339
|
|
|
|
340
|
|
|
}); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Run an SQL statement and get the number of rows affected. |
345
|
|
|
* |
346
|
|
|
* @param string $query |
347
|
|
|
* @param array $bindings |
348
|
|
|
* |
349
|
|
|
* @return int |
350
|
|
|
*/ |
351
|
|
|
public function affectingStatement($query, $bindings = []) |
352
|
|
|
{ |
353
|
|
|
return $this->run($query, $bindings, function ($query, $bindings) { |
354
|
|
|
|
355
|
|
|
if ($this->pretending()) |
356
|
|
|
{ |
357
|
|
|
return 0; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
$statement = $this->getPdo()->prepare($query); |
361
|
|
|
|
362
|
|
|
$this->bindValues($statement, $this->prepareBindings($bindings)); |
363
|
|
|
|
364
|
|
|
$statement->execute(); |
365
|
|
|
|
366
|
|
|
$count = $statement->rowCount() > 0; |
367
|
|
|
|
368
|
|
|
return $count; |
369
|
|
|
|
370
|
|
|
}); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* Execute the given callback in "dry run" mode. |
375
|
|
|
* |
376
|
|
|
* @param \Closure $callback |
377
|
|
|
* |
378
|
|
|
* @return array |
379
|
|
|
*/ |
380
|
|
|
public function prepend(Closure $callback) |
381
|
|
|
{ |
382
|
|
|
return $this->withFreshQueryLog(function () use ($callback) { |
383
|
|
|
|
384
|
|
|
$this->pretending = true; |
385
|
|
|
|
386
|
|
|
$callback($this); |
387
|
|
|
|
388
|
|
|
$this->pretending = false; |
389
|
|
|
|
390
|
|
|
return $this->queryLog; |
391
|
|
|
|
392
|
|
|
}); |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* Execute the given callback in "dry run" mode. |
397
|
|
|
* |
398
|
|
|
* @param \Closure $callback |
399
|
|
|
* |
400
|
|
|
* @return array |
401
|
|
|
*/ |
402
|
|
|
protected function withFreshQueryLog($callback) |
403
|
|
|
{ |
404
|
|
|
$loggingQueries = $this->loggingQueries; |
405
|
|
|
|
406
|
|
|
$this->enableQueryLog(); |
407
|
|
|
|
408
|
|
|
$this->queryLog = []; |
409
|
|
|
|
410
|
|
|
$result = $callback(); |
411
|
|
|
|
412
|
|
|
$this->loggingQueries = $loggingQueries; |
413
|
|
|
|
414
|
|
|
return $result; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
/** |
418
|
|
|
* Bind values to their parameters in the given statement. |
419
|
|
|
* |
420
|
|
|
* @param \PDOStatement $statement |
421
|
|
|
* @param array $bindings |
422
|
|
|
* |
423
|
|
|
* @return void |
424
|
|
|
*/ |
425
|
|
|
public function bindValues($statement, $bindings) |
426
|
|
|
{ |
427
|
|
|
foreach ($bindings as $key => $value) { |
428
|
|
|
$statement->bindValue( |
429
|
|
|
is_string($key) ? $key : $key + 1, |
430
|
|
|
$value, |
431
|
|
|
is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR |
432
|
|
|
); |
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* Run a SQL statement and log its execution context. |
438
|
|
|
* |
439
|
|
|
* @param string $query |
440
|
|
|
* @param array $bindings |
441
|
|
|
* @param \Closure $callback |
442
|
|
|
* |
443
|
|
|
* @return mixed |
444
|
|
|
* |
445
|
|
|
* @throws \Syscodes\Database\Exceptions\QueryException |
446
|
|
|
*/ |
447
|
|
|
protected function run($query, $bindings, Closure $callback) |
448
|
|
|
{ |
449
|
|
|
$this->reconnectIfMissingConnection(); |
450
|
|
|
|
451
|
|
|
$start = microtime(true); |
452
|
|
|
|
453
|
|
|
try { |
454
|
|
|
$result = $this->runQueryCallback($query, $bindings, $callback); |
455
|
|
|
} catch (QueryException $e) { |
456
|
|
|
$result = $this->handleQueryException( |
457
|
|
|
$e, $query, $bindings, $callback |
458
|
|
|
); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
$this->logQuery( |
462
|
|
|
$query, $bindings, $this->getElapsedTime($start) |
|
|
|
|
463
|
|
|
); |
464
|
|
|
|
465
|
|
|
return $result; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* Run a SQL statement. |
470
|
|
|
* |
471
|
|
|
* @param string $query |
472
|
|
|
* @param array $bindings |
473
|
|
|
* @param \Closure $callback |
474
|
|
|
* |
475
|
|
|
* @return mixed |
476
|
|
|
* |
477
|
|
|
* @throws \Syscodes\Database\Exceptions\QueryException |
478
|
|
|
*/ |
479
|
|
|
protected function runQueryCallback($query, $bindings, Closure $callback) |
480
|
|
|
{ |
481
|
|
|
$result = ''; |
|
|
|
|
482
|
|
|
|
483
|
|
|
try { |
484
|
|
|
$result = $callback($query, $bindings); |
485
|
|
|
} catch (Exception $e) { |
486
|
|
|
// throw new QueryException( |
487
|
|
|
// $query, $this->prepareBindings($bindings), $e |
488
|
|
|
// ); |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
return $result; |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* Prepare the query bindings for execution. |
496
|
|
|
* |
497
|
|
|
* @param array $bindings |
498
|
|
|
* |
499
|
|
|
* @return array |
500
|
|
|
*/ |
501
|
|
|
public function prepareBindings(array $bindings) |
502
|
|
|
{ |
503
|
|
|
foreach ($bindings as $key => $value) { |
504
|
|
|
if ($value instanceof DateTime) { |
505
|
|
|
$bindings[$key] = $value->format($this->getQueryGrammar()->getDateFormat()); |
506
|
|
|
} elseif (is_bool($value)) { |
507
|
|
|
$bindings[$key] = (int) $value; |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
return $bindings; |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
/** |
515
|
|
|
* Handle a query exception. |
516
|
|
|
* |
517
|
|
|
* @param \Syscodes\Database\Exceptions\QueryException $e |
518
|
|
|
* @param string $query |
519
|
|
|
* @param array $bindings |
520
|
|
|
* @param \Closure $callback |
521
|
|
|
* |
522
|
|
|
* @return mixed |
523
|
|
|
* |
524
|
|
|
* @throws \Syscodes\Database\Exceptions\QueryException |
525
|
|
|
*/ |
526
|
|
|
protected function handleQueryException(QueryException $e, $query, $bindings, Closure $callback) |
527
|
|
|
{ |
528
|
|
|
if ($this->transactions >= 1) { |
529
|
|
|
throw $e; |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
return $this->tryIfAgainCausedByLostConnection( |
533
|
|
|
$e, $query, $bindings, $callback |
534
|
|
|
); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* Handle a query exception that occurred during query execution. |
539
|
|
|
* |
540
|
|
|
* @param \Syscodes\Database\Exceptions\QueryException $e |
541
|
|
|
* @param string $query |
542
|
|
|
* @param array $bindings |
543
|
|
|
* @param \Closure $callback |
544
|
|
|
* |
545
|
|
|
* @return mixed |
546
|
|
|
* |
547
|
|
|
* @throws \Syscodes\Database\Exceptions\QueryException |
548
|
|
|
*/ |
549
|
|
|
protected function tryIfAgainCausedByLostConnection(QueryException $e, $query, $bindings, Closure $callback) |
550
|
|
|
{ |
551
|
|
|
if ($this->causedByLostConnections($e->getPrevious())) { |
552
|
|
|
$this->reconnect(); |
553
|
|
|
|
554
|
|
|
return $this->runQueryCallback($query, $bindings, $callback); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
throw $e; |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Log a query in the connection's query log. |
562
|
|
|
* |
563
|
|
|
* @param string $query |
564
|
|
|
* @param array $bindings |
565
|
|
|
* @param float|null $time |
566
|
|
|
* |
567
|
|
|
* @return void |
568
|
|
|
*/ |
569
|
|
|
public function logQuery($query, $bindings, $time = null) |
570
|
|
|
{ |
571
|
|
|
$this->event(new QueryExecuted($query, $bindings, $time, $this)); |
572
|
|
|
|
573
|
|
|
if ($this->loggingQueries) { |
574
|
|
|
$this->queryLog[] = compact('query', 'bindings', 'time'); |
575
|
|
|
} |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
/** |
579
|
|
|
* Fire an event for this connection. |
580
|
|
|
* |
581
|
|
|
* @param string $event |
582
|
|
|
* |
583
|
|
|
* @return array|null |
584
|
|
|
*/ |
585
|
|
|
protected function fireConnectionEvent($event) |
586
|
|
|
{ |
587
|
|
|
if ( ! $this->events) { |
588
|
|
|
return; |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
switch($event) |
592
|
|
|
{ |
593
|
|
|
case 'beginTransaction': |
594
|
|
|
return $this->events->dispatch(new TransactionBegin($this)); |
595
|
|
|
case 'committed': |
596
|
|
|
return $this->events->dispatch(new TransactionCommitted($this)); |
597
|
|
|
case 'rollingback': |
598
|
|
|
return $this->events->dispatch(new TransactionRollback($this)); |
599
|
|
|
} |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* Reconnect to the database if a PDO connection is missing. |
604
|
|
|
* |
605
|
|
|
* @return void |
606
|
|
|
*/ |
607
|
|
|
public function reconnectIfMissingConnection() |
608
|
|
|
{ |
609
|
|
|
if (is_null($this->pdo)) { |
610
|
|
|
$this->reconnect(); |
611
|
|
|
} |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
/** |
615
|
|
|
* Disconnect from the underlying PDO connection. |
616
|
|
|
* |
617
|
|
|
* @return void |
618
|
|
|
*/ |
619
|
|
|
public function disconnect() |
620
|
|
|
{ |
621
|
|
|
$this->setPdo(null)->$this->setReadPdo(null); |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
/** |
625
|
|
|
* Get the elapsed time since a given starting point. |
626
|
|
|
* |
627
|
|
|
* @param int $start |
628
|
|
|
* |
629
|
|
|
* @return float |
630
|
|
|
*/ |
631
|
|
|
protected function getElapsedTime($start) |
632
|
|
|
{ |
633
|
|
|
return round((microtime(true) - $start) * 1000, 2); |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/** |
637
|
|
|
* Fire the given event if possible. |
638
|
|
|
* |
639
|
|
|
* @param mixed $event |
640
|
|
|
* |
641
|
|
|
* @return void |
642
|
|
|
*/ |
643
|
|
|
public function event($event) |
644
|
|
|
{ |
645
|
|
|
if (isset($this->events)) { |
646
|
|
|
$this->events->dispatch($event); |
647
|
|
|
} |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
/** |
651
|
|
|
* Reconnect to the database. |
652
|
|
|
* |
653
|
|
|
* @return void |
654
|
|
|
* |
655
|
|
|
* @throws \LogicException |
656
|
|
|
*/ |
657
|
|
|
public function reconnect() |
658
|
|
|
{ |
659
|
|
|
if (is_callable($this->reconnector)) { |
660
|
|
|
return call_user_func($this->reconnector, $this); |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
throw new LogicException('Lost connection and no reconnector available'); |
664
|
|
|
} |
665
|
|
|
|
666
|
|
|
/** |
667
|
|
|
* Configure the PDO prepared statement. |
668
|
|
|
* |
669
|
|
|
* @param \PDOStatement $statement |
670
|
|
|
* |
671
|
|
|
* @return \PDOStatement |
672
|
|
|
*/ |
673
|
|
|
protected function prepared(PDOStatement $statement) |
674
|
|
|
{ |
675
|
|
|
$statement->setFetchMode($this->fetchMode); |
676
|
|
|
|
677
|
|
|
$this->event( |
678
|
|
|
new statementPrepared($this, $statement) |
679
|
|
|
); |
680
|
|
|
|
681
|
|
|
return $statement; |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
/** |
685
|
|
|
* Get the PDO connection to use for a select query. |
686
|
|
|
* |
687
|
|
|
* @param bool $useReadPdo |
688
|
|
|
* |
689
|
|
|
* @return \PDO |
690
|
|
|
*/ |
691
|
|
|
protected function getPdoForSelect($useReadPdo = true) |
692
|
|
|
{ |
693
|
|
|
return $useReadPdo ? $this->getReadPdo() : $this->getPdo(); |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
/** |
697
|
|
|
* Get the PDO instance. |
698
|
|
|
* |
699
|
|
|
* @return \PDO |
700
|
|
|
*/ |
701
|
|
|
public function getPdo() |
702
|
|
|
{ |
703
|
|
|
if ($this->pdo instanceof Closure) { |
|
|
|
|
704
|
|
|
return $this->pdo = call_user_func($this->pdo); |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
return $this->pdo; |
708
|
|
|
} |
709
|
|
|
|
710
|
|
|
/** |
711
|
|
|
* Get the current PDO connection parameter without executing any reconnect logic. |
712
|
|
|
* |
713
|
|
|
* @return \PDO|\Closure|null |
714
|
|
|
*/ |
715
|
|
|
public function getRawPdo() |
716
|
|
|
{ |
717
|
|
|
return $this->pdo; |
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
/** |
721
|
|
|
* Get the current PDO connection used for reading. |
722
|
|
|
* |
723
|
|
|
* @return \PDO |
724
|
|
|
*/ |
725
|
|
|
public function getReadPdo() |
726
|
|
|
{ |
727
|
|
|
if ($this->transactions > 0) { |
728
|
|
|
return $this->getPdo(); |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
if ($this->readPdo instanceof Closure) { |
|
|
|
|
732
|
|
|
return $this->readPdo = call_user_func($this->readPdo); |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
return $this->readPdo ?: $this->getPdo(); |
736
|
|
|
} |
737
|
|
|
|
738
|
|
|
/** |
739
|
|
|
* Get the current read PDO connection parameter without executing any reconnect logic. |
740
|
|
|
* |
741
|
|
|
* @return \PDO|\Closure|null |
742
|
|
|
*/ |
743
|
|
|
public function getRawReadPdo() |
744
|
|
|
{ |
745
|
|
|
return $this->readPdo; |
746
|
|
|
} |
747
|
|
|
|
748
|
|
|
/** |
749
|
|
|
* Set the PDO connection. |
750
|
|
|
* |
751
|
|
|
* @param \PDO|\Closure|null $pdo |
752
|
|
|
* |
753
|
|
|
* @return $this |
754
|
|
|
*/ |
755
|
|
|
public function setPdo($pdo) |
756
|
|
|
{ |
757
|
|
|
$this->transactions = 0; |
758
|
|
|
|
759
|
|
|
$this->pdo = $pdo; |
|
|
|
|
760
|
|
|
|
761
|
|
|
return $this; |
762
|
|
|
} |
763
|
|
|
|
764
|
|
|
/** |
765
|
|
|
* Set the PDO connection used for reading. |
766
|
|
|
* |
767
|
|
|
* @param \PDO|\Closure|null $pdo |
768
|
|
|
* |
769
|
|
|
* @return $this |
770
|
|
|
*/ |
771
|
|
|
public function setReadPdo($pdo) |
772
|
|
|
{ |
773
|
|
|
$this->readPdo = $pdo; |
|
|
|
|
774
|
|
|
|
775
|
|
|
return $this; |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
/** |
779
|
|
|
* Set the reconnect instance on the connection. |
780
|
|
|
* |
781
|
|
|
* @param \Callable $reconnector |
|
|
|
|
782
|
|
|
* |
783
|
|
|
* @return $this |
784
|
|
|
*/ |
785
|
|
|
public function setReconnector(callable $reconnector) |
786
|
|
|
{ |
787
|
|
|
$this->reconnector = $reconnector; |
788
|
|
|
|
789
|
|
|
return $this; |
790
|
|
|
} |
791
|
|
|
|
792
|
|
|
/** |
793
|
|
|
* Get the PDO driver name. |
794
|
|
|
* |
795
|
|
|
* @return string |
796
|
|
|
*/ |
797
|
|
|
public function getConfigDriver() |
798
|
|
|
{ |
799
|
|
|
return $this->getConfig('driver'); |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
/** |
803
|
|
|
* Get the database connection name. |
804
|
|
|
* |
805
|
|
|
* @return string |
806
|
|
|
*/ |
807
|
|
|
public function getName() |
808
|
|
|
{ |
809
|
|
|
return $this->getConfig('name'); |
810
|
|
|
} |
811
|
|
|
|
812
|
|
|
/** |
813
|
|
|
* Get an option from the configuration options. |
814
|
|
|
* |
815
|
|
|
* @param string|null $option |
816
|
|
|
* |
817
|
|
|
* @return mixed |
818
|
|
|
*/ |
819
|
|
|
public function getConfig($option = null) |
820
|
|
|
{ |
821
|
|
|
return Arr::get($this->config, $option); |
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
/** |
825
|
|
|
* Get the query grammar used by the connection. |
826
|
|
|
* |
827
|
|
|
* @return \Syscodes\Database\Query\Grammar |
828
|
|
|
*/ |
829
|
|
|
public function getQueryGrammar() |
830
|
|
|
{ |
831
|
|
|
return $this->queryGrammar; |
|
|
|
|
832
|
|
|
} |
833
|
|
|
|
834
|
|
|
/** |
835
|
|
|
* Set the query grammar to the default implementation. |
836
|
|
|
* |
837
|
|
|
* @return void |
838
|
|
|
*/ |
839
|
|
|
public function useDefaultQueryGrammar() |
840
|
|
|
{ |
841
|
|
|
$this->queryGrammar = $this->getDefaultQueryGrammar(); |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
/** |
845
|
|
|
* Get the default query grammar instance. |
846
|
|
|
* |
847
|
|
|
* @return \Syscodes\Database\Query\Grammar |
848
|
|
|
*/ |
849
|
|
|
protected function getDefaultQueryGrammar() |
850
|
|
|
{ |
851
|
|
|
return new QueryGrammar; |
852
|
|
|
} |
853
|
|
|
|
854
|
|
|
/** |
855
|
|
|
* Get the query post processor used by the connection. |
856
|
|
|
* |
857
|
|
|
* @return \Syscodes\Database\Query\Processor |
858
|
|
|
*/ |
859
|
|
|
public function getPostProcessor() |
860
|
|
|
{ |
861
|
|
|
return $this->postProcessor; |
|
|
|
|
862
|
|
|
} |
863
|
|
|
/** |
864
|
|
|
* Set the query post processor to the default implementation. |
865
|
|
|
* |
866
|
|
|
* @return void |
867
|
|
|
*/ |
868
|
|
|
public function useDefaultPostProcessor() |
869
|
|
|
{ |
870
|
|
|
$this->postProcessor = $this->getDefaultProcessor(); |
871
|
|
|
} |
872
|
|
|
|
873
|
|
|
/** |
874
|
|
|
* Get the default post processor instance. |
875
|
|
|
* |
876
|
|
|
* @return \Syscodes\Database\Query\Processor |
877
|
|
|
*/ |
878
|
|
|
protected function getDefaultProcessor() |
879
|
|
|
{ |
880
|
|
|
return new Processor; |
881
|
|
|
} |
882
|
|
|
|
883
|
|
|
/** |
884
|
|
|
* Get the name of the connected database. |
885
|
|
|
* |
886
|
|
|
* @return string |
887
|
|
|
*/ |
888
|
|
|
public function getDatabase() |
889
|
|
|
{ |
890
|
|
|
return $this->database; |
891
|
|
|
} |
892
|
|
|
|
893
|
|
|
/** |
894
|
|
|
* Set the name of the connected database. |
895
|
|
|
* |
896
|
|
|
* @param string $database |
897
|
|
|
* |
898
|
|
|
* @return $this |
899
|
|
|
*/ |
900
|
|
|
public function setDatabase($database) |
901
|
|
|
{ |
902
|
|
|
$this->database = $database; |
903
|
|
|
|
904
|
|
|
return $this; |
905
|
|
|
} |
906
|
|
|
|
907
|
|
|
/** |
908
|
|
|
* Set the event dispatcher instance on the connection. |
909
|
|
|
* |
910
|
|
|
* @param \Syscodes\Contracts\Events\Dispatcher $events |
911
|
|
|
* |
912
|
|
|
* @return $this |
913
|
|
|
*/ |
914
|
|
|
public function setEventDispatcher(Dispatcher $events) |
915
|
|
|
{ |
916
|
|
|
$this->events = $events; |
917
|
|
|
|
918
|
|
|
return $this; |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
/** |
922
|
|
|
* Determine if the connection is in a "dry run". |
923
|
|
|
* |
924
|
|
|
* @return bool |
925
|
|
|
*/ |
926
|
|
|
public function pretending() |
927
|
|
|
{ |
928
|
|
|
return $this->pretending === true; |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
/** |
932
|
|
|
* Get the connection query log. |
933
|
|
|
* |
934
|
|
|
* @return array |
935
|
|
|
*/ |
936
|
|
|
public function getQueryLog() |
937
|
|
|
{ |
938
|
|
|
return $this->queryLog; |
939
|
|
|
} |
940
|
|
|
|
941
|
|
|
/** |
942
|
|
|
* Clear the query log. |
943
|
|
|
* |
944
|
|
|
* @return void |
945
|
|
|
*/ |
946
|
|
|
public function flushQueryLog() |
947
|
|
|
{ |
948
|
|
|
$this->queryLog = []; |
949
|
|
|
} |
950
|
|
|
|
951
|
|
|
/** |
952
|
|
|
* Enable the query log on the connection. |
953
|
|
|
* |
954
|
|
|
* @return void |
955
|
|
|
*/ |
956
|
|
|
public function EnableQueryLog() |
957
|
|
|
{ |
958
|
|
|
$this->loggingQueries = true; |
959
|
|
|
} |
960
|
|
|
|
961
|
|
|
/** |
962
|
|
|
* Disable the query log on the connection. |
963
|
|
|
* |
964
|
|
|
* @return void |
965
|
|
|
*/ |
966
|
|
|
public function disableQueryLog() |
967
|
|
|
{ |
968
|
|
|
$this->loggingQueries = false; |
969
|
|
|
} |
970
|
|
|
|
971
|
|
|
/** |
972
|
|
|
* Determine whether we're logging queries. |
973
|
|
|
* |
974
|
|
|
* @return bool |
975
|
|
|
*/ |
976
|
|
|
public function logging() |
977
|
|
|
{ |
978
|
|
|
return $this->loggingQueries; |
979
|
|
|
} |
980
|
|
|
|
981
|
|
|
/** |
982
|
|
|
* Get the table prefix for the connection. |
983
|
|
|
* |
984
|
|
|
* @return string |
985
|
|
|
*/ |
986
|
|
|
public function getTablePrefix() |
987
|
|
|
{ |
988
|
|
|
return $this->tablePrefix; |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
/** |
992
|
|
|
* Set the table prefix in use by the connection. |
993
|
|
|
* |
994
|
|
|
* @param string $tablePrefix |
995
|
|
|
* |
996
|
|
|
* @return $this |
997
|
|
|
*/ |
998
|
|
|
public function setTablePrefix($tablePrefix) |
999
|
|
|
{ |
1000
|
|
|
$this->tablePrefix = $tablePrefix; |
1001
|
|
|
|
1002
|
|
|
$this->getQueryGrammar()->setTablePrefix($tablePrefix); |
1003
|
|
|
|
1004
|
|
|
return $this; |
1005
|
|
|
} |
1006
|
|
|
|
1007
|
|
|
/** |
1008
|
|
|
* Set the table prefix and return the grammar. |
1009
|
|
|
* |
1010
|
|
|
* @param \Syscodes\Database\Grammar $grammar |
1011
|
|
|
* |
1012
|
|
|
* @return \Syscodes\Database\Grammar |
1013
|
|
|
*/ |
1014
|
|
|
public function withTablePrefix(Grammar $grammar) |
1015
|
|
|
{ |
1016
|
|
|
$grammar->setTablePrefix($this->tablePrefix); |
1017
|
|
|
|
1018
|
|
|
return $grammar; |
1019
|
|
|
} |
1020
|
|
|
|
1021
|
|
|
/** |
1022
|
|
|
* Get the connection resolver for the given driver. |
1023
|
|
|
* |
1024
|
|
|
* @param string $driver |
1025
|
|
|
* |
1026
|
|
|
* @return mixed |
1027
|
|
|
*/ |
1028
|
|
|
public static function getResolver($driver) |
1029
|
|
|
{ |
1030
|
|
|
return static::$resolvers[$driver] ?? null; |
1031
|
|
|
} |
1032
|
|
|
} |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.