diff --git a/module/CLI/config/dependencies.config.php b/module/CLI/config/dependencies.config.php index b4bf15d2..a9bb47f2 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -32,6 +32,7 @@ return [ RedirectRule\RedirectRuleHandler::class => InvokableFactory::class, Util\ProcessRunner::class => ConfigAbstractFactory::class, + Util\PhpProcessRunner::class => ConfigAbstractFactory::class, ApiKey\RoleResolver::class => ConfigAbstractFactory::class, @@ -79,6 +80,7 @@ return [ ConfigAbstractFactory::class => [ Util\ProcessRunner::class => [SymfonyCli\Helper\ProcessHelper::class], + Util\PhpProcessRunner::class => [Util\ProcessRunner::class, PhpExecutableFinder::class], ApiKey\RoleResolver::class => [DomainService::class, UrlShortenerOptions::class], Command\ShortUrl\CreateShortUrlCommand::class => [ @@ -136,16 +138,11 @@ return [ Command\Db\CreateDatabaseCommand::class => [ LockFactory::class, - Util\ProcessRunner::class, - PhpExecutableFinder::class, + Util\PhpProcessRunner::class, 'em', NoDbNameConnectionFactory::SERVICE_NAME, ], - Command\Db\MigrateDatabaseCommand::class => [ - LockFactory::class, - Util\ProcessRunner::class, - PhpExecutableFinder::class, - ], + Command\Db\MigrateDatabaseCommand::class => [LockFactory::class, Util\PhpProcessRunner::class], ], ]; diff --git a/module/CLI/src/Command/Db/AbstractDatabaseCommand.php b/module/CLI/src/Command/Db/AbstractDatabaseCommand.php index a38abc72..b7bfbc9d 100644 --- a/module/CLI/src/Command/Db/AbstractDatabaseCommand.php +++ b/module/CLI/src/Command/Db/AbstractDatabaseCommand.php @@ -6,31 +6,17 @@ namespace Shlinkio\Shlink\CLI\Command\Db; use Shlinkio\Shlink\CLI\Command\Util\CommandUtils; use Shlinkio\Shlink\CLI\Command\Util\LockConfig; -use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Lock\LockFactory; -use Symfony\Component\Process\PhpExecutableFinder; abstract class AbstractDatabaseCommand extends Command { - private string $phpBinary; - - public function __construct( - private readonly LockFactory $locker, - private readonly ProcessRunnerInterface $processRunner, - PhpExecutableFinder $phpFinder, - ) { - parent::__construct(); - $this->phpBinary = $phpFinder->find(false) ?: 'php'; - } - - protected function runPhpCommand(OutputInterface $output, array $command): void + public function __construct(private readonly LockFactory $locker) { - $command = [$this->phpBinary, ...$command, '--no-interaction']; - $this->processRunner->run($output, $command); + parent::__construct(); } final protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/module/CLI/src/Command/Db/CreateDatabaseCommand.php b/module/CLI/src/Command/Db/CreateDatabaseCommand.php index 6dc11dfc..876128ad 100644 --- a/module/CLI/src/Command/Db/CreateDatabaseCommand.php +++ b/module/CLI/src/Command/Db/CreateDatabaseCommand.php @@ -12,7 +12,6 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Lock\LockFactory; -use Symfony\Component\Process\PhpExecutableFinder; use Throwable; use function array_map; @@ -24,18 +23,17 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand private readonly Connection $regularConn; public const string NAME = 'db:create'; - public const string DOCTRINE_SCRIPT = 'bin/doctrine'; - public const string DOCTRINE_CREATE_SCHEMA_COMMAND = 'orm:schema-tool:create'; + public const string SCRIPT = 'bin/doctrine'; + public const string COMMAND = 'orm:schema-tool:create'; public function __construct( LockFactory $locker, - ProcessRunnerInterface $processRunner, - PhpExecutableFinder $phpFinder, + private readonly ProcessRunnerInterface $processRunner, private readonly EntityManagerInterface $em, private readonly Connection $noDbNameConn, ) { $this->regularConn = $this->em->getConnection(); - parent::__construct($locker, $processRunner, $phpFinder); + parent::__construct($locker); } protected function configure(): void @@ -59,7 +57,7 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand // Create database $io->writeln('Creating database tables...'); - $this->runPhpCommand($output, [self::DOCTRINE_SCRIPT, self::DOCTRINE_CREATE_SCHEMA_COMMAND]); + $this->processRunner->run($output, [self::SCRIPT, self::COMMAND, '--no-interaction']); $io->success('Database properly created!'); return self::SUCCESS; diff --git a/module/CLI/src/Command/Db/MigrateDatabaseCommand.php b/module/CLI/src/Command/Db/MigrateDatabaseCommand.php index 669b190b..f4a8c2ac 100644 --- a/module/CLI/src/Command/Db/MigrateDatabaseCommand.php +++ b/module/CLI/src/Command/Db/MigrateDatabaseCommand.php @@ -4,15 +4,24 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\Db; +use Shlinkio\Shlink\CLI\Util\ProcessRunnerInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Lock\LockFactory; class MigrateDatabaseCommand extends AbstractDatabaseCommand { public const string NAME = 'db:migrate'; - public const string DOCTRINE_MIGRATIONS_SCRIPT = 'vendor/doctrine/migrations/bin/doctrine-migrations.php'; - public const string DOCTRINE_MIGRATE_COMMAND = 'migrations:migrate'; + public const string SCRIPT = 'vendor/doctrine/migrations/bin/doctrine-migrations.php'; + public const string COMMAND = 'migrations:migrate'; + + public function __construct( + LockFactory $locker, + private readonly ProcessRunnerInterface $processRunner, + ) { + parent::__construct($locker); + } protected function configure(): void { @@ -27,7 +36,7 @@ class MigrateDatabaseCommand extends AbstractDatabaseCommand $io = new SymfonyStyle($input, $output); $io->writeln('Migrating database...'); - $this->runPhpCommand($output, [self::DOCTRINE_MIGRATIONS_SCRIPT, self::DOCTRINE_MIGRATE_COMMAND]); + $this->processRunner->run($output, [self::SCRIPT, self::COMMAND, '--no-interaction']); $io->success('Database properly migrated!'); return self::SUCCESS; diff --git a/module/CLI/src/Util/PhpProcessRunner.php b/module/CLI/src/Util/PhpProcessRunner.php new file mode 100644 index 00000000..47f8161c --- /dev/null +++ b/module/CLI/src/Util/PhpProcessRunner.php @@ -0,0 +1,26 @@ +phpBinary = $phpFinder->find(includeArgs: false) ?: 'php'; + } + + public function run(OutputInterface $output, array $cmd): void + { + $this->wrappedProcessRunner->run($output, [$this->phpBinary, ...$cmd]); + } +} diff --git a/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php b/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php index 6e680c7a..4ea9dc32 100644 --- a/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php +++ b/module/CLI/test/Command/Db/CreateDatabaseCommandTest.php @@ -25,7 +25,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\SharedLockInterface; -use Symfony\Component\Process\PhpExecutableFinder; class CreateDatabaseCommandTest extends TestCase { @@ -44,9 +43,6 @@ class CreateDatabaseCommandTest extends TestCase $lock->method('acquire')->willReturn(true); $locker->method('createLock')->willReturn($lock); - $phpExecutableFinder = $this->createStub(PhpExecutableFinder::class); - $phpExecutableFinder->method('find')->willReturn('/usr/local/bin/php'); - $this->processHelper = $this->createMock(ProcessRunnerInterface::class); $this->schemaManager = $this->createMock(AbstractSchemaManager::class); @@ -63,7 +59,7 @@ class CreateDatabaseCommandTest extends TestCase $noDbNameConn = $this->createStub(Connection::class); $noDbNameConn->method('createSchemaManager')->willReturn($this->schemaManager); - $command = new CreateDatabaseCommand($locker, $this->processHelper, $phpExecutableFinder, $em, $noDbNameConn); + $command = new CreateDatabaseCommand($locker, $this->processHelper, $em, $noDbNameConn); $this->commandTester = CliTestUtils::testerForCommand($command); } @@ -112,9 +108,8 @@ class CreateDatabaseCommandTest extends TestCase $this->schemaManager->expects($this->never())->method('createDatabase'); $this->schemaManager->expects($this->once())->method('listTableNames')->willReturn($tables); $this->processHelper->expects($this->once())->method('run')->with($this->isInstanceOf(OutputInterface::class), [ - '/usr/local/bin/php', - CreateDatabaseCommand::DOCTRINE_SCRIPT, - CreateDatabaseCommand::DOCTRINE_CREATE_SCHEMA_COMMAND, + CreateDatabaseCommand::SCRIPT, + CreateDatabaseCommand::COMMAND, '--no-interaction', ]); diff --git a/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php b/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php index f3312803..b2987c6e 100644 --- a/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php +++ b/module/CLI/test/Command/Db/MigrateDatabaseCommandTest.php @@ -14,7 +14,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\SharedLockInterface; -use Symfony\Component\Process\PhpExecutableFinder; class MigrateDatabaseCommandTest extends TestCase { @@ -28,12 +27,9 @@ class MigrateDatabaseCommandTest extends TestCase $lock->method('acquire')->willReturn(true); $locker->method('createLock')->willReturn($lock); - $phpExecutableFinder = $this->createStub(PhpExecutableFinder::class); - $phpExecutableFinder->method('find')->willReturn('/usr/local/bin/php'); - $this->processHelper = $this->createMock(ProcessRunnerInterface::class); - $command = new MigrateDatabaseCommand($locker, $this->processHelper, $phpExecutableFinder); + $command = new MigrateDatabaseCommand($locker, $this->processHelper); $this->commandTester = CliTestUtils::testerForCommand($command); } @@ -41,9 +37,8 @@ class MigrateDatabaseCommandTest extends TestCase public function migrationsCommandIsRunWithProperVerbosity(): void { $this->processHelper->expects($this->once())->method('run')->with($this->isInstanceOf(OutputInterface::class), [ - '/usr/local/bin/php', - MigrateDatabaseCommand::DOCTRINE_MIGRATIONS_SCRIPT, - MigrateDatabaseCommand::DOCTRINE_MIGRATE_COMMAND, + MigrateDatabaseCommand::SCRIPT, + MigrateDatabaseCommand::COMMAND, '--no-interaction', ]); diff --git a/module/CLI/test/Util/PhpProcessRunnerTest.php b/module/CLI/test/Util/PhpProcessRunnerTest.php new file mode 100644 index 00000000..fb3293c4 --- /dev/null +++ b/module/CLI/test/Util/PhpProcessRunnerTest.php @@ -0,0 +1,40 @@ +wrapped = $this->createMock(ProcessRunnerInterface::class); + $this->executableFinder = $this->createMock(PhpExecutableFinder::class); + } + + #[Test] + #[TestWith([false, 'php'])] + #[TestWith(['/usr/local/bin/php', '/usr/local/bin/php'])] + public function commandsArePrefixedWithPhp(string|false $resolvedExecutable, string $expectedExecutable): void + { + $output = $this->createStub(OutputInterface::class); + $command = ['foo', 'bar', 'baz']; + + $this->wrapped->expects($this->once())->method('run')->with($output, [$expectedExecutable, ...$command]); + $this->executableFinder->expects($this->once())->method('find')->with(false)->willReturn($resolvedExecutable); + + new PhpProcessRunner($this->wrapped, $this->executableFinder)->run($output, $command); + } +}