migrationsDir = __DIR__ . '/../Migrations'; $this->templateFile = __DIR__ . '/{level}MigrationTemplate.php'; } public function getMigrationsDir(): string { return $this->migrationsDir; } /** @return array{name: string, path: string} */ public function create(string $level): array { if (!in_array($level, [self::MIGRATIONS_LEVEL_APP, self::MIGRATIONS_LEVEL_DB], true)) { throw MigratorException::invalidMigrationLevel($level); } $templateFile = str_replace('{level}', $level, $this->templateFile); $template = @file_get_contents($templateFile); if (!$template) { throw MigratorException::templateFileReadFailed($templateFile); } $name = $this->generateName($level); $migration = str_replace('{level}', $level, 'class {level}MigrationTemplate '); $migration = str_replace($migration, "class $name ", $template); $path = "$this->migrationsDir/$level/$name.php"; $result = @file_put_contents($path, $migration); if (!$result) { throw MigratorException::migrationFileWriteFailed($path); } return [ 'name' => $name, 'path' => $path, ]; } /** * Array of filenames. * Db migrations are loaded first, then app migrations. This ensures that Db migrator is run before app migrations * @return array */ public function loadAll(): array { $migrations = array_merge( $this->loadForLevel(self::MIGRATIONS_LEVEL_DB), $this->loadForLevel(self::MIGRATIONS_LEVEL_APP) ); $duplicateNames = array_diff_assoc(array_column($migrations, 'name'), array_unique(array_column($migrations, 'name'))); if (!empty($duplicateNames)) { throw MigratorException::duplicateMigrationNames($duplicateNames); } return $migrations; } private function loadForLevel(string $level): array { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($this->migrationsDir . '/' . $level, RecursiveDirectoryIterator::SKIP_DOTS) ); $migrations = []; foreach ($files as $file) { if (!$file instanceof SplFileInfo || !$file->isFile()) { continue; } if (strtolower($file->getFilename()) === 'index.php') { continue; } if (strtolower($file->getExtension()) === 'php') { $migrations[] = $file->getBasename('.' . $file->getExtension()); } } sort($migrations); return array_map(function ($migration) use ($level) { return [ 'level' => $level, 'name' => $migration, ]; }, $migrations) ; } private function generateName(string $level): string { return 'Migration_' . gmdate('Ymd_His') . '_' . $level; } }