PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.20
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.20
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / vendor / wpfluent / framework / src / WPFluent / Database / Query / WPDBConnection.php

WPDBConnection.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.20, at vendor/wpfluent/framework/src/WPFluent/Database/Query/WPDBConnection.php

721 lines 17.0 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 FluentBoards\Framework\Database\Query;
8
9 use Closure;
10 use Exception;
11 use DateTimeInterface;
12 use FluentBoards\Framework\Database\Schema;
13 use FluentBoards\Framework\Database\QueryException;
14 use FluentBoards\Framework\Database\Query\Processor;
15 use FluentBoards\Framework\Database\Query\Expression;
16 use FluentBoards\Framework\Database\ConnectionInterface;
17 use FluentBoards\Framework\Database\Query\Builder as QueryBuilder;
18 use FluentBoards\Framework\Database\Query\Grammar as QueryGrammar;
19
20 class WPDBConnection implements ConnectionInterface
21 {
22 /**
23 * $wpdb Global $wpdb instance
24 * @var Object
25 */
26 protected $wpdb;
27
28 /**
29 * The name of the connected database.
30 *
31 * @var string
32 */
33 protected $database;
34
35 /**
36 * The table prefix for the connection.
37 *
38 * @var string
39 */
40 protected $tablePrefix = '';
41
42 /**
43 * The database connection configuration options.
44 *
45 * @var array
46 */
47 protected $config = [];
48
49 /**
50 * The query grammar implementation.
51 *
52 * @var \FluentBoards\Framework\Database\Query\Grammar
53 */
54 protected $queryGrammar;
55
56 /**
57 * The query post processor implementation.
58 *
59 * @var \FluentBoards\Framework\Database\Query\Processor
60 */
61 protected $postProcessor;
62
63 /**
64 * The number of active transactions.
65 *
66 * @var int
67 */
68 protected $transactions = 0;
69
70 /**
71 * The number of total transactions.
72 *
73 * @var int
74 */
75 protected $transactionCount = 0;
76
77 /**
78 * Create a new database connection instance.
79 *
80 * @param $wpdb $pdo
81 * @param string $database
82 * @param string $tablePrefix
83 * @param array $config
84 * @return void
85 */
86 public function __construct($pdo, $database = '', $tablePrefix = '', array $config = [])
87 {
88 $this->setupWpdbInstance($pdo);
89
90 // First we will setup the default properties. We keep track of the DB
91 // name we are connected to since it is needed when some reflective
92 // type commands are run such as checking whether a table exists.
93 $this->database = $database;
94
95 $this->tablePrefix = $tablePrefix;
96
97 $this->config = $config;
98
99 // We need to initialize a query grammar and the query post processors
100 // which are both very important parts of the database abstractions
101 // so we initialize these to their default values while starting.
102 $this->useDefaultQueryGrammar();
103
104 $this->useDefaultPostProcessor();
105 }
106
107 /**
108 * Populate $wpdb instance & turn off db errors
109 *
110 * @param $wpdb Global $wpdb instance
111 * @return Null
112 */
113 protected function setupWpdbInstance($wpdb)
114 {
115 $this->wpdb = $wpdb;
116
117 $this->wpdb->show_errors(false);
118 }
119
120 /**
121 * Set the query grammar to the default implementation.
122 *
123 * @return void
124 */
125 public function useDefaultQueryGrammar()
126 {
127 $this->queryGrammar = $this->getDefaultQueryGrammar();
128 }
129
130 /**
131 * Get the default query grammar instance.
132 *
133 * @return \FluentBoards\Framework\Database\Query\Grammar
134 */
135 protected function getDefaultQueryGrammar()
136 {
137 return new QueryGrammar;
138 }
139
140 /**
141 * Set the query post processor to the default implementation.
142 *
143 * @return void
144 */
145 public function useDefaultPostProcessor()
146 {
147 $this->postProcessor = $this->getDefaultPostProcessor();
148 }
149
150 /**
151 * Get the default post processor instance.
152 *
153 * @return \FluentBoards\Framework\Database\Query\Processor
154 */
155 protected function getDefaultPostProcessor()
156 {
157 return new Processor;
158 }
159
160 /**
161 * Begin a fluent query against a database table.
162 *
163 * @param \Closure|\FluentBoards\Framework\Database\Query\Builder|string $table
164 * @param string|null $as
165 * @return \FluentBoards\Framework\Database\Query\Builder
166 */
167 public function table($table, $as = null)
168 {
169 return $this->query()->from($table, $as);
170 }
171
172 /**
173 * Get a new query builder instance.
174 *
175 * @return \FluentBoards\Framework\Database\Query\Builder
176 */
177 public function query()
178 {
179 return new QueryBuilder(
180 $this, $this->getQueryGrammar(), $this->getPostProcessor()
181 );
182 }
183
184 /**
185 * Run a select statement and return a single result.
186 *
187 * @param string $query
188 * @param array $bindings
189 * @param bool $useReadPdo
190 * @return mixed
191 */
192 public function selectOne($query, $bindings = [], $useReadPdo = true)
193 {
194 $query = $this->bindParams($query, $bindings);
195
196 $result = $this->wpdb->get_row($query);
197
198 if ($result === false || $this->wpdb->last_error) {
199 throw new QueryException($query, $bindings, new Exception($this->wpdb->last_error));
200 }
201
202 return $result;
203 }
204
205 /**
206 * Run a select statement against the database.
207 *
208 * @param string $query
209 * @param array $bindings
210 * @param bool $useReadPdo
211 * @return array
212 */
213 public function select($query, $bindings = [], $useReadPdo = true)
214 {
215 $query = $this->bindParams($query, $bindings);
216
217 $result = $this->wpdb->get_results($query);
218
219 if ($result === false || $this->wpdb->last_error) {
220 throw new QueryException($query, $bindings, new Exception($this->wpdb->last_error));
221 }
222
223 return $result;
224 }
225
226 /**
227 * A hacky way to emulate bind parameters into SQL query
228 *
229 * @param $query
230 * @param $bindings
231 *
232 * @return mixed
233 */
234 protected function bindParams($query, $bindings, $update = false)
235 {
236 $query = str_replace('"', '`', $query);
237
238 $bindings = $this->prepareBindings($bindings);
239
240 if (!$bindings) {
241 return $query;
242 }
243
244 $bindings = array_map(function ($replace) {
245
246 if (is_string($replace)) {
247 $replace = "'" . esc_sql($replace) . "'";
248 } elseif ($replace === null) {
249 $replace = "null";
250 }
251
252 return $replace;
253
254 }, $bindings);
255
256 $query = str_replace(array('%', '?'), array('%%', '%s'), $query);
257
258 $query = vsprintf($query, $bindings);
259
260 return $query;
261 }
262
263 /**
264 * A hacky way to emulate bind parameters into SQL query for mysqli
265 * Only used to run a cursor query using the underlying mysqli instance.
266 *
267 * @param $query
268 * @param $bindings
269 *
270 * @return mixed
271 */
272 protected function bindParamsForSqli($query, $bindings, $update = false)
273 {
274 $query = str_replace('"', '`', $query);
275
276 $bindings = $this->prepareBindings($bindings);
277
278 if (!$bindings) {
279 return $query;
280 }
281
282 $bindings = array_map(function ($replace) {
283
284 if (is_string($replace)) {
285 $replace = "'" . esc_sql($replace) . "'";
286 } elseif ($replace === null) {
287 $replace = "null";
288 }
289
290 return $replace;
291
292 }, $bindings);
293
294 $query = vsprintf($query, $bindings);
295
296 return $query;
297 }
298
299 /**
300 * Run a select statement against the database and returns a generator.
301 *
302 * @param string $query
303 * @param array $bindings
304 * @param bool $useReadPdo
305 * @return \Generator
306 */
307 public function cursor($query, $bindings = [], $useReadPdo = true)
308 {
309 // When the underlying driver is not the mysqli.
310 // it's not a pure cursor just mimicked like one.
311 if (!$this->wpdb->dbh instanceof \mysqli) {
312 foreach ($this->select($query, $bindings) as $row) {
313 yield $row;
314 }
315 return;
316 }
317
318 // The underlying driver is the mysqli
319 $this->wpdb->flush();
320 $this->wpdb->insert_id = 0;
321 $this->wpdb->check_current_query = true;
322
323 if (!$this->wpdb->check_connection()) {
324 throw new QueryException(
325 $query, $bindings, new Exception(
326 $this->wpdb->last_error || 'Error reconnecting to the database.'
327 )
328 );
329 }
330
331 if (defined('SAVEQUERIES') && SAVEQUERIES) {
332 $this->wpdb->timer_start();
333 }
334
335 $statement = $this->wpdb->dbh->prepare(
336 $this->bindParamsForSqli($query, $bindings)
337 );
338
339 $bindings && $statement->bind_param(
340 str_repeat('s', count($bindings)),
341 ...$bindings
342 );
343
344 if ($statement->execute()) {
345
346 $result = $statement->get_result();
347
348 $this->wpdb->num_queries++;
349 $this->wpdb->last_query = $query;
350 $this->wpdb->num_rows = $result->num_rows;
351
352 if (defined('SAVEQUERIES') && SAVEQUERIES) {
353 $this->wpdb->log_query(
354 $query,
355 $this->wpdb->timer_stop(),
356 $this->wpdb->get_caller(),
357 $this->wpdb->time_start,
358 []
359 );
360 }
361
362 $i = 0;
363 while ($row = $result->fetch_assoc()) {
364 $this->wpdb->last_result[$i] = $row;
365 $i++;
366 yield $row;
367 }
368
369 return;
370
371 }
372
373 if ($statement->error || $statement->errno) {
374 $this->wpdb->last_error = __($statement->error || 'Mysqli Error No: ' . $statement->errno);
375 throw new QueryException(
376 $query, $bindings, new Exception(
377 $statement->error || 'Mysqli Error No: ' . $statement->errno
378 )
379 );
380 }
381 }
382
383 /**
384 * Run an insert statement against the database.
385 *
386 * @param string $query
387 * @param array $bindings
388 * @return bool
389 */
390 public function insert($query, $bindings = [])
391 {
392 return $this->statement($query, $bindings);
393 }
394
395 /**
396 * Run an update statement against the database.
397 *
398 * @param string $query
399 * @param array $bindings
400 * @return int
401 */
402 public function update($query, $bindings = [])
403 {
404 return $this->affectingStatement($query, $bindings);
405 }
406
407 /**
408 * Run a delete statement against the database.
409 *
410 * @param string $query
411 * @param array $bindings
412 * @return int
413 */
414 public function delete($query, $bindings = [])
415 {
416 return $this->affectingStatement($query, $bindings);
417 }
418
419 /**
420 * Execute an SQL statement and return the boolean result.
421 *
422 * @param string $query
423 * @param array $bindings
424 * @return bool
425 */
426 public function statement($query, $bindings = [])
427 {
428 $newQuery = $this->bindParams($query, $bindings, true);
429
430 $result = $this->unprepared($newQuery);
431
432 if ($result === false || $this->wpdb->last_error) {
433 throw new QueryException($newQuery, $bindings, new Exception($this->wpdb->last_error));
434 }
435
436 return $result;
437 }
438
439 /**
440 * Run an SQL statement and get the number of rows affected.
441 *
442 * @param string $query
443 * @param array $bindings
444 * @return int
445 */
446 public function affectingStatement($query, $bindings = [])
447 {
448 $newQuery = $this->bindParams($query, $bindings, true);
449
450 $result = $this->wpdb->query($newQuery);
451
452 if ($result === false || $this->wpdb->last_error) {
453 throw new QueryException($newQuery, $bindings, new Exception($this->wpdb->last_error));
454 }
455
456 return intval($result);
457 }
458
459 /**
460 * Run a raw, unprepared query against the PDO connection.
461 *
462 * @param string $query
463 * @return bool
464 */
465 public function unprepared($query)
466 {
467 return $this->wpdb->query($query);
468 }
469
470 /**
471 * Execute the given callback in "dry run" mode.
472 *
473 * @param \Closure $callback
474 * @return array
475 */
476 public function pretend(Closure $callback)
477 {
478 // ...
479 }
480
481 /**
482 * Prepare the query bindings for execution.
483 *
484 * @param array $bindings
485 * @return array
486 */
487 public function prepareBindings(array $bindings)
488 {
489 $grammar = $this->getQueryGrammar();
490
491 foreach ($bindings as $key => $value) {
492 // We need to transform all instances of DateTimeInterface into the actual
493 // date string. Each query grammar maintains its own date string format
494 // so we'll just ask the grammar for the format to get from the date.
495 if ($value instanceof DateTimeInterface) {
496 $bindings[$key] = $value->format($grammar->getDateFormat());
497 } elseif (is_bool($value)) {
498 $bindings[$key] = (int) $value;
499 }
500 }
501
502 return $bindings;
503 }
504
505 /**
506 * Get a new raw query expression.
507 *
508 * @param mixed $value
509 * @return \FluentBoards\Framework\Database\Query\Expression
510 */
511 public function raw($value)
512 {
513 return new Expression($value);
514 }
515
516 /**
517 * Get the query grammar used by the connection.
518 *
519 * @return \FluentBoards\Framework\Database\Query\Grammar
520 */
521 public function getQueryGrammar()
522 {
523 $this->queryGrammar->setTablePrefix($this->wpdb->prefix);
524
525 return $this->queryGrammar;
526 }
527
528 /**
529 * Set the query grammar used by the connection.
530 *
531 * @param \FluentBoards\Framework\Database\Query\Grammar $grammar
532 * @return $this
533 */
534 public function setQueryGrammar(Grammar $grammar)
535 {
536 $this->queryGrammar = $grammar;
537
538 return $this;
539 }
540
541 /**
542 * Get the query post processor used by the connection.
543 *
544 * @return \FluentBoards\Framework\Database\Query\Processor
545 */
546 public function getPostProcessor()
547 {
548 return $this->postProcessor;
549 }
550
551 /**
552 * Set the query post processor used by the connection.
553 *
554 * @param \FluentBoards\Framework\Database\Query\Processor $processor
555 * @return $this
556 */
557 public function setPostProcessor(Processor $processor)
558 {
559 $this->postProcessor = $processor;
560
561 return $this;
562 }
563
564 /**
565 * Return the last insert id
566 *
567 * @param string $args
568 *
569 * @return int
570 */
571 public function lastInsertId($args)
572 {
573 return $this->wpdb->insert_id;
574 }
575
576 /**
577 * Return self as PDO, the Processor instance uses it.
578 *
579 * @return \FluentBoards\Framework\Database\Query\WPDBConnection
580 */
581 public function getPdo()
582 {
583 return $this;
584 }
585
586 /**
587 * Returns the $wpdb object.
588 *
589 * @return Object $wpdb
590 */
591 public function getWPDB()
592 {
593 return $this->wpdb;
594 }
595
596 /**
597 * Get the database connection name.
598 *
599 * @return string|null
600 */
601 public function getName()
602 {
603 return 'mysql';
604 }
605
606 /**
607 * Get the name of the connected database.
608 *
609 * @return string
610 */
611 public function getDatabaseName()
612 {
613 return $this->wpdb->dbname;
614 }
615
616 /**
617 * Execute a Closure within a transaction.
618 *
619 * @param Closure $callback
620 * @param int $attempts
621 *
622 * @return mixed
623 *
624 * @throws Exception
625 */
626 public function transaction(Closure $callback, $attempts = 1)
627 {
628 $this->beginTransaction();
629 try {
630 $data = $callback();
631 $this->commit();
632 return $data;
633 } catch (Exception $e) {
634 $this->rollBack();
635 throw $e;
636 }
637 }
638
639 /**
640 * Start a new database transaction.
641 *
642 * @return void
643 */
644 public function beginTransaction()
645 {
646 $transaction = $this->unprepared("START TRANSACTION;");
647
648 if (false !== $transaction) {
649 $this->transactionCount++;
650 }
651 }
652
653 /**
654 * Commit the active database transaction.
655 *
656 * @return void
657 */
658 public function commit()
659 {
660 if ($this->transactionCount < 1) {
661 return;
662 }
663
664 $transaction = $this->unprepared("COMMIT;");
665
666 if (false !== $transaction) {
667 $this->transactionCount--;
668 }
669 }
670
671 /**
672 * Rollback the active database transaction.
673 *
674 * @return void
675 */
676 public function rollBack()
677 {
678 if ($this->transactionCount < 1) {
679 return;
680 }
681
682 $transaction = $this->unprepared("ROLLBACK;");
683
684 if ($transaction !== false) {
685 $this->transactionCount--;
686 }
687 }
688
689 /**
690 * Get the number of active transactions.
691 *
692 * @return int
693 */
694 public function transactionLevel()
695 {
696 return $this->transactionCount;
697 }
698
699 /**
700 * Get the column listing for a given table.
701 *
702 * @param string $table
703 * @return array
704 */
705 public function getColumnListing($table)
706 {
707 return Schema::getColumns($table);
708 }
709
710 /**
711 * Alias for getColumnListing.
712 *
713 * @param @param string $t
714 * @return array
715 */
716 public function getColumns($t)
717 {
718 return $this->getColumnListing($t);
719 }
720 }
721