PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.25
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.25
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / vendor / wpfluent / framework / src / WPFluent / Database / Schema.php

Schema.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.25, at vendor/wpfluent/framework/src/WPFluent/Database/Schema.php

884 lines 23.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\Framework\Database;
4
5 use FluentCart\Framework\Database\Concerns\MaintainsDatabase;
6
7 class Schema
8 {
9 use MaintainsDatabase;
10
11 /**
12 * Keep track of custom tables when unit testing
13 *
14 * @var array
15 */
16 public static $customTempTables = [];
17
18 /**
19 * Get the global $wpdb instance
20 *
21 * @return \wpdb The global $wpdb
22 */
23 public static function db()
24 {
25 return $GLOBALS['wpdb'];
26 }
27
28 /**
29 * Get schema/db information
30 *
31 * @return string|array
32 */
33 public static function getInfo($key = null)
34 {
35 $db = static::db();
36
37 $info = [
38 // @phpstan-ignore-next-line
39 'dbname' => $db->dbname,
40 'prefix' => $db->prefix,
41 // @phpstan-ignore-next-line
42 'dbhost' => $db->dbhost,
43 // @phpstan-ignore-next-line
44 'username' => $db->dbuser,
45 // @phpstan-ignore-next-line
46 'password' => $db->dbpassword,
47 'charset' => $db->charset,
48 'collation' => $db->collate,
49 'tables' => static::getTableList(),
50 ];
51
52 return $key ? $info[$key] : $info;
53 }
54
55 /**
56 * Migrates database table(s)
57 *
58 * @param string|array $table The table name without prefix
59 * or an array where each key is table name and value is sql.
60 *
61 * @param string $sql Optional
62 * @return mixed
63 */
64 public static function migrate($table, $sql = null)
65 {
66 if (!$sql && is_array($table)) {
67 $result = [];
68 foreach ($table as $t => $s) {
69 $result = array_merge(
70 $result, (array) static::createTable($t, $s)
71 );
72 }
73 return $result;
74 } else {
75 return static::createTable($table, $sql);
76 }
77 }
78
79 /**
80 * Creates a new table if doesn't exist using dbDelta function
81 *
82 * @param string $table The table name without the prefix
83 * @param string $sql The sql to create table or an absolute path of a
84 * .sql file containing the column definations for creating the new table.
85 *
86 * @return string Message
87 */
88 public static function createTableIfNotExist($table, $sql)
89 {
90 if (!static::hasTable($table)) {
91 return static::createTable($table, $sql);
92 }
93 }
94
95 /**
96 * Checks if a column exists in a table
97 *
98 * @param string $column The column name of the table
99 * @param string $table The table name without prefix
100 * @return boolean
101 */
102
103 public static function hasColumn($column, $table)
104 {
105 $wpdb = static::db();
106
107 $table = static::table($table);
108
109 $columns = $wpdb->get_col("DESCRIBE $table");
110
111 return in_array($column, $columns);
112 }
113
114 /**
115 * Checks if a table exists
116 *
117 * @param string $table The table name without prefix
118 * @return boolean
119 */
120 public static function hasTable($table)
121 {
122 $wpdb = static::db();
123
124 $table = static::table($table);
125
126 $result = $wpdb->get_var("SHOW TABLES LIKE '" . $table . "'") == $table;
127
128 if ($result) {
129 return $result;
130 }
131
132 // Check if any temporary table exists by this table name.
133
134 // At first, store the original state of the error suppress
135 // error and then turn off the error from being shown, so
136 // error will be not shown if there is no temporary
137 // table, then restore the error state.
138 $isErrorSuppressed = $wpdb->suppress_errors;
139
140 $wpdb->suppress_errors = true;
141
142 static::query("SELECT 1 FROM %{$table}% WHERE 0");
143
144 $hasError = !empty($wpdb->last_error);
145
146 $wpdb->suppress_errors = $isErrorSuppressed;
147
148 return !$hasError;
149 }
150
151 /**
152 * Resolves the table prefix and makes the table name with prefix
153 *
154 * @param string $table The table name without the prefix
155 * @return string The resolved table name with the prefix
156 */
157 public static function table($table)
158 {
159 $wpdb = static::db();
160
161 $prefix = $wpdb->prefix;
162
163 if (strpos($table, $prefix) === 0) {
164 return $table;
165 }
166
167 return isset($wpdb->{$table}) ? $wpdb->{$table} : ($wpdb->prefix.$table);
168 }
169
170
171 /**
172 * Resolves the sql prefix
173 *
174 * @param string $sql The file name or the raw sql
175 * @return string The resolved sql
176 */
177 public static function sql($sql = '')
178 {
179 $allowedSqlFileFormats = [
180 'sql'
181 ];
182
183 foreach ($allowedSqlFileFormats as $format) {
184 if (str_ends_with($sql, '.' . $format)) {
185 $sql = @file_exists($sql) ? file_get_contents($sql) : $sql;
186 break;
187 }
188 }
189
190 return $sql;
191 }
192
193 /**
194 * Clean the sql string by removing the comments.
195 *
196 * @param string $sql
197 * @return string
198 */
199 public static function cleanUp($sql)
200 {
201 $sql = static::sql($sql);
202
203 // Remove inline -- comments
204 $sql = preg_replace('/--.*$/m', '', $sql);
205
206 // Remove inline # comments
207 $sql = preg_replace('/#.*$/m', '', $sql);
208
209 // Remove block /* ... */
210 $sql = preg_replace('/\/\*.*?\*\//s', '', $sql);
211
212 // Collapse multiple spaces & newlines
213 // $sql = preg_replace('/\s+/', ' ', $sql);
214
215 // Remove dangling commas before closing parenthesis
216 $sql = preg_replace('/,\s*\)/', ')', $sql);
217
218 return trim($sql);
219 }
220
221 /**
222 * Creates a new table using dbDelta function or alters the table if exists.
223 *
224 * @param string $table The table name without the prefix
225 * @param string $sql The sql to create table or an absolute path of a
226 * .sql file containing the column definations for creating the new table.
227 *
228 * @return string message
229 */
230 public static function createTable($table, $sql)
231 {
232 $table = static::table($table);
233
234 $sql = static::cleanUp($sql);
235
236 if (static::isSqlite()) {
237 $sql = preg_replace('/\bjson\b/i', 'longtext', $sql);
238 }
239
240 $collate = static::db()->get_charset_collate();
241
242 return static::callDBDelta(
243 "CREATE TABLE $table (
244 ".PHP_EOL.trim(trim($sql), ',').PHP_EOL."
245 ) $collate;"
246 );
247 }
248
249 /**
250 * Alters an existing table if exists
251 *
252 * @param string $table The table name without the prefix
253 * @param string $sql The sql to create table or an absolute path of a
254 * .sql file containing the column definations for creating the new table.
255 *
256 * @return string message
257 */
258 public static function alterTableIfExists($table, $sql)
259 {
260 if (static::hasTable($table)) {
261 return static::alterTable($table, $sql);
262 }
263 }
264
265 /**
266 * Alters an existing table
267 *
268 * @param string $table The table name without the prefix
269 * @param string $sql The sql to create table or an absolute path of a
270 * .sql file containing the column definations for creating the new table.
271 *
272 * @return string message
273 */
274 public static function alterTable($table, $sql)
275 {
276 $table = static::table($table);
277
278 $sql = static::cleanUp($sql);
279
280 if (static::isSqlite()) {
281 $sql = preg_replace('/\bjson\b/i', 'longtext', $sql);
282 }
283
284 $parts = array_filter(array_map('trim', static::splitAlterClauses($sql)));
285
286 if (static::isSqlite()) {
287 // SQLite only supports one ALTER TABLE operation per statement.
288 $result = null;
289 foreach ($parts as $part) {
290 $result = static::query("ALTER TABLE $table {$part};");
291 }
292 return $result;
293 }
294
295 $sql = "ALTER TABLE $table ".PHP_EOL.rtrim(
296 trim(implode(','.PHP_EOL, $parts)), ';'
297 ).";";
298
299 return static::query($sql);
300 }
301
302 /**
303 * Split a comma-separated ALTER TABLE clause list, respecting parentheses
304 * so that DECIMAL(10,2) and similar types are not split mid-definition.
305 *
306 * @param string $sql
307 * @return string[]
308 */
309 protected static function splitAlterClauses($sql)
310 {
311 $parts = [];
312 $depth = 0;
313 $current = '';
314
315 for ($i = 0, $len = strlen($sql); $i < $len; $i++) {
316 $char = $sql[$i];
317 if ($char === '(') {
318 $depth++;
319 } elseif ($char === ')') {
320 $depth--;
321 } elseif ($char === ',' && $depth === 0) {
322 $parts[] = $current;
323 $current = '';
324 continue;
325 }
326 $current .= $char;
327 }
328
329 if ($current !== '') {
330 $parts[] = $current;
331 }
332
333 return $parts;
334 }
335
336 /**
337 * Alters an existing table using dbDelta function if exists, otherwise creates
338 * it. Alters an existing table but takes the table creation column defination.
339 * This is because, the dbDelta functioin can create or update a table using
340 * the table creation defination. In this, case, if a table exists and the
341 * columns are matched then nothing happens but if there's any difference
342 * in the new sql then the dbDelta alters the table using the new sql
343 * defination but doesn't delete any columns. So, after the dbDelta
344 * finishes it's job, any non-existing columns in the new sql
345 * defination will be deleted from the existing table. if
346 * table is not there then the table gets created.
347 *
348 * @param string $table The table name without the prefix
349 * @param string $sql The sql to create table or an absolute path of a
350 * .sql file containing the column definations for creating the new table.
351 *
352 * @return string message
353 */
354 public static function updateTable($table, $sql)
355 {
356 $sql = static::cleanUp($sql);
357
358 if (static::isSqlite()) {
359 $sql = preg_replace('/\bjson\b/i', 'longtext', $sql);
360 }
361
362 $columnsDefinitions = array_map('trim', static::splitAlterClauses($sql));
363 $schemaSql = implode(",\n", $columnsDefinitions);
364
365 // Extract desired column names (skip constraint/index clauses)
366 $columns = [];
367 foreach ($columnsDefinitions as $definition) {
368 if (preg_match('/^(?:PRIMARY\s+KEY|UNIQUE(?:(?:\s+KEY|\s+INDEX))?|KEY|INDEX|CONSTRAINT|FOREIGN\s+KEY|FULLTEXT|SPATIAL)\b/i', ltrim($definition))) {
369 continue;
370 }
371
372 if (preg_match('/^`?(\w+)`?\s+/i', $definition, $matches)) {
373 $columns[$matches[1]] = $definition;
374 }
375 }
376
377 $tbl = static::table($table);
378
379 // SQLite cannot drop PRIMARY KEY columns or columns referenced by indexes
380 // via native ALTER TABLE DROP COLUMN.
381 //
382 // Workarounds:
383 // - Indexed columns: drop the index first with ALTER TABLE DROP INDEX,
384 // then drop the column.
385 // - PRIMARY KEY columns: use CHANGE COLUMN to rebuild the table without
386 // the PK attribute (renaming the column to a temp name), then drop
387 // the renamed column.
388 //
389 // Note: ALTER TABLE … RENAME TO and standalone DROP INDEX are NOT
390 // supported by this version of the WP SQLite integration, so we cannot
391 // use a full-table-rebuild via rename.
392 if (static::isSqlite()) {
393 // Collect PRIMARY KEY columns and per-column index key names.
394 $indexRows = (array) static::db()->get_results("SHOW INDEX FROM {$tbl}");
395 $pkColumns = [];
396 $columnIndexes = []; // column_name => [key_name, ...]
397 foreach ($indexRows as $row) {
398 if ($row->Key_name === 'PRIMARY') {
399 $pkColumns[] = $row->Column_name;
400 } else {
401 $columnIndexes[$row->Column_name][] = $row->Key_name;
402 }
403 }
404
405 $existingColumns = static::getColumns($table) ?: [];
406
407 // 1. Add any desired columns not yet in the table.
408 foreach ($columns as $colName => $definition) {
409 if (!in_array($colName, $existingColumns)) {
410 static::db()->query("ALTER TABLE {$tbl} ADD COLUMN {$definition}");
411 }
412 }
413
414 // 2. Drop every column that is not in the desired schema.
415 foreach ($existingColumns as $column) {
416 if (isset($columns[$column])) {
417 continue; // desired — keep it
418 }
419
420 // Drop non-primary indexes referencing this column first, otherwise
421 // native SQLite ALTER TABLE DROP COLUMN fails.
422 foreach ($columnIndexes[$column] ?? [] as $keyName) {
423 static::db()->query("ALTER TABLE {$tbl} DROP INDEX {$keyName}");
424 }
425
426 if (in_array($column, $pkColumns)) {
427 // Native SQLite ALTER TABLE DROP COLUMN rejects PRIMARY KEY columns.
428 // Use CHANGE COLUMN to trigger an internal table rebuild that strips
429 // the PK attribute, then drop the (now ordinary) renamed column.
430 $tmpCol = $column . '_wpf_drop';
431 static::db()->query("ALTER TABLE {$tbl} CHANGE COLUMN {$column} {$tmpCol} INT NULL");
432 static::db()->query("ALTER TABLE {$tbl} DROP COLUMN {$tmpCol}");
433 } else {
434 static::db()->query("ALTER TABLE {$tbl} DROP COLUMN {$column}");
435 }
436 }
437
438 return [$tbl => "Updated table structure"];
439 }
440
441 // MySQL path: use dbDelta to create/update, then drop extra columns.
442 $result = static::createTable($table, $schemaSql);
443 $existingColumns = static::getColumns($table) ?: [];
444
445 // Add missing columns
446 foreach ($columns as $colName => $definition) {
447 if (!in_array($colName, $existingColumns)) {
448 static::query("ALTER TABLE $tbl ADD COLUMN $definition");
449 $result[$tbl.'.'.$colName] = "Added column {$tbl}.{$colName}";
450 }
451 }
452
453 // Drop extra columns
454 foreach ($existingColumns as $column) {
455 if (!isset($columns[$column])) {
456 static::query("ALTER TABLE $tbl DROP COLUMN $column");
457 $result[$tbl.'.'.$column] = "Dropped column {$tbl}.{$column}";
458 }
459 }
460
461 return $result;
462 }
463
464 /**
465 * Drops/deletes an existing table.
466 *
467 * @param string $table The table name without the prefix
468 * @param bool $disableForeignKeyCheck Optional.
469 * @return bool
470 */
471 public static function dropTable($table, $disableForeignKeyCheck = true)
472 {
473 return static::db()->query('DROP TABLE ' . static::table($table));
474 }
475
476 /**
477 * Drops/deletes an existing table if exists
478 *
479 * @param string $table The table name without the prefix
480 * @param bool $disableForeignKeyCheck Optional.
481 * @return bool
482 */
483 public static function dropTableIfExists($table, $disableForeignKeyCheck = true)
484 {
485 if (static::hasTable($table)) {
486 return static::dropTable($table, $disableForeignKeyCheck);
487 }
488 }
489
490 /**
491 * Truncate a table.
492 *
493 * @param string $table
494 * @return bool
495 */
496 public static function truncate($table)
497 {
498 $table = static::table($table);
499
500 $result = static::db()->query("TRUNCATE TABLE $table");
501
502 if (static::isSqlite()) {
503 // SQLite translates TRUNCATE TABLE to DELETE FROM, which does NOT reset
504 // the auto-increment counter. Manually clear the sqlite_sequence row so
505 // that the next INSERT starts at id=1 (matching MySQL TRUNCATE behaviour).
506 static::db()->query("DELETE FROM sqlite_sequence WHERE name='{$table}'");
507 }
508
509 return $result;
510 }
511
512 /**
513 * Truncate a table if exists.
514 *
515 * @param string $table
516 * @return bool
517 */
518 public static function truncateTableIfExists($table)
519 {
520 if (static::hasTable($table)) {
521 return static::truncate($table);
522 }
523 }
524
525 /**
526 * Adds a new index to a column of given table.
527 *
528 * @param string $table Table name
529 * @param string $index Columns name
530 * @return bool
531 * @see https://developer.wordpress.org/reference/functions/add_clean_index
532 */
533 public static function addIndex($table, $index)
534 {
535 return add_clean_index(static::table($table), $index);
536 }
537
538 /**
539 * Drops an index from a column of given table.
540 *
541 * @param string $table Table name
542 * @param string $index Columns name
543 * @return bool
544 * @see https://developer.wordpress.org/reference/functions/drop_index
545 */
546 public static function dropIndex($table, $index)
547 {
548 return drop_index(static::table($table), $index);
549 }
550
551 /**
552 * Makes raw query and can resolve the table name from the query
553 * and can form a full table name including the table prefix if
554 * the table name is wrapped like: %table_name% in the query.
555 *
556 * @param string $query
557 * @return mixed
558 */
559 public static function query($query)
560 {
561 $query = preg_replace_callback('/(?<![\'"])%([a-zA-Z0-9_-]+)%(?![\'"])/', function ($matches) {
562 return static::table($matches[1]);
563 }, $query);
564
565 if (preg_match('/^(SELECT|SHOW|DESCRIBE|EXPLAIN)\s+/i', trim($query))) {
566 return static::db()->get_results($query, OBJECT);
567 }
568
569 return static::db()->query($query);
570 }
571
572 /**
573 * Get a list of all columns from the given table name.
574 *
575 * @param string $table The table name without the prefix
576 * @return array|null
577 */
578 public static function getColumns($table)
579 {
580 if (!static::hasTable($table)) {
581 return null;
582 }
583
584 return static::db()->get_col(
585 'SHOW COLUMNS FROM ' . static::table($table), 0
586 );
587 }
588
589 /**
590 * Gets a list of all columns including column information
591 *
592 * @param string $table The table name without the prefix
593 * @return array
594 */
595 protected static function protectedGetColumnsWithTypes($table)
596 {
597 if (!static::hasTable($table)) return;
598
599 // @phpstan-ignore-next-line
600 $db = static::db()->dbname;
601
602 $table = static::table($table);
603
604 $fields = [
605 'COLUMN_NAME',
606 'ORDINAL_POSITION',
607 'COLUMN_DEFAULT',
608 'IS_NULLABLE',
609 'DATA_TYPE',
610 'CHARACTER_MAXIMUM_LENGTH',
611 'NUMERIC_PRECISION',
612 'NUMERIC_SCALE',
613 'COLUMN_KEY',
614 'EXTRA',
615 ];
616
617 $sql = "SELECT " . implode(',', $fields) . " FROM INFORMATION_SCHEMA.COLUMNS";
618 $sql .= " WHERE TABLE_NAME = '".$table."' AND TABLE_SCHEMA = '".$db."'";
619
620 return array_map(function($i) {
621 $item = [];
622 foreach ((array) $i as $key => $value) {
623 $item[strtolower($key)] = $value;
624 }
625 return $item;
626 }, (array) static::db()->get_results($sql));
627 }
628
629 public static function getColumnsWithTypes($table)
630 {
631 $columns = static::protectedGetColumnsWithTypes($table);
632
633 if (!empty($columns)) {
634 return $columns;
635 }
636
637 $columns = static::db()->get_results(
638 'SHOW COLUMNS FROM `'.static::table($table).'`'
639 );
640
641 return array_map(function ($col) {
642 return [
643 'column_name' => $col->Field,
644 'ordinal_position' => null,
645 'column_default' => $col->Default,
646 'is_nullable' => ($col->Null === 'YES' ? 'YES' : 'NO'),
647 'data_type' => strtolower(
648 preg_replace('/\(.*/', '', $col->Type)
649 ),
650 'character_maximum_length' => preg_match(
651 '/\((\d+)\)/', $col->Type, $matches
652 ) ? (int)$matches[1] : null,
653 'numeric_precision' => null,
654 'numeric_scale' => null,
655 'column_key' => $col->Key,
656 'extra' => $col->Extra,
657 ];
658 }, (array) $columns);
659 }
660
661 /**
662 * Gets a list of all columns including column information
663 *
664 * @param string $table The table name without the prefix
665 * @return array
666 */
667 public static function describeTable($table)
668 {
669 return static::getColumnsWithTypes($table);
670 }
671
672 /**
673 * Gets a list of all foreign keys from the given table name.
674 *
675 * @param string $table
676 * @return array
677 */
678 public static function getTableForeignKeys($table)
679 {
680 $table = static::table($table);
681
682 if (static::isSqlite()) {
683 return array_map(function ($i) {
684 return [
685 'column_name' => $i->from,
686 'referenced_table' => $i->table,
687 'referenced_column' => $i->to,
688 ];
689 }, (array) static::db()->get_results("PRAGMA foreign_key_list({$table})"));
690 }
691
692 // @phpstan-ignore-next-line
693 $db = static::db()->dbname;
694
695 $sql = "SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
696 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
697 WHERE TABLE_NAME = '{$table}'
698 AND TABLE_SCHEMA = '{$db}'
699 AND REFERENCED_TABLE_NAME IS NOT NULL";
700
701 return array_map(function ($i) {
702 return [
703 'column_name' => $i->COLUMN_NAME,
704 'referenced_table' => $i->REFERENCED_TABLE_NAME,
705 'referenced_column' => $i->REFERENCED_COLUMN_NAME,
706 ];
707 }, (array) static::db()->get_results($sql));
708 }
709
710 /**
711 * Retrieves the list of all available built-in tables from
712 * the database using WordPress' $wpdb->tables native method.
713 *
714 * @param string $scope
715 * @param boolean $prefix
716 * @param integer $blogId
717 * @return string[] WP Table names. When a prefix is requested,
718 * the key is the unprefixed table name.
719 * @see https://developer.wordpress.org/reference/classes/wpdb/tables/
720 */
721 public static function tables($scope = 'all', $prefix = true, $blogId = 0)
722 {
723 return static::db()->tables($scope, $prefix, $blogId);
724 }
725
726 /**
727 * Retrieves the list of all available tables from the database.
728 *
729 * @param string $dbname optional
730 * @return array
731 */
732 public static function getTables($dbname = null)
733 {
734 return static::getTableList($dbname);
735 }
736
737 /**
738 * Retrieves the list of all available tables in the database.
739 *
740 * @param string $dbname optional
741 * @return array
742 */
743 public static function getTableList($dbname = null)
744 {
745 if (static::isSqlite()) {
746 return array_map(function ($i) {
747 return $i->name;
748 }, (array) static::db()->get_results(
749 "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
750 ));
751 }
752
753 // @phpstan-ignore-next-line
754 $dbname = $dbname ?: static::db()->dbname;
755 $sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";
756 $sql .= " WHERE TABLE_SCHEMA = '".$dbname."'";
757
758 return array_map(function($i) {
759 return $i->TABLE_NAME;
760 }, (array) static::db()->get_results($sql));
761 }
762
763 /**
764 * Retrieves the list of all available views in the database.
765 *
766 * @param string $dbname optional
767 * @return array
768 */
769 public static function getViews($dbname = null)
770 {
771 if (static::isSqlite()) {
772 return array_map(function ($i) {
773 return $i->name;
774 }, (array) static::db()->get_results(
775 "SELECT name FROM sqlite_master WHERE type='view'"
776 ));
777 }
778
779 // @phpstan-ignore-next-line
780 $dbname = $dbname ?: static::db()->dbname;
781 $sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS";
782 $sql .= " WHERE TABLE_SCHEMA = '".$dbname."'";
783
784 return array_map(function ($i) {
785 return $i->TABLE_NAME;
786 }, (array) static::db()->get_results($sql));
787 }
788
789 /**
790 * Retrieves the SQL definition of a specific view.
791 *
792 * @param string $name The view name
793 * @param string $dbname optional
794 * @return string|null
795 */
796 public static function getView($name, $dbname = null)
797 {
798 if (static::isSqlite()) {
799 $result = static::db()->get_row(
800 "SELECT sql FROM sqlite_master WHERE type='view' AND name='".$name."'"
801 );
802
803 return $result ? $result->sql : null;
804 }
805
806 // @phpstan-ignore-next-line
807 $dbname = $dbname ?: static::db()->dbname;
808 $result = static::db()->get_row(
809 "SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS"
810 . " WHERE TABLE_SCHEMA = '".$dbname."'"
811 . " AND TABLE_NAME = '".$name."'"
812 );
813
814 return $result ? $result->VIEW_DEFINITION : null;
815 }
816
817 /**
818 * Determine if the connected database is a sqlite database.
819 *
820 * @return bool
821 */
822 public static function isSqlite()
823 {
824 return defined('DB_ENGINE') && DB_ENGINE === 'sqlite';
825 }
826
827 /**
828 * Determine if the connected database is a mariadb database.
829 *
830 * @return bool
831 */
832 public static function isMaria()
833 {
834 return str_contains(
835 static::db()->get_var('SELECT VERSION()'), 'MariaDB'
836 );
837 }
838
839 /**
840 * Retrieve the current database engine name.
841 *
842 * @param string $table
843 * @return string
844 */
845 public static function getEngine($table)
846 {
847 return static::db()->get_var(
848 'SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = "' . static::table($table) . '"'
849 );
850 }
851
852 /**
853 * The wrapper for calling dbDelta function
854 *
855 * @param string $sql
856 * @return mixed
857 */
858 public static function callDBDelta($sql)
859 {
860 if (!function_exists('dbDelta')) {
861 require (ABSPATH . 'wp-admin/includes/upgrade.php');
862 }
863
864 $result = dbDelta($sql);
865
866 if (php_sapi_name() === 'cli') {
867 $key = array_key_first($result);
868 if ($key && !str_contains($key, '.')) {
869 static::$customTempTables[] = $key;
870 }
871 }
872
873 return $result;
874 }
875
876 /**
877 * Helper to get the driver-specific JSON type.
878 */
879 public static function jsonType()
880 {
881 return static::isSqlite() ? 'longtext' : 'json';
882 }
883 }
884