diff --git a/lib/Doctrine/Migrations/Tools/Console/Command/DiffCommand.php b/lib/Doctrine/Migrations/Tools/Console/Command/DiffCommand.php
index 3d9de7f465..c5f897b345 100644
--- a/lib/Doctrine/Migrations/Tools/Console/Command/DiffCommand.php
+++ b/lib/Doctrine/Migrations/Tools/Console/Command/DiffCommand.php
@@ -5,6 +5,8 @@
namespace Doctrine\Migrations\Tools\Console\Command;
use Doctrine\Migrations\Generator\Exception\NoChangesDetected;
+use Doctrine\Migrations\Metadata\AvailableMigrationsList;
+use Doctrine\Migrations\Metadata\ExecutedMigrationsSet;
use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage;
use OutOfBoundsException;
use Symfony\Component\Console\Input\InputInterface;
@@ -13,6 +15,7 @@
use function addslashes;
use function assert;
use function class_exists;
+use function count;
use function filter_var;
use function is_string;
use function key;
@@ -123,6 +126,18 @@ public function execute(
assert(is_string($namespace));
+ $statusCalculator = $this->getDependencyFactory()->getMigrationStatusCalculator();
+ $executedUnavailableMigrations = $statusCalculator->getExecutedUnavailableMigrations();
+ $newMigrations = $statusCalculator->getNewMigrations();
+
+ if ($this->checkNewMigrations($newMigrations, $input, $output) === false) {
+ return 3;
+ }
+
+ if ($this->checkExecutedUnavailableMigrations($executedUnavailableMigrations, $input, $output) === false) {
+ return 3;
+ }
+
$fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace);
$diffGenerator = $this->getDependencyFactory()->getDiffGenerator();
@@ -168,4 +183,60 @@ public function execute(
return 0;
}
+
+ private function checkNewMigrations(
+ AvailableMigrationsList $newMigrations,
+ InputInterface $input,
+ OutputInterface $output
+ ) : bool {
+ if (count($newMigrations) !== 0) {
+ $output->writeln(sprintf(
+ 'WARNING! You have %s available migrations to execute.',
+ count($newMigrations)
+ ));
+
+ $question = 'Are you sure you wish to continue? (y/n)';
+
+ if (! $this->canExecute($question, $input, $output)) {
+ $output->writeln('Migration cancelled!');
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function checkExecutedUnavailableMigrations(
+ ExecutedMigrationsSet $executedUnavailableMigrations,
+ InputInterface $input,
+ OutputInterface $output
+ ) : bool {
+ if (count($executedUnavailableMigrations) !== 0) {
+ $output->writeln(sprintf(
+ 'WARNING! You have %s previously executed migrations in the database that are not registered migrations.',
+ count($executedUnavailableMigrations)
+ ));
+
+ foreach ($executedUnavailableMigrations->getItems() as $executedUnavailableMigration) {
+ $output->writeln(sprintf(
+ ' >> %s (%s)',
+ $executedUnavailableMigration->getExecutedAt() !== null
+ ? $executedUnavailableMigration->getExecutedAt()->format('Y-m-d H:i:s')
+ : null,
+ $executedUnavailableMigration->getVersion()
+ ));
+ }
+
+ $question = 'Are you sure you wish to continue? (y/n)';
+
+ if (! $this->canExecute($question, $input, $output)) {
+ $output->writeln('Migration cancelled!');
+
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/tests/Doctrine/Migrations/Tests/Tools/Console/Command/DiffCommandTest.php b/tests/Doctrine/Migrations/Tests/Tools/Console/Command/DiffCommandTest.php
index 739fe6fb0d..a246a2f4d9 100644
--- a/tests/Doctrine/Migrations/Tests/Tools/Console/Command/DiffCommandTest.php
+++ b/tests/Doctrine/Migrations/Tests/Tools/Console/Command/DiffCommandTest.php
@@ -4,11 +4,18 @@
namespace Doctrine\Migrations\Tests\Tools\Console\Command;
+use Doctrine\Migrations\AbstractMigration;
use Doctrine\Migrations\Configuration\Configuration;
use Doctrine\Migrations\DependencyFactory;
use Doctrine\Migrations\Generator\ClassNameGenerator;
use Doctrine\Migrations\Generator\DiffGenerator;
+use Doctrine\Migrations\Metadata\AvailableMigration;
+use Doctrine\Migrations\Metadata\AvailableMigrationsList;
+use Doctrine\Migrations\Metadata\ExecutedMigration;
+use Doctrine\Migrations\Metadata\ExecutedMigrationsSet;
use Doctrine\Migrations\Tools\Console\Command\DiffCommand;
+use Doctrine\Migrations\Version\MigrationStatusCalculator;
+use Doctrine\Migrations\Version\Version;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Input\InputInterface;
@@ -20,6 +27,9 @@ final class DiffCommandTest extends TestCase
/** @var DiffGenerator|MockObject */
private $migrationDiffGenerator;
+ /** @var MigrationStatusCalculator|MockObject */
+ private $migrationStatusCalculator;
+
/** @var Configuration */
private $configuration;
@@ -91,10 +101,57 @@ public function testExecute() : void
$this->diffCommand->execute($input, $output);
}
+ public function testAvailableMigrationsCancel() : void
+ {
+ $input = $this->createMock(InputInterface::class);
+ $output = $this->createMock(OutputInterface::class);
+
+ $m1 = new AvailableMigration(new Version('A'), $this->createMock(AbstractMigration::class));
+
+ $this->migrationStatusCalculator
+ ->method('getNewMigrations')
+ ->willReturn(new AvailableMigrationsList([$m1]));
+
+ $this->diffCommand->expects(self::once())
+ ->method('canExecute')
+ ->with('Are you sure you wish to continue? (y/n)')
+ ->willReturn(false);
+
+ $this->migrationDiffGenerator->expects(self::never())->method('generate');
+
+ $statusCode = $this->diffCommand->execute($input, $output);
+
+ self::assertSame(3, $statusCode);
+ }
+
+ public function testExecutedUnavailableMigrationsCancel() : void
+ {
+ $input = $this->createMock(InputInterface::class);
+ $output = $this->createMock(OutputInterface::class);
+
+ $e1 = new ExecutedMigration(new Version('A'));
+
+ $this->migrationStatusCalculator
+ ->method('getExecutedUnavailableMigrations')
+ ->willReturn(new ExecutedMigrationsSet([$e1]));
+
+ $this->diffCommand->expects(self::once())
+ ->method('canExecute')
+ ->with('Are you sure you wish to continue? (y/n)')
+ ->willReturn(false);
+
+ $this->migrationDiffGenerator->expects(self::never())->method('generate');
+
+ $statusCode = $this->diffCommand->execute($input, $output);
+
+ self::assertSame(3, $statusCode);
+ }
+
protected function setUp() : void
{
- $this->migrationDiffGenerator = $this->createMock(DiffGenerator::class);
- $this->configuration = new Configuration();
+ $this->migrationDiffGenerator = $this->createMock(DiffGenerator::class);
+ $this->migrationStatusCalculator = $this->createMock(MigrationStatusCalculator::class);
+ $this->configuration = new Configuration();
$this->configuration->addMigrationsDirectory('FooNs', sys_get_temp_dir());
$this->dependencyFactory = $this->createMock(DependencyFactory::class);
@@ -117,6 +174,10 @@ protected function setUp() : void
->method('getDiffGenerator')
->willReturn($this->migrationDiffGenerator);
+ $this->dependencyFactory->expects(self::any())
+ ->method('getMigrationStatusCalculator')
+ ->willReturn($this->migrationStatusCalculator);
+
$this->diffCommand = $this->getMockBuilder(DiffCommand::class)
->setConstructorArgs([$this->dependencyFactory])
->onlyMethods(['procOpen'])