setConfig($config); $this->setOutput($output); } /** * Prints the specified environment's migration status. * * @param string $environment * @param null $format * @return void */ public function printStatus($environment, $format = null) { $output = $this->getOutput(); $migrations = array(); if (count($this->getMigrations())) { $output->writeln(''); $output->writeln(' Status Migration ID Migration Name '); $output->writeln('-----------------------------------------'); $env = $this->getEnvironment($environment); $versions = $env->getVersions(); foreach ($this->getMigrations() as $migration) { if (in_array($migration->getVersion(), $versions)) { $status = ' up '; unset($versions[array_search($migration->getVersion(), $versions)]); } else { $status = ' down '; } $output->writeln( $status . sprintf(' %14.0f ', $migration->getVersion()) . ' ' . $migration->getName() . '' ); $migrations[] = array('migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName()); } foreach ($versions as $missing) { $output->writeln( ' up ' . sprintf(' %14.0f ', $missing) . ' ** MISSING **' ); } } else { // there are no migrations $output->writeln(''); $output->writeln('There are no available migrations. Try creating one using the create command.'); } // write an empty line $output->writeln(''); if ($format != null) { switch ($format) { case 'json': $output->writeln(json_encode($migrations)); break; default: $output->writeln('Unsupported format: '.$format.''); break; } } } /** * Migrate to the version of the database on a given date. * * @param string $environment Environment * @param \DateTime $dateTime Date to migrate to * * @return void */ public function migrateToDateTime($environment, \DateTime $dateTime) { $env = $this->getEnvironment($environment); $versions = array_keys($this->getMigrations()); $dateString = $dateTime->format('Ymdhis'); $earlierVersion = null; foreach ($versions as $version) { if ($version > $dateString) { if (!is_null($earlierVersion)) { $this->getOutput()->writeln( 'Migrating to version ' . $earlierVersion ); } return $this->migrate($environment, $earlierVersion); } $earlierVersion = $version; } //If the date is greater than the latest version, migrate //to the latest version. $this->getOutput()->writeln( 'Migrating to version ' . $earlierVersion ); return $this->migrate($environment, $earlierVersion); } /** * Roll back to the version of the database on a given date. * * @param string $environment Environment * @param \DateTime $dateTime Date to roll back to * * @return void */ public function rollbackToDateTime($environment, \DateTime $dateTime) { $env = $this->getEnvironment($environment); $versions = $env->getVersions(); $dateString = $dateTime->format('Ymdhis'); sort($versions); $laterVersion = null; foreach (array_reverse($versions) as $version) { if ($version < $dateString) { if (!is_null($laterVersion)) { $this->getOutput()->writeln('Rolling back to version '.$version); } return $this->rollback($environment, $version); } $laterVersion = $version; } $this->getOutput()->writeln('Rolling back to version ' . $laterVersion); return $this->rollback($environment, $laterVersion); } /** * Migrate an environment to the specified version. * * @param string $environment Environment * @param int $version * @return void */ public function migrate($environment, $version = null) { $migrations = $this->getMigrations(); $env = $this->getEnvironment($environment); $versions = $env->getVersions(); $current = $env->getCurrentVersion(); if (empty($versions) && empty($migrations)) { return; } if (null === $version) { $version = max(array_merge($versions, array_keys($migrations))); } else { if (0 != $version && !isset($migrations[$version])) { $this->output->writeln(sprintf( 'warning %s is not a valid version', $version )); return; } } // are we migrating up or down? $direction = $version > $current ? MigrationInterface::UP : MigrationInterface::DOWN; if ($direction == MigrationInterface::DOWN) { // run downs first krsort($migrations); foreach ($migrations as $migration) { if ($migration->getVersion() <= $version) { break; } if (in_array($migration->getVersion(), $versions)) { $this->executeMigration($environment, $migration, MigrationInterface::DOWN); } } } ksort($migrations); foreach ($migrations as $migration) { if ($migration->getVersion() > $version) { break; } if (!in_array($migration->getVersion(), $versions)) { $this->executeMigration($environment, $migration, MigrationInterface::UP); } } } /** * Execute a migration against the specified Environment. * * @param string $name Environment Name * @param MigrationInterface $migration Migration * @param string $direction Direction * @return void */ public function executeMigration($name, MigrationInterface $migration, $direction = MigrationInterface::UP) { $this->getOutput()->writeln(''); $this->getOutput()->writeln( ' ==' . ' ' . $migration->getVersion() . ' ' . $migration->getName() . ':' . ' ' . ($direction == 'up' ? 'migrating' : 'reverting') . '' ); // Execute the migration and log the time elapsed. $start = microtime(true); $this->getEnvironment($name)->executeMigration($migration, $direction); $end = microtime(true); $this->getOutput()->writeln( ' ==' . ' ' . $migration->getVersion() . ' ' . $migration->getName() . ':' . ' ' . ($direction == 'up' ? 'migrated' : 'reverted') . ' ' . sprintf('%.4fs', $end - $start) . '' ); } /** * Rollback an environment to the specified version. * * @param string $environment Environment * @param int $version * @return void */ public function rollback($environment, $version = null) { $migrations = $this->getMigrations(); $env = $this->getEnvironment($environment); $versions = $env->getVersions(); ksort($migrations); sort($versions); // Check we have at least 1 migration to revert if (empty($versions) || $version == end($versions)) { $this->getOutput()->writeln('No migrations to rollback'); return; } // If no target version was supplied, revert the last migration if (null === $version) { // Get the migration before the last run migration $prev = count($versions) - 2; $version = $prev >= 0 ? $versions[$prev] : 0; } else { // Get the first migration number $first = reset($versions); // If the target version is before the first migration, revert all migrations if ($version < $first) { $version = 0; } } // Check the target version exists if (0 !== $version && !isset($migrations[$version])) { $this->getOutput()->writeln("Target version ($version) not found"); return; } // Revert the migration(s) krsort($migrations); foreach ($migrations as $migration) { if ($migration->getVersion() <= $version) { break; } if (in_array($migration->getVersion(), $versions)) { $this->executeMigration($environment, $migration, MigrationInterface::DOWN); } } } /** * Sets the environments. * * @param array $environments Environments * @return Manager */ public function setEnvironments($environments = array()) { $this->environments = $environments; return $this; } /** * Gets the manager class for the given environment. * * @param string $name Environment Name * @throws \InvalidArgumentException * @return Environment */ public function getEnvironment($name) { if (isset($this->environments[$name])) { return $this->environments[$name]; } // check the environment exists if (!$this->getConfig()->hasEnvironment($name)) { throw new \InvalidArgumentException(sprintf( 'The environment "%s" does not exist', $name )); } // create an environment instance and cache it $environment = new Environment($name, $this->getConfig()->getEnvironment($name)); $this->environments[$name] = $environment; $environment->setOutput($this->getOutput()); return $environment; } /** * Sets the console output. * * @param OutputInterface $output Output * @return Manager */ public function setOutput(OutputInterface $output) { $this->output = $output; return $this; } /** * Gets the console output. * * @return OutputInterface */ public function getOutput() { return $this->output; } /** * Sets the database migrations. * * @param array $migrations Migrations * @return Manager */ public function setMigrations(array $migrations) { $this->migrations = $migrations; return $this; } /** * Gets an array of the database migrations. * * @throws \InvalidArgumentException * @return AbstractMigration[] */ public function getMigrations() { if (null === $this->migrations) { $config = $this->getConfig(); $phpFiles = glob($config->getMigrationPath() . DIRECTORY_SEPARATOR . '*.php'); // filter the files to only get the ones that match our naming scheme $fileNames = array(); /** @var AbstractMigration[] $versions */ $versions = array(); foreach ($phpFiles as $filePath) { if (preg_match('/([0-9]+)_([_a-z0-9]*).php/', basename($filePath))) { $matches = array(); preg_match('/^[0-9]+/', basename($filePath), $matches); // get the version from the start of the filename $version = $matches[0]; if (isset($versions[$version])) { throw new \InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion())); } // convert the filename to a class name $class = preg_replace('/^[0-9]+_/', '', basename($filePath)); $class = str_replace('_', ' ', $class); $class = ucwords($class); $class = str_replace(' ', '', $class); if (false !== strpos($class, '.')) { $class = substr($class, 0, strpos($class, '.')); } if (isset($fileNames[$class])) { throw new \InvalidArgumentException(sprintf( 'Migration "%s" has the same name as "%s"', basename($filePath), $fileNames[$class] )); } $fileNames[$class] = basename($filePath); // load the migration file /** @noinspection PhpIncludeInspection */ require_once $filePath; if (!class_exists($class)) { throw new \InvalidArgumentException(sprintf( 'Could not find class "%s" in file "%s"', $class, $filePath )); } // instantiate it $migration = new $class($version); if (!($migration instanceof AbstractMigration)) { throw new \InvalidArgumentException(sprintf( 'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration', $class, $filePath )); } $migration->setOutput($this->getOutput()); $versions[$version] = $migration; } } ksort($versions); $this->setMigrations($versions); } return $this->migrations; } /** * Sets the config. * * @param ConfigInterface $config Configuration Object * @return Manager */ public function setConfig(ConfigInterface $config) { $this->config = $config; return $this; } /** * Gets the config. * * @return ConfigInterface */ public function getConfig() { return $this->config; } }