exec( "\n CREATE TABLE IF NOT EXISTS migrations (\n id INT AUTO_INCREMENT PRIMARY KEY,\n migration_name VARCHAR(255) NOT NULL UNIQUE,\n executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n " ); } catch (PDOException $e) { return ["Error creating migrations table: " . $e->getMessage()]; } // 2. Get executed migrations $executed = []; try { $stmt = $pdo->query("SELECT migration_name FROM migrations"); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $executed[] = $row['migration_name']; } } catch (PDOException $e) { return ["Error fetching executed migrations: " . $e->getMessage()]; } // 3. Scan for migration files $migrationFiles = glob(__DIR__ . '/migrations/*.php'); if ($migrationFiles === false) { return ["Error scanning migration directory."]; } // Sort files to ensure order (by name usually works if named correctly) sort($migrationFiles); $count = 0; foreach ($migrationFiles as $file) { $filename = basename($file); if (in_array($filename, $executed)) { continue; } // Run migration try { // We use output buffering to capture echo output from migration files ob_start(); // Include inside a closure/function scope to avoid variable collisions // but we need to ensure $pdo is available if they use it. // Most files do: require config.php; $pdo = db(); // Since we already required config.php, require_once will skip it. // So they will just get $pdo = db(); which works. include $file; $output = ob_get_clean(); // Log success $messages[] = "Executed $filename: " . trim($output); // Record in DB $stmt = $pdo->prepare("INSERT INTO migrations (migration_name) VALUES (?)"); $stmt->execute([$filename]); $count++; } catch (Throwable $e) { ob_end_clean(); // Clean buffer if error $messages[] = "Failed to execute $filename: " . $e->getMessage(); return $messages; // Stop on error } } if ($count === 0) { $messages[] = "No new migrations to run."; } else { $messages[] = "Successfully ran $count migrations."; } return $messages; } // If run directly from CLI if (php_sapi_name() === 'cli' && basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME'])) { $results = run_migrations(); echo implode("\n", $results) . "\n"; }