diff --git a/.travis.yml b/.travis.yml index 4377483..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 @@ -38,6 +39,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 +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 + - 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 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 diff --git a/src/ComponentInstaller.php b/src/ComponentInstaller.php index 21127dd..7c348da 100644 --- a/src/ComponentInstaller.php +++ b/src/ComponentInstaller.php @@ -10,7 +10,7 @@ use ArrayObject; use Composer\Composer; -use Composer\DependencyResolver\GenericRule; +use Composer\DependencyResolver\Operation\InstallOperation; use Composer\EventDispatcher\EventSubscriberInterface; use Composer\Installer\PackageEvent; use Composer\IO\IOInterface; @@ -20,10 +20,10 @@ 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; use function array_keys; use function array_map; use function array_unshift; @@ -121,6 +121,11 @@ class ComponentInstaller implements */ private $projectRoot; + /** + * @var PackageProviderDetectionFactory + */ + private $packageProviderFactory; + /** * Constructor * @@ -141,15 +146,17 @@ 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 = []; + $this->packageProviderFactory = PackageProviderDetectionFactory::create($composer); } /** @@ -160,7 +167,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', ]; } @@ -182,6 +189,7 @@ public static function getSubscribedEvents() * writing their values into the `modules` list. * * @param PackageEvent $event + * * @return void */ public function onPostPackageInstall(PackageEvent $event) @@ -191,11 +199,11 @@ public function onPostPackageInstall(PackageEvent $event) return; } - /** @var GenericRule $genericRule */ - $genericRule = $event->getOperation()->getReason(); - $package = $event->getOperation()->getPackage(); - $name = $package->getName(); - $extra = $this->getExtraMetadata($package->getExtra()); + $operation = $event->getOperation(); + assert($operation instanceof InstallOperation); + $package = $operation->getPackage(); + $name = $package->getName(); + $extra = $this->getExtraMetadata($package->getExtra()); if (empty($extra)) { // Package does not define anything of interest; do nothing. @@ -211,13 +219,12 @@ public function onPostPackageInstall(PackageEvent $event) return; } - $requireDev = $this->isADevDependency($genericRule, $name); + $packageProviderDetection = $this->packageProviderFactory->detect($event, $name); + $requireDev = $this->isADevDependency($packageProviderDetection, $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 @@ -232,6 +239,7 @@ public function onPostPackageInstall(PackageEvent $event) $whitelist, $requireDev ); + return $injectors; }, new Collection([])) // Inject modules into configuration @@ -250,13 +258,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) { @@ -313,6 +323,7 @@ private function findApplicationModules() * removing their values from the `modules` list. * * @param PackageEvent $event + * * @return void */ public function onPostPackageUninstall(PackageEvent $event) @@ -334,8 +345,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); } @@ -343,6 +354,7 @@ public function onPostPackageUninstall(PackageEvent $event) * Retrieve the metadata from the "extra" section * * @param array $extra + * * @return array */ private function getExtraMetadata(array $extra) @@ -354,8 +366,7 @@ private function getExtraMetadata(array $extra) // supports legacy "extra.zf" configuration return isset($extra['zf']) && is_array($extra['zf']) ? $extra['zf'] - : [] - ; + : []; } /** @@ -363,12 +374,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); @@ -379,6 +392,7 @@ private function discoverPackageTypes(array $extra) foreach ($packages as $package) { $discoveredTypes[$package] = $packageTypes[$type]; } + return $discoveredTypes; }, new Collection([])); } @@ -387,6 +401,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) @@ -401,9 +416,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) @@ -421,6 +437,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 @@ -430,9 +447,10 @@ 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 string[] List of packages to install + * + * @return Collection List of packages to install */ private function marshalInstallableModules(array $extra, Collection $options) { @@ -448,11 +466,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( @@ -484,6 +503,7 @@ private function promptForConfigOption( $index, $option->getPromptText() ); + return $ask; }, []); @@ -499,6 +519,7 @@ private function promptForConfigOption( if (is_numeric($answer) && isset($options[(int) $answer])) { $injector = $options[(int) $answer]->getInjector(); $this->promptToRememberOption($injector, $packageType); + return $injector; } @@ -509,10 +530,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) { @@ -524,6 +546,7 @@ private function promptToRememberOption(Injector\InjectorInterface $injector, $p switch ($answer) { case 'y': $this->cacheInjector($injector, $packageType); + return; case 'n': // intentionally fall-through @@ -536,10 +559,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) @@ -561,10 +585,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) @@ -595,9 +620,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) @@ -616,6 +642,7 @@ private function removeModuleFromConfig($module, $package, Collection $injectors /** * @param InjectorInterface $injector + * * @return string * @todo remove after InjectorInterface has getConfigName defined */ @@ -632,6 +659,7 @@ private function getInjectorConfigFileName(InjectorInterface $injector) /** * @param ConfigInjectorChain $injector + * * @return string * @todo remove after InjectorInterface has getConfigName defined */ @@ -644,6 +672,7 @@ private function getInjectorChainConfigFileName(ConfigInjectorChain $injector) /** * @param AbstractInjector $injector + * * @return string * @todo remove after InjectorInterface has getConfigName defined */ @@ -656,6 +685,7 @@ private function getAbstractInjectorConfigFileName(AbstractInjector $injector) * Is a given module name valid? * * @param string $module + * * @return bool */ private function moduleIsValid($module) @@ -667,7 +697,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) @@ -689,6 +720,7 @@ private function metadataForKeyIsValid($key, array $metadata) if (false === $valid) { return $valid; } + return $this->moduleIsValid($value); }, null); } @@ -697,6 +729,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) @@ -712,7 +745,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) @@ -723,9 +757,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) @@ -738,10 +773,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) @@ -755,11 +791,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) @@ -772,11 +809,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) @@ -834,6 +872,7 @@ private function mapPath($path, $namespace, $type, ArrayObject $dependencies, $p /** * @param string $file + * * @return array */ private function getModuleDependencies($file) @@ -859,17 +898,30 @@ private function getModuleDependencies($file) return []; } - private function isADevDependency(GenericRule $genericRule, string $name): bool - { - if (array_key_exists($name, $this->composer->getPackage()->getDevRequires())) { + private function isADevDependency( + PackageProviderDetectionInterface $packageProviderDetection, + PackageInterface $package + ): bool { + $packageName = $package->getName(); + $devRequirements = $this->composer->getPackage()->getDevRequires(); + if (isset($devRequirements[$packageName])) { return true; } - $dependentFor = is_string($genericRule->getReasonData()) - ? $genericRule->getReasonData() - : $genericRule->getReasonData()->getSource(); + $packages = $packageProviderDetection->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/src/PackageProvider/ComposerV1.php b/src/PackageProvider/ComposerV1.php new file mode 100644 index 0000000..7b49856 --- /dev/null +++ b/src/PackageProvider/ComposerV1.php @@ -0,0 +1,32 @@ +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..cbb2c66 --- /dev/null +++ b/src/PackageProvider/ComposerV2.php @@ -0,0 +1,32 @@ +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..d643459 --- /dev/null +++ b/src/PackageProvider/PackageProviderDetectionFactory.php @@ -0,0 +1,77 @@ +composer = $composer; + if (false === self::isComposerV1()) { + $this->packageRepository = new RootPackageRepository($composer->getPackage()); + } + } + + 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([ + $this->packageRepository, + $this->composer->getRepositoryManager()->getLocalRepository(), + new PlatformRepository([], $platformOverrides), + ]); + + $defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos(new NullIO())); + if (($match = $defaultRepos->findPackage($packageName, '*')) + && false === $installedRepo->hasPackage($match) + ) { + $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..34d6126 --- /dev/null +++ b/src/PackageProvider/PackageProviderDetectionInterface.php @@ -0,0 +1,21 @@ +projectRoot = vfsStream::setup('project'); - $this->installer = new ComponentInstaller(vfsStream::url('project')); + $this->installer = new ComponentInstaller( + 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()); - $this->rootPackage->getExtra()->willReturn([]); $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->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() @@ -176,16 +195,21 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage + ->getName() + ->willReturn('some/component'); + + $this->rootPackage + ->getRequires() + ->willReturn([]); $this->io->ask(Argument::that(function ($argument) { return ComponentInstallerTest::assertPrompt($argument, 'SomeComponent'); @@ -528,15 +552,18 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage + ->getName() + ->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) use ($packageName) { return ComponentInstallerTest::assertPrompt($argument, $packageName); @@ -627,15 +654,18 @@ 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->prepareEventForPackageProviderDetection($event, 'some/module'); + $this->prepareEventForPackageProviderDetection($event, 'some/module'); + + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) { return ComponentInstallerTest::assertPrompt($argument, 'SomeModule'); @@ -679,11 +709,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 +729,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 +745,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 +768,15 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::any())->shouldNotBeCalled(); @@ -775,15 +796,16 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->rootPackage->getExtra()->willReturn(['laminas' => [ 'component-whitelist' => ['some/component'], @@ -809,15 +831,16 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -876,15 +899,16 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -978,15 +1002,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()); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1038,15 +1060,16 @@ 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->prepareEventForPackageProviderDetection($event, 'other/component'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('other/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1103,15 +1126,13 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1163,15 +1184,16 @@ 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->prepareEventForPackageProviderDetection($event, 'other/component'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/component'); $this->io->write(Argument::that(function ($argument) { return strstr($argument, 'Installing Other\Component from package other/component'); @@ -1213,11 +1235,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 +1277,15 @@ 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->io ->write(' Removing Some\Component from package some/component') ->shouldBeCalled(); @@ -1304,15 +1322,17 @@ 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->prepareEventForPackageProviderDetection($event, 'some/module'); + + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/module'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1375,15 +1395,16 @@ 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->prepareEventForPackageProviderDetection($event, 'some/package'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage->getName()->willReturn('some/package'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1474,15 +1495,18 @@ 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->prepareEventForPackageProviderDetection($event, 'some/package'); + + $this->rootPackage->getRequires()->willReturn([]); + $this->rootPackage + ->getName() + ->willReturn('some/package'); $this->io->ask(Argument::that(function ($argument) { if (! is_string($argument)) { @@ -1734,6 +1758,8 @@ public function testUninstallMessageWithDifferentInjectors($configContents, arra $event->isDevMode()->willReturn(true); $event->getOperation()->willReturn($operation->reveal()); + $this->rootPackage->getRequires()->willReturn([]); + $this->io ->write(' Removing Some\Component from package some/component') ->shouldBeCalled(); @@ -1777,7 +1803,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" => [ @@ -1797,15 +1823,15 @@ 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->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage->getRequires()->willReturn([]); $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); @@ -1852,15 +1878,18 @@ 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->prepareEventForPackageProviderDetection($event, 'some/module'); + + $this->rootPackage + ->getName() + ->willReturn('some/module'); + $this->rootPackage->getRequires()->willReturn([]); $this->assertNull($this->installer->onPostPackageInstall($event->reveal())); $config = include vfsStream::url('project/config/modules.config.php'); @@ -1913,4 +1942,18 @@ private function createConfigFile($name, $contents) ->at($this->projectRoot) ->setContent($contents); } + + /** + * @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()); + } + } }