JezK
Edit File: Tables.php
<?php namespace GeminiLabs\SiteReviews\Database; use GeminiLabs\SiteReviews\Database; use GeminiLabs\SiteReviews\Database\Query; use GeminiLabs\SiteReviews\Database\Tables\TableAssignedPosts; use GeminiLabs\SiteReviews\Database\Tables\TableAssignedTerms; use GeminiLabs\SiteReviews\Database\Tables\TableAssignedUsers; use GeminiLabs\SiteReviews\Database\Tables\TableFields; use GeminiLabs\SiteReviews\Database\Tables\TableRatings; use GeminiLabs\SiteReviews\Database\Tables\TableStats; use GeminiLabs\SiteReviews\Database\Tables\TableTmp; use GeminiLabs\SiteReviews\Helpers\Cast; use GeminiLabs\SiteReviews\Helpers\Str; class Tables { public string $database; public \wpdb $db; public string $engine; public string $prefix; public array $tables; public function __construct() { global $wpdb; $this->database = $wpdb->dbname ?: \DB_NAME; $this->db = $wpdb; $this->engine = defined('DB_ENGINE') && 'sqlite' === \DB_ENGINE ? 'sqlite' : 'mysql'; $this->prefix = $wpdb->get_blog_prefix(); $this->tables = wp_parse_args($this->customTables(), $wpdb->tables()); } public function addForeignConstraints(): void { foreach ($this->tables() as $table) { glsr($table)->addForeignConstraints(); } } public function columnExists(string $table, string $column): bool { if ($this->isSqlite()) { $result = $this->db->get_col( glsr(Query::class)->sql("SHOW COLUMNS FROM table|{$table}") ); return in_array($column, $result); } $result = $this->db->query( glsr(Query::class)->sql("SHOW COLUMNS FROM table|{$table} LIKE %s", $column) ); return Cast::toBool($result); } public function convertTableEngine(string $table): int { if (!$this->isMyisam($table)) { return -1; } $table = $this->table($table); $result = glsr(Database::class)->dbQuery( glsr(Query::class)->sql("ALTER TABLE {$table} ENGINE = InnoDB;") ); if (true === $result) { update_option(glsr()->prefix."engine_{$table}", 'innodb'); $this->addForeignConstraints(); // apply InnoDB constraints return 1; } return 0; } public function createTables(): void { foreach ($this->tables as $tablename) { delete_option(sprintf('%sengine_%s', glsr()->prefix, $tablename)); } array_map(fn ($table) => glsr($table)->create(), $this->tables()); } public function customTables(): array { $tables = []; foreach ($this->tables() as $table) { $tables[glsr($table)->name] = glsr($table)->tablename; } return $tables; } public function dropForeignConstraints(): void { if ($this->isSqlite()) { return; } $constraints = $this->db->get_results(" SELECT CONSTRAINT_NAME, TABLE_NAME FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = '{$this->database}' "); foreach ($this->tables() as $table) { $tablename = glsr($table)->tablename; foreach ($constraints as $constraint) { if ($tablename !== $constraint->TABLE_NAME) { continue; } $this->db->query(" ALTER TABLE {$constraint->TABLE_NAME} DROP FOREIGN KEY {$constraint->CONSTRAINT_NAME}; "); // true if constraint exists, false if it doesn't exist } } } public function isInnodb(string $table): bool { return 'innodb' === $this->tableEngine($table); } public function isMyisam(string $table): bool { return 'myisam' === $this->tableEngine($table); } public function isSqlite(): bool { return 'sqlite' === $this->engine; } public function table(string $name = '', bool $logError = true): string { if (in_array($name, $this->tables)) { return $name; } if (array_key_exists($name, $this->tables)) { return $this->tables[$name]; } if ($logError) { glsr_log()->error("The [{$name}] table was not found."); } return $name; // @todo maybe throw an exception here instead? } public function tableEngine(string $table): string { if ($this->isSqlite()) { return ''; } $table = $this->table($table); $option = sprintf('%sengine_%s', glsr()->prefix, $table); if (empty($engine = get_option($option))) { $sql = " SELECT ENGINE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s "; $engine = $this->db->get_var($this->db->prepare($sql, $this->database, $table)); if (empty($engine)) { glsr_log()->warning("DB Table Engine: The {$table} table does not exist in {$this->database}."); return ''; } $engine = strtolower($engine); update_option($option, $engine); } return $engine; } public function tableEngines(bool $removePrefix = false): array { if ($this->isSqlite()) { return []; } $results = $this->db->get_results(" SELECT TABLE_NAME as table_name, ENGINE as engine FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{$this->database}' AND TABLE_NAME IN ('{$this->db->options}','{$this->db->posts}','{$this->db->terms}','{$this->db->users}') "); $engines = []; foreach ($results as $result) { if (!array_key_exists($result->engine, $engines)) { $engines[$result->engine] = []; } if ($removePrefix) { $result->table_name = Str::removePrefix((string) $result->table_name, $this->prefix); } $engines[$result->engine][] = $result->table_name; } return $engines; } public function tableExists(string $table): bool { $tablename = $this->table($table, false); $query = $this->db->prepare('SHOW TABLES LIKE %s', $this->db->esc_like($tablename)); return !empty($this->db->get_var($query)); } public function tables(): array { return glsr()->filterArray('database/tables', [ TableAssignedPosts::class, TableAssignedTerms::class, TableAssignedUsers::class, // TableFields::class, // @todo add the fields table TableRatings::class, TableStats::class, TableTmp::class, ]); } public function tablesExist(): bool { $prefix = $this->db->esc_like($this->prefix.glsr()->prefix); $tables = $this->db->get_col( $this->db->prepare("SHOW TABLES LIKE %s", "{$prefix}%") ); foreach ($this->tables() as $table) { if (!in_array(glsr($table)->tablename, $tables)) { return false; } } return true; } }