From d92ef81d78d33753670d50a7086ddd45478d6890 Mon Sep 17 00:00:00 2001 From: Henk Poley Date: Thu, 4 Jun 2026 14:14:09 +0200 Subject: [PATCH] Warn when target PHP misses project requirement --- .../InstallExtensionsForProjectCommand.php | 25 ++++++++++++-- ...InstallExtensionsForProjectCommandTest.php | 34 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/Command/InstallExtensionsForProjectCommand.php b/src/Command/InstallExtensionsForProjectCommand.php index 0e999619..78a40c9a 100644 --- a/src/Command/InstallExtensionsForProjectCommand.php +++ b/src/Command/InstallExtensionsForProjectCommand.php @@ -22,6 +22,7 @@ use Php\Pie\Installing\InstallForPhpProject\InstallSelectedPackage; use Php\Pie\Platform; use Php\Pie\Platform\InstalledPiePackages; +use Php\Pie\Platform\TargetPlatform; use Php\Pie\Util\Emoji; use Psr\Container\ContainerInterface; use Safe\Exceptions\DirException; @@ -103,6 +104,8 @@ public function execute(InputInterface $input, OutputInterface $output): int CommandHelper::applyNoCacheOptionIfSet($input, $this->io); $rootPackage = $this->composerFactoryForProject->rootPackage($this->io); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); + self::warnIfTargetPhpDoesNotSatisfyComposerRequirement($rootPackage->getRequires()['php'] ?? null, $targetPlatform, $this->io); if (ExtensionType::isValid($rootPackage->getType())) { try { @@ -122,7 +125,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $this, $cwd, $rootPackage, - PieJsonEditor::fromTargetPlatform(CommandHelper::determineTargetPlatformFromInputs($input, new NullIO())), + PieJsonEditor::fromTargetPlatform($targetPlatform), $input, $this->io, ); @@ -142,8 +145,6 @@ public function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); - $this->io->write(sprintf( 'Checking extensions for your project %s (path: %s)', $rootPackage->getPrettyName(), @@ -315,4 +316,22 @@ static function (array $match): string { return $anyErrorsHappened ? self::FAILURE : self::SUCCESS; } + + private static function warnIfTargetPhpDoesNotSatisfyComposerRequirement(Link|null $phpRequirement, TargetPlatform $targetPlatform, IOInterface $io): void + { + if ($phpRequirement === null) { + return; + } + + $targetPhpVersion = $targetPlatform->phpBinaryPath->version(); + if ($phpRequirement->getConstraint()->matches((new VersionParser())->parseConstraints($targetPhpVersion))) { + return; + } + + $io->writeError(sprintf( + 'Target PHP %s does not satisfy composer.json requirement php:%s.', + $targetPhpVersion, + $phpRequirement->getPrettyConstraint(), + )); + } } diff --git a/test/integration/Command/InstallExtensionsForProjectCommandTest.php b/test/integration/Command/InstallExtensionsForProjectCommandTest.php index bad81fb6..ce99af5b 100644 --- a/test/integration/Command/InstallExtensionsForProjectCommandTest.php +++ b/test/integration/Command/InstallExtensionsForProjectCommandTest.php @@ -201,6 +201,40 @@ public function testInstallingExtensionsForPhpProjectWithMultipleMatches(): void self::assertStringContainsString('Multiple packages were found for ext-foobar', $outputString); } + public function testWarnsWhenTargetPhpDoesNotSatisfyProjectPhpRequirement(): void + { + $rootPackage = new RootPackage('my/project', '1.2.3.0', '1.2.3'); + $rootPackage->setRequires([ + 'php' => new Link('my/project', 'php', new Constraint('>=', '999.0.0.0-dev'), Link::TYPE_REQUIRE, '^999.0'), + ]); + $this->composerFactoryForProject->method('rootPackage')->willReturn($rootPackage); + + $installedRepository = new InstalledArrayRepository([$rootPackage]); + + $repositoryManager = $this->createMock(RepositoryManager::class); + $repositoryManager->method('getLocalRepository')->willReturn($installedRepository); + + $composer = $this->createMock(Composer::class); + $composer->method('getPackage')->willReturn($rootPackage); + $composer->method('getRepositoryManager')->willReturn($repositoryManager); + + $this->composerFactoryForProject->method('composer')->willReturn($composer); + $this->installedPiePackages->method('allPiePackages')->willReturn(new PiePackageList([])); + + $this->commandTester->execute( + ['--allow-non-interactive-project-install' => true], + ['verbosity' => BufferedOutput::VERBOSITY_VERY_VERBOSE], + ); + + $outputString = $this->commandTester->getDisplay(); + + $this->commandTester->assertCommandIsSuccessful($outputString); + self::assertStringContainsString( + 'does not satisfy composer.json requirement php:^999.0.', + $outputString, + ); + } + public function testInstallingExtensionsForPieProject(): void { $rootPackage = new RootPackage('my/project', '1.2.3.0', '1.2.3');