registry->getModules(); $modulesEnabled = count($modules); if ($modulesEnabled === 0) { return ['ok' => true, 'applied' => 0, 'skipped_empty' => 0, 'modules_enabled' => 0]; } $this->repository->ensureTrackingTable(); $totalApplied = 0; $skippedEmpty = 0; foreach ($modules as $manifest) { $migrationsPath = $manifest->migrationsPath; if ($migrationsPath === null || !is_dir($migrationsPath)) { continue; } $files = glob($migrationsPath . '/*.sql'); if ($files === false || count($files) === 0) { continue; } sort($files); $applied = $this->repository->getAppliedFilenames((string) $manifest->id); foreach ($files as $file) { $filename = basename($file); if (isset($applied[$filename])) { continue; } $sql = file_get_contents($file); if ($sql === false || trim($sql) === '') { $skippedEmpty++; continue; } $this->repository->beginTransaction(); try { $statements = SqlStatementParser::splitStatements($sql); foreach ($statements as $statement) { $this->repository->executeStatement($statement); } $this->repository->recordApplied((string) $manifest->id, $filename); $this->repository->commit(); } catch (\Throwable $migrationError) { $this->repository->rollback(); throw new RuntimeException( sprintf("Migration %s/%s failed: %s", $manifest->id, $filename, $migrationError->getMessage()), 0, $migrationError ); } $totalApplied++; } } return [ 'ok' => true, 'applied' => $totalApplied, 'skipped_empty' => $skippedEmpty, 'modules_enabled' => $modulesEnabled, ]; } }