name = $name; foreach (self::$data[$name] as $key => $value) { $this->$key = $value; } } public static function list(): array { if (!isset(self::$data)) { self::$data = self::getData(); } return array_keys(self::$data); } public static function listPDO(): array { if (!isset(self::$data)) { self::$data = self::getData(); } return array_values(array_filter(array_keys(self::$data), function($k) { return self::$data[$k]['pdo']; })); } protected static function getData() { $sqlite3TableList = function($db): array { $listTables = "SELECT name from sqlite_master where type = 'table' and name like 'arsse_%'"; if ($db instanceof Driver) { $tables = $db->query($listTables)->getAll(); $tables = sizeof($tables) ? array_column($tables, "name") : []; } elseif ($db instanceof \PDO) { retry: try { $tables = $db->query($listTables)->fetchAll(\PDO::FETCH_ASSOC); } catch (\PDOException $e) { goto retry; } $tables = sizeof($tables) ? array_column($tables, "name") : []; } else { $tables = []; $result = $db->query($listTables); while ($r = $result->fetchArray(\SQLITE3_ASSOC)) { $tables[] = $r['name']; } $result->finalize(); } return $tables; }; $sqlite3TruncateFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) { // rollback any pending transaction try { $db->exec("ROLLBACK"); } catch (\Throwable $e) { } foreach ($sqlite3TableList($db) as $table) { if ($table === "arsse_meta") { $db->exec("DELETE FROM $table where key <> 'schema_version'"); } else { $db->exec("DELETE FROM $table"); } } foreach ($afterStatements as $st) { $db->exec($st); } }; $sqlite3RazeFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) { // rollback any pending transaction try { $db->exec("ROLLBACK"); } catch (\Throwable $e) { } $db->exec("PRAGMA foreign_keys=0"); foreach ($sqlite3TableList($db) as $table) { $db->exec("DROP TABLE IF EXISTS $table"); } $db->exec("PRAGMA user_version=0"); $db->exec("PRAGMA foreign_keys=1"); foreach ($afterStatements as $st) { $db->exec($st); } }; $pgObjectList = function($db): array { $listObjects = "SELECT table_name as name, 'TABLE' as type from information_schema.tables where table_schema = current_schema() and table_name like 'arsse_%' union SELECT collation_name as name, 'COLLATION' as type from information_schema.collations where collation_schema = current_schema()"; if ($db instanceof Driver) { return $db->query($listObjects)->getAll(); } elseif ($db instanceof \PDO) { return $db->query($listObjects)->fetchAll(\PDO::FETCH_ASSOC); } else { $r = @pg_query($db, $listObjects); $out = $r ? pg_fetch_all($r) : false; return $out ? $out : []; } }; $pgExecFunction = function($db, $q) { if ($db instanceof Driver) { $db->exec($q); } elseif ($db instanceof \PDO) { $db->exec($q); } else { pg_query($db, $q); } }; $pgTruncateFunction = function($db, array $afterStatements = []) use ($pgObjectList, $pgExecFunction) { // rollback any pending transaction try { @$pgExecFunction($db, "ROLLBACK"); } catch (\Throwable $e) { } foreach ($pgObjectList($db) as $obj) { if ($obj['type'] !== "TABLE") { continue; } elseif ($obj['name'] === "arsse_meta") { $pgExecFunction($db, "DELETE FROM {$obj['name']} where key <> 'schema_version'"); } else { $pgExecFunction($db, "TRUNCATE TABLE {$obj['name']} restart identity cascade"); } } foreach ($afterStatements as $st) { $pgExecFunction($db, $st); } }; $pgRazeFunction = function($db, array $afterStatements = []) use ($pgObjectList, $pgExecFunction) { // rollback any pending transaction try { $pgExecFunction($db, "ROLLBACK"); } catch (\Throwable $e) { } foreach ($pgObjectList($db) as $obj) { $pgExecFunction($db, "DROP {$obj['type']} IF EXISTS {$obj['name']} cascade"); } foreach ($afterStatements as $st) { $pgExecFunction($db, $st); } }; $mysqlTableList = function($db): array { $listTables = "SELECT table_name as name from information_schema.tables where table_schema = database() and table_name like 'arsse_%'"; if ($db instanceof Driver) { $tables = $db->query($listTables)->getAll(); } elseif ($db instanceof \PDO) { $tables = $db->query($listTables)->fetchAll(\PDO::FETCH_ASSOC); } else { $tables = $db->query($listTables)->fetch_all(\MYSQLI_ASSOC); } $tables = sizeof($tables) ? array_column($tables, "name") : []; return $tables; }; $mysqlTruncateFunction = function($db, array $afterStatements = []) use ($mysqlTableList) { // rollback any pending transaction try { $db->query("UNLOCK TABLES; ROLLBACK"); } catch (\Throwable $e) { } foreach ($mysqlTableList($db) as $table) { if ($table === "arsse_meta") { $db->query("DELETE FROM $table where `key` <> 'schema_version'"); } else { $db->query("DELETE FROM $table"); } $db->query("ALTER TABLE $table auto_increment = 1"); } foreach ($afterStatements as $st) { $db->query($st); } }; $mysqlRazeFunction = function($db, array $afterStatements = []) use ($mysqlTableList) { // rollback any pending transaction try { $db->query("UNLOCK TABLES; ROLLBACK"); } catch (\Throwable $e) { } foreach ($mysqlTableList($db) as $table) { $db->query("DROP TABLE IF EXISTS $table"); } foreach ($afterStatements as $st) { $db->query($st); } }; return [ 'SQLite 3' => [ 'pdo' => false, 'backend' => "SQLite 3", 'statementClass' => \JKingWeb\Arsse\Db\SQLite3\Statement::class, 'resultClass' => \JKingWeb\Arsse\Db\SQLite3\Result::class, 'driverClass' => \JKingWeb\Arsse\Db\SQLite3\Driver::class, 'stringOutput' => false, 'interfaceConstructor' => function() { try { $d = new \SQLite3(Arsse::$conf->dbSQLite3File); } catch (\Throwable $e) { return; } $d->enableExceptions(true); return $d; }, 'truncateFunction' => $sqlite3TruncateFunction, 'razeFunction' => $sqlite3RazeFunction, ], 'PDO SQLite 3' => [ 'pdo' => true, 'backend' => "SQLite 3", 'statementClass' => \JKingWeb\Arsse\Db\SQLite3\PDOStatement::class, 'resultClass' => \JKingWeb\Arsse\Db\PDOResult::class, 'driverClass' => \JKingWeb\Arsse\Db\SQLite3\PDODriver::class, 'stringOutput' => true, 'interfaceConstructor' => function() { try { $d = new \PDO("sqlite:".Arsse::$conf->dbSQLite3File, "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); $d->exec("PRAGMA busy_timeout=0"); return $d; } catch (\Throwable $e) { return; } }, 'truncateFunction' => $sqlite3TruncateFunction, 'razeFunction' => $sqlite3RazeFunction, ], 'PostgreSQL' => [ 'pdo' => false, 'backend' => "PostgreSQL", 'statementClass' => \JKingWeb\Arsse\Db\PostgreSQL\Statement::class, 'resultClass' => \JKingWeb\Arsse\Db\PostgreSQL\Result::class, 'driverClass' => \JKingWeb\Arsse\Db\PostgreSQL\Driver::class, 'stringOutput' => true, 'interfaceConstructor' => function() { $connString = \JKingWeb\Arsse\Db\PostgreSQL\Driver::makeConnectionString(false, Arsse::$conf->dbPostgreSQLUser, Arsse::$conf->dbPostgreSQLPass, Arsse::$conf->dbPostgreSQLDb, Arsse::$conf->dbPostgreSQLHost, Arsse::$conf->dbPostgreSQLPort, ""); if ($d = @pg_connect($connString, \PGSQL_CONNECT_FORCE_NEW)) { foreach (\JKingWeb\Arsse\Db\PostgreSQL\Driver::makeSetupQueries(Arsse::$conf->dbPostgreSQLSchema) as $q) { pg_query($d, $q); } return $d; } else { return; } }, 'truncateFunction' => $pgTruncateFunction, 'razeFunction' => $pgRazeFunction, ], 'PDO PostgreSQL' => [ 'pdo' => true, 'backend' => "PostgreSQL", 'statementClass' => \JKingWeb\Arsse\Db\PostgreSQL\PDOStatement::class, 'resultClass' => \JKingWeb\Arsse\Db\PDOResult::class, 'driverClass' => \JKingWeb\Arsse\Db\PostgreSQL\PDODriver::class, 'stringOutput' => false, 'interfaceConstructor' => function() { $connString = \JKingWeb\Arsse\Db\PostgreSQL\Driver::makeConnectionString(true, Arsse::$conf->dbPostgreSQLUser, Arsse::$conf->dbPostgreSQLPass, Arsse::$conf->dbPostgreSQLDb, Arsse::$conf->dbPostgreSQLHost, Arsse::$conf->dbPostgreSQLPort, ""); try { $d = new \PDO("pgsql:".$connString, Arsse::$conf->dbPostgreSQLUser, Arsse::$conf->dbPostgreSQLPass, [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); } catch (\Throwable $e) { return; } foreach (\JKingWeb\Arsse\Db\PostgreSQL\PDODriver::makeSetupQueries(Arsse::$conf->dbPostgreSQLSchema) as $q) { $d->exec($q); } return $d; }, 'truncateFunction' => $pgTruncateFunction, 'razeFunction' => $pgRazeFunction, ], 'PDO MySQL' => [ 'pdo' => true, 'backend' => "MySQL", 'statementClass' => \JKingWeb\Arsse\Db\MySQL\PDOStatement::class, 'resultClass' => \JKingWeb\Arsse\Db\PDOResult::class, 'driverClass' => \JKingWeb\Arsse\Db\MySQL\PDODriver::class, 'stringOutput' => true, 'interfaceConstructor' => function() { try { $dsn = []; $params = [ 'charset' => "utf8mb4", 'host' => Arsse::$conf->dbMySQLHost, 'port' => Arsse::$conf->dbMySQLPort, 'dbname' => Arsse::$conf->dbMySQLDb, ]; foreach ($params as $k => $v) { $dsn[] = "$k=$v"; } $dsn = "mysql:".implode(";", $dsn); return new \PDO($dsn, Arsse::$conf->dbMySQLUser, Arsse::$conf->dbMySQLPass, [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::MYSQL_ATTR_MULTI_STATEMENTS => false, \PDO::MYSQL_ATTR_INIT_COMMAND => "SET sql_mode = '".\JKingWeb\Arsse\Db\MySQL\PDODriver::SQL_MODE."'",]); } catch (\Throwable $e) { return; } }, 'truncateFunction' => $mysqlTruncateFunction, 'razeFunction' => $mysqlRazeFunction, ], ]; } }