#!/usr/bin/env php setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Create migrations tracking table $pdo->exec(" CREATE TABLE IF NOT EXISTS _migrations ( name TEXT PRIMARY KEY, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP ) "); $applied = $pdo->query("SELECT name FROM _migrations")->fetchAll(PDO::FETCH_COLUMN); $pendingDir = $root . '/migrations/pending'; $appliedDir = $root . '/migrations/applied'; if (!is_dir($pendingDir)) { echo "No pending migrations directory.\n"; exit(0); } if (!is_dir($appliedDir)) { mkdir($appliedDir, 0755, true); } $files = glob($pendingDir . '/*.sql'); sort($files); if (empty($files)) { echo "No pending migration files.\n"; exit(0); } $count = 0; foreach ($files as $file) { $name = basename($file); if (in_array($name, $applied, true)) { echo "Skip (already applied): $name\n"; continue; } echo "Applying: $name\n"; $sql = file_get_contents($file); try { $pdo->exec($sql); $pdo->prepare("INSERT INTO _migrations (name) VALUES (?)")->execute([$name]); rename($file, $appliedDir . '/' . $name); $count++; } catch (PDOException $e) { $msg = $e->getMessage(); // SQLite: skip if column already exists if (stripos($msg, 'duplicate column name') !== false) { echo " Already exists (skipping): $name\n"; $pdo->prepare("INSERT OR REPLACE INTO _migrations (name) VALUES (?)")->execute([$name]); rename($file, $appliedDir . '/' . $name); continue; } throw $e; } } echo "$count migration(s) applied.\n";