setConfig($config);
$this->setInput($input);
$this->setOutput($output);
}
/**
* Prints the specified environment's migration status.
*
* @param string $environment environment to print status of
* @param string|null $format format to print status in (either text, json, or null)
* @throws \RuntimeException
* @return array array indicating if there are any missing or down migrations
*/
public function printStatus(string $environment, ?string $format = null): array
{
$output = $this->getOutput();
$hasDownMigration = false;
$hasMissingMigration = false;
$migrations = $this->getMigrations($environment);
$migrationCount = 0;
$missingCount = 0;
$pendingMigrationCount = 0;
$finalMigrations = [];
$verbosity = $output->getVerbosity();
if ($format === 'json') {
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
}
if (count($migrations)) {
// rewrite using Symfony Table Helper as we already have this library
// included and it will fix formatting issues (e.g drawing the lines)
$output->writeln('', $this->verbosityLevel);
switch ($this->getConfig()->getVersionOrder()) {
case Config::VERSION_ORDER_CREATION_TIME:
$migrationIdAndStartedHeader = '[Migration ID] Started ';
break;
case Config::VERSION_ORDER_EXECUTION_TIME:
$migrationIdAndStartedHeader = 'Migration ID [Started ]';
break;
default:
throw new RuntimeException('Invalid version_order configuration option');
}
$output->writeln(" Status $migrationIdAndStartedHeader Finished Migration Name ", $this->verbosityLevel);
$output->writeln('----------------------------------------------------------------------------------', $this->verbosityLevel);
$env = $this->getEnvironment($environment);
$versions = $env->getVersionLog();
$maxNameLength = $versions ? max(array_map(function ($version) {
return strlen($version['migration_name']);
}, $versions)) : 0;
$missingVersions = array_diff_key($versions, $migrations);
$missingCount = count($missingVersions);
$hasMissingMigration = !empty($missingVersions);
// get the migrations sorted in the same way as the versions
/** @var \Phinx\Migration\AbstractMigration[] $sortedMigrations */
$sortedMigrations = [];
foreach ($versions as $versionCreationTime => $version) {
if (isset($migrations[$versionCreationTime])) {
array_push($sortedMigrations, $migrations[$versionCreationTime]);
unset($migrations[$versionCreationTime]);
}
}
if (empty($sortedMigrations) && !empty($missingVersions)) {
// this means we have no up migrations, so we write all the missing versions already so they show up
// before any possible down migration
foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
$this->printMissingVersion($missingVersion, $maxNameLength);
unset($missingVersions[$missingVersionCreationTime]);
}
}
// any migration left in the migrations (ie. not unset when sorting the migrations by the version order) is
// a migration that is down, so we add them to the end of the sorted migrations list
if (!empty($migrations)) {
$sortedMigrations = array_merge($sortedMigrations, $migrations);
}
$migrationCount = count($sortedMigrations);
foreach ($sortedMigrations as $migration) {
$version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false;
if ($version) {
// check if there are missing versions before this version
foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
if ($this->getConfig()->isVersionOrderCreationTime()) {
if ($missingVersion['version'] > $version['version']) {
break;
}
} else {
if ($missingVersion['start_time'] > $version['start_time']) {
break;
} elseif (
$missingVersion['start_time'] == $version['start_time'] &&
$missingVersion['version'] > $version['version']
) {
break;
}
}
$this->printMissingVersion($missingVersion, $maxNameLength);
unset($missingVersions[$missingVersionCreationTime]);
}
$status = ' up ';
} else {
$pendingMigrationCount++;
$hasDownMigration = true;
$status = ' down ';
}
$maxNameLength = max($maxNameLength, strlen($migration->getName()));
$output->writeln(
sprintf(
'%s %14.0f %19s %19s %s',
$status,
$migration->getVersion(),
($version ? $version['start_time'] : ''),
($version ? $version['end_time'] : ''),
$migration->getName()
),
$this->verbosityLevel
);
if ($version && $version['breakpoint']) {
$output->writeln(' BREAKPOINT SET', $this->verbosityLevel);
}
$finalMigrations[] = ['migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName()];
unset($versions[$migration->getVersion()]);
}
// and finally add any possibly-remaining missing migrations
foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
$this->printMissingVersion($missingVersion, $maxNameLength);
unset($missingVersions[$missingVersionCreationTime]);
}
} else {
// there are no migrations
$output->writeln('', $this->verbosityLevel);
$output->writeln('There are no available migrations. Try creating one using the create command.', $this->verbosityLevel);
}
// write an empty line
$output->writeln('', $this->verbosityLevel);
if ($format !== null) {
switch ($format) {
case AbstractCommand::FORMAT_JSON:
$output->setVerbosity($verbosity);
$output->writeln(json_encode(
[
'pending_count' => $pendingMigrationCount,
'missing_count' => $missingCount,
'total_count' => $migrationCount + $missingCount,
'migrations' => $finalMigrations,
]
));
break;
default:
$output->writeln('Unsupported format: ' . $format . '');
}
}
return [
'hasMissingMigration' => $hasMissingMigration,
'hasDownMigration' => $hasDownMigration,
];
}
/**
* Print Missing Version
*
* @param array $version The missing version to print (in the format returned by Environment.getVersionLog).
* @param int $maxNameLength The maximum migration name length.
* @return void
*/
protected function printMissingVersion(array $version, int $maxNameLength): void
{
$this->getOutput()->writeln(sprintf(
' up %14.0f %19s %19s %s ** MISSING MIGRATION FILE **',
$version['version'],
$version['start_time'],
$version['end_time'],
str_pad($version['migration_name'], $maxNameLength, ' ')
));
if ($version && $version['breakpoint']) {
$this->getOutput()->writeln(' BREAKPOINT SET');
}
}
/**
* Migrate to the version of the database on a given date.
*
* @param string $environment Environment
* @param \DateTime $dateTime Date to migrate to
* @param bool $fake flag that if true, we just record running the migration, but not actually do the
* migration
* @return void
*/
public function migrateToDateTime(string $environment, DateTime $dateTime, bool $fake = false): void
{
$versions = array_keys($this->getMigrations($environment));
$dateString = $dateTime->format('YmdHis');
$outstandingMigrations = array_filter($versions, function ($version) use ($dateString) {
return $version <= $dateString;
});
if (count($outstandingMigrations) > 0) {
$migration = max($outstandingMigrations);
$this->getOutput()->writeln('Migrating to version ' . $migration, $this->verbosityLevel);
$this->migrate($environment, $migration, $fake);
}
}
/**
* Migrate an environment to the specified version.
*
* @param string $environment Environment
* @param int|null $version version to migrate to
* @param bool $fake flag that if true, we just record running the migration, but not actually do the migration
* @return void
*/
public function migrate(string $environment, ?int $version = null, bool $fake = false): void
{
$migrations = $this->getMigrations($environment);
$env = $this->getEnvironment($environment);
$versions = $env->getVersions();
$current = $env->getCurrentVersion();
if (empty($versions) && empty($migrations)) {
return;
}
if ($version === null) {
$version = max(array_merge($versions, array_keys($migrations)));
} else {
if ($version != 0 && !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, $fake);
}
}
}
ksort($migrations);
foreach ($migrations as $migration) {
if ($migration->getVersion() > $version) {
break;
}
if (!in_array($migration->getVersion(), $versions)) {
$this->executeMigration($environment, $migration, MigrationInterface::UP, $fake);
}
}
}
/**
* Execute a migration against the specified environment.
*
* @param string $name Environment Name
* @param \Phinx\Migration\MigrationInterface $migration Migration
* @param string $direction Direction
* @param bool $fake flag that if true, we just record running the migration, but not actually do the migration
* @return void
*/
public function executeMigration(string $name, MigrationInterface $migration, string $direction = MigrationInterface::UP, bool $fake = false): void
{
$this->getOutput()->writeln('', $this->verbosityLevel);
// Skip the migration if it should not be executed
if (!$migration->shouldExecute()) {
$this->printMigrationStatus($migration, 'skipped');
return;
}
$this->printMigrationStatus($migration, ($direction === MigrationInterface::UP ? 'migrating' : 'reverting'));
// Execute the migration and log the time elapsed.
$start = microtime(true);
$this->getEnvironment($name)->executeMigration($migration, $direction, $fake);
$end = microtime(true);
$this->printMigrationStatus(
$migration,
($direction === MigrationInterface::UP ? 'migrated' : 'reverted'),
sprintf('%.4fs', $end - $start)
);
}
/**
* Execute a seeder against the specified environment.
*
* @param string $name Environment Name
* @param \Phinx\Seed\SeedInterface $seed Seed
* @return void
*/
public function executeSeed(string $name, SeedInterface $seed): void
{
$this->getOutput()->writeln('', $this->verbosityLevel);
// Skip the seed if it should not be executed
if (!$seed->shouldExecute()) {
$this->printSeedStatus($seed, 'skipped');
return;
}
$this->printSeedStatus($seed, 'seeding');
// Execute the seeder and log the time elapsed.
$start = microtime(true);
$this->getEnvironment($name)->executeSeed($seed);
$end = microtime(true);
$this->printSeedStatus(
$seed,
'seeded',
sprintf('%.4fs', $end - $start)
);
}
/**
* Print Migration Status
*
* @param \Phinx\Migration\MigrationInterface $migration Migration
* @param string $status Status of the migration
* @param string|null $duration Duration the migration took the be executed
* @return void
*/
protected function printMigrationStatus(MigrationInterface $migration, string $status, ?string $duration = null): void
{
$this->printStatusOutput(
$migration->getVersion() . ' ' . $migration->getName(),
$status,
$duration
);
}
/**
* Print Seed Status
*
* @param \Phinx\Seed\SeedInterface $seed Seed
* @param string $status Status of the seed
* @param string|null $duration Duration the seed took the be executed
* @return void
*/
protected function printSeedStatus(SeedInterface $seed, string $status, ?string $duration = null): void
{
$this->printStatusOutput(
$seed->getName(),
$status,
$duration
);
}
/**
* Print Status in Output
*
* @param string $name Name of the migration or seed
* @param string $status Status of the migration or seed
* @param string|null $duration Duration the migration or seed took the be executed
* @return void
*/
protected function printStatusOutput(string $name, string $status, ?string $duration = null): void
{
$this->getOutput()->writeln(
' ==' .
' ' . $name . ':' .
' ' . $status . ' ' . $duration . '',
$this->verbosityLevel
);
}
/**
* Rollback an environment to the specified version.
*
* @param string $environment Environment
* @param int|string|null $target Target
* @param bool $force Force
* @param bool $targetMustMatchVersion Target must match version
* @param bool $fake Flag that if true, we just record running the migration, but not actually do the migration
* @return void
*/
public function rollback(string $environment, $target = null, bool $force = false, bool $targetMustMatchVersion = true, bool $fake = false): void
{
// note that the migrations are indexed by name (aka creation time) in ascending order
$migrations = $this->getMigrations($environment);
// note that the version log are also indexed by name with the proper ascending order according to the version order
$executedVersions = $this->getEnvironment($environment)->getVersionLog();
// get a list of migrations sorted in the opposite way of the executed versions
$sortedMigrations = [];
foreach ($executedVersions as $versionCreationTime => &$executedVersion) {
// if we have a date (ie. the target must not match a version) and we are sorting by execution time, we
// convert the version start time so we can compare directly with the target date
if (!$this->getConfig()->isVersionOrderCreationTime() && !$targetMustMatchVersion) {
/** @var \DateTime $dateTime */
$dateTime = DateTime::createFromFormat('Y-m-d H:i:s', $executedVersion['start_time']);
$executedVersion['start_time'] = $dateTime->format('YmdHis');
}
if (isset($migrations[$versionCreationTime])) {
array_unshift($sortedMigrations, $migrations[$versionCreationTime]);
} else {
// this means the version is missing so we unset it so that we don't consider it when rolling back
// migrations (or choosing the last up version as target)
unset($executedVersions[$versionCreationTime]);
}
}
if ($target === 'all' || $target === '0') {
$target = 0;
} elseif (!is_numeric($target) && $target !== null) { // try to find a target version based on name
// search through the migrations using the name
$migrationNames = array_map(function ($item) {
return $item['migration_name'];
}, $executedVersions);
$found = array_search($target, $migrationNames, true);
// check on was found
if ($found !== false) {
$target = (string)$found;
} else {
$this->getOutput()->writeln("No migration found with name ($target)");
return;
}
}
// Check we have at least 1 migration to revert
$executedVersionCreationTimes = array_keys($executedVersions);
if (empty($executedVersionCreationTimes) || $target == end($executedVersionCreationTimes)) {
$this->getOutput()->writeln('No migrations to rollback');
return;
}
// If no target was supplied, revert the last migration
if ($target === null) {
// Get the migration before the last run migration
$prev = count($executedVersionCreationTimes) - 2;
$target = $prev >= 0 ? $executedVersionCreationTimes[$prev] : 0;
}
// If the target must match a version, check the target version exists
if ($targetMustMatchVersion && $target !== 0 && !isset($migrations[$target])) {
$this->getOutput()->writeln("Target version ($target) not found");
return;
}
// Rollback all versions until we find the wanted rollback target
$rollbacked = false;
foreach ($sortedMigrations as $migration) {
if ($targetMustMatchVersion && $migration->getVersion() == $target) {
break;
}
if (in_array($migration->getVersion(), $executedVersionCreationTimes)) {
$executedVersion = $executedVersions[$migration->getVersion()];
if (!$targetMustMatchVersion) {
if (
($this->getConfig()->isVersionOrderCreationTime() && $executedVersion['version'] <= $target) ||
(!$this->getConfig()->isVersionOrderCreationTime() && $executedVersion['start_time'] <= $target)
) {
break;
}
}
if ($executedVersion['breakpoint'] != 0 && !$force) {
$this->getOutput()->writeln('Breakpoint reached. Further rollbacks inhibited.');
break;
}
$this->executeMigration($environment, $migration, MigrationInterface::DOWN, $fake);
$rollbacked = true;
}
}
if (!$rollbacked) {
$this->getOutput()->writeln('No migrations to rollback');
}
}
/**
* Run database seeders against an environment.
*
* @param string $environment Environment
* @param string|null $seed Seeder
* @throws \InvalidArgumentException
* @return void
*/
public function seed(string $environment, ?string $seed = null): void
{
$seeds = $this->getSeeds($environment);
if ($seed === null) {
// run all seeders
foreach ($seeds as $seeder) {
if (array_key_exists($seeder->getName(), $seeds)) {
$this->executeSeed($environment, $seeder);
}
}
} else {
// run only one seeder
if (array_key_exists($seed, $seeds)) {
$this->executeSeed($environment, $seeds[$seed]);
} else {
throw new InvalidArgumentException(sprintf('The seed class "%s" does not exist', $seed));
}
}
}
/**
* Sets the environments.
*
* @param \Phinx\Migration\Manager\Environment[] $environments Environments
* @return $this
*/
public function setEnvironments(array $environments = [])
{
$this->environments = $environments;
return $this;
}
/**
* Gets the manager class for the given environment.
*
* @param string $name Environment Name
* @throws \InvalidArgumentException
* @return \Phinx\Migration\Manager\Environment
*/
public function getEnvironment(string $name): Environment
{
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
$envOptions = $this->getConfig()->getEnvironment($name);
$envOptions['version_order'] = $this->getConfig()->getVersionOrder();
$envOptions['data_domain'] = $this->getConfig()->getDataDomain();
$environment = new Environment($name, $envOptions);
$this->environments[$name] = $environment;
$environment->setInput($this->getInput());
$environment->setOutput($this->getOutput());
return $environment;
}
/**
* Sets the user defined PSR-11 container
*
* @param \Psr\Container\ContainerInterface $container Container
* @return $this
*/
public function setContainer(ContainerInterface $container)
{
$this->container = $container;
return $this;
}
/**
* Sets the console input.
*
* @param \think\console\Input $input Input
* @return $this
*/
public function setInput(InputInterface $input)
{
$this->input = $input;
return $this;
}
/**
* Gets the console input.
*
* @return \think\console\Input
*/
public function getInput(): InputInterface
{
return $this->input;
}
/**
* Sets the console output.
*
* @param \think\console\Output $output Output
* @return $this
*/
public function setOutput(OutputInterface $output)
{
$this->output = $output;
return $this;
}
/**
* Gets the console output.
*
* @return \think\console\Output
*/
public function getOutput(): OutputInterface
{
return $this->output;
}
/**
* Sets the database migrations.
*
* @param \Phinx\Migration\AbstractMigration[] $migrations Migrations
* @return $this
*/
public function setMigrations(array $migrations)
{
$this->migrations = $migrations;
return $this;
}
/**
* Gets an array of the database migrations, indexed by migration name (aka creation time) and sorted in ascending
* order
*
* @param string $environment Environment
* @throws \InvalidArgumentException
* @return \Phinx\Migration\MigrationInterface[]
*/
public function getMigrations(string $environment): array
{
if ($this->migrations === null) {
$phpFiles = $this->getMigrationFiles();
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
$this->getOutput()->writeln('Migration file');
$this->getOutput()->writeln(
array_map(
function ($phpFile) {
return " {$phpFile}";
},
$phpFiles
)
);
}
// filter the files to only get the ones that match our naming scheme
$fileNames = [];
/** @var \Phinx\Migration\AbstractMigration[] $versions */
$versions = [];
foreach ($phpFiles as $filePath) {
if (Util::isValidMigrationFileName(basename($filePath))) {
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
$this->getOutput()->writeln("Valid migration file {$filePath}.");
}
$version = Util::getVersionFromFileName(basename($filePath));
if (isset($versions[$version])) {
throw new InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion()));
}
$config = $this->getConfig();
$namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath(dirname($filePath)) : null;
// convert the filename to a class name
$class = ($namespace === null ? '' : $namespace . '\\') . Util::mapFileNameToClassName(basename($filePath));
if (isset($fileNames[$class])) {
throw new InvalidArgumentException(sprintf(
'Migration "%s" has the same name as "%s"',
basename($filePath),
$fileNames[$class]
));
}
$fileNames[$class] = basename($filePath);
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
$this->getOutput()->writeln("Loading class $class from $filePath.");
}
// load the migration file
$orig_display_errors_setting = ini_get('display_errors');
ini_set('display_errors', 'On');
/** @noinspection PhpIncludeInspection */
require_once $filePath;
ini_set('display_errors', $orig_display_errors_setting);
if (!class_exists($class)) {
throw new InvalidArgumentException(sprintf(
'Could not find class "%s" in file "%s"',
$class,
$filePath
));
}
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
$this->getOutput()->writeln("Running $class.");
}
// instantiate it
$migration = new $class($environment, $version, $this->getInput(), $this->getOutput());
if (!($migration instanceof AbstractMigration)) {
throw new InvalidArgumentException(sprintf(
'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration',
$class,
$filePath
));
}
$versions[$version] = $migration;
} else {
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
$this->getOutput()->writeln("Invalid migration file {$filePath}.");
}
}
}
ksort($versions);
$this->setMigrations($versions);
}
return $this->migrations;
}
/**
* Returns a list of migration files found in the provided migration paths.
*
* @return string[]
*/
protected function getMigrationFiles(): array
{
return Util::getFiles($this->getConfig()->getMigrationPaths());
}
/**
* Sets the database seeders.
*
* @param \Phinx\Seed\SeedInterface[] $seeds Seeders
* @return $this
*/
public function setSeeds(array $seeds)
{
$this->seeds = $seeds;
return $this;
}
/**
* Get seed dependencies instances from seed dependency array
*
* @param \Phinx\Seed\SeedInterface $seed Seed
* @return \Phinx\Seed\SeedInterface[]
*/
protected function getSeedDependenciesInstances(SeedInterface $seed): array
{
$dependenciesInstances = [];
$dependencies = $seed->getDependencies();
if (!empty($dependencies)) {
foreach ($dependencies as $dependency) {
foreach ($this->seeds as $seed) {
if (get_class($seed) === $dependency) {
$dependenciesInstances[get_class($seed)] = $seed;
}
}
}
}
return $dependenciesInstances;
}
/**
* Order seeds by dependencies
*
* @param \Phinx\Seed\SeedInterface[] $seeds Seeds
* @return \Phinx\Seed\SeedInterface[]
*/
protected function orderSeedsByDependencies(array $seeds): array
{
$orderedSeeds = [];
foreach ($seeds as $seed) {
$orderedSeeds[get_class($seed)] = $seed;
$dependencies = $this->getSeedDependenciesInstances($seed);
if (!empty($dependencies)) {
$orderedSeeds = array_merge($this->orderSeedsByDependencies($dependencies), $orderedSeeds);
}
}
return $orderedSeeds;
}
/**
* Gets an array of database seeders.
*
* @param string $environment Environment
* @throws \InvalidArgumentException
* @return \Phinx\Seed\SeedInterface[]
*/
public function getSeeds(string $environment): array
{
if ($this->seeds === null) {
$phpFiles = $this->getSeedFiles();
// filter the files to only get the ones that match our naming scheme
$fileNames = [];
/** @var \Phinx\Seed\SeedInterface[] $seeds */
$seeds = [];
foreach ($phpFiles as $filePath) {
if (Util::isValidSeedFileName(basename($filePath))) {
$config = $this->getConfig();
$namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath(dirname($filePath)) : null;
// convert the filename to a class name
$class = ($namespace === null ? '' : $namespace . '\\') . pathinfo($filePath, PATHINFO_FILENAME);
$fileNames[$class] = basename($filePath);
// load the seed 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
/** @var \Phinx\Seed\AbstractSeed $seed */
if ($this->container !== null) {
$seed = $this->container->get($class);
} else {
$seed = new $class();
}
$seed->setEnvironment($environment);
$input = $this->getInput();
if ($input !== null) {
$seed->setInput($input);
}
$output = $this->getOutput();
if ($output !== null) {
$seed->setOutput($output);
}
if (!($seed instanceof AbstractSeed)) {
throw new InvalidArgumentException(sprintf(
'The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed',
$class,
$filePath
));
}
$seeds[$class] = $seed;
}
}
ksort($seeds);
$this->setSeeds($seeds);
}
$this->seeds = $this->orderSeedsByDependencies($this->seeds);
return $this->seeds;
}
/**
* Returns a list of seed files found in the provided seed paths.
*
* @return string[]
*/
protected function getSeedFiles(): array
{
return Util::getFiles($this->getConfig()->getSeedPaths());
}
/**
* Sets the config.
*
* @param \Phinx\Config\ConfigInterface $config Configuration Object
* @return $this
*/
public function setConfig(ConfigInterface $config)
{
$this->config = $config;
return $this;
}
/**
* Gets the config.
*
* @return \Phinx\Config\ConfigInterface
*/
public function getConfig(): ConfigInterface
{
return $this->config;
}
/**
* Toggles the breakpoint for a specific version.
*
* @param string $environment Environment name
* @param int|null $version Version
* @return void
*/
public function toggleBreakpoint(string $environment, ?int $version): void
{
$this->markBreakpoint($environment, $version, self::BREAKPOINT_TOGGLE);
}
/**
* Updates the breakpoint for a specific version.
*
* @param string $environment The required environment
* @param int|null $version The version of the target migration
* @param int $mark The state of the breakpoint as defined by self::BREAKPOINT_xxxx constants.
* @return void
*/
protected function markBreakpoint(string $environment, ?int $version, int $mark): void
{
$migrations = $this->getMigrations($environment);
$env = $this->getEnvironment($environment);
$versions = $env->getVersionLog();
if (empty($versions) || empty($migrations)) {
return;
}
if ($version === null) {
$lastVersion = end($versions);
$version = $lastVersion['version'];
}
if ($version != 0 && (!isset($versions[$version]) || !isset($migrations[$version]))) {
$this->output->writeln(sprintf(
'warning %s is not a valid version',
$version
));
return;
}
switch ($mark) {
case self::BREAKPOINT_TOGGLE:
$env->getAdapter()->toggleBreakpoint($migrations[$version]);
break;
case self::BREAKPOINT_SET:
if ($versions[$version]['breakpoint'] == 0) {
$env->getAdapter()->setBreakpoint($migrations[$version]);
}
break;
case self::BREAKPOINT_UNSET:
if ($versions[$version]['breakpoint'] == 1) {
$env->getAdapter()->unsetBreakpoint($migrations[$version]);
}
break;
}
$versions = $env->getVersionLog();
$this->getOutput()->writeln(
' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') .
' for ' . $version . '' .
' ' . $migrations[$version]->getName() . ''
);
}
/**
* Remove all breakpoints
*
* @param string $environment The required environment
* @return void
*/
public function removeBreakpoints(string $environment): void
{
$this->getOutput()->writeln(sprintf(
' %d breakpoints cleared.',
$this->getEnvironment($environment)->getAdapter()->resetAllBreakpoints()
));
}
/**
* Set the breakpoint for a specific version.
*
* @param string $environment The required environment
* @param int|null $version The version of the target migration
* @return void
*/
public function setBreakpoint(string $environment, ?int $version): void
{
$this->markBreakpoint($environment, $version, self::BREAKPOINT_SET);
}
/**
* Unset the breakpoint for a specific version.
*
* @param string $environment The required environment
* @param int|null $version The version of the target migration
* @return void
*/
public function unsetBreakpoint(string $environment, ?int $version): void
{
$this->markBreakpoint($environment, $version, self::BREAKPOINT_UNSET);
}
/**
* @param int $verbosityLevel Verbosity level for info messages
* @return $this
*/
public function setVerbosityLevel(int $verbosityLevel)
{
$this->verbosityLevel = $verbosityLevel;
return $this;
}
}