From baf02bd30262262a5e0dd68c958eca99c03623fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Sun, 4 Oct 2020 21:48:44 +0200 Subject: [PATCH 1/8] Another approach to resolve feature of #9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/ComponentInstaller.php | 37 ++++-- test/ComponentInstallerTest.php | 222 ++++++++++++++++++++++---------- 2 files changed, 180 insertions(+), 79 deletions(-) diff --git a/src/ComponentInstaller.php b/src/ComponentInstaller.php index 21127dd..58277fd 100644 --- a/src/ComponentInstaller.php +++ b/src/ComponentInstaller.php @@ -10,7 +10,8 @@ use ArrayObject; use Composer\Composer; -use Composer\DependencyResolver\GenericRule; +use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Pool; use Composer\EventDispatcher\EventSubscriberInterface; use Composer\Installer\PackageEvent; use Composer\IO\IOInterface; @@ -191,9 +192,9 @@ public function onPostPackageInstall(PackageEvent $event) return; } - /** @var GenericRule $genericRule */ - $genericRule = $event->getOperation()->getReason(); - $package = $event->getOperation()->getPackage(); + $operation = $event->getOperation(); + assert($operation instanceof InstallOperation); + $package = $operation->getPackage(); $name = $package->getName(); $extra = $this->getExtraMetadata($package->getExtra()); @@ -211,13 +212,11 @@ public function onPostPackageInstall(PackageEvent $event) return; } - $requireDev = $this->isADevDependency($genericRule, $name); + $requireDev = $this->isADevDependency($event->getPool(), $package); $dependencies = $this->loadModuleClassesDependencies($package); $applicationModules = $this->findApplicationModules(); $this->marshalInstallableModules($extra, $options) - ->each(function ($module) use ($name) { - }) // Create injectors ->reduce(function ($injectors, $module) use ($options, $packageTypes, $name, $requireDev) { // Get extra from root package @@ -432,7 +431,7 @@ private function marshalPackageModules(array $extra, Collection $packageTypes, C * * @param string[] $extra * @param Collection $options - * @return string[] List of packages to install + * @return Collection List of packages to install */ private function marshalInstallableModules(array $extra, Collection $options) { @@ -859,17 +858,27 @@ private function getModuleDependencies($file) return []; } - private function isADevDependency(GenericRule $genericRule, string $name): bool + private function isADevDependency(Pool $pool, PackageInterface $package): bool { - if (array_key_exists($name, $this->composer->getPackage()->getDevRequires())) { + $packageName = $package->getName(); + if (array_key_exists($packageName, $this->composer->getPackage()->getDevRequires())) { return true; } - $dependentFor = is_string($genericRule->getReasonData()) - ? $genericRule->getReasonData() - : $genericRule->getReasonData()->getSource(); + $packages = $pool->whatProvides($packageName); + if (empty($packages)) { + return false; + } + + $requirements = $this->composer->getPackage()->getRequires(); + foreach ($packages as $parent) { + // Package is required by any package which is NOT a dev-requirement + if (isset($requirements[$parent->getName()])) { + return false; + } + } - return array_key_exists($dependentFor, $this->composer->getPackage()->getDevRequires()); + return true; } public function deactivate(Composer $composer, IOInterface $io) diff --git a/test/ComponentInstallerTest.php b/test/ComponentInstallerTest.php index c6fd836..57d0906 100644 --- a/test/ComponentInstallerTest.php +++ b/test/ComponentInstallerTest.php @@ -9,8 +9,8 @@ namespace LaminasTest\ComponentInstaller; use Composer\Composer; -use Composer\DependencyResolver\GenericRule; use Composer\DependencyResolver\Operation\InstallOperation; +use Composer\DependencyResolver\Pool; use Composer\Installer\InstallationManager; use Composer\Installer\PackageEvent; use Composer\IO\IOInterface; @@ -65,6 +65,11 @@ class ComponentInstallerTest extends TestCase */ private $installationManager; + /** + * @var Pool|\PHPUnit\Framework\MockObject\MockObject + */ + private $pool; + protected function setUp() : void { $this->projectRoot = vfsStream::setup('project'); @@ -85,6 +90,8 @@ protected function setUp() : void $this->installationManager = $this->prophesize(InstallationManager::class); $this->composer->getInstallationManager()->willReturn($this->installationManager->reveal()); + + $this->pool = $this->createMock(Pool::class); } public static function assertPrompt($argument, $packageName = null) @@ -176,17 +183,25 @@ public function getModuleDependencies() $this->installationManager->getInstallPath(Argument::exact($package->reveal())) ->willReturn(vfsStream::url('project/' . $installPath)); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); - $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->pool + ->expects($this->atLeastOnce()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + + $this->rootPackage + ->getRequires() + ->willReturn([]); + $this->io->ask(Argument::that(function ($argument) { return ComponentInstallerTest::assertPrompt($argument, 'SomeComponent'); }), 1)->willReturn(1); @@ -528,16 +543,22 @@ public function getModuleDependencies() $this->installationManager->getInstallPath(Argument::exact($package->reveal())) ->willReturn(vfsStream::url('project/' . $installPath)); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) use ($packageName) { return ComponentInstallerTest::assertPrompt($argument, $packageName); }), 1)->willReturn(1); @@ -627,16 +648,22 @@ public function testModuleBeforeApplicationModules(array $availableModules, arra ]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/module'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/module') + ->willReturn($package->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { return ComponentInstallerTest::assertPrompt($argument, 'SomeModule'); }), 1)->willReturn(1); @@ -679,11 +706,8 @@ public function testPostPackageInstallDoesNothingIfComposerExtraIsEmpty() $package->getName()->willReturn('some/component'); $package->getExtra()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); @@ -702,11 +726,8 @@ public function testOnPostPackageInstallReturnsEarlyIfApplicationConfigIsMissing 'module' => 'Some\\Component', ]]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); @@ -721,11 +742,8 @@ public function testPostPackageInstallDoesNothingIfLaminasExtraSectionDoesNotCon $package->getName()->willReturn('some/component'); $package->getExtra()->willReturn(['laminas' => []]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); @@ -747,15 +765,20 @@ public function testOnPostPackageInstallDoesNotPromptIfPackageIsAlreadyInConfigu ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); $this->io->ask(Argument::any())->shouldNotBeCalled(); @@ -775,16 +798,22 @@ public function testOnPostPackageInstallDoesNotPromptForWhitelistedPackages() ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->rootPackage->getExtra()->willReturn(['laminas' => [ 'component-whitelist' => ['some/component'], ]]); @@ -809,16 +838,22 @@ public function testOnPostPackageInstallPromptsForConfigOptions() ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -876,16 +911,22 @@ public function testOnPostPackageInstallPromptsForConfigOptionsWhenDefinedAsArra ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -978,15 +1019,13 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanPromptMultipleTi ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $event->getPool()->willReturn($this->pool); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1038,16 +1077,22 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanPromptMultipleTi ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('other/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -1103,16 +1148,15 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanReuseOptions() ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -1163,16 +1207,22 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanReuseOptions() ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->withConsecutive(['other/component'], ['some/component']) + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->write(Argument::that(function ($argument) { return strstr($argument, 'Installing Other\Component from package other/component'); }))->shouldBeCalled(); @@ -1213,11 +1263,8 @@ public function testOnPostPackageUninstallRemovesPackageFromConfiguration() ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); @@ -1258,16 +1305,22 @@ public function testOnPostPackageUninstallCanRemovePackageArraysFromConfiguratio ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io ->write(' Removing Some\Component from package some/component') ->shouldBeCalled(); @@ -1304,16 +1357,22 @@ public function testModuleIsAppended() ]]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/module') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -1375,16 +1434,22 @@ public function testAppendModuleAndPrependComponent() 'component' => 'Some\\Component', ]]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/package') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -1474,16 +1539,22 @@ public function testPrependComponentAndAppendModule() 'module' => 'Some\\Module', ]]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/package') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -1734,6 +1805,15 @@ public function testUninstallMessageWithDifferentInjectors($configContents, arra $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->io ->write(' Removing Some\Component from package some/component') ->shouldBeCalled(); @@ -1797,16 +1877,22 @@ public function testInstallWhitelistedDevModuleWithDifferentInjectors() ]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/component'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/component') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); $modules = $config['modules']; @@ -1852,16 +1938,22 @@ public function testInstallWhitelistedDevModuleWithUniqueInjector() ]); $package->getAutoload()->willReturn([]); - $genericRule = $this->prophesize(GenericRule::class); - $genericRule->getReasonData()->willReturn('some/module'); $operation = $this->prophesize(InstallOperation::class); $operation->getPackage()->willReturn($package->reveal()); - $operation->getReason()->willReturn($genericRule->reveal()); $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->pool + ->expects($this->any()) + ->method('whatProvides') + ->with('some/module') + ->willReturn($this->rootPackage->reveal()); + + $event->getPool()->willReturn($this->pool); + $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); $modules = $config['modules']; From 07bd7c9fbf81e7d700f9d2ec0c5056dfe848e009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Sun, 4 Oct 2020 22:46:15 +0200 Subject: [PATCH 2/8] Forward compatibility for composer v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/ComponentInstaller.php | 157 ++++++++++++------ src/PackageProvider/ComposerV1.php | 25 +++ src/PackageProvider/ComposerV2.php | 25 +++ .../PackageProviderDetectionFactory.php | 61 +++++++ .../PackageProviderDetectionInterface.php | 14 ++ test/ComponentInstallerTest.php | 121 ++++++-------- 6 files changed, 277 insertions(+), 126 deletions(-) create mode 100644 src/PackageProvider/ComposerV1.php create mode 100644 src/PackageProvider/ComposerV2.php create mode 100644 src/PackageProvider/PackageProviderDetectionFactory.php create mode 100644 src/PackageProvider/PackageProviderDetectionInterface.php diff --git a/src/ComponentInstaller.php b/src/ComponentInstaller.php index 58277fd..258b16e 100644 --- a/src/ComponentInstaller.php +++ b/src/ComponentInstaller.php @@ -11,7 +11,6 @@ use ArrayObject; use Composer\Composer; use Composer\DependencyResolver\Operation\InstallOperation; -use Composer\DependencyResolver\Pool; use Composer\EventDispatcher\EventSubscriberInterface; use Composer\Installer\PackageEvent; use Composer\IO\IOInterface; @@ -21,7 +20,8 @@ use Laminas\ComponentInstaller\Injector\AbstractInjector; use Laminas\ComponentInstaller\Injector\ConfigInjectorChain; use Laminas\ComponentInstaller\Injector\InjectorInterface; - +use Laminas\ComponentInstaller\PackageProvider\PackageProviderDetectionFactory; +use Laminas\ComponentInstaller\PackageProvider\PackageProviderDetectionInterface; use function array_filter; use function array_flip; use function array_key_exists; @@ -122,6 +122,11 @@ class ComponentInstaller implements */ private $projectRoot; + /** + * @var callable(Composer $composer, PackageEvent $packageEvent, string $packageName):PackageProviderDetectionFactory + */ + private $packageProviderFactory; + /** * Constructor * @@ -129,11 +134,19 @@ class ComponentInstaller implements * * @param string $projectRoot */ - public function __construct($projectRoot = '') + public function __construct($projectRoot = '', callable $packageProviderFactory = null) { if (is_string($projectRoot) && ! empty($projectRoot) && is_dir($projectRoot)) { $this->projectRoot = $projectRoot; } + $this->packageProviderFactory = $packageProviderFactory ?? + static function ( + Composer $composer, + PackageEvent $event, + string $packageName + ): PackageProviderDetectionInterface { + return PackageProviderDetectionFactory::create($composer)->detect($event, $packageName); + }; } /** @@ -142,14 +155,15 @@ public function __construct($projectRoot = '') * Sets internal pointers to Composer and IOInterface instances, and resets * cached injector map. * - * @param Composer $composer + * @param Composer $composer * @param IOInterface $io + * * @return void */ public function activate(Composer $composer, IOInterface $io) { - $this->composer = $composer; - $this->io = $io; + $this->composer = $composer; + $this->io = $io; $this->cachedInjectors = []; } @@ -161,7 +175,7 @@ public function activate(Composer $composer, IOInterface $io) public static function getSubscribedEvents() { return [ - 'post-package-install' => 'onPostPackageInstall', + 'post-package-install' => 'onPostPackageInstall', 'post-package-uninstall' => 'onPostPackageUninstall', ]; } @@ -183,6 +197,7 @@ public static function getSubscribedEvents() * writing their values into the `modules` list. * * @param PackageEvent $event + * * @return void */ public function onPostPackageInstall(PackageEvent $event) @@ -195,8 +210,8 @@ public function onPostPackageInstall(PackageEvent $event) $operation = $event->getOperation(); assert($operation instanceof InstallOperation); $package = $operation->getPackage(); - $name = $package->getName(); - $extra = $this->getExtraMetadata($package->getExtra()); + $name = $package->getName(); + $extra = $this->getExtraMetadata($package->getExtra()); if (empty($extra)) { // Package does not define anything of interest; do nothing. @@ -212,7 +227,8 @@ public function onPostPackageInstall(PackageEvent $event) return; } - $requireDev = $this->isADevDependency($event->getPool(), $package); + $packageProviderDetection = ($this->packageProviderFactory)($this->composer, $event, $name); + $requireDev = $this->isADevDependency($packageProviderDetection, $package); $dependencies = $this->loadModuleClassesDependencies($package); $applicationModules = $this->findApplicationModules(); @@ -231,6 +247,7 @@ public function onPostPackageInstall(PackageEvent $event) $whitelist, $requireDev ); + return $injectors; }, new Collection([])) // Inject modules into configuration @@ -249,13 +266,15 @@ public function onPostPackageInstall(PackageEvent $event) * via method `getModuleDependencies` of Module class. * * These dependencies are used later + * + * @param PackageInterface $package + * + * @return array * @see \Laminas\ComponentInstaller\Injector\AbstractInjector::injectAfterDependencies - * to add component in a correct order on the module list - after dependencies. + * to add component in a correct order on the module list - after dependencies. * * It works with PSR-0, PSR-4, 'classmap' and 'files' composer autoloading. * - * @param PackageInterface $package - * @return array */ private function loadModuleClassesDependencies(PackageInterface $package) { @@ -312,6 +331,7 @@ private function findApplicationModules() * removing their values from the `modules` list. * * @param PackageEvent $event + * * @return void */ public function onPostPackageUninstall(PackageEvent $event) @@ -333,8 +353,8 @@ public function onPostPackageUninstall(PackageEvent $event) } $package = $event->getOperation()->getPackage(); - $name = $package->getName(); - $extra = $this->getExtraMetadata($package->getExtra()); + $name = $package->getName(); + $extra = $this->getExtraMetadata($package->getExtra()); $this->removePackageFromConfig($name, $extra, $options); } @@ -342,6 +362,7 @@ public function onPostPackageUninstall(PackageEvent $event) * Retrieve the metadata from the "extra" section * * @param array $extra + * * @return array */ private function getExtraMetadata(array $extra) @@ -353,8 +374,7 @@ private function getExtraMetadata(array $extra) // supports legacy "extra.zf" configuration return isset($extra['zf']) && is_array($extra['zf']) ? $extra['zf'] - : [] - ; + : []; } /** @@ -362,12 +382,14 @@ private function getExtraMetadata(array $extra) * exposes in the extra configuration. * * @param string[] $extra + * * @return Collection Collection of Injector\InjectorInterface::TYPE_* constants. */ private function discoverPackageTypes(array $extra) { $packageTypes = array_flip($this->packageTypes); - $knownTypes = array_keys($packageTypes); + $knownTypes = array_keys($packageTypes); + return Collection::create($extra) ->filter(function ($packages, $type) use ($knownTypes) { return in_array($type, $knownTypes, true); @@ -378,6 +400,7 @@ private function discoverPackageTypes(array $extra) foreach ($packages as $package) { $discoveredTypes[$package] = $packageTypes[$type]; } + return $discoveredTypes; }, new Collection([])); } @@ -386,6 +409,7 @@ private function discoverPackageTypes(array $extra) * Marshal a collection of defined package types. * * @param array $extra extra.laminas value + * * @return Collection */ private function marshalPackageTypes(array $extra) @@ -400,9 +424,10 @@ private function marshalPackageTypes(array $extra) /** * Marshal a collection of package modules. * - * @param array $extra extra.laminas value + * @param array $extra extra.laminas value * @param Collection $packageTypes * @param Collection $options ConfigOption instances + * * @return Collection */ private function marshalPackageModules(array $extra, Collection $packageTypes, Collection $options) @@ -420,6 +445,7 @@ private function marshalPackageModules(array $extra, Collection $packageTypes, C if (! in_array($type, $supportedTypes, true)) { return $modules; } + return $modules->merge((array) $extra[$configKey]); }, new Collection([])) // Make sure the list is unique @@ -429,8 +455,9 @@ private function marshalPackageModules(array $extra, Collection $packageTypes, C /** * Prepare a list of modules to install/register with configuration. * - * @param string[] $extra + * @param string[] $extra * @param Collection $options + * * @return Collection List of packages to install */ private function marshalInstallableModules(array $extra, Collection $options) @@ -447,11 +474,12 @@ private function marshalInstallableModules(array $extra, Collection $options) /** * Prompt for the user to select a configuration location to update. * - * @param string $name + * @param string $name * @param Collection $options - * @param int $packageType - * @param string $packageName - * @param array $whitelist + * @param int $packageType + * @param string $packageName + * @param array $whitelist + * * @return Injector\InjectorInterface */ private function promptForConfigOption( @@ -483,6 +511,7 @@ private function promptForConfigOption( $index, $option->getPromptText() ); + return $ask; }, []); @@ -498,6 +527,7 @@ private function promptForConfigOption( if (is_numeric($answer) && isset($options[(int) $answer])) { $injector = $options[(int) $answer]->getInjector(); $this->promptToRememberOption($injector, $packageType); + return $injector; } @@ -508,10 +538,11 @@ private function promptForConfigOption( /** * Prompt the user to determine if the selection should be remembered for later packages. * - * @todo Will need to store selection in filesystem and remove when all packages are complete * @param Injector\InjectorInterface $injector - * @param int $packageType + * @param int $packageType * return void + * + * @todo Will need to store selection in filesystem and remove when all packages are complete */ private function promptToRememberOption(Injector\InjectorInterface $injector, $packageType) { @@ -523,6 +554,7 @@ private function promptToRememberOption(Injector\InjectorInterface $injector, $p switch ($answer) { case 'y': $this->cacheInjector($injector, $packageType); + return; case 'n': // intentionally fall-through @@ -535,10 +567,11 @@ private function promptToRememberOption(Injector\InjectorInterface $injector, $p /** * Inject a module into available configuration. * - * @param string $package Package name - * @param string $module Module to install in configuration + * @param string $package Package name + * @param string $module Module to install in configuration * @param Injector\InjectorInterface $injector Injector to use. - * @param int $packageType + * @param int $packageType + * * @return void */ private function injectModuleIntoConfig($package, $module, Injector\InjectorInterface $injector, $packageType) @@ -560,10 +593,11 @@ private function injectModuleIntoConfig($package, $module, Injector\InjectorInte /** * Remove a package from configuration. * - * @param string $package Package name - * @param array $metadata Metadata pulled from extra.laminas + * @param string $package Package name + * @param array $metadata Metadata pulled from extra.laminas * @param Collection $configOptions Discovered configuration options from - * which to remove package. + * which to remove package. + * * @return void */ private function removePackageFromConfig($package, array $metadata, Collection $configOptions) @@ -594,9 +628,10 @@ private function removePackageFromConfig($package, array $metadata, Collection $ /** * Remove an individual module defined in a package from configuration. * - * @param string $module Module to remove - * @param string $package Package in which module is defined + * @param string $module Module to remove + * @param string $package Package in which module is defined * @param Collection $injectors Injectors to use for removal + * * @return void */ private function removeModuleFromConfig($module, $package, Collection $injectors) @@ -615,6 +650,7 @@ private function removeModuleFromConfig($module, $package, Collection $injectors /** * @param InjectorInterface $injector + * * @return string * @todo remove after InjectorInterface has getConfigName defined */ @@ -631,6 +667,7 @@ private function getInjectorConfigFileName(InjectorInterface $injector) /** * @param ConfigInjectorChain $injector + * * @return string * @todo remove after InjectorInterface has getConfigName defined */ @@ -643,6 +680,7 @@ private function getInjectorChainConfigFileName(ConfigInjectorChain $injector) /** * @param AbstractInjector $injector + * * @return string * @todo remove after InjectorInterface has getConfigName defined */ @@ -655,6 +693,7 @@ private function getAbstractInjectorConfigFileName(AbstractInjector $injector) * Is a given module name valid? * * @param string $module + * * @return bool */ private function moduleIsValid($module) @@ -666,7 +705,8 @@ private function moduleIsValid($module) * Is a given metadata value (extra.laminas.*) valid? * * @param string $key Key to examine in metadata - * @param array $metadata + * @param array $metadata + * * @return bool */ private function metadataForKeyIsValid($key, array $metadata) @@ -688,6 +728,7 @@ private function metadataForKeyIsValid($key, array $metadata) if (false === $valid) { return $valid; } + return $this->moduleIsValid($value); }, null); } @@ -696,6 +737,7 @@ private function metadataForKeyIsValid($key, array $metadata) * Attempt to retrieve a cached injector for the current package type. * * @param int $packageType + * * @return null|Injector\InjectorInterface */ private function getCachedInjector($packageType) @@ -711,7 +753,8 @@ private function getCachedInjector($packageType) * Cache an injector for later use. * * @param Injector\InjectorInterface $injector - * @param int $packageType + * @param int $packageType + * * @return void */ private function cacheInjector(Injector\InjectorInterface $injector, $packageType) @@ -722,9 +765,10 @@ private function cacheInjector(Injector\InjectorInterface $injector, $packageTyp /** * Iterate through each autoloader type to find dependencies. * - * @param array $autoload List of autoloader types and associated autoloader definitions. + * @param array $autoload List of autoloader types and associated autoloader definitions. * @param ArrayObject $dependencies Module dependencies defined by the module. - * @param string $packagePath Path to the package on the filesystem. + * @param string $packagePath Path to the package on the filesystem. + * * @return void */ private function mapAutoloaders(array $autoload, ArrayObject $dependencies, $packagePath) @@ -737,10 +781,11 @@ private function mapAutoloaders(array $autoload, ArrayObject $dependencies, $pac /** * Iterate through a single autolaoder type to find dependencies. * - * @param array $map Map of namespace => path(s) pairs. - * @param string $type Type of autoloader being iterated. + * @param array $map Map of namespace => path(s) pairs. + * @param string $type Type of autoloader being iterated. * @param ArrayObject $dependencies Module dependencies defined by the module. - * @param string $packagePath Path to the package on the filesystem. + * @param string $packagePath Path to the package on the filesystem. + * * @return void */ private function mapType(array $map, $type, ArrayObject $dependencies, $packagePath) @@ -754,11 +799,12 @@ private function mapType(array $map, $type, ArrayObject $dependencies, $packageP /** * Iterate through the paths defined for a given namespace. * - * @param array $paths Paths defined for the given namespace. - * @param string $namespace PHP namespace to which the paths map. - * @param string $type Type of autoloader being iterated. + * @param array $paths Paths defined for the given namespace. + * @param string $namespace PHP namespace to which the paths map. + * @param string $type Type of autoloader being iterated. * @param ArrayObject $dependencies Module dependencies defined by the module. - * @param string $packagePath Path to the package on the filesystem. + * @param string $packagePath Path to the package on the filesystem. + * * @return void */ private function mapNamespacePaths(array $paths, $namespace, $type, ArrayObject $dependencies, $packagePath) @@ -771,11 +817,12 @@ private function mapNamespacePaths(array $paths, $namespace, $type, ArrayObject /** * Find module dependencies for a given namespace for a given path. * - * @param string $path Path to inspect. - * @param string $namespace PHP namespace to which the paths map. - * @param string $type Type of autoloader being iterated. + * @param string $path Path to inspect. + * @param string $namespace PHP namespace to which the paths map. + * @param string $type Type of autoloader being iterated. * @param ArrayObject $dependencies Module dependencies defined by the module. - * @param string $packagePath Path to the package on the filesystem. + * @param string $packagePath Path to the package on the filesystem. + * * @return void */ private function mapPath($path, $namespace, $type, ArrayObject $dependencies, $packagePath) @@ -833,6 +880,7 @@ private function mapPath($path, $namespace, $type, ArrayObject $dependencies, $p /** * @param string $file + * * @return array */ private function getModuleDependencies($file) @@ -858,14 +906,17 @@ private function getModuleDependencies($file) return []; } - private function isADevDependency(Pool $pool, PackageInterface $package): bool - { + private function isADevDependency( + PackageProviderDetectionInterface $packageProviderDetection, + PackageInterface $package + ): bool { $packageName = $package->getName(); - if (array_key_exists($packageName, $this->composer->getPackage()->getDevRequires())) { + $devRequirements = $this->composer->getPackage()->getDevRequires(); + if (isset($devRequirements[$packageName])) { return true; } - $packages = $pool->whatProvides($packageName); + $packages = $packageProviderDetection->whatProvides($packageName); if (empty($packages)) { return false; } diff --git a/src/PackageProvider/ComposerV1.php b/src/PackageProvider/ComposerV1.php new file mode 100644 index 0000000..1ee310f --- /dev/null +++ b/src/PackageProvider/ComposerV1.php @@ -0,0 +1,25 @@ +pool = $pool; + } + + public function whatProvides(string $packageName): array + { + return $this->pool->whatProvides($packageName); + } +} diff --git a/src/PackageProvider/ComposerV2.php b/src/PackageProvider/ComposerV2.php new file mode 100644 index 0000000..59183b4 --- /dev/null +++ b/src/PackageProvider/ComposerV2.php @@ -0,0 +1,25 @@ +installedRepository = $installedRepository; + } + + public function whatProvides(string $packageName): array + { + return $this->installedRepository->findPackagesWithReplacersAndProviders($packageName); + } +} diff --git a/src/PackageProvider/PackageProviderDetectionFactory.php b/src/PackageProvider/PackageProviderDetectionFactory.php new file mode 100644 index 0000000..f8baaae --- /dev/null +++ b/src/PackageProvider/PackageProviderDetectionFactory.php @@ -0,0 +1,61 @@ +composer = $composer; + } + + public static function create(Composer $composer): self + { + return new self($composer); + } + + public static function isComposerV1(): bool + { + return version_compare(PluginInterface::PLUGIN_API_VERSION, '2.0.0', '<') === true; + } + + public function detect(PackageEvent $event, string $packageName): PackageProviderDetectionInterface + { + if (self::isComposerV1()) { + return new ComposerV1($event->getPool()); + } + + $platformOverrides = $this->composer->getConfig()->get('platform') ?? []; + + $installedRepo = new InstalledRepository([ + new RootPackageRepository($this->composer->getPackage()), + $this->composer->getRepositoryManager()->getLocalRepository(), + new PlatformRepository([], $platformOverrides), + ]); + + $defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos()); + if ($match = $defaultRepos->findPackage($packageName, '')) { + $installedRepo->addRepository(new InstalledArrayRepository([clone $match])); + } + + return new ComposerV2($installedRepo); + } +} diff --git a/src/PackageProvider/PackageProviderDetectionInterface.php b/src/PackageProvider/PackageProviderDetectionInterface.php new file mode 100644 index 0000000..67c9f9a --- /dev/null +++ b/src/PackageProvider/PackageProviderDetectionInterface.php @@ -0,0 +1,14 @@ +projectRoot = vfsStream::setup('project'); - $this->installer = new ComponentInstaller(vfsStream::url('project')); + $this->packageProviderDetection = $this->createMock(PackageProviderDetectionInterface::class); + $this->installer = new ComponentInstaller( + vfsStream::url('project'), + function (Composer $composer, PackageEvent $event, string $packageName): PackageProviderDetectionInterface { + return $this->packageProviderDetection; + } + ); $this->composer = $this->prophesize(Composer::class); $this->rootPackage = $this->prophesize(RootPackageInterface::class); @@ -90,8 +98,6 @@ protected function setUp() : void $this->installationManager = $this->prophesize(InstallationManager::class); $this->composer->getInstallationManager()->willReturn($this->installationManager->reveal()); - - $this->pool = $this->createMock(Pool::class); } public static function assertPrompt($argument, $packageName = null) @@ -190,13 +196,11 @@ public function getModuleDependencies() $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); - $this->pool + $this->packageProviderDetection ->expects($this->atLeastOnce()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->rootPackage ->getRequires() @@ -551,13 +555,11 @@ public function getModuleDependencies() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) use ($packageName) { return ComponentInstallerTest::assertPrompt($argument, $packageName); @@ -656,13 +658,11 @@ public function testModuleBeforeApplicationModules(array $availableModules, arra $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/module') - ->willReturn($package->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([$package->reveal()]); $this->io->ask(Argument::that(function ($argument) { return ComponentInstallerTest::assertPrompt($argument, 'SomeModule'); @@ -772,13 +772,11 @@ public function testOnPostPackageInstallDoesNotPromptIfPackageIsAlreadyInConfigu $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::any())->shouldNotBeCalled(); @@ -806,13 +804,11 @@ public function testOnPostPackageInstallDoesNotPromptForWhitelistedPackages() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->rootPackage->getExtra()->willReturn(['laminas' => [ 'component-whitelist' => ['some/component'], @@ -846,13 +842,11 @@ public function testOnPostPackageInstallPromptsForConfigOptions() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -919,13 +913,11 @@ public function testOnPostPackageInstallPromptsForConfigOptionsWhenDefinedAsArra $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1025,7 +1017,6 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanPromptMultipleTi $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); - $event->getPool()->willReturn($this->pool); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1085,13 +1076,11 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanPromptMultipleTi $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('other/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1155,8 +1144,6 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanReuseOptions() $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); - $event->getPool()->willReturn($this->pool); - $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { return false; @@ -1215,13 +1202,11 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanReuseOptions() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->withConsecutive(['other/component'], ['some/component']) - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->write(Argument::that(function ($argument) { return strstr($argument, 'Installing Other\Component from package other/component'); @@ -1313,13 +1298,11 @@ public function testOnPostPackageUninstallCanRemovePackageArraysFromConfiguratio $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io ->write(' Removing Some\Component from package some/component') @@ -1365,13 +1348,11 @@ public function testModuleIsAppended() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/module') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1442,13 +1423,11 @@ public function testAppendModuleAndPrependComponent() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/package') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1547,13 +1526,11 @@ public function testPrependComponentAndAppendModule() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/package') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1806,13 +1783,11 @@ public function testUninstallMessageWithDifferentInjectors($configContents, arra $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->io ->write(' Removing Some\Component from package some/component') @@ -1857,7 +1832,7 @@ public function testInstallWhitelistedDevModuleWithDifferentInjectors() $this->createConfigFile($configName, $configContents); } - $this->rootPackage->getDevRequires()->willReturn(['some/component' => null]); + $this->rootPackage->getDevRequires()->willReturn(['some/component' => '*']); $this->rootPackage->getExtra()->willReturn([ 'laminas' => [ "component-whitelist" => [ @@ -1885,13 +1860,11 @@ public function testInstallWhitelistedDevModuleWithDifferentInjectors() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/component') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); @@ -1946,13 +1919,11 @@ public function testInstallWhitelistedDevModuleWithUniqueInjector() $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->pool + $this->packageProviderDetection ->expects($this->any()) ->method('whatProvides') ->with('some/module') - ->willReturn($this->rootPackage->reveal()); - - $event->getPool()->willReturn($this->pool); + ->willReturn([]); $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); @@ -2005,4 +1976,8 @@ private function createConfigFile($name, $contents) ->at($this->projectRoot) ->setContent($contents); } + + private function prepareEventForPackageProviderDetection(ObjectProphecy $event): ObjectProphecy + { + } } From 21ae20b4cd6546d0527c3685c4890c63c3d868d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Sun, 4 Oct 2020 22:54:28 +0200 Subject: [PATCH 3/8] Add `final` keyword to `PackageProviderDetectionFactory` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- src/PackageProvider/PackageProviderDetectionFactory.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PackageProvider/PackageProviderDetectionFactory.php b/src/PackageProvider/PackageProviderDetectionFactory.php index f8baaae..b68a133 100644 --- a/src/PackageProvider/PackageProviderDetectionFactory.php +++ b/src/PackageProvider/PackageProviderDetectionFactory.php @@ -5,7 +5,6 @@ use Composer\Composer; use Composer\Installer\PackageEvent; -use Composer\Package\PackageInterface; use Composer\Plugin\PluginInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\InstalledArrayRepository; @@ -15,7 +14,7 @@ use Composer\Repository\RootPackageRepository; use function version_compare; -class PackageProviderDetectionFactory +final class PackageProviderDetectionFactory { /** * @var Composer From 1afc845d446f00fbd376e50ba5b9d0caef4ffb75 Mon Sep 17 00:00:00 2001 From: kpicaza <1093654+kpicaza@users.noreply.github.com> Date: Mon, 12 Oct 2020 12:40:04 +0200 Subject: [PATCH 4/8] fix issues in composer2 Signed-off-by: kpicaza <1093654+kpicaza@users.noreply.github.com> --- src/PackageProvider/PackageProviderDetectionFactory.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/PackageProvider/PackageProviderDetectionFactory.php b/src/PackageProvider/PackageProviderDetectionFactory.php index b68a133..400c313 100644 --- a/src/PackageProvider/PackageProviderDetectionFactory.php +++ b/src/PackageProvider/PackageProviderDetectionFactory.php @@ -5,6 +5,7 @@ use Composer\Composer; use Composer\Installer\PackageEvent; +use Composer\IO\NullIO; use Composer\Plugin\PluginInterface; use Composer\Repository\CompositeRepository; use Composer\Repository\InstalledArrayRepository; @@ -45,13 +46,14 @@ public function detect(PackageEvent $event, string $packageName): PackageProvide $platformOverrides = $this->composer->getConfig()->get('platform') ?? []; $installedRepo = new InstalledRepository([ - new RootPackageRepository($this->composer->getPackage()), $this->composer->getRepositoryManager()->getLocalRepository(), new PlatformRepository([], $platformOverrides), ]); - $defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos()); - if ($match = $defaultRepos->findPackage($packageName, '')) { + $defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos(new NullIO())); + if (($match = $defaultRepos->findPackage($packageName, '*')) + && false === $installedRepo->hasPackage($match) + ) { $installedRepo->addRepository(new InstalledArrayRepository([clone $match])); } From 336476d7a7c050c24445dda455368cfe7cbc3301 Mon Sep 17 00:00:00 2001 From: kpicaza <1093654+kpicaza@users.noreply.github.com> Date: Fri, 23 Oct 2020 21:31:40 +0200 Subject: [PATCH 5/8] Enable root package on composer2 Signed-off-by: kpicaza <1093654+kpicaza@users.noreply.github.com> --- src/ComponentInstaller.php | 26 +++++++++++++++--- src/PackageProvider/ComposerV1.php | 7 +++++ src/PackageProvider/ComposerV2.php | 7 +++++ .../PackageProviderDetectionFactory.php | 15 +++++++++-- .../PackageProviderDetectionInterface.php | 7 +++++ test/ComponentInstallerTest.php | 27 +++++++++++++++++-- 6 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/ComponentInstaller.php b/src/ComponentInstaller.php index 258b16e..b97ee3f 100644 --- a/src/ComponentInstaller.php +++ b/src/ComponentInstaller.php @@ -16,6 +16,7 @@ use Composer\IO\IOInterface; use Composer\Package\PackageInterface; use Composer\Plugin\PluginInterface; +use Composer\Repository\RootPackageRepository; use DirectoryIterator; use Laminas\ComponentInstaller\Injector\AbstractInjector; use Laminas\ComponentInstaller\Injector\ConfigInjectorChain; @@ -123,10 +124,20 @@ class ComponentInstaller implements private $projectRoot; /** - * @var callable(Composer $composer, PackageEvent $packageEvent, string $packageName):PackageProviderDetectionFactory + * @var callable( + * Composer $composer, + * PackageEvent $packageEvent, + * ?RootPackageRepository $rootRepository, + * string $packageName + * ): PackageProviderDetectionFactory */ private $packageProviderFactory; + /** + * @var ?RootPackageRepository + */ + private $rootRepository = null; + /** * Constructor * @@ -143,9 +154,10 @@ public function __construct($projectRoot = '', callable $packageProviderFactory static function ( Composer $composer, PackageEvent $event, + ?RootPackageRepository $rootRepository, string $packageName ): PackageProviderDetectionInterface { - return PackageProviderDetectionFactory::create($composer)->detect($event, $packageName); + return PackageProviderDetectionFactory::create($composer)->detect($event, $packageName, $rootRepository); }; } @@ -165,6 +177,9 @@ public function activate(Composer $composer, IOInterface $io) $this->composer = $composer; $this->io = $io; $this->cachedInjectors = []; + if (false === PackageProviderDetectionFactory::isComposerV1()) { + $this->rootRepository = new RootPackageRepository($this->composer->getPackage()); + } } /** @@ -227,7 +242,12 @@ public function onPostPackageInstall(PackageEvent $event) return; } - $packageProviderDetection = ($this->packageProviderFactory)($this->composer, $event, $name); + $packageProviderDetection = ($this->packageProviderFactory)( + $this->composer, + $event, + $this->rootRepository, + $name + ); $requireDev = $this->isADevDependency($packageProviderDetection, $package); $dependencies = $this->loadModuleClassesDependencies($package); $applicationModules = $this->findApplicationModules(); diff --git a/src/PackageProvider/ComposerV1.php b/src/PackageProvider/ComposerV1.php index 1ee310f..7b49856 100644 --- a/src/PackageProvider/ComposerV1.php +++ b/src/PackageProvider/ComposerV1.php @@ -1,4 +1,11 @@ getPool()); } @@ -46,6 +56,7 @@ public function detect(PackageEvent $event, string $packageName): PackageProvide $platformOverrides = $this->composer->getConfig()->get('platform') ?? []; $installedRepo = new InstalledRepository([ + $packageRepository, $this->composer->getRepositoryManager()->getLocalRepository(), new PlatformRepository([], $platformOverrides), ]); diff --git a/src/PackageProvider/PackageProviderDetectionInterface.php b/src/PackageProvider/PackageProviderDetectionInterface.php index 67c9f9a..34d6126 100644 --- a/src/PackageProvider/PackageProviderDetectionInterface.php +++ b/src/PackageProvider/PackageProviderDetectionInterface.php @@ -1,4 +1,11 @@ packageProviderDetection = $this->createMock(PackageProviderDetectionInterface::class); $this->installer = new ComponentInstaller( vfsStream::url('project'), - function (Composer $composer, PackageEvent $event, string $packageName): PackageProviderDetectionInterface { + function ( + Composer $composer, + PackageEvent $event, + ?RootPackageRepository $repository, + string $packageName + ): PackageProviderDetectionInterface { return $this->packageProviderDetection; } ); @@ -88,8 +99,20 @@ function (Composer $composer, PackageEvent $event, string $packageName): Package $this->io = $this->prophesize(IOInterface::class); $this->composer->getPackage()->willReturn($this->rootPackage->reveal()); - $this->rootPackage->getExtra()->willReturn([]); + $config = $this->prophesize(Config::class); + $this->rootPackage->getDevRequires()->willReturn([]); + $this->rootPackage->getExtra()->willReturn([]); + if (false === PackageProviderDetectionFactory::isComposerV1()) { + $this->composer->getConfig()->willReturn($config->reveal()); + $repositoryManager = $this->prophesize(RepositoryManager::class); + $localRepository = $this->prophesize(PlatformRepository::class); + $repositoryManager->getLocalRepository()->willReturn($localRepository->reveal()); + $this->composer->getRepositoryManager()->willReturn($repositoryManager->reveal()); + $this->rootPackage + ->setRepository(Argument::type(RootPackageRepository::class)) + ->willReturn([]); + } $this->installer->activate( $this->composer->reveal(), From 71dd7d5e125b90a2142e1db462f00a0a62de35f0 Mon Sep 17 00:00:00 2001 From: kpicaza <1093654+kpicaza@users.noreply.github.com> Date: Sat, 24 Oct 2020 13:50:09 +0200 Subject: [PATCH 6/8] use root repository on package provider Signed-off-by: kpicaza <1093654+kpicaza@users.noreply.github.com> --- .travis.yml | 9 + src/ComponentInstaller.php | 36 +--- .../PackageProviderDetectionFactory.php | 16 +- test/ComponentInstallerTest.php | 163 +++++++----------- 4 files changed, 81 insertions(+), 143 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4377483..db1d45e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,14 @@ matrix: - php: 7.4 env: - DEPS=latest + - php: 7.4 + env: + - DEPS=lowest + - COMPOSER_VERSION=2 + - php: 7.4 + env: + - DEPS=latest + - COMPOSER_VERSION=2 before_install: - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi @@ -45,6 +53,7 @@ before_install: install: - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi + - if [[ $COMPOSER_VERSION == '2' ]]; then travis_retry composer self-update --2 ; fi - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update $COMPOSER_ARGS --prefer-lowest --prefer-stable ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi diff --git a/src/ComponentInstaller.php b/src/ComponentInstaller.php index b97ee3f..7c348da 100644 --- a/src/ComponentInstaller.php +++ b/src/ComponentInstaller.php @@ -16,7 +16,6 @@ use Composer\IO\IOInterface; use Composer\Package\PackageInterface; use Composer\Plugin\PluginInterface; -use Composer\Repository\RootPackageRepository; use DirectoryIterator; use Laminas\ComponentInstaller\Injector\AbstractInjector; use Laminas\ComponentInstaller\Injector\ConfigInjectorChain; @@ -25,7 +24,6 @@ use Laminas\ComponentInstaller\PackageProvider\PackageProviderDetectionInterface; use function array_filter; use function array_flip; -use function array_key_exists; use function array_keys; use function array_map; use function array_unshift; @@ -124,20 +122,10 @@ class ComponentInstaller implements private $projectRoot; /** - * @var callable( - * Composer $composer, - * PackageEvent $packageEvent, - * ?RootPackageRepository $rootRepository, - * string $packageName - * ): PackageProviderDetectionFactory + * @var PackageProviderDetectionFactory */ private $packageProviderFactory; - /** - * @var ?RootPackageRepository - */ - private $rootRepository = null; - /** * Constructor * @@ -145,20 +133,11 @@ class ComponentInstaller implements * * @param string $projectRoot */ - public function __construct($projectRoot = '', callable $packageProviderFactory = null) + public function __construct($projectRoot = '') { if (is_string($projectRoot) && ! empty($projectRoot) && is_dir($projectRoot)) { $this->projectRoot = $projectRoot; } - $this->packageProviderFactory = $packageProviderFactory ?? - static function ( - Composer $composer, - PackageEvent $event, - ?RootPackageRepository $rootRepository, - string $packageName - ): PackageProviderDetectionInterface { - return PackageProviderDetectionFactory::create($composer)->detect($event, $packageName, $rootRepository); - }; } /** @@ -177,9 +156,7 @@ public function activate(Composer $composer, IOInterface $io) $this->composer = $composer; $this->io = $io; $this->cachedInjectors = []; - if (false === PackageProviderDetectionFactory::isComposerV1()) { - $this->rootRepository = new RootPackageRepository($this->composer->getPackage()); - } + $this->packageProviderFactory = PackageProviderDetectionFactory::create($composer); } /** @@ -242,12 +219,7 @@ public function onPostPackageInstall(PackageEvent $event) return; } - $packageProviderDetection = ($this->packageProviderFactory)( - $this->composer, - $event, - $this->rootRepository, - $name - ); + $packageProviderDetection = $this->packageProviderFactory->detect($event, $name); $requireDev = $this->isADevDependency($packageProviderDetection, $package); $dependencies = $this->loadModuleClassesDependencies($package); $applicationModules = $this->findApplicationModules(); diff --git a/src/PackageProvider/PackageProviderDetectionFactory.php b/src/PackageProvider/PackageProviderDetectionFactory.php index d25fce1..d643459 100644 --- a/src/PackageProvider/PackageProviderDetectionFactory.php +++ b/src/PackageProvider/PackageProviderDetectionFactory.php @@ -28,10 +28,17 @@ final class PackageProviderDetectionFactory * @var Composer */ private $composer; + /** + * @var null|RootPackageRepository + */ + private $packageRepository = null; public function __construct(Composer $composer) { $this->composer = $composer; + if (false === self::isComposerV1()) { + $this->packageRepository = new RootPackageRepository($composer->getPackage()); + } } public static function create(Composer $composer): self @@ -44,11 +51,8 @@ public static function isComposerV1(): bool return version_compare(PluginInterface::PLUGIN_API_VERSION, '2.0.0', '<') === true; } - public function detect( - PackageEvent $event, - string $packageName, - ?RootPackageRepository $packageRepository - ): PackageProviderDetectionInterface { + public function detect(PackageEvent $event, string $packageName): PackageProviderDetectionInterface + { if (self::isComposerV1()) { return new ComposerV1($event->getPool()); } @@ -56,7 +60,7 @@ public function detect( $platformOverrides = $this->composer->getConfig()->get('platform') ?? []; $installedRepo = new InstalledRepository([ - $packageRepository, + $this->packageRepository, $this->composer->getRepositoryManager()->getLocalRepository(), new PlatformRepository([], $platformOverrides), ]); diff --git a/test/ComponentInstallerTest.php b/test/ComponentInstallerTest.php index e75d631..df23695 100644 --- a/test/ComponentInstallerTest.php +++ b/test/ComponentInstallerTest.php @@ -18,15 +18,12 @@ use Composer\Package\PackageInterface; use Composer\Package\RootPackageInterface; use Composer\Repository\PlatformRepository; -use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryManager; use Composer\Repository\RootPackageRepository; use Laminas\ComponentInstaller\ComponentInstaller; use Laminas\ComponentInstaller\PackageProvider\PackageProviderDetectionFactory; -use Laminas\ComponentInstaller\PackageProvider\PackageProviderDetectionInterface; use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStreamDirectory; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; @@ -73,47 +70,33 @@ class ComponentInstallerTest extends TestCase */ private $installationManager; - /** - * @var PackageProviderDetectionInterface|MockObject - */ - private $packageProviderDetection; - protected function setUp() : void { $this->projectRoot = vfsStream::setup('project'); - $this->packageProviderDetection = $this->createMock(PackageProviderDetectionInterface::class); $this->installer = new ComponentInstaller( - vfsStream::url('project'), - function ( - Composer $composer, - PackageEvent $event, - ?RootPackageRepository $repository, - string $packageName - ): PackageProviderDetectionInterface { - return $this->packageProviderDetection; - } + vfsStream::url('project') ); $this->composer = $this->prophesize(Composer::class); $this->rootPackage = $this->prophesize(RootPackageInterface::class); $this->io = $this->prophesize(IOInterface::class); - $this->composer->getPackage()->willReturn($this->rootPackage->reveal()); - $config = $this->prophesize(Config::class); - $this->rootPackage->getDevRequires()->willReturn([]); $this->rootPackage->getExtra()->willReturn([]); if (false === PackageProviderDetectionFactory::isComposerV1()) { + $config = $this->prophesize(Config::class); $this->composer->getConfig()->willReturn($config->reveal()); $repositoryManager = $this->prophesize(RepositoryManager::class); $localRepository = $this->prophesize(PlatformRepository::class); + $localRepository->getPackages()->willReturn([]); $repositoryManager->getLocalRepository()->willReturn($localRepository->reveal()); $this->composer->getRepositoryManager()->willReturn($repositoryManager->reveal()); - $this->rootPackage - ->setRepository(Argument::type(RootPackageRepository::class)) - ->willReturn([]); + $this->rootPackage->getProvides()->willReturn([]); + $this->rootPackage->getReplaces()->willReturn([]); + $this->rootPackage->setRepository(Argument::type(RootPackageRepository::class))->willReturn([]); } + $this->composer->getPackage()->willReturn($this->rootPackage->reveal()); $this->installer->activate( $this->composer->reveal(), $this->io->reveal() @@ -218,12 +201,11 @@ public function getModuleDependencies() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); - $this->packageProviderDetection - ->expects($this->atLeastOnce()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); + $this->rootPackage + ->getName() + ->willReturn('some/component'); $this->rootPackage ->getRequires() @@ -576,13 +558,12 @@ public function getModuleDependencies() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); + $this->rootPackage + ->getName() + ->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) use ($packageName) { return ComponentInstallerTest::assertPrompt($argument, $packageName); @@ -679,13 +660,12 @@ public function testModuleBeforeApplicationModules(array $availableModules, arra $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/module'); + $this->prepareEventForPackageProviderDetection($event, 'some/module'); + $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/module') - ->willReturn([$package->reveal()]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) { return ComponentInstallerTest::assertPrompt($argument, 'SomeModule'); @@ -794,12 +774,9 @@ public function testOnPostPackageInstallDoesNotPromptIfPackageIsAlreadyInConfigu $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::any())->shouldNotBeCalled(); @@ -825,13 +802,10 @@ public function testOnPostPackageInstallDoesNotPromptForWhitelistedPackages() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->rootPackage->getExtra()->willReturn(['laminas' => [ 'component-whitelist' => ['some/component'], @@ -863,13 +837,10 @@ public function testOnPostPackageInstallPromptsForConfigOptions() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -934,13 +905,10 @@ public function testOnPostPackageInstallPromptsForConfigOptionsWhenDefinedAsArra $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1040,6 +1008,7 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanPromptMultipleTi $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1097,13 +1066,10 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanPromptMultipleTi $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'other/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('other/component') - ->willReturn([]); + $this->rootPackage->getName()->willReturn('other/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1166,6 +1132,7 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanReuseOptions() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1223,13 +1190,10 @@ public function testMultipleInvocationsOfOnPostPackageInstallCanReuseOptions() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'other/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->withConsecutive(['other/component'], ['some/component']) - ->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->write(Argument::that(function ($argument) { return strstr($argument, 'Installing Other\Component from package other/component'); @@ -1321,11 +1285,6 @@ public function testOnPostPackageUninstallCanRemovePackageArraysFromConfiguratio $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); $this->io ->write(' Removing Some\Component from package some/component') @@ -1369,13 +1328,11 @@ public function testModuleIsAppended() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/module'); + $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/module') - ->willReturn([]); + $this->rootPackage->getName()->willReturn('some/module'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1444,13 +1401,10 @@ public function testAppendModuleAndPrependComponent() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/package'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/package') - ->willReturn([]); + $this->rootPackage->getName()->willReturn('some/package'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1547,13 +1501,12 @@ public function testPrependComponentAndAppendModule() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/package'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/package') - ->willReturn([]); + $this->rootPackage + ->getName() + ->willReturn('some/package'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1806,11 +1759,6 @@ public function testUninstallMessageWithDifferentInjectors($configContents, arra $event->getOperation()->willReturn($operation->reveal()); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); $this->io ->write(' Removing Some\Component from package some/component') @@ -1881,13 +1829,9 @@ public function testInstallWhitelistedDevModuleWithDifferentInjectors() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/component') - ->willReturn([]); $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); @@ -1940,13 +1884,12 @@ public function testInstallWhitelistedDevModuleWithUniqueInjector() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->prepareEventForPackageProviderDetection($event, 'some/module'); + $this->rootPackage + ->getName() + ->willReturn('some/module'); $this->rootPackage->getRequires()->willReturn([]); - $this->packageProviderDetection - ->expects($this->any()) - ->method('whatProvides') - ->with('some/module') - ->willReturn([]); $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); @@ -2000,7 +1943,17 @@ private function createConfigFile($name, $contents) ->setContent($contents); } - private function prepareEventForPackageProviderDetection(ObjectProphecy $event): ObjectProphecy + /** + * @param ObjectProphecy|PackageEvent $event + * @param string $packageName + * @return ObjectProphecy + */ + private function prepareEventForPackageProviderDetection(ObjectProphecy $event, string $packageName): void { + if (method_exists(PackageEvent::class, 'getPool')) { + $pool = $this->prophesize(Pool::class); + $pool->whatProvides($packageName)->willReturn([]); + $event->getPool()->willReturn($pool->reveal()); + } } } From 5434f84663d0cb0eeeee2ed23e0926fe1085dbf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Sat, 24 Oct 2020 17:41:38 +0200 Subject: [PATCH 7/8] Ensure travis will use `composer` v1 and v2 when needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index db1d45e..429cdca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ env: global: - COMPOSER_ARGS="--no-interaction" - COVERAGE_DEPS="php-coveralls/php-coveralls" + - COMPOSER_VERSION=1 matrix: fast_finish: true @@ -53,7 +54,7 @@ before_install: install: - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi - - if [[ $COMPOSER_VERSION == '2' ]]; then travis_retry composer self-update --2 ; fi + - travis_retry composer self-update --$COMPOSER_VERSION - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update $COMPOSER_ARGS --prefer-lowest --prefer-stable ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi From 88b4479f6a0855278e87805b52220924dd13b270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Sat, 24 Oct 2020 20:09:49 +0200 Subject: [PATCH 8/8] Add `CHANGELOG` entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db80f24..7e30b5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ All notable changes to this project will be documented in this file, in reverse ### Fixed -- Nothing. +- [#16](https://github.com/laminas/laminas-component-installer/pull/16) Fixed issue with detection packages in composer v2 as dev dependencies. ## 2.3.0 - 2020-09-02