query("CREATE TABLE IF NOT EXISTS migrations (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, applied_at DATETIME NOT NULL)"); function migration_applied($conn, $name){ $st = $conn->prepare('SELECT COUNT(*) AS c FROM migrations WHERE name = ?'); if(!$st){ return false; } $st->bind_param('s', $name); $st->execute(); $r = $st->get_result(); $row = $r ? $r->fetch_assoc() : null; $st->close(); return $row && intval($row['c']) > 0; } function run_migration_file($conn, $path){ $name = basename($path); if(migration_applied($conn, $name)){ echo "Skipping already applied: $name\n"; return true; } $sql = file_get_contents($path); if($sql === false){ echo "Failed to read $name\n"; return false; } if(!$conn->multi_query($sql)){ echo "Failed executing $name: " . $conn->error . "\n"; return false; } do { if($res = $conn->store_result()){ $res->free(); } } while($conn->more_results() && $conn->next_result()); if($conn->errno){ echo "Failed executing $name: " . $conn->error . "\n"; return false; } $st = $conn->prepare('INSERT INTO migrations (name, applied_at) VALUES (?, NOW())'); if($st){ $st->bind_param('s', $name); $st->execute(); $st->close(); } echo "Applied: $name\n"; return true; } $migDir = __DIR__ . '/../sql/migrations'; $files = array_values(array_filter(scandir($migDir), function($f){ return preg_match('/^\d+_.*\.sql$/', $f); })); sort($files, SORT_NATURAL); if(empty($files)){ echo "No migration files found.\n"; exit(0); } foreach($files as $file){ if(!run_migration_file($conn, $migDir . '/' . $file)){ exit(1); } } echo "Migrations complete.\n";