PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.8.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.8.0
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Database / Query / WPDBConnection.php

WPDBConnection.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.8.0, at vendor/wpfluent/framework/src/WPFluent/Database/Query/WPDBConnection.php

994 lines 25.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * WPDB Connection
5 */
6
7 namespace FluentCommunity\Framework\Database\Query;
8
9 use Closure;
10 use Exception;
11 use DateTimeInterface;
12 use FluentCommunity\Framework\Foundation\App;
13 use FluentCommunity\Framework\Database\Schema;
14 use FluentCommunity\Framework\Database\QueryException;
15 use FluentCommunity\Framework\Database\ConnectionInterface;
16 use FluentCommunity\Framework\Database\MultipleColumnsSelectedException;
17 use FluentCommunity\Framework\Database\Events\QueryExecuted;
18 use FluentCommunity\Framework\Database\Query\Expression;
19 use FluentCommunity\Framework\Database\Query\Processors\Processor;
20 use FluentCommunity\Framework\Database\Query\Processors\MySqlProcessor;
21 use FluentCommunity\Framework\Database\Query\Processors\SQLiteProcessor;
22 use FluentCommunity\Framework\Database\Query\Builder as QueryBuilder;
23 use FluentCommunity\Framework\Database\Query\Grammars\Grammar;
24 use FluentCommunity\Framework\Database\Query\Grammars\MySqlGrammar;
25 use FluentCommunity\Framework\Database\Query\Grammars\SQLiteGrammar;
26 use FluentCommunity\Framework\Database\Concerns\ManagesTransactions;
27 use FluentCommunity\Framework\Database\DetectsLostConnections;
28
29 use FluentCommunity\Framework\Database\Events\TransactionBeginning;
30 use FluentCommunity\Framework\Database\Events\TransactionCommitted;
31 use FluentCommunity\Framework\Database\Events\TransactionCommitting;
32 use FluentCommunity\Framework\Database\Events\TransactionRolledBack;
33
34 class WPDBConnection implements ConnectionInterface
35 {
36 use DetectsLostConnections, ManagesTransactions;
37
38 /**
39 * $wpdb Global $wpdb instance
40 * @var Object
41 */
42 protected $wpdb;
43
44 /**
45 * The name of the connected database.
46 *
47 * @var string
48 */
49 protected $database;
50
51 /**
52 * The table prefix for the connection.
53 *
54 * @var string
55 */
56 protected $tablePrefix = '';
57
58 /**
59 * The database connection configuration options.
60 *
61 * @var array
62 */
63 protected $config = [];
64
65 /**
66 * The query grammar implementation.
67 *
68 * @var \FluentCommunity\Framework\Database\Query\Grammars\Grammar
69 */
70 protected $queryGrammar;
71
72 /**
73 * The query post processor implementation.
74 *
75 * @var \FluentCommunity\Framework\Database\Query\Processors\Processor
76 */
77 protected $postProcessor;
78
79 /**
80 * The number of active transactions.
81 *
82 * @var int
83 */
84 protected $transactions = 0;
85
86 /**
87 * The transaction manager instance.
88 *
89 * @var \FluentCommunity\Framework\Database\DatabaseTransactionsManager|null
90 */
91 protected $transactionsManager;
92
93 /**
94 * All of the callbacks that should be invoked before a transaction is started.
95 *
96 * @var \Closure[]
97 */
98 protected $beforeStartingTransaction = [];
99
100 /**
101 * The event dispatcher.
102 *
103 * @var \FluentCommunity\Framework\Events\Dispatcher
104 */
105 protected $event = null;
106
107 /**
108 * Create a new database connection instance.
109 *
110 * @param \wpdb $wpdb The WordPress database instance.
111 * @return void
112 */
113 public function __construct($wpdb)
114 {
115 $this->setupWpdbInstance($wpdb);
116
117 $this->useDefaultQueryGrammar();
118
119 $this->useDefaultPostProcessor();
120
121 $this->event = App::make('events');
122 }
123
124 /**
125 * Populate $wpdb instance & turn off db errors
126 *
127 * @param $wpdb Global $wpdb instance
128 * @return Null
129 */
130 protected function setupWpdbInstance($wpdb)
131 {
132 $this->wpdb = $wpdb;
133
134 $this->wpdb->show_errors(
135 $this->shouldShowErrors()
136 );
137
138 $this->registerSqliteFunctions();
139 }
140
141 /**
142 * Register PHP-backed SOUNDEX() and LEVENSHTEIN() functions on the SQLite
143 * connection so phonetic ("sounds like") and fuzzy ("similar") queries
144 * work, mirroring MySQL's native/stored equivalents.
145 *
146 * SQLite ships neither function; PHP provides both natively, so we bind
147 * them as UDFs. Using PHP's soundex() here matches the term that
148 * SQLiteGrammar encodes with the same soundex() on the binding side.
149 *
150 * No-ops on MySQL and silently skips if the underlying PDO is unreachable,
151 * so a missing SQLite layer never breaks booting.
152 *
153 * @return void
154 */
155 protected function registerSqliteFunctions()
156 {
157 if (! $this->isSqlite()) {
158 return;
159 }
160
161 if (! ($pdo = $this->resolveSqlitePdo())) {
162 return;
163 }
164
165 try {
166 $pdo->sqliteCreateFunction('soundex', 'soundex', 1);
167 $pdo->sqliteCreateFunction('levenshtein', 'levenshtein', 2);
168 } catch (\Throwable $e) {
169 // Leave the functions unregistered rather than break booting;
170 // whereSoundsLike()/whereSimilar() will only surface a SQL error
171 // if they are actually used on this connection.
172 }
173 }
174
175 /**
176 * Resolve the real PDO handle behind the WordPress SQLite layer.
177 *
178 * Supports the official "SQLite Database Integration" plugin
179 * (WP_SQLite_Translator::get_pdo()), a dbh that is itself a PDO
180 * (WP-SQLite-DB's PDOEngine), and the shared $GLOBALS['@pdo'] cache.
181 *
182 * @return \PDO|null
183 */
184 protected function resolveSqlitePdo()
185 {
186 $dbh = $this->wpdb->dbh ?? null;
187
188 if ($dbh && method_exists($dbh, 'get_pdo')) {
189 $pdo = $dbh->get_pdo();
190 } elseif ($dbh instanceof \PDO) {
191 $pdo = $dbh;
192 } elseif (isset($GLOBALS['@pdo'])) {
193 $pdo = $GLOBALS['@pdo'];
194 } else {
195 $pdo = null;
196 }
197
198 return $pdo instanceof \PDO ? $pdo : null;
199 }
200
201 /**
202 * Determine if database errors should be shown.
203 *
204 * @return bool
205 */
206 protected function shouldShowErrors()
207 {
208 return strpos(App::env(), 'prod') === false;
209 }
210
211 /**
212 * Set the query grammar to the default implementation.
213 *
214 * @return void
215 */
216 public function useDefaultQueryGrammar()
217 {
218 $this->queryGrammar = $this->getDefaultQueryGrammar();
219 }
220
221 /**
222 * Get the default query grammar instance.
223 *
224 * @return \FluentCommunity\Framework\Database\Query\Grammars\Grammar
225 */
226 protected function getDefaultQueryGrammar()
227 {
228 return $this->isSqlite() ? new SQLiteGrammar : new MySqlGrammar;
229 }
230
231 /**
232 * Set the query post processor to the default implementation.
233 *
234 * @return void
235 */
236 public function useDefaultPostProcessor()
237 {
238 $this->postProcessor = $this->getDefaultPostProcessor();
239 }
240
241 /**
242 * Get the default post processor instance.
243 *
244 * @return \FluentCommunity\Framework\Database\Query\Processors\Processor
245 */
246 protected function getDefaultPostProcessor()
247 {
248 return $this->isSqlite() ? new SQLiteProcessor : new MySqlProcessor;
249 }
250
251 /**
252 * Begin a fluent query against a database table.
253 *
254 * @param \Closure|\FluentCommunity\Framework\Database\Query\Builder|string $table
255 * @param string|null $as
256 * @return \FluentCommunity\Framework\Database\Query\Builder
257 */
258 public function table($table, $as = null)
259 {
260 return $this->query()->from($table, $as);
261 }
262
263 /**
264 * Get a new query builder instance.
265 *
266 * @return \FluentCommunity\Framework\Database\Query\Builder
267 */
268 public function query()
269 {
270 return new QueryBuilder(
271 $this, $this->getQueryGrammar(), $this->getPostProcessor()
272 );
273 }
274
275 /**
276 * Run a select statement and return a single result.
277 *
278 * @param string $query
279 * @param array $bindings
280 * @return mixed
281 */
282 public function selectOne($query, $bindings = [])
283 {
284 return $this->run($query, $bindings, function ($query, $bindings) {
285 $query = $this->bindParams($query, $bindings);
286
287 $result = $this->wpdb->get_row($query);
288
289 if ($result === false || $this->wpdb->last_error) {
290 throw new QueryException(
291 $query, $bindings, new Exception($this->wpdb->last_error)
292 );
293 }
294
295 return $result;
296 });
297 }
298
299 /**
300 * Run a select statement and return the first column of the first row.
301 *
302 * @param string $query
303 * @param array $bindings
304 * @return mixed
305 *
306 * @throws \FluentCommunity\Framework\Database\MultipleColumnsSelectedException
307 */
308 public function scalar($query, $bindings = [])
309 {
310 $record = $this->selectOne($query, $bindings);
311
312 if (is_null($record)) {
313 return null;
314 }
315
316 $record = (array)$record;
317
318 if (count($record) > 1) {
319 throw new MultipleColumnsSelectedException(
320 'The query returned more than one column.'
321 );
322 }
323
324 return reset($record);
325 }
326
327 /**
328 * Run a select statement against the database.
329 *
330 * @param string $query
331 * @param array $bindings
332 * @return array
333 */
334 public function select($query, $bindings = [])
335 {
336 return $this->run($query, $bindings, function ($query, $bindings) {
337 $query = $this->bindParams($query, $bindings);
338
339 $result = $this->wpdb->get_results($query);
340
341 if ($result === false || $this->wpdb->last_error) {
342 throw new QueryException(
343 $query, $bindings, new Exception($this->wpdb->last_error)
344 );
345 }
346
347 return $result;
348 });
349 }
350
351 /**
352 * Bind the parameters into SQL query
353 *
354 * @param $query
355 * @param $bindings
356 * @return string
357 */
358 protected function bindParams(string $query, array $bindings)
359 {
360 $query = str_replace('"', '`', $query);
361
362 $bindings = $this->prepareBindings($bindings);
363
364 if (empty($bindings)) {
365 return $query;
366 }
367
368 $query = str_replace(['%', '?'], ['%%', '%s'], $query);
369
370 if ($this->wpdb->dbh instanceof \mysqli) {
371 // wpdb->prepare() casts null to '' for %s, which on TIMESTAMP
372 // columns becomes 0000-00-00 00:00:00. Splice literal NULL into
373 // the SQL for null bindings before prepare() ever sees them.
374 [$query, $bindings] = $this->spliceNullBindings($query, $bindings, '%s');
375
376 if (empty($bindings)) {
377 return $query;
378 }
379
380 return $this->wpdb->prepare($query, ...$bindings);
381 }
382
383 $bindings = array_map(function ($value) {
384 if ($value === null) return 'NULL';
385 if (is_bool($value)) return $value ? '1' : '0';
386 if (is_string($value)) return "'" . esc_sql($value) . "'";
387 return (string) $value;
388 }, $bindings);
389
390 return vsprintf($query, $bindings);
391 }
392
393 /**
394 * Replace placeholder occurrences whose binding is null with the
395 * literal SQL keyword NULL, returning the rewritten query and the
396 * remaining (non-null) bindings re-indexed.
397 *
398 * @param string $query
399 * @param array $bindings
400 * @param string $placeholder '%s' for wpdb->prepare path, '?' for mysqli native prepare
401 * @return array{0:string,1:array}
402 *
403 * @phpstan-ignore-next-line
404 */
405 protected function spliceNullBindings(string $query, array $bindings, string $placeholder): array
406 {
407 $bindings = array_values($bindings);
408
409 if (empty($bindings) || strpos($query, $placeholder) === false) {
410 return [$query, $bindings];
411 }
412
413 $parts = explode($placeholder, $query);
414 $last = count($parts) - 1;
415 $rebuilt = '';
416 $kept = [];
417
418 foreach ($parts as $idx => $part) {
419 $rebuilt .= $part;
420
421 if ($idx === $last) {
422 continue;
423 }
424
425 if (array_key_exists($idx, $bindings) && $bindings[$idx] === null) {
426 $rebuilt .= 'NULL';
427 } else {
428 $rebuilt .= $placeholder;
429 if (array_key_exists($idx, $bindings)) {
430 $kept[] = $bindings[$idx];
431 }
432 }
433 }
434
435 return [$rebuilt, $kept];
436 }
437
438 /**
439 * Run a select statement against the database and returns a generator.
440 *
441 * @param string $query
442 * @param array $bindings
443 * @return \Generator
444 * @throws \FluentAccount\Framework\Database\QueryException
445 */
446 public function cursor($query, $bindings = [])
447 {
448 // If not mysqli (e.g., SQLite), fallback to standard select
449 if (!$this->wpdb->dbh instanceof \mysqli) {
450 foreach ($this->select($query, $bindings) as $row) {
451 yield $row;
452 }
453 return;
454 }
455
456 $preparedQuery = str_replace('"', '`', $query);
457
458 $preparedQuery = str_replace('%', '%%', $preparedQuery);
459
460 $bindings = $this->prepareBindings($bindings);
461
462 // mysqli's bind_param can't bind null with type 's' (becomes '').
463 // Replace null bindings with literal NULL in the SQL itself.
464 [$preparedQuery, $bindings] = $this->spliceNullBindings($preparedQuery, $bindings, '?');
465
466 $this->wpdb->flush();
467 $this->wpdb->insert_id = 0;
468 $this->wpdb->check_current_query = true;
469
470 if (!$this->wpdb->check_connection()) {
471 throw new QueryException(
472 $query, $bindings, new Exception(
473 $this->wpdb->last_error ?: 'Error reconnecting to database.'
474 )
475 );
476 }
477
478 if (defined('SAVEQUERIES') && SAVEQUERIES) {
479 $this->wpdb->timer_start();
480 }
481
482 $statement = $this->wpdb->dbh->prepare($preparedQuery);
483
484 if ($statement === false) {
485 throw new QueryException(
486 $query, $bindings, new Exception(
487 'Failed to prepare statement: ' . $this->wpdb->dbh->error
488 )
489 );
490 }
491
492 if (!empty($bindings)) {
493 $types = '';
494 foreach ($bindings as $binding) {
495 if (is_int($binding)) {
496 $types .= 'i';
497 } elseif (is_double($binding)) {
498 $types .= 'd';
499 } else {
500 $types .= 's';
501 }
502 }
503
504 $statement->bind_param($types, ...$bindings);
505 }
506
507 if ($statement->execute()) {
508 $result = $statement->get_result();
509
510 if ($result) {
511 while ($row = $result->fetch_assoc()) {
512 yield (object) $row;
513 }
514 $result->free();
515 } else {
516 if ($statement->errno) {
517 throw new QueryException(
518 $query, $bindings, new Exception($statement->error)
519 );
520 }
521 }
522 $statement->close();
523 return;
524 }
525
526 // Error handling if statement execution fails
527 if ($statement->error || $statement->errno) {
528 $err = $statement->error
529 ? $statement->error
530 : 'Mysqli Error No: ' . $statement->errno;
531
532 $this->wpdb->last_error = $err;
533
534 throw new QueryException(
535 $query, $bindings, new Exception($err)
536 );
537 }
538 }
539
540 /**
541 * Raw cursor query for MySQLi (non-prepared).
542 *
543 * @param string $query
544 * @param array $bindings
545 * @return \Generator
546 * @throws \FluentAccount\Framework\Database\QueryException
547 */
548 public function rawCursor($query, $bindings = [])
549 {
550 if (!empty($bindings)) {
551 $query = str_replace(['%', '?'], ['%%', '%s'], $query);
552 $query = $this->wpdb->prepare($query, ...$bindings);
553 }
554
555 if (!$this->wpdb->dbh instanceof \mysqli) {
556 foreach ($this->select($query) as $row) { yield $row; }
557 return;
558 }
559
560 $stmt = $this->wpdb->dbh->query($query, MYSQLI_USE_RESULT);
561
562 if ($stmt instanceof \mysqli_result) {
563 try {
564 while ($row = $stmt->fetch_assoc()) {
565 yield (object) $row;
566 }
567 } finally {
568 $stmt->free();
569 }
570 } elseif ($this->wpdb->dbh->error) {
571 throw new QueryException(
572 $query, $bindings, new Exception($this->wpdb->dbh->error)
573 );
574 }
575 }
576
577 /**
578 * Run an insert statement against the database.
579 *
580 * @param string $query
581 * @param array $bindings
582 * @return bool
583 */
584 public function insert($query, $bindings = [])
585 {
586 return $this->statement($query, $bindings);
587 }
588
589 /**
590 * Run an update statement against the database.
591 *
592 * @param string $query
593 * @param array $bindings
594 * @return int
595 */
596 public function update($query, $bindings = [])
597 {
598 return $this->affectingStatement($query, $bindings);
599 }
600
601 /**
602 * Run a delete statement against the database.
603 *
604 * @param string $query
605 * @param array $bindings
606 * @return int
607 */
608 public function delete($query, $bindings = [])
609 {
610 return $this->affectingStatement($query, $bindings);
611 }
612
613 /**
614 * Execute an SQL statement and return the boolean result.
615 *
616 * @param string $query
617 * @param array $bindings
618 * @return bool
619 */
620 public function statement($query, $bindings = [])
621 {
622 return $this->run($query, $bindings, function ($query, $bindings) {
623 $query = $this->bindParams($query, $bindings, true);
624
625 $result = $this->unprepared($query);
626
627 if ($result === false || $this->wpdb->last_error) {
628 throw new QueryException(
629 $query, $bindings, new Exception($this->wpdb->last_error)
630 );
631 }
632
633 return $result;
634 });
635 }
636
637 /**
638 * Run an SQL statement and get the number of rows affected.
639 *
640 * @param string $query
641 * @param array $bindings
642 * @return int
643 */
644 public function affectingStatement($query, $bindings = [])
645 {
646 return $this->run($query, $bindings, function ($query, $bindings) {
647 $query = $this->bindParams($query, $bindings, true);
648
649 $result = $this->wpdb->query($query);
650
651 if ($result === false || $this->wpdb->last_error) {
652 throw new QueryException(
653 $query, $bindings, new Exception($this->wpdb->last_error)
654 );
655 }
656
657 return intval($result);
658 });
659 }
660
661 /**
662 * Run a raw, unprepared query against the PDO connection.
663 *
664 * @param string $query
665 * @return bool
666 */
667 public function unprepared($query)
668 {
669 return $this->wpdb->query($query);
670 }
671
672 /**
673 * Execute the given callback in "dry run" mode.
674 *
675 * @param \Closure $callback
676 * @return array
677 */
678 public function pretend(Closure $callback)
679 {
680 // ...
681 }
682
683 /**
684 * Prepare the query bindings for execution.
685 *
686 * @param array $bindings
687 * @return array
688 */
689 public function prepareBindings(array $bindings)
690 {
691 $grammar = $this->getQueryGrammar();
692
693 foreach ($bindings as $key => $value) {
694 // We need to transform all instances of DateTimeInterface into
695 // the actual date string. Each query grammar maintains its
696 // own date string format so we'll just ask the grammar
697 // for the format to get from the date.
698 if ($value instanceof DateTimeInterface) {
699 $bindings[$key] = $value->format($grammar->getDateFormat());
700 } elseif (is_bool($value)) {
701 $bindings[$key] = (int)$value;
702 }
703 }
704
705 return $bindings;
706 }
707
708 /**
709 * Run a SQL statement and log its execution context.
710 *
711 * @param string $query
712 * @param array $bindings
713 * @param \Closure $callback
714 * @return mixed
715 */
716 public function run($query, $bindings, $callback)
717 {
718 $start = microtime(true);
719
720 try {
721 return $callback($query, $bindings);
722 } finally {
723 $time = $this->getElapsedTime($start);
724 $this->event->dispatch(
725 new QueryExecuted($query, $bindings, $time, $this)
726 );
727 }
728 }
729
730 /**
731 * Get a new raw query expression.
732 *
733 * @param mixed $value
734 * @return \FluentCommunity\Framework\Database\Query\Expression
735 */
736 public function raw($value)
737 {
738 return new Expression($value);
739 }
740
741 /**
742 * Get the query grammar used by the connection.
743 *
744 * @return \FluentCommunity\Framework\Database\Query\Grammars\Grammar
745 */
746 public function getQueryGrammar()
747 {
748 $this->queryGrammar->setTablePrefix($this->wpdb);
749
750 return $this->queryGrammar;
751 }
752
753 /**
754 * Set the query grammar used by the connection.
755 *
756 * @param \FluentCommunity\Framework\Database\Query\Grammars\Grammar $grammar
757 * @return $this
758 */
759 public function setQueryGrammar(Grammar $grammar)
760 {
761 $this->queryGrammar = $grammar;
762
763 return $this;
764 }
765
766 /**
767 * Get the query post processor used by the connection.
768 *
769 * @return \FluentCommunity\Framework\Database\Query\Processors\Processor
770 */
771 public function getPostProcessor()
772 {
773 return $this->postProcessor;
774 }
775
776 /**
777 * Set the query post processor used by the connection.
778 *
779 * @param \FluentCommunity\Framework\Database\Query\Processors\Processor $processor
780 * @return $this
781 */
782 public function setPostProcessor(Processor $processor)
783 {
784 $this->postProcessor = $processor;
785
786 return $this;
787 }
788
789 /**
790 * Return the last insert id
791 *
792 * @param string $args
793 *
794 * @return int
795 */
796 public function lastInsertId($args)
797 {
798 return $this->wpdb->insert_id;
799 }
800
801 /**
802 * Return self as PDO, the Processor instance uses it.
803 *
804 * @return \FluentCommunity\Framework\Database\Query\WPDBConnection
805 */
806 public function getPdo()
807 {
808 return $this;
809 }
810
811 /**
812 * Returns the $wpdb object.
813 *
814 * @return Object $wpdb
815 */
816 public function getWPDB()
817 {
818 return $this->wpdb;
819 }
820
821 /**
822 * Get the database connection name.
823 *
824 * @return string|null
825 */
826 public function getName()
827 {
828 return $this->isSqlite() ? 'sqlite' : 'mysql';
829 }
830
831 /**
832 * Get the name of the connected database.
833 *
834 * @return string
835 */
836 public function getDatabaseName()
837 {
838 if ($this->isSqlite()) {
839 return 'sqlite';
840 }
841
842 return $this->wpdb->dbname;
843 }
844
845 /**
846 * Get the server version for the connection.
847 *
848 * @return string
849 *
850 * @phpstan-ignore-next-line
851 */
852 public function getServerVersion(): string
853 {
854 return $this->getWPDB()->db_version();
855 }
856
857 /**
858 * Get the column listing for a given table.
859 *
860 * @param string $table
861 * @return array
862 */
863 public function getColumnListing($table)
864 {
865 return Schema::getColumns($table);
866 }
867
868 /**
869 * Alias for getColumnListing.
870 *
871 * @param string $t
872 * @return array
873 */
874 public function getColumns($t)
875 {
876 return $this->getColumnListing($t);
877 }
878
879 /**
880 * Determine if the connected database is a sqlite database.
881 *
882 * @return bool
883 */
884 public function isSqlite()
885 {
886 return Schema::isSqlite();
887 }
888
889 /**
890 * Determine if the connected database is a mariadb database.
891 *
892 * @return bool
893 */
894 public function isMaria()
895 {
896 return Schema::isMaria();
897 }
898
899 /**
900 * Register a hook to be run just before a database transaction is started.
901 *
902 * @param \Closure $callback
903 * @return $this
904 */
905 public function beforeStartingTransaction(Closure $callback)
906 {
907 $this->beforeStartingTransaction[] = $callback;
908
909 return $this;
910 }
911
912 /**
913 * Register a database query listener with the connection.
914 *
915 * @param \Closure $callback
916 * @return void
917 */
918 public function listen(Closure $callback)
919 {
920 $this->event->listen(QueryExecuted::class, $callback);
921 }
922
923 /**
924 * Fire an event for this connection.
925 *
926 * @param string $event
927 * @return array|null
928 */
929 protected function fireConnectionEvent($event)
930 {
931 if (!$this->event) {
932 return;
933 }
934
935 switch ($event) {
936 case 'beganTransaction':
937 $payload = new TransactionBeginning($this);
938 break;
939 case 'committed':
940 $payload = new TransactionCommitted($this);
941 break;
942 case 'committing':
943 $payload = new TransactionCommitting($this);
944 break;
945 case 'rollingBack':
946 $payload = new TransactionRolledBack($this);
947 break;
948 default:
949 $payload = null;
950 break;
951 }
952
953 if ($payload !== null) {
954 return $this->event->dispatch($payload);
955 }
956 }
957
958 /**
959 * Get the elapsed time since a given starting point.
960 *
961 * @param int $start
962 * @return float
963 */
964 protected function getElapsedTime($start)
965 {
966 return round((microtime(true) - $start) * 1000, 2);
967 }
968
969 /**
970 * Get the table prefix for the connection.
971 *
972 * @return [type] [description]
973 */
974 public function getTablePrefix()
975 {
976 if (!$this->tablePrefix) {
977 $this->tablePrefix = $this->queryGrammar->getTablePrefix();
978 }
979
980 return $this->tablePrefix;
981 }
982
983 /**
984 * Get the table name with the table prefix.
985 *
986 * @param string $table
987 * @return string
988 */
989 public function getTableName($table)
990 {
991 return $this->getTablePrefix() . $table;
992 }
993 }
994