PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); if ($c['db_driver'] === 'sqlite') { $pdo->exec('PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;'); } _run_migrations($pdo); } return $pdo; } function _table_exists(PDO $pdo, string $table, string $driver): bool { if ($driver === 'sqlite') { return (bool) $pdo->query( "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=" . $pdo->quote($table) )->fetchColumn(); } return (bool) $pdo->query( "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=" . $pdo->quote($table) )->fetchColumn(); } function _run_migrations(PDO $pdo): void { $c = require __DIR__ . '/config.php'; $driver = $c['db_driver']; $migrationTableExisted = _table_exists($pdo, 'schema_migrations', $driver); $pdo->exec(" CREATE TABLE IF NOT EXISTS schema_migrations ( version VARCHAR(255) NOT NULL PRIMARY KEY, applied_at VARCHAR(32) NOT NULL ) "); $files = glob(__DIR__ . '/../migrations/*.php'); if (!$files) return; sort($files); // Existing DB without migration table: stamp all known migrations as applied. if (!$migrationTableExisted && _table_exists($pdo, 'users', $driver)) { $insertIgnore = $driver === 'sqlite' ? 'INSERT OR IGNORE' : 'INSERT IGNORE'; $stmt = $pdo->prepare( "$insertIgnore INTO schema_migrations (version, applied_at) VALUES (?, ?)" ); foreach ($files as $file) { $stmt->execute([basename($file, '.php'), date('c')]); } return; } $applied = $pdo->query('SELECT version FROM schema_migrations') ->fetchAll(PDO::FETCH_COLUMN); $applied = array_flip($applied); $useTransactions = ($driver === 'sqlite'); foreach ($files as $file) { $version = basename($file, '.php'); if (isset($applied[$version])) continue; $migration = require $file; if ($useTransactions) $pdo->beginTransaction(); try { foreach ((array) $migration['up'] as $sql) { $pdo->exec($sql); } $pdo->prepare( 'INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)' )->execute([$version, date('c')]); if ($useTransactions) $pdo->commit(); } catch (Throwable $e) { if ($useTransactions && $pdo->inTransaction()) $pdo->rollBack(); throw new RuntimeException( "Migration {$version} failed: " . $e->getMessage(), 0, $e ); } } }