From 6ef65e5319a31c6ad1ad4913af50dae7f2832356 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 10 Sep 2018 15:23:40 +0200 Subject: [PATCH 01/11] Add a new RepositorySet class and restrict pool usage to the solver Breaking change for the plugin interface so bumping the version of composer-plugin-api to 2.0.0 First step for a refactoring of the package metadata loading mechanism --- .../Command/BaseDependencyCommand.php | 9 +- src/Composer/Command/CreateProjectCommand.php | 7 +- src/Composer/Command/InitCommand.php | 19 ++-- src/Composer/Command/ShowCommand.php | 35 +++---- src/Composer/DependencyResolver/Pool.php | 1 + src/Composer/DependencyResolver/Solver.php | 23 +++-- .../EventDispatcher/EventDispatcher.php | 16 ++-- src/Composer/Installer.php | 90 ++++++++--------- src/Composer/Installer/InstallerEvent.php | 18 ++-- src/Composer/Installer/PackageEvent.php | 7 +- .../Package/Version/VersionSelector.php | 9 +- src/Composer/Plugin/PluginInterface.php | 2 +- src/Composer/Plugin/PluginManager.php | 29 +++--- src/Composer/Repository/RepositorySet.php | 59 ++++++++++++ .../Test/DependencyResolver/SolverTest.php | 19 ++-- .../EventDispatcher/EventDispatcherTest.php | 6 +- .../Test/Installer/InstallerEventTest.php | 6 +- .../Package/Version/VersionSelectorTest.php | 96 ++++++++++--------- .../Plugin/Fixtures/plugin-v1/composer.json | 2 +- .../Plugin/Fixtures/plugin-v2/composer.json | 2 +- .../Plugin/Fixtures/plugin-v3/composer.json | 2 +- .../Plugin/Fixtures/plugin-v4/composer.json | 2 +- .../Plugin/Fixtures/plugin-v8/composer.json | 2 +- .../Plugin/Fixtures/plugin-v9/composer.json | 2 +- 24 files changed, 265 insertions(+), 198 deletions(-) create mode 100644 src/Composer/Repository/RepositorySet.php diff --git a/src/Composer/Command/BaseDependencyCommand.php b/src/Composer/Command/BaseDependencyCommand.php index 4c8766ba3..ff7bae2fe 100644 --- a/src/Composer/Command/BaseDependencyCommand.php +++ b/src/Composer/Command/BaseDependencyCommand.php @@ -21,6 +21,7 @@ use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Repository\RepositorySet; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Composer\Package\Version\VersionParser; use Symfony\Component\Console\Helper\Table; @@ -71,15 +72,15 @@ class BaseDependencyCommand extends BaseCommand $commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); - // Prepare repositories and set up a pool + // Prepare repositories and set up a repo set $platformOverrides = $composer->getConfig()->get('platform') ?: array(); $repository = new CompositeRepository(array( new ArrayRepository(array($composer->getPackage())), $composer->getRepositoryManager()->getLocalRepository(), new PlatformRepository(array(), $platformOverrides), )); - $pool = new Pool(); - $pool->addRepository($repository); + $repositorySet = new RepositorySet(new Pool()); + $repositorySet->addRepository($repository); // Parse package name and constraint list($needle, $textConstraint) = array_pad( @@ -89,7 +90,7 @@ class BaseDependencyCommand extends BaseCommand ); // Find packages that are or provide the requested package first - $packages = $pool->whatProvides(strtolower($needle)); + $packages = $repositorySet->findPackages(strtolower($needle)); // TODO this does not search providers if (empty($packages)) { throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle)); } diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index cca5f1871..4c5a21794 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -28,6 +28,7 @@ use Composer\Repository\RepositoryFactory; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\InstalledFilesystemRepository; +use Composer\Repository\RepositorySet; use Composer\Script\ScriptEvents; use Composer\Util\Silencer; use Symfony\Component\Console\Input\InputArgument; @@ -290,8 +291,8 @@ EOT throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities))); } - $pool = new Pool($stability); - $pool->addRepository($sourceRepo); + $repositorySet = new RepositorySet(new Pool($stability)); + $repositorySet->addRepository($sourceRepo); $phpVersion = null; $prettyPhpVersion = null; @@ -305,7 +306,7 @@ EOT } // find the latest version if there are multiple - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability); if (!$package) { diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index be56b23fb..86c55b152 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -21,6 +21,7 @@ use Composer\Package\Version\VersionSelector; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; +use Composer\Repository\RepositorySet; use Composer\Util\ProcessExecutor; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -40,8 +41,8 @@ class InitCommand extends BaseCommand /** @var array */ private $gitConfig; - /** @var Pool[] */ - private $pools; + /** @var RepositorySet[] */ + private $repositorySets; /** * {@inheritdoc} @@ -637,16 +638,16 @@ EOT return false !== filter_var($email, FILTER_VALIDATE_EMAIL); } - private function getPool(InputInterface $input, $minimumStability = null) + private function getRepositorySet(InputInterface $input, $minimumStability = null) { $key = $minimumStability ?: 'default'; - if (!isset($this->pools[$key])) { - $this->pools[$key] = $pool = new Pool($minimumStability ?: $this->getMinimumStability($input)); - $pool->addRepository($this->getRepos()); + if (!isset($this->repositorySets[$key])) { + $this->repositorySets[$key] = $repositorySet = new RepositorySet(new Pool($minimumStability ?: $this->getMinimumStability($input))); + $repositorySet->addRepository($this->getRepos()); } - return $this->pools[$key]; + return $this->repositorySets[$key]; } private function getMinimumStability(InputInterface $input) @@ -681,8 +682,8 @@ EOT */ private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null) { - // find the latest version allowed in this pool - $versionSelector = new VersionSelector($this->getPool($input, $minimumStability)); + // find the latest version allowed in this repo set + $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability)); $package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability); // retry without phpVersion if platform requirements are ignored in case nothing was found diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index ccea6a960..edb7198c9 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -29,6 +29,7 @@ use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; use Composer\Repository\RepositoryInterface; +use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Semver; use Composer\Spdx\SpdxLicenses; @@ -52,8 +53,8 @@ class ShowCommand extends BaseCommand protected $versionParser; protected $colors; - /** @var Pool */ - private $pool; + /** @var RepositorySet */ + private $repositorySet; protected function configure() { @@ -523,19 +524,13 @@ EOT $constraint = is_string($version) ? $this->versionParser->parseConstraints($version) : $version; $policy = new DefaultPolicy(); - $pool = new Pool('dev'); - $pool->addRepository($repos); + $repositorySet = new RepositorySet(new Pool('dev')); + $repositorySet->addRepository($repos); $matchedPackage = null; $versions = array(); - $matches = $pool->whatProvides($name, $constraint); + $matches = $repositorySet->findPackages($name, $constraint); foreach ($matches as $index => $package) { - // skip providers/replacers - if ($package->getName() !== $name) { - unset($matches[$index]); - continue; - } - // select an exact match if it is in the installed repo and no specific version was required if (null === $version && $installedRepo->hasPackage($package)) { $matchedPackage = $package; @@ -546,8 +541,8 @@ EOT } // select preferred package according to policy rules - if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, array(), $matches)) { - $matchedPackage = $pool->literalToPackage($preferred[0]); + if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($repositorySet->getPoolTemp(), array(), $matches)) { // TODO get rid of the pool call + $matchedPackage = $repositorySet->getPoolTemp()->literalToPackage($preferred[0]); } return array($matchedPackage, $versions); @@ -961,9 +956,9 @@ EOT */ private function findLatestPackage(PackageInterface $package, Composer $composer, $phpVersion, $minorOnly = false) { - // find the latest version allowed in this pool + // find the latest version allowed in this repo set $name = $package->getName(); - $versionSelector = new VersionSelector($this->getPool($composer)); + $versionSelector = new VersionSelector($this->getRepositorySet($composer)); $stability = $composer->getPackage()->getMinimumStability(); $flags = $composer->getPackage()->getStabilityFlags(); if (isset($flags[$name])) { @@ -987,13 +982,13 @@ EOT return $versionSelector->findBestCandidate($name, $targetVersion, $phpVersion, $bestStability); } - private function getPool(Composer $composer) + private function getRepositorySet(Composer $composer) { - if (!$this->pool) { - $this->pool = new Pool($composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags()); - $this->pool->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories())); + if (!$this->repositorySet) { + $this->repositorySet = new RepositorySet(new Pool($composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags())); + $this->repositorySet->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories())); } - return $this->pool; + return $this->repositorySet; } } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 085aaa7bf..c63556974 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -15,6 +15,7 @@ namespace Composer\DependencyResolver; use Composer\Package\BasePackage; use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; +use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\Constraint; use Composer\Semver\Constraint\EmptyConstraint; diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 1ed35ad9c..f5226fca5 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -15,6 +15,7 @@ namespace Composer\DependencyResolver; use Composer\IO\IOInterface; use Composer\Repository\RepositoryInterface; use Composer\Repository\PlatformRepository; +use Composer\Repository\RepositorySet; /** * @author Nils Adermann @@ -26,8 +27,8 @@ class Solver /** @var PolicyInterface */ protected $policy; - /** @var Pool */ - protected $pool; + /** @var RepositorySet */ + protected $repositorySet = null; /** @var RepositoryInterface */ protected $installed; /** @var RuleSet */ @@ -36,6 +37,8 @@ class Solver protected $ruleSetGenerator; /** @var array */ protected $jobs; + /** @var Pool */ + protected $pool = null; /** @var int[] */ protected $updateMap = array(); @@ -62,17 +65,16 @@ class Solver /** * @param PolicyInterface $policy - * @param Pool $pool + * @param RepositorySet $repositorySet * @param RepositoryInterface $installed * @param IOInterface $io */ - public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed, IOInterface $io) + public function __construct(PolicyInterface $policy, RepositorySet $repositorySet, RepositoryInterface $installed, IOInterface $io) { $this->io = $io; $this->policy = $policy; - $this->pool = $pool; + $this->repositorySet = $repositorySet; $this->installed = $installed; - $this->ruleSetGenerator = new RuleSetGenerator($policy, $pool); } /** @@ -83,6 +85,11 @@ class Solver return count($this->rules); } + public function getPool() + { + return $this->pool; + } + // aka solver_makeruledecisions private function makeAssertionRuleDecisions() @@ -210,7 +217,11 @@ class Solver { $this->jobs = $request->getJobs(); + $this->pool = $this->repositorySet->createPool(); + $this->setupInstalledMap(); + + $this->ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool); $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap, $ignorePlatformReqs); $this->checkForRootRequireProblems($ignorePlatformReqs); $this->decisions = new Decisions($this->pool); diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 145944b07..f0fcdaef6 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -13,13 +13,13 @@ namespace Composer\EventDispatcher; use Composer\DependencyResolver\PolicyInterface; -use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\Installer\InstallerEvent; use Composer\IO\IOInterface; use Composer\Composer; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositorySet; use Composer\Script; use Composer\Installer\PackageEvent; use Composer\Installer\BinaryInstaller; @@ -102,7 +102,7 @@ class EventDispatcher * @param string $eventName The constant in PackageEvents * @param bool $devMode Whether or not we are in dev mode * @param PolicyInterface $policy The policy - * @param Pool $pool The pool + * @param RepositorySet $repositorySet The repository set * @param CompositeRepository $installedRepo The installed repository * @param Request $request The request * @param array $operations The list of operations @@ -111,9 +111,9 @@ class EventDispatcher * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchPackageEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation) + public function dispatchPackageEvent($eventName, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation) { - return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations, $operation)); + return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation)); } /** @@ -122,7 +122,7 @@ class EventDispatcher * @param string $eventName The constant in InstallerEvents * @param bool $devMode Whether or not we are in dev mode * @param PolicyInterface $policy The policy - * @param Pool $pool The pool + * @param RepositorySet $repositorySet The repository set * @param CompositeRepository $installedRepo The installed repository * @param Request $request The request * @param array $operations The list of operations @@ -130,9 +130,9 @@ class EventDispatcher * @return int return code of the executed script if any, for php scripts a false return * value is changed to 1, anything else to 0 */ - public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array()) + public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations = array()) { - return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations)); + return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations)); } /** @@ -340,7 +340,7 @@ class EventDispatcher $event->getIO(), $event->isDevMode(), $event->getPolicy(), - $event->getPool(), + $event->getRepositorySet(), $event->getInstalledRepo(), $event->getRequest(), $event->getOperations(), diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index a729710c0..73120ea4b 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -37,6 +37,7 @@ use Composer\Package\CompletePackage; use Composer\Package\Link; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; +use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\Constraint; use Composer\Package\Locker; use Composer\Package\PackageInterface; @@ -368,21 +369,21 @@ class Installer $this->io->writeError('Loading composer repositories with package information'); - // creating repository pool + // creating repository set $policy = $this->createPolicy(); - $pool = $this->createPool($this->update ? null : $lockedRepository); - $pool->addRepository($installedRepo, $aliases); + $repositorySet = $this->createRepositorySet($this->update ? null : $lockedRepository); + $repositorySet->addRepository($installedRepo, $aliases); if ($this->update) { $repositories = $this->repositoryManager->getRepositories(); foreach ($repositories as $repository) { - $pool->addRepository($repository, $aliases); + $repositorySet->addRepository($repository, $aliases); } } // Add the locked repository after the others in case we are doing a // partial update so missing packages can be found there still. // For installs from lock it's the only one added so it is first if ($lockedRepository) { - $pool->addRepository($lockedRepository, $aliases); + $repositorySet->addRepository($lockedRepository, $aliases); } // creating requirements request @@ -393,7 +394,7 @@ class Installer $removedUnstablePackages = array(); foreach ($localRepo->getPackages() as $package) { if ( - !$pool->isPackageAcceptable($package->getNames(), $package->getStability()) + !$repositorySet->isPackageAcceptable($package->getNames(), $package->getStability()) && $this->installationManager->isPackageInstalled($localRepo, $package) ) { $removedUnstablePackages[$package->getName()] = true; @@ -465,11 +466,11 @@ class Installer } // force dev packages to have the latest links if we update or install from a (potentially new) lock - $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links'); + $this->processDevPackages($localRepo, $repositorySet, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links'); // solve dependencies - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request); - $solver = new Solver($policy, $pool, $installedRepo, $this->io); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request); + $solver = new Solver($policy, $repositorySet, $installedRepo, $this->io); try { $operations = $solver->solve($request, $this->ignorePlatformReqs); } catch (SolverProblemsException $e) { @@ -483,11 +484,10 @@ class Installer } // force dev packages to be updated if we update or install from a (potentially new) lock - $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-updates', $operations); + $operations = $this->processDevPackages($localRepo, $repositorySet, $policy, $repositories, $installedRepo, $lockedRepository, 'force-updates', $operations); - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request, $operations); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations); - $this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies", true, IOInterface::VERBOSE); $this->io->writeError("Analyzed ".$solver->getRuleSetSize()." rules to resolve dependencies", true, IOInterface::VERBOSE); // execute operations @@ -581,7 +581,7 @@ class Installer $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($jobType); if (defined($event) && $this->runScripts) { - $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation); + $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation); } // output non-alias ops when not executing operations (i.e. dry run), output alias ops in debug verbosity @@ -599,11 +599,11 @@ class Installer if ($reason instanceof Rule) { switch ($reason->getReason()) { case Rule::RULE_JOB_INSTALL: - $this->io->writeError(' REASON: Required by the root package: '.$reason->getPrettyString($pool)); + $this->io->writeError(' REASON: Required by the root package: '.$reason->getPrettyString($solver->getPool())); $this->io->writeError(''); break; case Rule::RULE_PACKAGE_REQUIRES: - $this->io->writeError(' REASON: '.$reason->getPrettyString($pool)); + $this->io->writeError(' REASON: '.$reason->getPrettyString($solver->getPool())); $this->io->writeError(''); break; } @@ -612,7 +612,7 @@ class Installer $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($jobType); if (defined($event) && $this->runScripts) { - $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation); + $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation); } if ($this->executeOperations || $this->writeLock) { @@ -622,7 +622,7 @@ class Installer if ($this->executeOperations) { // force source/dist urls to be updated for all packages - $this->processPackageUrls($pool, $policy, $localRepo, $repositories); + $this->processPackageUrls($repositorySet, $policy, $localRepo, $repositories); $localRepo->write(); } @@ -685,9 +685,9 @@ class Installer unset($tempLocalRepo, $loader, $dumper); $policy = $this->createPolicy(); - $pool = $this->createPool(); + $repositorySet = $this->createRepositorySet(); $installedRepo = $this->createInstalledRepo($localRepo, $platformRepo); - $pool->addRepository($installedRepo, $aliases); + $repositorySet->addRepository($installedRepo, $aliases); // creating requirements request without dev requirements $request = $this->createRequest($this->package, $platformRepo); @@ -697,10 +697,10 @@ class Installer } // solve deps to see which get removed - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request); - $solver = new Solver($policy, $pool, $installedRepo, $this->io); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request); + $solver = new Solver($policy, $repositorySet, $installedRepo, $this->io); $ops = $solver->solve($request, $this->ignorePlatformReqs); - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops); + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request, $ops); $devPackages = array(); foreach ($ops as $op) { @@ -844,6 +844,12 @@ class Installer return $installedRepo; } + private function createRepositorySet($lockedRepository = null) + { + $pool = $this->createPool($lockedRepository); + return new RepositorySet($pool); + } + /** * @param RepositoryInterface|null $lockedRepository * @return Pool @@ -946,7 +952,7 @@ class Installer /** * @param WritableRepositoryInterface $localRepo - * @param Pool $pool + * @param RepositorySet $repositorySet * @param PolicyInterface $policy * @param array $repositories * @param RepositoryInterface $installedRepo @@ -955,7 +961,7 @@ class Installer * @param array|null $operations * @return array */ - private function processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $task, array $operations = null) + private function processDevPackages($localRepo, $repositorySet, $policy, $repositories, $installedRepo, $lockedRepository, $task, array $operations = null) { if ($task === 'force-updates' && null === $operations) { throw new \InvalidArgumentException('Missing operations argument'); @@ -1010,7 +1016,7 @@ class Installer } // find similar packages (name/version) in all repositories - $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion())); + $matches = $repositorySet->findPackages($package->getName(), new Constraint('=', $package->getVersion())); foreach ($matches as $index => $match) { // skip local packages if (!in_array($match->getRepository(), $repositories, true)) { @@ -1018,18 +1024,12 @@ class Installer continue; } - // skip providers/replacers - if ($match->getName() !== $package->getName()) { - unset($matches[$index]); - continue; - } - $matches[$index] = $match->getId(); } // select preferred package according to policy rules - if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) { - $newPackage = $pool->literalToPackage($matches[0]); + if ($matches && $matches = $policy->selectPreferredPackages($repositorySet->getPoolTemp(), array(), $matches)) { // TODO remove temp call + $newPackage = $repositorySet->getPoolTemp()->literalToPackage($matches[0]); if ($task === 'force-links' && $newPackage) { $package->setRequires($newPackage->getRequires()); @@ -1130,12 +1130,12 @@ class Installer } /** - * @param Pool $pool + * @param RepositorySet $repositorySet * @param PolicyInterface $policy * @param WritableRepositoryInterface $localRepo * @param array $repositories */ - private function processPackageUrls($pool, $policy, $localRepo, $repositories) + private function processPackageUrls($repositorySet, $policy, $localRepo, $repositories) { if (!$this->update) { return; @@ -1145,7 +1145,7 @@ class Installer foreach ($localRepo->getCanonicalPackages() as $package) { // find similar packages (name/version) in all repositories - $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion())); + $matches = $repositorySet->findPackages($package->getName(), new Constraint('=', $package->getVersion())); foreach ($matches as $index => $match) { // skip local packages if (!in_array($match->getRepository(), $repositories, true)) { @@ -1153,18 +1153,12 @@ class Installer continue; } - // skip providers/replacers - if ($match->getName() !== $package->getName()) { - unset($matches[$index]); - continue; - } - $matches[$index] = $match->getId(); } // select preferred package according to policy rules - if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) { - $newPackage = $pool->literalToPackage($matches[0]); + if ($matches && $matches = $policy->selectPreferredPackages($repositorySet->getPoolTemp(), array(), $matches)) { // TODO get rid of pool + $newPackage = $repositorySet->getPoolTemp()->literalToPackage($matches[0]); // update the dist and source URLs $sourceUrl = $package->getSourceUrl(); @@ -1325,8 +1319,8 @@ class Installer } } - $pool = new Pool('dev'); - $pool->addRepository($localOrLockRepo); + $repositorySet = new RepositorySet(new Pool('dev')); + $repositorySet->addRepository($localOrLockRepo); $seen = array(); @@ -1335,7 +1329,7 @@ class Installer foreach ($this->updateWhitelist as $packageName => $void) { $packageQueue = new \SplQueue; - $depPackages = $pool->whatProvides($packageName); + $depPackages = $repositorySet->findPackages($packageName); // TODO does this need replacers/providers? $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true); @@ -1374,7 +1368,7 @@ class Installer $requires = $package->getRequires(); foreach ($requires as $require) { - $requirePackages = $pool->whatProvides($require->getTarget()); + $requirePackages = $repositorySet->findPackages($require->getTarget()); // TODO does this need replacers/providers? foreach ($requirePackages as $requirePackage) { if (isset($this->updateWhitelist[$requirePackage->getName()])) { diff --git a/src/Composer/Installer/InstallerEvent.php b/src/Composer/Installer/InstallerEvent.php index 87153bd51..2d30940a9 100644 --- a/src/Composer/Installer/InstallerEvent.php +++ b/src/Composer/Installer/InstallerEvent.php @@ -15,11 +15,11 @@ namespace Composer\Installer; use Composer\Composer; use Composer\DependencyResolver\PolicyInterface; use Composer\DependencyResolver\Operation\OperationInterface; -use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\EventDispatcher\Event; use Composer\IO\IOInterface; use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositorySet; /** * An event for all installer. @@ -49,9 +49,9 @@ class InstallerEvent extends Event private $policy; /** - * @var Pool + * @var RepositorySet */ - private $pool; + private $repositorySet; /** * @var CompositeRepository @@ -76,12 +76,12 @@ class InstallerEvent extends Event * @param IOInterface $io * @param bool $devMode * @param PolicyInterface $policy - * @param Pool $pool + * @param RepositorySet $repositorySet * @param CompositeRepository $installedRepo * @param Request $request * @param OperationInterface[] $operations */ - public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array()) + public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations = array()) { parent::__construct($eventName); @@ -89,7 +89,7 @@ class InstallerEvent extends Event $this->io = $io; $this->devMode = $devMode; $this->policy = $policy; - $this->pool = $pool; + $this->repositorySet = $repositorySet; $this->installedRepo = $installedRepo; $this->request = $request; $this->operations = $operations; @@ -128,11 +128,11 @@ class InstallerEvent extends Event } /** - * @return Pool + * @return RepositorySet */ - public function getPool() + public function getRepositorySet() { - return $this->pool; + return $this->repositorySet; } /** diff --git a/src/Composer/Installer/PackageEvent.php b/src/Composer/Installer/PackageEvent.php index f5cf0ed6e..a563a91ba 100644 --- a/src/Composer/Installer/PackageEvent.php +++ b/src/Composer/Installer/PackageEvent.php @@ -16,7 +16,6 @@ use Composer\Composer; use Composer\IO\IOInterface; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\PolicyInterface; -use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\Repository\CompositeRepository; @@ -40,15 +39,15 @@ class PackageEvent extends InstallerEvent * @param IOInterface $io * @param bool $devMode * @param PolicyInterface $policy - * @param Pool $pool + * @param RepositorySet $repositorySet * @param CompositeRepository $installedRepo * @param Request $request * @param OperationInterface[] $operations * @param OperationInterface $operation */ - public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation) + public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation) { - parent::__construct($eventName, $composer, $io, $devMode, $policy, $pool, $installedRepo, $request, $operations); + parent::__construct($eventName, $composer, $io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations); $this->operation = $operation; } diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index 8e225d803..d99780ab1 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -17,6 +17,7 @@ use Composer\Package\BasePackage; use Composer\Package\PackageInterface; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; +use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\Constraint; /** @@ -27,13 +28,13 @@ use Composer\Semver\Constraint\Constraint; */ class VersionSelector { - private $pool; + private $repositorySet; private $parser; - public function __construct(Pool $pool) + public function __construct(RepositorySet $repositorySet) { - $this->pool = $pool; + $this->repositorySet = $repositorySet; } /** @@ -49,7 +50,7 @@ class VersionSelector public function findBestCandidate($packageName, $targetPackageVersion = null, $targetPhpVersion = null, $preferredStability = 'stable') { $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null; - $candidates = $this->pool->whatProvides(strtolower($packageName), $constraint, true); + $candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint); if ($targetPhpVersion) { $phpConstraint = new Constraint('==', $this->getParser()->normalize($targetPhpVersion)); diff --git a/src/Composer/Plugin/PluginInterface.php b/src/Composer/Plugin/PluginInterface.php index 6eaca4e90..5158b66f6 100644 --- a/src/Composer/Plugin/PluginInterface.php +++ b/src/Composer/Plugin/PluginInterface.php @@ -27,7 +27,7 @@ interface PluginInterface * * @var string */ - const PLUGIN_API_VERSION = '1.1.0'; + const PLUGIN_API_VERSION = '2.0.0'; /** * Apply plugin modifications to Composer diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index e8f4b58c3..c6bdefc5a 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -21,6 +21,7 @@ use Composer\Repository\RepositoryInterface; use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; use Composer\Package\Link; +use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\Constraint; use Composer\DependencyResolver\Pool; use Composer\Plugin\Capability\Capability; @@ -157,14 +158,14 @@ class PluginManager $localRepo = $this->composer->getRepositoryManager()->getLocalRepository(); $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; - $pool = new Pool('dev'); - $pool->addRepository($localRepo); + $repositorySet = new RepositorySet(new Pool('dev')); + $repositorySet->addRepository($localRepo); if ($globalRepo) { - $pool->addRepository($globalRepo); + $repositorySet->addRepository($globalRepo); } $autoloadPackages = array($package->getName() => $package); - $autoloadPackages = $this->collectDependencies($pool, $autoloadPackages, $package); + $autoloadPackages = $this->collectDependencies($repositorySet, $autoloadPackages, $package); $generator = $this->composer->getAutoloadGenerator(); $autoloads = array(); @@ -269,13 +270,13 @@ class PluginManager /** * Recursively generates a map of package names to packages for all deps * - * @param Pool $pool Package pool of installed packages - * @param array $collected Current state of the map for recursion - * @param PackageInterface $package The package to analyze + * @param RepositorySet $repositorySet Repository set of installed packages + * @param array $collected Current state of the map for recursion + * @param PackageInterface $package The package to analyze * * @return array Map of package names to packages */ - private function collectDependencies(Pool $pool, array $collected, PackageInterface $package) + private function collectDependencies(RepositorySet $repositorySet, array $collected, PackageInterface $package) { $requires = array_merge( $package->getRequires(), @@ -283,10 +284,10 @@ class PluginManager ); foreach ($requires as $requireLink) { - $requiredPackage = $this->lookupInstalledPackage($pool, $requireLink); + $requiredPackage = $this->lookupInstalledPackage($repositorySet, $requireLink); if ($requiredPackage && !isset($collected[$requiredPackage->getName()])) { $collected[$requiredPackage->getName()] = $requiredPackage; - $collected = $this->collectDependencies($pool, $collected, $requiredPackage); + $collected = $this->collectDependencies($repositorySet, $collected, $requiredPackage); } } @@ -294,18 +295,18 @@ class PluginManager } /** - * Resolves a package link to a package in the installed pool + * Resolves a package link to a package in the installed repo set * * Since dependencies are already installed this should always find one. * - * @param Pool $pool Pool of installed packages only + * @param RepositorySet $repositorySet Repository set of installed packages only * @param Link $link Package link to look up * * @return PackageInterface|null The found package */ - private function lookupInstalledPackage(Pool $pool, Link $link) + private function lookupInstalledPackage(RepositorySet $repositorySet, Link $link) { - $packages = $pool->whatProvides($link->getTarget(), $link->getConstraint()); + $packages = $repositorySet->findPackages($link->getTarget(), $link->getConstraint()); // TODO this no longer returns providers return !empty($packages) ? $packages[0] : null; } diff --git a/src/Composer/Repository/RepositorySet.php b/src/Composer/Repository/RepositorySet.php new file mode 100644 index 000000000..e6b6db26b --- /dev/null +++ b/src/Composer/Repository/RepositorySet.php @@ -0,0 +1,59 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\DependencyResolver\Pool; +use Composer\Package\BasePackage; +use Composer\Package\Version\VersionParser; +use Composer\Repository\CompositeRepository; +use Composer\Repository\PlatformRepository; +use Composer\Semver\Constraint\ConstraintInterface; + +/** + * @author Nils Adermann + */ +class RepositorySet +{ + private $pool; + + public function __construct(Pool $pool) + { + $this->pool = $pool; + } + + public function addRepository(RepositoryInterface $repo, $rootAliases = array()) + { + return $this->pool->addRepository($repo, $rootAliases); + } + + public function isPackageAcceptable($name, $stability) + { + return $this->pool->isPackageAcceptable($name, $stability); + } + + public function findPackages($name, ConstraintInterface $constraint = null) + { + return $this->pool->whatProvides($name, $constraint, true); + } + + public function createPool() + { + return $this->pool; + } + + // TODO get rid of this function + public function getPoolTemp() + { + return $this->pool; + } +} diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 28c439b9e..0142818fc 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -20,12 +20,13 @@ use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Solver; use Composer\DependencyResolver\SolverProblemsException; use Composer\Package\Link; +use Composer\Repository\RepositorySet; use Composer\TestCase; use Composer\Semver\Constraint\MultiConstraint; class SolverTest extends TestCase { - protected $pool; + protected $repoSet; protected $repo; protected $repoInstalled; protected $request; @@ -33,13 +34,13 @@ class SolverTest extends TestCase public function setUp() { - $this->pool = new Pool; + $this->repoSet = new RepositorySet(new Pool); $this->repo = new ArrayRepository; $this->repoInstalled = new ArrayRepository; - $this->request = new Request($this->pool); + $this->request = new Request($this->repoSet); $this->policy = new DefaultPolicy; - $this->solver = new Solver($this->policy, $this->pool, $this->repoInstalled, new NullIO()); + $this->solver = new Solver($this->policy, $this->repoSet, $this->repoInstalled, new NullIO()); } public function testSolverInstallSingle() @@ -90,9 +91,9 @@ class SolverTest extends TestCase $repo1->addPackage($foo1 = $this->getPackage('foo', '1')); $repo2->addPackage($foo2 = $this->getPackage('foo', '1')); - $this->pool->addRepository($this->repoInstalled); - $this->pool->addRepository($repo1); - $this->pool->addRepository($repo2); + $this->repoSet->addRepository($this->repoInstalled); + $this->repoSet->addRepository($repo1); + $this->repoSet->addRepository($repo2); $this->request->install('foo'); @@ -839,8 +840,8 @@ class SolverTest extends TestCase protected function reposComplete() { - $this->pool->addRepository($this->repoInstalled); - $this->pool->addRepository($this->repo); + $this->repoSet->addRepository($this->repoInstalled); + $this->repoSet->addRepository($this->repo); } protected function checkSolverResult(array $expected) diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php index 7f0327d9c..54460d705 100644 --- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php +++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php @@ -411,12 +411,12 @@ class EventDispatcherTest extends TestCase ->will($this->returnValue(array())); $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock(); - $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock(); + $repositorySet = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock(); $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock(); $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock(); - $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request); - $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request, array()); + $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, true, $policy, $repositorySet, $installedRepo, $request); + $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, true, $policy, $repositorySet, $installedRepo, $request, array()); } public static function call() diff --git a/tests/Composer/Test/Installer/InstallerEventTest.php b/tests/Composer/Test/Installer/InstallerEventTest.php index 8c99ba565..2847432e3 100644 --- a/tests/Composer/Test/Installer/InstallerEventTest.php +++ b/tests/Composer/Test/Installer/InstallerEventTest.php @@ -22,18 +22,18 @@ class InstallerEventTest extends TestCase $composer = $this->getMockBuilder('Composer\Composer')->getMock(); $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(); $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock(); - $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock(); + $repositorySet = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock(); $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock(); $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock(); $operations = array($this->getMockBuilder('Composer\DependencyResolver\Operation\OperationInterface')->getMock()); - $event = new InstallerEvent('EVENT_NAME', $composer, $io, true, $policy, $pool, $installedRepo, $request, $operations); + $event = new InstallerEvent('EVENT_NAME', $composer, $io, true, $policy, $repositorySet, $installedRepo, $request, $operations); $this->assertSame('EVENT_NAME', $event->getName()); $this->assertInstanceOf('Composer\Composer', $event->getComposer()); $this->assertInstanceOf('Composer\IO\IOInterface', $event->getIO()); $this->assertTrue($event->isDevMode()); $this->assertInstanceOf('Composer\DependencyResolver\PolicyInterface', $event->getPolicy()); - $this->assertInstanceOf('Composer\DependencyResolver\Pool', $event->getPool()); + $this->assertInstanceOf('Composer\Repository\RepositorySet', $event->getRepositorySet()); $this->assertInstanceOf('Composer\Repository\CompositeRepository', $event->getInstalledRepo()); $this->assertInstanceOf('Composer\DependencyResolver\Request', $event->getRequest()); $this->assertCount(1, $event->getOperations()); diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index d3e831e5c..2617eecf2 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -21,7 +21,7 @@ use PHPUnit\Framework\TestCase; class VersionSelectorTest extends TestCase { // A) multiple versions, get the latest one - // B) targetPackageVersion will pass to pool + // B) targetPackageVersion will pass to repo set // C) No results, throw exception public function testLatestVersionIsReturned() @@ -33,13 +33,13 @@ class VersionSelectorTest extends TestCase $package3 = $this->createPackage('1.2.0'); $packages = array($package1, $package2, $package3); - $pool = $this->createMockPool(); - $pool->expects($this->once()) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->once()) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue($packages)); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate($packageName); // 1.2.2 should be returned because it's the latest of the returned versions @@ -57,13 +57,13 @@ class VersionSelectorTest extends TestCase $package2->setRequires(array('php' => new Link($packageName, 'php', $parser->parseConstraints('>=5.6'), 'requires', '>=5.6'))); $packages = array($package1, $package2); - $pool = $this->createMockPool(); - $pool->expects($this->once()) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->once()) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue($packages)); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate($packageName, null, '5.5.0'); $this->assertSame($package1, $best, 'Latest version supporting php 5.5 should be returned (1.0.0)'); @@ -77,13 +77,13 @@ class VersionSelectorTest extends TestCase $package2 = $this->createPackage('1.1.0-beta'); $packages = array($package1, $package2); - $pool = $this->createMockPool(); - $pool->expects($this->once()) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->once()) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue($packages)); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate($packageName); $this->assertSame($package1, $best, 'Latest most stable version should be returned (1.0.0)'); @@ -97,18 +97,18 @@ class VersionSelectorTest extends TestCase $package2 = $this->createPackage('2.0.0-beta3'); $packages = array($package1, $package2); - $pool = $this->createMockPool(); - $pool->expects($this->at(0)) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->at(0)) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue($packages)); - $pool->expects($this->at(1)) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet->expects($this->at(1)) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue(array_reverse($packages))); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate($packageName, null, null); $this->assertSame($package2, $best, 'Expecting 2.0.0-beta3, cause beta is more stable than dev'); @@ -124,13 +124,13 @@ class VersionSelectorTest extends TestCase $package2 = $this->createPackage('1.1.0-beta'); $packages = array($package1, $package2); - $pool = $this->createMockPool(); - $pool->expects($this->once()) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->once()) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue($packages)); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate($packageName, null, null, 'dev'); $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)'); @@ -145,13 +145,13 @@ class VersionSelectorTest extends TestCase $package3 = $this->createPackage('1.2.0-alpha'); $packages = array($package1, $package2, $package3); - $pool = $this->createMockPool(); - $pool->expects($this->once()) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->once()) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue($packages)); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate($packageName, null, null, 'beta'); $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)'); @@ -165,13 +165,13 @@ class VersionSelectorTest extends TestCase $package3 = $this->createPackage('1.2.0-alpha'); $packages = array($package2, $package3); - $pool = $this->createMockPool(); - $pool->expects($this->once()) - ->method('whatProvides') - ->with($packageName, null, true) + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->once()) + ->method('findPackages') + ->with($packageName, null) ->will($this->returnValue($packages)); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate($packageName, null, null, 'stable'); $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)'); @@ -179,12 +179,12 @@ class VersionSelectorTest extends TestCase public function testFalseReturnedOnNoPackages() { - $pool = $this->createMockPool(); - $pool->expects($this->once()) - ->method('whatProvides') + $repositorySet = $this->createMockRepositorySet(); + $repositorySet->expects($this->once()) + ->method('findPackages') ->will($this->returnValue(array())); - $versionSelector = new VersionSelector($pool); + $versionSelector = new VersionSelector($repositorySet); $best = $versionSelector->findBestCandidate('foobaz'); $this->assertFalse($best, 'No versions are available returns false'); } @@ -194,8 +194,8 @@ class VersionSelectorTest extends TestCase */ public function testFindRecommendedRequireVersion($prettyVersion, $isDev, $stability, $expectedVersion, $branchAlias = null) { - $pool = $this->createMockPool(); - $versionSelector = new VersionSelector($pool); + $repositorySet = $this->createMockRepositorySet(); + $versionSelector = new VersionSelector($repositorySet); $versionParser = new VersionParser(); $package = $this->getMockBuilder('\Composer\Package\PackageInterface')->getMock(); @@ -273,8 +273,10 @@ class VersionSelectorTest extends TestCase return new Package('foo', $parser->normalize($version), $version); } - private function createMockPool() + private function createMockRepositorySet() { - return $this->getMockBuilder('Composer\DependencyResolver\Pool')->getMock(); + return $this->getMockBuilder('Composer\Repository\RepositorySet') + ->disableOriginalConstructor() + ->getMock(); } } diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json index 574c4402f..335f772c9 100644 --- a/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json +++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json @@ -7,6 +7,6 @@ "class": "Installer\\Plugin" }, "require": { - "composer-plugin-api": "^1.0" + "composer-plugin-api": "^2.0" } } diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json index 27432acfa..4104f4be6 100644 --- a/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json +++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json @@ -7,6 +7,6 @@ "class": "Installer\\Plugin2" }, "require": { - "composer-plugin-api": "^1.0" + "composer-plugin-api": "^2.0" } } diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json index 881eb5cae..ee087e2d7 100644 --- a/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json +++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json @@ -7,6 +7,6 @@ "class": "Installer\\Plugin2" }, "require": { - "composer-plugin-api": "^1.0" + "composer-plugin-api": "^2.0" } } diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json index f61cb3fbd..a349ccc2c 100644 --- a/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json +++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json @@ -10,6 +10,6 @@ ] }, "require": { - "composer-plugin-api": "^1.0" + "composer-plugin-api": "^2.0" } } diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json index 799df2e61..aa44b5a3d 100644 --- a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json +++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json @@ -9,6 +9,6 @@ ] }, "require": { - "composer-plugin-api": "1.1.0" + "composer-plugin-api": "2.0.0" } } diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json b/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json index f3ccb9397..45d8d794b 100644 --- a/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json +++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json @@ -7,6 +7,6 @@ "class": "Installer\\Plugin" }, "require": { - "composer-plugin-api": "^1.0" + "composer-plugin-api": "^2.0" } } From 1228bcdffc2fca0806b16dfb9d148ffa9d845383 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 11 Sep 2018 13:33:29 +0200 Subject: [PATCH 02/11] Internalize pool creation in repository set, store root aliases in set The pool is still exposed too early in a few places which will require further refactoring --- .../Command/BaseDependencyCommand.php | 3 +- src/Composer/Command/CreateProjectCommand.php | 3 +- src/Composer/Command/InitCommand.php | 3 +- src/Composer/Command/ShowCommand.php | 5 +- src/Composer/DependencyResolver/Pool.php | 18 +-- src/Composer/Installer.php | 36 +++--- src/Composer/Plugin/PluginManager.php | 3 +- .../Repository/ComposerRepository.php | 15 ++- src/Composer/Repository/RepositorySet.php | 112 +++++++++++++++-- .../DependencyResolver/DefaultPolicyTest.php | 117 +++++++++++------- .../Test/DependencyResolver/PoolTest.php | 21 ++-- .../RuleSetIteratorTest.php | 3 +- .../Test/DependencyResolver/RuleSetTest.php | 3 +- .../Test/DependencyResolver/RuleTest.php | 3 +- .../Test/DependencyResolver/SolverTest.php | 2 +- .../Test/Plugin/PluginInstallerTest.php | 22 ++-- .../Repository/ComposerRepositoryTest.php | 12 +- 17 files changed, 248 insertions(+), 133 deletions(-) diff --git a/src/Composer/Command/BaseDependencyCommand.php b/src/Composer/Command/BaseDependencyCommand.php index ff7bae2fe..ca80fa246 100644 --- a/src/Composer/Command/BaseDependencyCommand.php +++ b/src/Composer/Command/BaseDependencyCommand.php @@ -12,7 +12,6 @@ namespace Composer\Command; -use Composer\DependencyResolver\Pool; use Composer\Package\Link; use Composer\Package\PackageInterface; use Composer\Repository\ArrayRepository; @@ -79,7 +78,7 @@ class BaseDependencyCommand extends BaseCommand $composer->getRepositoryManager()->getLocalRepository(), new PlatformRepository(array(), $platformOverrides), )); - $repositorySet = new RepositorySet(new Pool()); + $repositorySet = new RepositorySet(); $repositorySet->addRepository($repository); // Parse package name and constraint diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 4c5a21794..1b58d59c5 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -20,7 +20,6 @@ use Composer\Installer\InstallationManager; use Composer\Installer\SuggestedPackagesReporter; use Composer\IO\IOInterface; use Composer\Package\BasePackage; -use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\Package\Version\VersionSelector; use Composer\Package\AliasPackage; @@ -291,7 +290,7 @@ EOT throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities))); } - $repositorySet = new RepositorySet(new Pool($stability)); + $repositorySet = new RepositorySet(array(), $stability); $repositorySet->addRepository($sourceRepo); $phpVersion = null; diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 86c55b152..03504e29a 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -12,7 +12,6 @@ namespace Composer\Command; -use Composer\DependencyResolver\Pool; use Composer\Factory; use Composer\Json\JsonFile; use Composer\Package\BasePackage; @@ -643,7 +642,7 @@ EOT $key = $minimumStability ?: 'default'; if (!isset($this->repositorySets[$key])) { - $this->repositorySets[$key] = $repositorySet = new RepositorySet(new Pool($minimumStability ?: $this->getMinimumStability($input))); + $this->repositorySets[$key] = $repositorySet = new RepositorySet(array(), $minimumStability ?: $this->getMinimumStability($input)); $repositorySet->addRepository($this->getRepos()); } diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index edb7198c9..94ec78362 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -14,7 +14,6 @@ namespace Composer\Command; use Composer\Composer; use Composer\DependencyResolver\DefaultPolicy; -use Composer\DependencyResolver\Pool; use Composer\Json\JsonFile; use Composer\Package\BasePackage; use Composer\Package\CompletePackageInterface; @@ -524,7 +523,7 @@ EOT $constraint = is_string($version) ? $this->versionParser->parseConstraints($version) : $version; $policy = new DefaultPolicy(); - $repositorySet = new RepositorySet(new Pool('dev')); + $repositorySet = new RepositorySet(array(), 'dev'); $repositorySet->addRepository($repos); $matchedPackage = null; @@ -985,7 +984,7 @@ EOT private function getRepositorySet(Composer $composer) { if (!$this->repositorySet) { - $this->repositorySet = new RepositorySet(new Pool($composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags())); + $this->repositorySet = new RepositorySet(array(), $composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags()); $this->repositorySet->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories())); } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index c63556974..ee4bace97 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -54,22 +54,12 @@ class Pool implements \Countable protected $whitelist = null; protected $id = 1; - public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array()) + public function __construct(array $acceptableStabilities, array $stabilityFlags = array(), array $filterRequires = array()) { - $this->versionParser = new VersionParser; - $this->acceptableStabilities = array(); - foreach (BasePackage::$stabilities as $stability => $value) { - if ($value <= BasePackage::$stabilities[$minimumStability]) { - $this->acceptableStabilities[$stability] = $value; - } - } + $this->acceptableStabilities = $acceptableStabilities; $this->stabilityFlags = $stabilityFlags; $this->filterRequires = $filterRequires; - foreach ($filterRequires as $name => $constraint) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { - unset($this->filterRequires[$name]); - } - } + $this->versionParser = new VersionParser; } public function setWhitelist($whitelist) @@ -202,7 +192,7 @@ class Pool implements \Countable $candidates = array(); foreach ($this->providerRepos as $repo) { - foreach ($repo->whatProvides($this, $name, $bypassFilters) as $candidate) { + foreach ($repo->whatProvides($name, $bypassFilters, array($this, 'isPackageAcceptable')) as $candidate) { $candidates[] = $candidate; if ($candidate->id < 1) { $candidate->setId($this->id++); diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 73120ea4b..bb27bda3c 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -20,7 +20,6 @@ use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\PolicyInterface; -use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Rule; use Composer\DependencyResolver\Solver; @@ -371,19 +370,19 @@ class Installer // creating repository set $policy = $this->createPolicy(); - $repositorySet = $this->createRepositorySet($this->update ? null : $lockedRepository); - $repositorySet->addRepository($installedRepo, $aliases); + $repositorySet = $this->createRepositorySet($aliases, $this->update ? null : $lockedRepository); + $repositorySet->addRepository($installedRepo); if ($this->update) { $repositories = $this->repositoryManager->getRepositories(); foreach ($repositories as $repository) { - $repositorySet->addRepository($repository, $aliases); + $repositorySet->addRepository($repository); } } // Add the locked repository after the others in case we are doing a // partial update so missing packages can be found there still. // For installs from lock it's the only one added so it is first if ($lockedRepository) { - $repositorySet->addRepository($lockedRepository, $aliases); + $repositorySet->addRepository($lockedRepository); } // creating requirements request @@ -465,6 +464,8 @@ class Installer } } + $repositorySet->getPoolTemp(); // TODO remove this, but ensures ids are set before dev packages are processed in advance of solver + // force dev packages to have the latest links if we update or install from a (potentially new) lock $this->processDevPackages($localRepo, $repositorySet, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links'); @@ -685,9 +686,9 @@ class Installer unset($tempLocalRepo, $loader, $dumper); $policy = $this->createPolicy(); - $repositorySet = $this->createRepositorySet(); + $repositorySet = $this->createRepositorySet($aliases); $installedRepo = $this->createInstalledRepo($localRepo, $platformRepo); - $repositorySet->addRepository($installedRepo, $aliases); + $repositorySet->addRepository($installedRepo); // creating requirements request without dev requirements $request = $this->createRequest($this->package, $platformRepo); @@ -844,17 +845,12 @@ class Installer return $installedRepo; } - private function createRepositorySet($lockedRepository = null) - { - $pool = $this->createPool($lockedRepository); - return new RepositorySet($pool); - } - /** - * @param RepositoryInterface|null $lockedRepository - * @return Pool + * @param array $rootAliases + * @param RepositoryInterface|null $lockedRepository + * @return RepositorySet */ - private function createPool(RepositoryInterface $lockedRepository = null) + private function createRepositorySet(array $rootAliases = array(), $lockedRepository = null) { if ($this->update) { $minimumStability = $this->package->getMinimumStability(); @@ -886,7 +882,7 @@ class Installer } } - return new Pool($minimumStability, $stabilityFlags, $rootConstraints); + return new RepositorySet($rootAliases, $minimumStability, $stabilityFlags, $rootConstraints); } /** @@ -1319,7 +1315,7 @@ class Installer } } - $repositorySet = new RepositorySet(new Pool('dev')); + $repositorySet = new RepositorySet(array(), 'dev'); $repositorySet->addRepository($localOrLockRepo); $seen = array(); @@ -1354,11 +1350,11 @@ class Installer while (!$packageQueue->isEmpty()) { $package = $packageQueue->dequeue(); - if (isset($seen[$package->getId()])) { + if (isset($seen[spl_object_hash($package)])) { continue; } - $seen[$package->getId()] = true; + $seen[spl_object_hash($package)] = true; $this->updateWhitelist[$package->getName()] = true; if (!$this->whitelistDependencies && !$this->whitelistAllDependencies) { diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index c6bdefc5a..d16c51db1 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -23,7 +23,6 @@ use Composer\Package\PackageInterface; use Composer\Package\Link; use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\Constraint; -use Composer\DependencyResolver\Pool; use Composer\Plugin\Capability\Capability; /** @@ -158,7 +157,7 @@ class PluginManager $localRepo = $this->composer->getRepositoryManager()->getLocalRepository(); $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; - $repositorySet = new RepositorySet(new Pool('dev')); + $repositorySet = new RepositorySet(array(), 'dev'); $repositorySet->addRepository($localRepo); if ($globalRepo) { $repositorySet->addRepository($globalRepo); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 8a5da2b23..cec2ae948 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -16,7 +16,6 @@ use Composer\Package\Loader\ArrayLoader; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; -use Composer\DependencyResolver\Pool; use Composer\Json\JsonFile; use Composer\Cache; use Composer\Config; @@ -136,7 +135,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito foreach ($this->getProviderNames() as $providerName) { if ($name === $providerName) { - $packages = $this->whatProvides(new Pool('dev'), $providerName); + $packages = $this->whatProvides($providerName); foreach ($packages as $package) { if ($name === $package->getName()) { $pkgConstraint = new Constraint('==', $package->getVersion()); @@ -170,7 +169,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito foreach ($this->getProviderNames() as $providerName) { if ($name === $providerName) { - $candidates = $this->whatProvides(new Pool('dev'), $providerName); + $candidates = $this->whatProvides($providerName); // TODO what is the point of this? foreach ($candidates as $package) { if ($name === $package->getName()) { $pkgConstraint = new Constraint('==', $package->getVersion()); @@ -289,12 +288,12 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } /** - * @param Pool $pool - * @param string $name package name - * @param bool $bypassFilters If set to true, this bypasses the stability filtering, and forces a recompute without cache + * @param string $name package name + * @param bool $bypassFilters If set to true, this bypasses the stability filtering, and forces a recompute without cache + * @param callable $isPackageAcceptableCallable * @return array|mixed */ - public function whatProvides(Pool $pool, $name, $bypassFilters = false) + public function whatProvides($name, $bypassFilters = false, $isPackageAcceptableCallable = null) { if (isset($this->providers[$name]) && !$bypassFilters) { return $this->providers[$name]; @@ -395,7 +394,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } } } else { - if (!$bypassFilters && !$pool->isPackageAcceptable(strtolower($version['name']), VersionParser::parseStability($version['version']))) { + if (!$bypassFilters && (!$isPackageAcceptableCallable || !call_user_func($isPackageAcceptableCallable, strtolower($version['name']), VersionParser::parseStability($version['version'])))) { continue; } diff --git a/src/Composer/Repository/RepositorySet.php b/src/Composer/Repository/RepositorySet.php index e6b6db26b..d3c652ccb 100644 --- a/src/Composer/Repository/RepositorySet.php +++ b/src/Composer/Repository/RepositorySet.php @@ -24,36 +24,132 @@ use Composer\Semver\Constraint\ConstraintInterface; */ class RepositorySet { - private $pool; + /** @var array */ + private $rootAliases; - public function __construct(Pool $pool) + /** @var RepositoryInterface[] */ + private $repositories; + + /** @var ComposerRepository[] */ + private $providerRepos; + + private $acceptableStabilities; + private $stabilityFlags; + protected $filterRequires; + + /** @var Pool */ + private $pool; // TODO remove this + + public function __construct(array $rootAliases = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array()) { - $this->pool = $pool; + $this->rootAliases = $rootAliases; + + $this->acceptableStabilities = array(); + foreach (BasePackage::$stabilities as $stability => $value) { + if ($value <= BasePackage::$stabilities[$minimumStability]) { + $this->acceptableStabilities[$stability] = $value; + } + } + $this->stabilityFlags = $stabilityFlags; + $this->filterRequires = $filterRequires; + foreach ($filterRequires as $name => $constraint) { + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { + unset($this->filterRequires[$name]); + } + } } - public function addRepository(RepositoryInterface $repo, $rootAliases = array()) + /** + * Adds a repository to this repository set + * + * @param RepositoryInterface $repo A package repository + */ + public function addRepository(RepositoryInterface $repo) { - return $this->pool->addRepository($repo, $rootAliases); + if ($repo instanceof CompositeRepository) { + $repos = $repo->getRepositories(); + } else { + $repos = array($repo); + } + + foreach ($repos as $repo) { + $this->repositories[] = $repo; + if ($repo instanceof ComposerRepository && $repo->hasProviders()) { + $this->providerRepos[] = $repo; + } + } } public function isPackageAcceptable($name, $stability) { - return $this->pool->isPackageAcceptable($name, $stability); + foreach ((array) $name as $n) { + // allow if package matches the global stability requirement and has no exception + if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) { + return true; + } + + // allow if package matches the package-specific stability flag + if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) { + return true; + } + } + + return false; } + /** + * Find packages matching name and optionally a constraint in all repositories + * + * @param $name + * @param ConstraintInterface|null $constraint + * @return array + */ public function findPackages($name, ConstraintInterface $constraint = null) { - return $this->pool->whatProvides($name, $constraint, true); + $packages = array(); + foreach ($this->repositories as $repository) { + $packages[] = $repository->findPackages($name, $constraint) ?: array(); + } + + $candidates = $packages ? call_user_func_array('array_merge', $packages) : array(); + + $result = array(); + foreach ($candidates as $candidate) { + if ($this->isPackageAcceptable($candidate->getNames(), $candidate->getStability())) { + $result[] = $candidate; + } + } + + return $candidates; } + /** + * Create a pool for dependency resolution from the packages in this repository set. + * + * @return Pool + */ public function createPool() { + if ($this->pool) { + return $this->pool; + } + + $this->pool = new Pool($this->acceptableStabilities, $this->stabilityFlags, $this->filterRequires); + + foreach ($this->repositories as $repository) { + $this->pool->addRepository($repository, $this->rootAliases); + } + return $this->pool; } // TODO get rid of this function public function getPoolTemp() { - return $this->pool; + if (!$this->pool) { + return $this->createPool(); + } else { + return $this->pool; + } } } diff --git a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php index a73139d54..34a1c092b 100644 --- a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php +++ b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php @@ -18,13 +18,14 @@ use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\Pool; use Composer\Package\Link; use Composer\Package\AliasPackage; +use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\Constraint; use Composer\TestCase; class DefaultPolicyTest extends TestCase { - /** @var Pool */ - protected $pool; + /** @var RepositorySet */ + protected $repositorySet; /** @var ArrayRepository */ protected $repo; /** @var ArrayRepository */ @@ -34,7 +35,7 @@ class DefaultPolicyTest extends TestCase public function setUp() { - $this->pool = new Pool('dev'); + $this->repositorySet = new RepositorySet(array(), 'dev'); $this->repo = new ArrayRepository; $this->repoInstalled = new ArrayRepository; @@ -44,12 +45,14 @@ class DefaultPolicyTest extends TestCase public function testSelectSingle() { $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA->getId()); $expected = array($packageA->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -58,12 +61,14 @@ class DefaultPolicyTest extends TestCase { $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -72,12 +77,14 @@ class DefaultPolicyTest extends TestCase { $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0.0')); $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha')); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -86,13 +93,15 @@ class DefaultPolicyTest extends TestCase { $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0.0')); $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha')); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA1->getId()); $policy = new DefaultPolicy(true); - $selected = $policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -101,12 +110,14 @@ class DefaultPolicyTest extends TestCase { $this->repo->addPackage($packageA1 = $this->getPackage('A', 'dev-foo')); $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.0')); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -115,13 +126,15 @@ class DefaultPolicyTest extends TestCase { $this->repo->addPackage($packageA = $this->getPackage('A', '2.0')); $this->repoInstalled->addPackage($packageAInstalled = $this->getPackage('A', '1.0')); - $this->pool->addRepository($this->repoInstalled); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repoInstalled); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA->getId(), $packageAInstalled->getId()); $expected = array($packageA->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, $this->mapFromRepo($this->repoInstalled), $literals); + $selected = $this->policy->selectPreferredPackages($pool, $this->mapFromRepo($this->repoInstalled), $literals); $this->assertSame($expected, $selected); } @@ -133,14 +146,16 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $otherRepository->addPackage($packageAImportant = $this->getPackage('A', '1.0')); - $this->pool->addRepository($this->repoInstalled); - $this->pool->addRepository($otherRepository); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repoInstalled); + $this->repositorySet->addRepository($otherRepository); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA->getId(), $packageAImportant->getId()); $expected = array($packageAImportant->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -155,21 +170,25 @@ class DefaultPolicyTest extends TestCase $repo2->addPackage($package3 = $this->getPackage('A', '1.1')); $repo2->addPackage($package4 = $this->getPackage('A', '1.2')); - $this->pool->addRepository($repo1); - $this->pool->addRepository($repo2); + $this->repositorySet->addRepository($repo1); + $this->repositorySet->addRepository($repo2); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($package1->getId(), $package2->getId(), $package3->getId(), $package4->getId()); $expected = array($package2->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); - $this->pool = new Pool('dev'); - $this->pool->addRepository($repo2); - $this->pool->addRepository($repo1); + $this->repositorySet = new RepositorySet(array(), 'dev'); + $this->repositorySet->addRepository($repo2); + $this->repositorySet->addRepository($repo1); + + $pool = $this->repositorySet->getPoolTemp(); $expected = array($package4->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -186,11 +205,13 @@ class DefaultPolicyTest extends TestCase $repoImportant->addPackage($packageA2AliasImportant = new AliasPackage($packageA2Important, '2.1.9999999.9999999-dev', '2.1.x-dev')); $packageAAliasImportant->setRootPackageAlias(true); - $this->pool->addRepository($this->repoInstalled); - $this->pool->addRepository($repoImportant); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repoInstalled); + $this->repositorySet->addRepository($repoImportant); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); - $packages = $this->pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev')); + $packages = $pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev')); $literals = array(); foreach ($packages as $package) { $literals[] = $package->getId(); @@ -198,7 +219,7 @@ class DefaultPolicyTest extends TestCase $expected = array($packageAAliasImportant->getId()); - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -211,12 +232,14 @@ class DefaultPolicyTest extends TestCase $packageA->setProvides(array(new Link('A', 'X', new Constraint('==', '1.0'), 'provides'))); $packageB->setProvides(array(new Link('B', 'X', new Constraint('==', '1.0'), 'provides'))); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -228,12 +251,14 @@ class DefaultPolicyTest extends TestCase $packageB->setReplaces(array(new Link('B', 'A', new Constraint('==', '1.0'), 'replaces'))); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } @@ -247,12 +272,14 @@ class DefaultPolicyTest extends TestCase $packageA->setReplaces(array(new Link('vendor-a/replacer', 'vendor-a/package', new Constraint('==', '1.0'), 'replaces'))); $packageB->setReplaces(array(new Link('vendor-b/replacer', 'vendor-a/package', new Constraint('==', '1.0'), 'replaces'))); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals, 'vendor-a/package'); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals, 'vendor-a/package'); $this->assertEquals($expected, $selected); // test with reversed order in repo @@ -260,13 +287,15 @@ class DefaultPolicyTest extends TestCase $repo->addPackage($packageA = clone $packageA); $repo->addPackage($packageB = clone $packageB); - $pool = new Pool('dev'); - $pool->addRepository($this->repo); + $repositorySet = new RepositorySet(array(), 'dev'); + $repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; - $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals, 'vendor-a/package'); + $selected = $this->policy->selectPreferredPackages($pool, array(), $literals, 'vendor-a/package'); $this->assertSame($expected, $selected); } @@ -286,12 +315,14 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); - $this->pool->addRepository($this->repo); + $this->repositorySet->addRepository($this->repo); + + $pool = $this->repositorySet->getPoolTemp(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA1->getId()); - $selected = $policy->selectPreferredPackages($this->pool, array(), $literals); + $selected = $policy->selectPreferredPackages($pool, array(), $literals); $this->assertSame($expected, $selected); } diff --git a/tests/Composer/Test/DependencyResolver/PoolTest.php b/tests/Composer/Test/DependencyResolver/PoolTest.php index 14b24fc9f..5169586f6 100644 --- a/tests/Composer/Test/DependencyResolver/PoolTest.php +++ b/tests/Composer/Test/DependencyResolver/PoolTest.php @@ -21,7 +21,7 @@ class PoolTest extends TestCase { public function testPool() { - $pool = new Pool; + $pool = $this->createPool(); $repo = new ArrayRepository; $package = $this->getPackage('foo', '1'); @@ -34,7 +34,7 @@ class PoolTest extends TestCase public function testPoolIgnoresIrrelevantPackages() { - $pool = new Pool('stable', array('bar' => BasePackage::STABILITY_BETA)); + $pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE), array('bar' => BasePackage::STABILITY_BETA)); $repo = new ArrayRepository; $repo->addPackage($package = $this->getPackage('bar', '1')); $repo->addPackage($betaPackage = $this->getPackage('bar', '1-beta')); @@ -53,7 +53,7 @@ class PoolTest extends TestCase */ public function testGetPriorityForNotRegisteredRepository() { - $pool = new Pool; + $pool = $this->createPool(); $repository = new ArrayRepository; $pool->getPriority($repository); @@ -61,7 +61,7 @@ class PoolTest extends TestCase public function testGetPriorityWhenRepositoryIsRegistered() { - $pool = new Pool; + $pool = $this->createPool(); $firstRepository = new ArrayRepository; $pool->addRepository($firstRepository); $secondRepository = new ArrayRepository; @@ -76,7 +76,7 @@ class PoolTest extends TestCase public function testWhatProvidesSamePackageForDifferentRepositories() { - $pool = new Pool; + $pool = $this->createPool(); $firstRepository = new ArrayRepository; $secondRepository = new ArrayRepository; @@ -96,7 +96,7 @@ class PoolTest extends TestCase public function testWhatProvidesPackageWithConstraint() { - $pool = new Pool; + $pool = $this->createPool(); $repository = new ArrayRepository; $firstPackage = $this->getPackage('foo', '1'); @@ -113,7 +113,7 @@ class PoolTest extends TestCase public function testPackageById() { - $pool = new Pool; + $pool = $this->createPool(); $repository = new ArrayRepository; $package = $this->getPackage('foo', '1'); @@ -125,8 +125,13 @@ class PoolTest extends TestCase public function testWhatProvidesWhenPackageCannotBeFound() { - $pool = new Pool; + $pool = $this->createPool(); $this->assertEquals(array(), $pool->whatProvides('foo')); } + + protected function createPool() + { + return new Pool(array('stable' => BasePackage::STABILITY_STABLE)); + } } diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 7789881df..24a2e7c54 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -17,6 +17,7 @@ use Composer\DependencyResolver\Rule; use Composer\DependencyResolver\RuleSet; use Composer\DependencyResolver\RuleSetIterator; use Composer\DependencyResolver\Pool; +use Composer\Package\BasePackage; use PHPUnit\Framework\TestCase; class RuleSetIteratorTest extends TestCase @@ -26,7 +27,7 @@ class RuleSetIteratorTest extends TestCase protected function setUp() { - $this->pool = new Pool; + $this->pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE)); $this->rules = array( RuleSet::TYPE_JOB => array( diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index cecae613d..e9753c848 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -16,6 +16,7 @@ use Composer\DependencyResolver\GenericRule; use Composer\DependencyResolver\Rule; use Composer\DependencyResolver\RuleSet; use Composer\DependencyResolver\Pool; +use Composer\Package\BasePackage; use Composer\Repository\ArrayRepository; use Composer\TestCase; @@ -25,7 +26,7 @@ class RuleSetTest extends TestCase public function setUp() { - $this->pool = new Pool; + $this->pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE)); } public function testAdd() diff --git a/tests/Composer/Test/DependencyResolver/RuleTest.php b/tests/Composer/Test/DependencyResolver/RuleTest.php index a0339f27a..c9b3dbe1a 100644 --- a/tests/Composer/Test/DependencyResolver/RuleTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleTest.php @@ -16,6 +16,7 @@ use Composer\DependencyResolver\GenericRule; use Composer\DependencyResolver\Rule; use Composer\DependencyResolver\RuleSet; use Composer\DependencyResolver\Pool; +use Composer\Package\BasePackage; use Composer\Repository\ArrayRepository; use Composer\TestCase; @@ -25,7 +26,7 @@ class RuleTest extends TestCase public function setUp() { - $this->pool = new Pool; + $this->pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE)); } public function testGetHash() diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 0142818fc..1a43c5b60 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -34,7 +34,7 @@ class SolverTest extends TestCase public function setUp() { - $this->repoSet = new RepositorySet(new Pool); + $this->repoSet = new RepositorySet(array()); $this->repo = new ArrayRepository; $this->repoInstalled = new ArrayRepository; diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php index 26fc63efa..7981177eb 100644 --- a/tests/Composer/Test/Plugin/PluginInstallerTest.php +++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php @@ -130,7 +130,7 @@ class PluginInstallerTest extends TestCase public function testInstallNewPlugin() { $this->repository - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array())); $installer = new PluginInstaller($this->io, $this->composer); @@ -145,7 +145,7 @@ class PluginInstallerTest extends TestCase public function testInstallMultiplePlugins() { $this->repository - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array($this->packages[3]))); $installer = new PluginInstaller($this->io, $this->composer); @@ -163,7 +163,7 @@ class PluginInstallerTest extends TestCase public function testUpgradeWithNewClassName() { $this->repository - ->expects($this->exactly(3)) + ->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array($this->packages[0]))); $this->repository @@ -182,7 +182,7 @@ class PluginInstallerTest extends TestCase public function testUpgradeWithSameClassName() { $this->repository - ->expects($this->exactly(3)) + ->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array($this->packages[1]))); $this->repository @@ -201,7 +201,7 @@ class PluginInstallerTest extends TestCase public function testRegisterPluginOnlyOneTime() { $this->repository - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array())); $installer = new PluginInstaller($this->io, $this->composer); @@ -240,11 +240,11 @@ class PluginInstallerTest extends TestCase // Add the plugins to the repo along with the internal Plugin package on which they all rely. $this->repository - ->expects($this->any()) - ->method('getPackages') - ->will($this->returnCallback(function () use ($plugApiInternalPackage, $plugins) { - return array_merge(array($plugApiInternalPackage), $plugins); - })); + ->expects($this->any()) + ->method('getPackages') + ->will($this->returnCallback(function () use ($plugApiInternalPackage, $plugins) { + return array_merge(array($plugApiInternalPackage), $plugins); + })); $this->pm->loadInstalledPlugins(); } @@ -300,7 +300,7 @@ class PluginInstallerTest extends TestCase public function testCommandProviderCapability() { $this->repository - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('getPackages') ->will($this->returnValue(array($this->packages[7]))); $installer = new PluginInstaller($this->io, $this->composer); diff --git a/tests/Composer/Test/Repository/ComposerRepositoryTest.php b/tests/Composer/Test/Repository/ComposerRepositoryTest.php index 38b459730..fa6809cbb 100644 --- a/tests/Composer/Test/Repository/ComposerRepositoryTest.php +++ b/tests/Composer/Test/Repository/ComposerRepositoryTest.php @@ -142,11 +142,6 @@ class ComposerRepositoryTest extends TestCase ), ))); - $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->getMock(); - $pool->expects($this->any()) - ->method('isPackageAcceptable') - ->will($this->returnValue(true)); - $versionParser = new VersionParser(); $repo->setRootAliases(array( 'a' => array( @@ -155,7 +150,7 @@ class ComposerRepositoryTest extends TestCase ), )); - $packages = $repo->whatProvides($pool, 'a'); + $packages = $repo->whatProvides('a', false, array($this, 'isPackageAcceptableReturnTrue')); $this->assertCount(7, $packages); $this->assertEquals(array('1', '1-alias', '2', '2-alias', '2-root', '3', '3-root'), array_keys($packages)); @@ -164,6 +159,11 @@ class ComposerRepositoryTest extends TestCase $this->assertSame($packages['2'], $packages['2-alias']->getAliasOf()); } + public function isPackageAcceptableReturnTrue() + { + return true; + } + public function testSearchWithType() { $repoConfig = array( From 190d263c74773e855ecc2d854766ec3963db448e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 11 Sep 2018 14:40:37 +0200 Subject: [PATCH 03/11] Fix logic for composer repository's optional acceptable callable filter --- src/Composer/Repository/ComposerRepository.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index cec2ae948..ea583ede6 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -169,7 +169,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito foreach ($this->getProviderNames() as $providerName) { if ($name === $providerName) { - $candidates = $this->whatProvides($providerName); // TODO what is the point of this? + $candidates = $this->whatProvides($providerName); foreach ($candidates as $package) { if ($name === $package->getName()) { $pkgConstraint = new Constraint('==', $package->getVersion()); @@ -394,7 +394,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } } } else { - if (!$bypassFilters && (!$isPackageAcceptableCallable || !call_user_func($isPackageAcceptableCallable, strtolower($version['name']), VersionParser::parseStability($version['version'])))) { + if (!$bypassFilters && $isPackageAcceptableCallable && !call_user_func($isPackageAcceptableCallable, strtolower($version['name']), VersionParser::parseStability($version['version']))) { continue; } From 7036f999990a4a2ec394cf0fb0506f860ecdd19b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 11 Sep 2018 14:52:44 +0200 Subject: [PATCH 04/11] RepositorySet::findPackages now has an exactMatch option Allows search for providers/replacers, or exact name search --- src/Composer/Command/BaseDependencyCommand.php | 2 +- src/Composer/Plugin/PluginManager.php | 2 +- src/Composer/Repository/RepositorySet.php | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/BaseDependencyCommand.php b/src/Composer/Command/BaseDependencyCommand.php index ca80fa246..b2b617c42 100644 --- a/src/Composer/Command/BaseDependencyCommand.php +++ b/src/Composer/Command/BaseDependencyCommand.php @@ -89,7 +89,7 @@ class BaseDependencyCommand extends BaseCommand ); // Find packages that are or provide the requested package first - $packages = $repositorySet->findPackages(strtolower($needle)); // TODO this does not search providers + $packages = $repositorySet->findPackages(strtolower($needle), null, false); if (empty($packages)) { throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle)); } diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index d16c51db1..786d846c5 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -305,7 +305,7 @@ class PluginManager */ private function lookupInstalledPackage(RepositorySet $repositorySet, Link $link) { - $packages = $repositorySet->findPackages($link->getTarget(), $link->getConstraint()); // TODO this no longer returns providers + $packages = $repositorySet->findPackages($link->getTarget(), $link->getConstraint(), false); return !empty($packages) ? $packages[0] : null; } diff --git a/src/Composer/Repository/RepositorySet.php b/src/Composer/Repository/RepositorySet.php index d3c652ccb..f83de3b14 100644 --- a/src/Composer/Repository/RepositorySet.php +++ b/src/Composer/Repository/RepositorySet.php @@ -98,13 +98,14 @@ class RepositorySet } /** - * Find packages matching name and optionally a constraint in all repositories + * Find packages providing or matching a name and optionally meeting a constraint in all repositories * - * @param $name + * @param string $name * @param ConstraintInterface|null $constraint + * @param bool $exactMatch * @return array */ - public function findPackages($name, ConstraintInterface $constraint = null) + public function findPackages($name, ConstraintInterface $constraint = null, $exactMatch = true) { $packages = array(); foreach ($this->repositories as $repository) { @@ -115,6 +116,10 @@ class RepositorySet $result = array(); foreach ($candidates as $candidate) { + if ($exactMatch && $candidate->getName() !== $name) { + continue; + } + if ($this->isPackageAcceptable($candidate->getNames(), $candidate->getStability())) { $result[] = $candidate; } From b6e2d60c9eee1a75ac5d1b8a1bb621ae4baf6b81 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 11 Sep 2018 15:49:08 +0200 Subject: [PATCH 05/11] Create the pool in the installer before giving it to the solver --- src/Composer/DependencyResolver/Solver.php | 14 +++---- src/Composer/Installer.php | 42 ++++++++++--------- src/Composer/Repository/RepositorySet.php | 28 ++++++------- .../DependencyResolver/DefaultPolicyTest.php | 30 ++++++------- .../Test/DependencyResolver/SolverTest.php | 5 ++- 5 files changed, 60 insertions(+), 59 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index f5226fca5..959f1dc4c 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -27,8 +27,8 @@ class Solver /** @var PolicyInterface */ protected $policy; - /** @var RepositorySet */ - protected $repositorySet = null; + /** @var Pool */ + protected $pool = null; /** @var RepositoryInterface */ protected $installed; /** @var RuleSet */ @@ -37,8 +37,6 @@ class Solver protected $ruleSetGenerator; /** @var array */ protected $jobs; - /** @var Pool */ - protected $pool = null; /** @var int[] */ protected $updateMap = array(); @@ -65,15 +63,15 @@ class Solver /** * @param PolicyInterface $policy - * @param RepositorySet $repositorySet + * @param Pool $pool * @param RepositoryInterface $installed * @param IOInterface $io */ - public function __construct(PolicyInterface $policy, RepositorySet $repositorySet, RepositoryInterface $installed, IOInterface $io) + public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed, IOInterface $io) { $this->io = $io; $this->policy = $policy; - $this->repositorySet = $repositorySet; + $this->pool = $pool; $this->installed = $installed; } @@ -217,8 +215,6 @@ class Solver { $this->jobs = $request->getJobs(); - $this->pool = $this->repositorySet->createPool(); - $this->setupInstalledMap(); $this->ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool); diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index bb27bda3c..c6f888493 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -20,6 +20,7 @@ use Composer\DependencyResolver\Operation\UninstallOperation; use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation; use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\PolicyInterface; +use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Rule; use Composer\DependencyResolver\Solver; @@ -464,14 +465,15 @@ class Installer } } - $repositorySet->getPoolTemp(); // TODO remove this, but ensures ids are set before dev packages are processed in advance of solver + $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request); + + $pool = $repositorySet->createPool(); // force dev packages to have the latest links if we update or install from a (potentially new) lock - $this->processDevPackages($localRepo, $repositorySet, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links'); + $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links'); // solve dependencies - $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request); - $solver = new Solver($policy, $repositorySet, $installedRepo, $this->io); + $solver = new Solver($policy, $pool, $installedRepo, $this->io); try { $operations = $solver->solve($request, $this->ignorePlatformReqs); } catch (SolverProblemsException $e) { @@ -485,7 +487,7 @@ class Installer } // force dev packages to be updated if we update or install from a (potentially new) lock - $operations = $this->processDevPackages($localRepo, $repositorySet, $policy, $repositories, $installedRepo, $lockedRepository, 'force-updates', $operations); + $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-updates', $operations); $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations); @@ -600,11 +602,11 @@ class Installer if ($reason instanceof Rule) { switch ($reason->getReason()) { case Rule::RULE_JOB_INSTALL: - $this->io->writeError(' REASON: Required by the root package: '.$reason->getPrettyString($solver->getPool())); + $this->io->writeError(' REASON: Required by the root package: '.$reason->getPrettyString($pool)); $this->io->writeError(''); break; case Rule::RULE_PACKAGE_REQUIRES: - $this->io->writeError(' REASON: '.$reason->getPrettyString($solver->getPool())); + $this->io->writeError(' REASON: '.$reason->getPrettyString($pool)); $this->io->writeError(''); break; } @@ -623,7 +625,7 @@ class Installer if ($this->executeOperations) { // force source/dist urls to be updated for all packages - $this->processPackageUrls($repositorySet, $policy, $localRepo, $repositories); + $this->processPackageUrls($pool, $policy, $localRepo, $repositories); $localRepo->write(); } @@ -699,7 +701,7 @@ class Installer // solve deps to see which get removed $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request); - $solver = new Solver($policy, $repositorySet, $installedRepo, $this->io); + $solver = new Solver($policy, $repositorySet->createPool(), $installedRepo, $this->io); $ops = $solver->solve($request, $this->ignorePlatformReqs); $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request, $ops); @@ -948,7 +950,7 @@ class Installer /** * @param WritableRepositoryInterface $localRepo - * @param RepositorySet $repositorySet + * @param Pool $pool * @param PolicyInterface $policy * @param array $repositories * @param RepositoryInterface $installedRepo @@ -957,7 +959,7 @@ class Installer * @param array|null $operations * @return array */ - private function processDevPackages($localRepo, $repositorySet, $policy, $repositories, $installedRepo, $lockedRepository, $task, array $operations = null) + private function processDevPackages($localRepo, Pool $pool, $policy, $repositories, $installedRepo, $lockedRepository, $task, array $operations = null) { if ($task === 'force-updates' && null === $operations) { throw new \InvalidArgumentException('Missing operations argument'); @@ -1012,7 +1014,7 @@ class Installer } // find similar packages (name/version) in all repositories - $matches = $repositorySet->findPackages($package->getName(), new Constraint('=', $package->getVersion())); + $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()), true); foreach ($matches as $index => $match) { // skip local packages if (!in_array($match->getRepository(), $repositories, true)) { @@ -1024,8 +1026,8 @@ class Installer } // select preferred package according to policy rules - if ($matches && $matches = $policy->selectPreferredPackages($repositorySet->getPoolTemp(), array(), $matches)) { // TODO remove temp call - $newPackage = $repositorySet->getPoolTemp()->literalToPackage($matches[0]); + if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) { + $newPackage = $pool->literalToPackage($matches[0]); if ($task === 'force-links' && $newPackage) { $package->setRequires($newPackage->getRequires()); @@ -1126,12 +1128,12 @@ class Installer } /** - * @param RepositorySet $repositorySet + * @param Pool $pool * @param PolicyInterface $policy * @param WritableRepositoryInterface $localRepo * @param array $repositories */ - private function processPackageUrls($repositorySet, $policy, $localRepo, $repositories) + private function processPackageUrls(Pool $pool, $policy, $localRepo, $repositories) { if (!$this->update) { return; @@ -1140,8 +1142,8 @@ class Installer $rootRefs = $this->package->getReferences(); foreach ($localRepo->getCanonicalPackages() as $package) { - // find similar packages (name/version) in all repositories - $matches = $repositorySet->findPackages($package->getName(), new Constraint('=', $package->getVersion())); + // find similar packages (name/version) in pool + $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()), true); foreach ($matches as $index => $match) { // skip local packages if (!in_array($match->getRepository(), $repositories, true)) { @@ -1153,8 +1155,8 @@ class Installer } // select preferred package according to policy rules - if ($matches && $matches = $policy->selectPreferredPackages($repositorySet->getPoolTemp(), array(), $matches)) { // TODO get rid of pool - $newPackage = $repositorySet->getPoolTemp()->literalToPackage($matches[0]); + if ($matches && $matches = $policy->selectPreferredPackages($pool, array(), $matches)) { + $newPackage = $pool->literalToPackage($matches[0]); // update the dist and source URLs $sourceUrl = $package->getSourceUrl(); diff --git a/src/Composer/Repository/RepositorySet.php b/src/Composer/Repository/RepositorySet.php index f83de3b14..4bea054f9 100644 --- a/src/Composer/Repository/RepositorySet.php +++ b/src/Composer/Repository/RepositorySet.php @@ -18,6 +18,7 @@ use Composer\Package\Version\VersionParser; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Semver\Constraint\ConstraintInterface; +use Composer\Test\DependencyResolver\PoolTest; /** * @author Nils Adermann @@ -28,17 +29,17 @@ class RepositorySet private $rootAliases; /** @var RepositoryInterface[] */ - private $repositories; + private $repositories = array(); /** @var ComposerRepository[] */ - private $providerRepos; + private $providerRepos = array(); private $acceptableStabilities; private $stabilityFlags; protected $filterRequires; /** @var Pool */ - private $pool; // TODO remove this + private $pool; public function __construct(array $rootAliases = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array()) { @@ -66,6 +67,10 @@ class RepositorySet */ public function addRepository(RepositoryInterface $repo) { + if ($this->pool) { + throw new \RuntimeException("Pool has already been created from this repository set, it cannot be modified anymore."); + } + if ($repo instanceof CompositeRepository) { $repos = $repo->getRepositories(); } else { @@ -135,10 +140,6 @@ class RepositorySet */ public function createPool() { - if ($this->pool) { - return $this->pool; - } - $this->pool = new Pool($this->acceptableStabilities, $this->stabilityFlags, $this->filterRequires); foreach ($this->repositories as $repository) { @@ -148,13 +149,12 @@ class RepositorySet return $this->pool; } - // TODO get rid of this function - public function getPoolTemp() + /** + * Access the pool object after it has been created, relevant for plugins which need to read info from the pool + * @return Pool + */ + public function getPool() { - if (!$this->pool) { - return $this->createPool(); - } else { - return $this->pool; - } + return $this->pool; } } diff --git a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php index 34a1c092b..dedb452e2 100644 --- a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php +++ b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php @@ -47,7 +47,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA->getId()); $expected = array($packageA->getId()); @@ -63,7 +63,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); @@ -79,7 +79,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); @@ -95,7 +95,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA1->getId()); @@ -112,7 +112,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); @@ -129,7 +129,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repoInstalled); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA->getId(), $packageAInstalled->getId()); $expected = array($packageA->getId()); @@ -150,7 +150,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($otherRepository); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA->getId(), $packageAImportant->getId()); $expected = array($packageAImportant->getId()); @@ -173,7 +173,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($repo1); $this->repositorySet->addRepository($repo2); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($package1->getId(), $package2->getId(), $package3->getId(), $package4->getId()); $expected = array($package2->getId()); @@ -185,7 +185,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($repo2); $this->repositorySet->addRepository($repo1); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $expected = array($package4->getId()); $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); @@ -209,7 +209,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($repoImportant); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $packages = $pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev')); $literals = array(); @@ -234,7 +234,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -253,7 +253,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -274,7 +274,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -290,7 +290,7 @@ class DefaultPolicyTest extends TestCase $repositorySet = new RepositorySet(array(), 'dev'); $repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -317,7 +317,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->getPoolTemp(); + $pool = $this->repositorySet->createPool(); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA1->getId()); diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 1a43c5b60..e4b02968f 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -40,7 +40,6 @@ class SolverTest extends TestCase $this->request = new Request($this->repoSet); $this->policy = new DefaultPolicy; - $this->solver = new Solver($this->policy, $this->repoSet, $this->repoInstalled, new NullIO()); } public function testSolverInstallSingle() @@ -95,6 +94,8 @@ class SolverTest extends TestCase $this->repoSet->addRepository($repo1); $this->repoSet->addRepository($repo2); + $this->solver = new Solver($this->policy, $this->repoSet->createPool(), $this->repoInstalled, new NullIO()); + $this->request->install('foo'); $this->checkSolverResult(array( @@ -842,6 +843,8 @@ class SolverTest extends TestCase { $this->repoSet->addRepository($this->repoInstalled); $this->repoSet->addRepository($this->repo); + + $this->solver = new Solver($this->policy, $this->repoSet->createPool(), $this->repoInstalled, new NullIO()); } protected function checkSolverResult(array $expected) From 1747df97e756430c33a3722f194f7a804afbbf97 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 11 Sep 2018 15:59:02 +0200 Subject: [PATCH 06/11] Create pool in show command to use policy, remove todos --- src/Composer/Command/ShowCommand.php | 6 ++++-- src/Composer/Installer.php | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 94ec78362..43546fdad 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -539,9 +539,11 @@ EOT $matches[$index] = $package->getId(); } + $pool = $repositorySet->createPool(); + // select preferred package according to policy rules - if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($repositorySet->getPoolTemp(), array(), $matches)) { // TODO get rid of the pool call - $matchedPackage = $repositorySet->getPoolTemp()->literalToPackage($preferred[0]); + if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, array(), $matches)) { + $matchedPackage = $pool->literalToPackage($preferred[0]); } return array($matchedPackage, $versions); diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index c6f888493..029e6e18e 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -127,7 +127,7 @@ class Installer * @var array|null */ protected $updateWhitelist = null; - protected $whitelistDependencies = false; // TODO 2.0 rename to whitelistTransitiveDependencies + protected $whitelistTransitiveDependencies = false; protected $whitelistAllDependencies = false; /** @@ -1327,7 +1327,7 @@ class Installer foreach ($this->updateWhitelist as $packageName => $void) { $packageQueue = new \SplQueue; - $depPackages = $repositorySet->findPackages($packageName); // TODO does this need replacers/providers? + $depPackages = $repositorySet->findPackages($packageName, null, false); $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true); @@ -1359,14 +1359,14 @@ class Installer $seen[spl_object_hash($package)] = true; $this->updateWhitelist[$package->getName()] = true; - if (!$this->whitelistDependencies && !$this->whitelistAllDependencies) { + if (!$this->whitelistTransitiveDependencies && !$this->whitelistAllDependencies) { continue; } $requires = $package->getRequires(); foreach ($requires as $require) { - $requirePackages = $repositorySet->findPackages($require->getTarget()); // TODO does this need replacers/providers? + $requirePackages = $repositorySet->findPackages($require->getTarget(), null, false); foreach ($requirePackages as $requirePackage) { if (isset($this->updateWhitelist[$requirePackage->getName()])) { @@ -1678,7 +1678,7 @@ class Installer */ public function setWhitelistTransitiveDependencies($updateTransitiveDependencies = true) { - $this->whitelistDependencies = (bool) $updateTransitiveDependencies; + $this->whitelistTransitiveDependencies = (bool) $updateTransitiveDependencies; return $this; } From 4c7d271a36ddc484b107a06f674b7eb932755334 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 11 Sep 2018 16:03:48 +0200 Subject: [PATCH 07/11] Remove deprecated function --- src/Composer/Installer.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 029e6e18e..8f6427a9a 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1659,14 +1659,6 @@ class Installer return $this; } - /** - * @deprecated use setWhitelistTransitiveDependencies instead - */ - public function setWhitelistDependencies($updateDependencies = true) - { - return $this->setWhitelistTransitiveDependencies($updateDependencies); - } - /** * Should dependencies of whitelisted packages (but not direct dependencies) be updated? * From c0f19f6c573550916373fbaae7245558ffb75af6 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 12 Sep 2018 11:49:09 +0200 Subject: [PATCH 08/11] Move construction of pool from repo set into a pool builder Pool construction depends on the install request now, so only required packages get loaded, add some structure for future asynchronously loading composer repositories --- .../DependencyResolver/DefaultPolicy.php | 11 +- src/Composer/DependencyResolver/Pool.php | 119 ++------------ .../DependencyResolver/PoolBuilder.php | 147 ++++++++++++++++++ src/Composer/DependencyResolver/Problem.php | 4 +- src/Composer/DependencyResolver/Rule.php | 15 +- src/Composer/DependencyResolver/Solver.php | 2 +- .../SolverProblemsException.php | 6 +- src/Composer/Installer.php | 4 +- src/Composer/Package/Locker.php | 4 +- .../Repository/AsyncRepositoryInterface.php | 38 +++++ src/Composer/Repository/BaseRepository.php | 15 ++ .../Repository/LockArrayRepository.php | 25 +++ .../Repository/RepositoryInterface.php | 9 ++ src/Composer/Repository/RepositorySet.php | 41 +++-- .../DependencyResolver/DefaultPolicyTest.php | 31 ++-- .../Test/DependencyResolver/PoolTest.php | 79 +--------- .../Test/DependencyResolver/RuleSetTest.php | 17 +- .../Test/DependencyResolver/RuleTest.php | 20 +-- .../Test/DependencyResolver/SolverTest.php | 19 ++- tests/Composer/Test/InstallerTest.php | 1 - 20 files changed, 358 insertions(+), 249 deletions(-) create mode 100644 src/Composer/DependencyResolver/PoolBuilder.php create mode 100644 src/Composer/Repository/AsyncRepositoryInterface.php create mode 100644 src/Composer/Repository/LockArrayRepository.php diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 542c6e625..051bc7449 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -57,11 +57,6 @@ class DefaultPolicy implements PolicyInterface return $packages; } - public function getPriority(Pool $pool, PackageInterface $package) - { - return $pool->getPriority($package->getRepository()); - } - public function selectPreferredPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null) { $packages = $this->groupLiteralsByNamePreferInstalled($pool, $installedMap, $literals); @@ -168,7 +163,7 @@ class DefaultPolicy implements PolicyInterface return 1; } - return ($this->getPriority($pool, $a) > $this->getPriority($pool, $b)) ? -1 : 1; + return ($pool->getPriority($a->id) > $pool->getPriority($b->id)) ? -1 : 1; } /** @@ -236,10 +231,10 @@ class DefaultPolicy implements PolicyInterface } if (null === $priority) { - $priority = $this->getPriority($pool, $package); + $priority = $pool->getPriority($package->id); } - if ($this->getPriority($pool, $package) != $priority) { + if ($pool->getPriority($package->id) != $priority) { break; } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index ee4bace97..6ad4d9f31 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -27,7 +27,7 @@ use Composer\Repository\PlatformRepository; use Composer\Package\PackageInterface; /** - * A package pool contains repositories that provide packages. + * A package pool contains all packages for dependency resolution * * @author Nils Adermann * @author Jordi Boggiano @@ -41,23 +41,18 @@ class Pool implements \Countable const MATCH_REPLACE = 3; const MATCH_FILTERED = 4; - protected $repositories = array(); protected $providerRepos = array(); protected $packages = array(); protected $packageByName = array(); protected $packageByExactName = array(); - protected $acceptableStabilities; - protected $stabilityFlags; + protected $priorities = array(); protected $versionParser; protected $providerCache = array(); protected $filterRequires; protected $whitelist = null; - protected $id = 1; - public function __construct(array $acceptableStabilities, array $stabilityFlags = array(), array $filterRequires = array()) + public function __construct(array $filterRequires = array()) { - $this->acceptableStabilities = $acceptableStabilities; - $this->stabilityFlags = $stabilityFlags; $this->filterRequires = $filterRequires; $this->versionParser = new VersionParser; } @@ -68,76 +63,24 @@ class Pool implements \Countable $this->providerCache = array(); } - /** - * Adds a repository and its packages to this package pool - * - * @param RepositoryInterface $repo A package repository - * @param array $rootAliases - */ - public function addRepository(RepositoryInterface $repo, $rootAliases = array()) + public function setPackages(array $packages, array $priorities = array()) { - if ($repo instanceof CompositeRepository) { - $repos = $repo->getRepositories(); - } else { - $repos = array($repo); - } + $this->priorities = $priorities; + $this->packages = $packages; - foreach ($repos as $repo) { - $this->repositories[] = $repo; - - $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface; - - if ($repo instanceof ComposerRepository && $repo->hasProviders()) { - $this->providerRepos[] = $repo; - $repo->setRootAliases($rootAliases); - $repo->resetPackageIds(); - } else { - foreach ($repo->getPackages() as $package) { - $names = $package->getNames(); - $stability = $package->getStability(); - if ($exempt || $this->isPackageAcceptable($names, $stability)) { - $package->setId($this->id++); - $this->packages[] = $package; - $this->packageByExactName[$package->getName()][$package->id] = $package; - - foreach ($names as $provided) { - $this->packageByName[$provided][] = $package; - } - - // handle root package aliases - $name = $package->getName(); - if (isset($rootAliases[$name][$package->getVersion()])) { - $alias = $rootAliases[$name][$package->getVersion()]; - if ($package instanceof AliasPackage) { - $package = $package->getAliasOf(); - } - $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']); - $aliasPackage->setRootPackageAlias(true); - $aliasPackage->setId($this->id++); - - $package->getRepository()->addPackage($aliasPackage); - $this->packages[] = $aliasPackage; - $this->packageByExactName[$aliasPackage->getName()][$aliasPackage->id] = $aliasPackage; - - foreach ($aliasPackage->getNames() as $name) { - $this->packageByName[$name][] = $aliasPackage; - } - } - } - } + foreach ($this->packages as $package) { + $names = $package->getNames(); + $this->packageByExactName[$package->getName()][$package->id] = $package; + + foreach ($names as $provided) { + $this->packageByName[$provided][] = $package; } } } - public function getPriority(RepositoryInterface $repo) + public function getPriority($id) { - $priority = array_search($repo, $this->repositories, true); - - if (false === $priority) { - throw new \RuntimeException("Could not determine repository priority. The repository was not registered in the pool."); - } - - return -$priority; + return $this->priorities[$id - 1]; } /** @@ -191,25 +134,12 @@ class Pool implements \Countable { $candidates = array(); - foreach ($this->providerRepos as $repo) { - foreach ($repo->whatProvides($name, $bypassFilters, array($this, 'isPackageAcceptable')) as $candidate) { - $candidates[] = $candidate; - if ($candidate->id < 1) { - $candidate->setId($this->id++); - $this->packages[$this->id - 2] = $candidate; - } - } - } - if ($mustMatchName) { - $candidates = array_filter($candidates, function ($candidate) use ($name) { - return $candidate->getName() == $name; - }); if (isset($this->packageByExactName[$name])) { - $candidates = array_merge($candidates, $this->packageByExactName[$name]); + $candidates = $this->packageByExactName[$name]; } } elseif (isset($this->packageByName[$name])) { - $candidates = array_merge($candidates, $this->packageByName[$name]); + $candidates = $this->packageByName[$name]; } $matches = $provideMatches = array(); @@ -287,23 +217,6 @@ class Pool implements \Countable return $prefix.' '.$package->getPrettyString(); } - public function isPackageAcceptable($name, $stability) - { - foreach ((array) $name as $n) { - // allow if package matches the global stability requirement and has no exception - if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) { - return true; - } - - // allow if package matches the package-specific stability flag - if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) { - return true; - } - } - - return false; - } - /** * Checks if the package matches the given constraint directly or through * provided or replaced packages diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php new file mode 100644 index 000000000..2ec6dd680 --- /dev/null +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -0,0 +1,147 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver; + +use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Package\PackageInterface; +use Composer\Repository\AsyncRepositoryInterface; +use Composer\Repository\InstalledRepositoryInterface; +use Composer\Repository\LockArrayRepository; +use Composer\Repository\PlatformRepository; + +/** + * @author Nils Adermann + */ +class PoolBuilder +{ + private $isPackageAcceptableCallable; + private $filterRequires; + private $rootAliases; + + private $loadedNames = array(); + + private $id = 1; + private $packages = array(); + private $priorities = array(); + + public function __construct($isPackageAcceptableCallable, array $filterRequires = array()) + { + $this->isPackageAcceptableCallable = $isPackageAcceptableCallable; + $this->filterRequires = $filterRequires; + } + + public function buildPool(array $repositories, array $rootAliases, Request $request) + { + $this->pool = new Pool($this->filterRequires); + $this->rootAliases = $rootAliases; + + // TODO do we really want the request here? kind of want a root requirements thingy instead + $loadNames = array(); + foreach ($request->getJobs() as $job) { + switch ($job['cmd']) { + case 'install': + $loadNames[$job['packageName']] = true; + break; + } + } + + foreach ($repositories as $repository) { + if ($repository instanceof ComposerRepository && $repository->hasProviders()) { + $this->providerRepos[] = $repository; + $repository->setRootAliases($this->rootAliases); + $repository->resetPackageIds(); + } + } + + while (!empty($loadNames)) { + $loadIds = array(); + foreach ($repositories as $key => $repository) { + if ($repository instanceof AsyncRepositoryInterface) { + $loadIds[$key] = $repository->requestPackages($loadNames); + } + } + + foreach ($loadNames as $name => $void) { + $this->loadedNames[$name] = true; + } + + $newLoadNames = array(); + foreach ($repositories as $key => $repository) { + if ($repository instanceof PlatformRepository || $repository instanceof InstalledRepositoryInterface) { + continue; + } + + if ($repository instanceof AsyncRepositoryInterface) { + $packages = $repository->returnPackages($loadIds[$key]); + } else { + $packages = $repository->loadPackages($loadNames); + } + + foreach ($packages as $package) { + if (call_user_func($this->isPackageAcceptableCallable, $package->getNames(), $package->getStability())) { + $newLoadNames += $this->loadPackage($package, $key); + } + } + } + + $loadNames = $newLoadNames; + } + + foreach ($repositories as $key => $repository) { + if ($repository instanceof PlatformRepository || + $repository instanceof InstalledRepositoryInterface) { + foreach ($repository->getPackages() as $package) { + $this->loadPackage($package, $key); + } + } + } + + $this->pool->setPackages($this->packages, $this->priorities); + + return $this->pool; + } + + private function loadPackage(PackageInterface $package, $repoIndex) + { + $package->setId($this->id++); + $this->packages[] = $package; + $this->priorities[$this->id - 2] = -$repoIndex; + + // handle root package aliases + $name = $package->getName(); + if (isset($this->rootAliases[$name][$package->getVersion()])) { + $alias = $this->rootAliases[$name][$package->getVersion()]; + if ($package instanceof AliasPackage) { + $package = $package->getAliasOf(); + } + $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']); + $aliasPackage->setRootPackageAlias(true); + $aliasPackage->setId($this->id++); + + $package->getRepository()->addPackage($aliasPackage); // TODO do we need this? + $this->packages[] = $aliasPackage; + } + + $loadNames = array(); + foreach ($package->getRequires() as $link) { + $require = $link->getTarget(); + if (!isset($this->loadedNames[$require])) { + $loadNames[$require] = true; + } + } + + return $loadNames; + } +} + diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index de24b0991..7cd3a9813 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -71,7 +71,7 @@ class Problem * @param array $installedMap A map of all installed packages * @return string */ - public function getPrettyString(array $installedMap = array()) + public function getPrettyString(array $installedMap = array(), array $learnedPool = array()) { $reasons = call_user_func_array('array_merge', array_reverse($this->reasons)); @@ -168,7 +168,7 @@ class Problem $messages[] = $this->jobToText($job); } elseif ($rule) { if ($rule instanceof Rule) { - $messages[] = $rule->getPrettyString($this->pool, $installedMap); + $messages[] = $rule->getPrettyString($this->pool, $installedMap, $learnedPool); } } } diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 1fe8cbd30..a627de61c 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -126,7 +126,7 @@ abstract class Rule abstract public function isAssertion(); - public function getPrettyString(Pool $pool, array $installedMap = array()) + public function getPrettyString(Pool $pool, array $installedMap = array(), array $learnedPool = array()) { $literals = $this->getLiterals(); @@ -230,7 +230,18 @@ abstract class Rule case self::RULE_PACKAGE_IMPLICIT_OBSOLETES: return $ruleText; case self::RULE_LEARNED: - return 'Conclusion: '.$ruleText; + // TODO not sure this is a good idea, most of these rules should be listed in the problem anyway + $learnedString = '(learned rule, '; + if (isset($learnedPool[$this->reasonData])) { + foreach ($learnedPool[$this->reasonData] as $learnedRule) { + $learnedString .= $learnedRule->getPrettyString($pool, $installedMap, $learnedPool); + } + } else { + $learnedString .= 'reasoning unavailable'; + } + $learnedString .= ')'; + + return 'Conclusion: '.$ruleText.' '.$learnedString; case self::RULE_PACKAGE_ALIAS: return $ruleText; default: diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 959f1dc4c..aa5432188 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -244,7 +244,7 @@ class Solver } if ($this->problems) { - throw new SolverProblemsException($this->problems, $this->installedMap); + throw new SolverProblemsException($this->problems, $this->installedMap, $this->learnedPool); } $transaction = new Transaction($this->policy, $this->pool, $this->installedMap, $this->decisions); diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index 142895697..0184bba9c 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -21,11 +21,13 @@ class SolverProblemsException extends \RuntimeException { protected $problems; protected $installedMap; + protected $learnedPool; - public function __construct(array $problems, array $installedMap) + public function __construct(array $problems, array $installedMap, array $learnedPool) { $this->problems = $problems; $this->installedMap = $installedMap; + $this->learnedPool = $learnedPool; parent::__construct($this->createMessage(), 2); } @@ -35,7 +37,7 @@ class SolverProblemsException extends \RuntimeException $text = "\n"; $hasExtensionProblems = false; foreach ($this->problems as $i => $problem) { - $text .= " Problem ".($i + 1).$problem->getPrettyString($this->installedMap)."\n"; + $text .= " Problem ".($i + 1).$problem->getPrettyString($this->installedMap, $this->learnedPool)."\n"; if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) { $hasExtensionProblems = true; diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 8f6427a9a..95d5b6605 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -467,7 +467,7 @@ class Installer $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request); - $pool = $repositorySet->createPool(); + $pool = $repositorySet->createPool($request); // force dev packages to have the latest links if we update or install from a (potentially new) lock $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links'); @@ -701,7 +701,7 @@ class Installer // solve deps to see which get removed $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request); - $solver = new Solver($policy, $repositorySet->createPool(), $installedRepo, $this->io); + $solver = new Solver($policy, $repositorySet->createPool($request), $installedRepo, $this->io); $ops = $solver->solve($request, $this->ignorePlatformReqs); $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request, $ops); diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index 57ec74233..405d43261 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -14,9 +14,9 @@ namespace Composer\Package; use Composer\Json\JsonFile; use Composer\Installer\InstallationManager; +use Composer\Repository\LockArrayRepository; use Composer\Repository\RepositoryManager; use Composer\Util\ProcessExecutor; -use Composer\Repository\ArrayRepository; use Composer\Package\Dumper\ArrayDumper; use Composer\Package\Loader\ArrayLoader; use Composer\Util\Git as GitUtil; @@ -150,7 +150,7 @@ class Locker public function getLockedRepository($withDevReqs = false) { $lockData = $this->getLockData(); - $packages = new ArrayRepository(); + $packages = new LockArrayRepository(); $lockedPackages = $lockData['packages']; if ($withDevReqs) { diff --git a/src/Composer/Repository/AsyncRepositoryInterface.php b/src/Composer/Repository/AsyncRepositoryInterface.php new file mode 100644 index 000000000..91f543dbf --- /dev/null +++ b/src/Composer/Repository/AsyncRepositoryInterface.php @@ -0,0 +1,38 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\PackageInterface; + +/** + * Repository interface. + * + * @author Nils Adermann + * @author Konstantin Kudryashov + * @author Jordi Boggiano + */ +interface AsyncRepositoryInterface +{ + /** + * @param array $names Names of packages to retrieve data for + * @return scalar Id to be passed to later loadPackages call + */ + public function requestPackages(array $names); + + /** + * @param array $names + * @return scalar id for load call + */ + public function returnPackages($loadId); +} + diff --git a/src/Composer/Repository/BaseRepository.php b/src/Composer/Repository/BaseRepository.php index 2b30b63cd..e9e97bb91 100644 --- a/src/Composer/Repository/BaseRepository.php +++ b/src/Composer/Repository/BaseRepository.php @@ -24,6 +24,21 @@ use Composer\Package\Link; */ abstract class BaseRepository implements RepositoryInterface { + // TODO should this stay here? some repos need a better implementation + public function loadPackages(array $packageNameMap) + { + $packages = $this->getPackages(); + + $result = array(); + foreach ($packages as $package) { + if (isset($packageNameMap[$package->getName()])) { + $result[] = $package; + } + } + + return $result; + } + /** * Returns a list of links causing the requested needle packages to be installed, as an associative array with the * dependent's name as key, and an array containing in order the PackageInterface and Link describing the relationship diff --git a/src/Composer/Repository/LockArrayRepository.php b/src/Composer/Repository/LockArrayRepository.php new file mode 100644 index 000000000..0ccc998d3 --- /dev/null +++ b/src/Composer/Repository/LockArrayRepository.php @@ -0,0 +1,25 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +/** + * Lock array repository. + * + * Regular array repository, only uses a different type to identify the lock file as the source of info + * + * @author Nils Adermann + */ +class LockArrayRepository extends ArrayRepository implements RepositoryInterface +{ +} + diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 9a2aaf3b5..703f92e35 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -55,6 +55,7 @@ interface RepositoryInterface extends \Countable */ public function findPackages($name, $constraint = null); + // TODO this should really not be in this generic interface anymore /** * Returns list of registered packages. * @@ -62,6 +63,14 @@ interface RepositoryInterface extends \Countable */ public function getPackages(); + /** + * Returns list of registered packages with the supplied name + * + * @param bool[] $packageNameMap + * @return PackageInterface[] + */ + public function loadPackages(array $packageNameMap); + /** * Searches the repository for packages containing the query * diff --git a/src/Composer/Repository/RepositorySet.php b/src/Composer/Repository/RepositorySet.php index 4bea054f9..bac63dd36 100644 --- a/src/Composer/Repository/RepositorySet.php +++ b/src/Composer/Repository/RepositorySet.php @@ -13,6 +13,8 @@ namespace Composer\Repository; use Composer\DependencyResolver\Pool; +use Composer\DependencyResolver\PoolBuilder; +use Composer\DependencyResolver\Request; use Composer\Package\BasePackage; use Composer\Package\Version\VersionParser; use Composer\Repository\CompositeRepository; @@ -31,9 +33,6 @@ class RepositorySet /** @var RepositoryInterface[] */ private $repositories = array(); - /** @var ComposerRepository[] */ - private $providerRepos = array(); - private $acceptableStabilities; private $stabilityFlags; protected $filterRequires; @@ -79,9 +78,6 @@ class RepositorySet foreach ($repos as $repo) { $this->repositories[] = $repo; - if ($repo instanceof ComposerRepository && $repo->hasProviders()) { - $this->providerRepos[] = $repo; - } } } @@ -133,20 +129,43 @@ class RepositorySet return $candidates; } + public function getPriority(RepositoryInterface $repo) + { + $priority = array_search($repo, $this->repositories, true); + + if (false === $priority) { + throw new \RuntimeException("Could not determine repository priority. The repository was not registered in the pool."); + } + + return -$priority; + } + /** * Create a pool for dependency resolution from the packages in this repository set. * * @return Pool */ - public function createPool() + public function createPool(Request $request) { - $this->pool = new Pool($this->acceptableStabilities, $this->stabilityFlags, $this->filterRequires); + $poolBuilder = new PoolBuilder(array($this, 'isPackageAcceptable'), $this->filterRequires); - foreach ($this->repositories as $repository) { - $this->pool->addRepository($repository, $this->rootAliases); + return $this->pool = $poolBuilder->buildPool($this->repositories, $this->rootAliases, $request); + } + + // TODO unify this with above in some simpler version without "request"? + public function createPoolForPackage($packageName) + { + return $this->createPoolForPackages(array($packageName)); + } + + public function createPoolForPackages($packageNames) + { + $request = new Request(); + foreach ($packageNames as $packageName) { + $request->install($packageName); } - return $this->pool; + return $this->createPool($request); } /** diff --git a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php index dedb452e2..c1637b8e4 100644 --- a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php +++ b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php @@ -21,6 +21,7 @@ use Composer\Package\AliasPackage; use Composer\Repository\RepositorySet; use Composer\Semver\Constraint\Constraint; use Composer\TestCase; +use http\Env\Request; class DefaultPolicyTest extends TestCase { @@ -47,7 +48,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA->getId()); $expected = array($packageA->getId()); @@ -63,7 +64,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); @@ -79,7 +80,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); @@ -95,7 +96,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA1->getId()); @@ -112,7 +113,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA2->getId()); @@ -129,7 +130,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repoInstalled); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA->getId(), $packageAInstalled->getId()); $expected = array($packageA->getId()); @@ -150,7 +151,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($otherRepository); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA->getId(), $packageAImportant->getId()); $expected = array($packageAImportant->getId()); @@ -173,7 +174,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($repo1); $this->repositorySet->addRepository($repo2); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($package1->getId(), $package2->getId(), $package3->getId(), $package4->getId()); $expected = array($package2->getId()); @@ -185,7 +186,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($repo2); $this->repositorySet->addRepository($repo1); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $expected = array($package4->getId()); $selected = $this->policy->selectPreferredPackages($pool, array(), $literals); @@ -209,7 +210,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($repoImportant); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $packages = $pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev')); $literals = array(); @@ -234,7 +235,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackages(array('A', 'B')); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -253,7 +254,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackages(array('A', 'B')); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -274,7 +275,7 @@ class DefaultPolicyTest extends TestCase $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackages(array('vendor-a/replacer', 'vendor-b/replacer')); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -290,7 +291,7 @@ class DefaultPolicyTest extends TestCase $repositorySet = new RepositorySet(array(), 'dev'); $repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackages(array('vendor-a/replacer', 'vendor-b/replacer')); $literals = array($packageA->getId(), $packageB->getId()); $expected = $literals; @@ -317,7 +318,7 @@ class DefaultPolicyTest extends TestCase $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); $this->repositorySet->addRepository($this->repo); - $pool = $this->repositorySet->createPool(); + $pool = $this->repositorySet->createPoolForPackage('A'); $literals = array($packageA1->getId(), $packageA2->getId()); $expected = array($packageA1->getId()); diff --git a/tests/Composer/Test/DependencyResolver/PoolTest.php b/tests/Composer/Test/DependencyResolver/PoolTest.php index 5169586f6..74767c525 100644 --- a/tests/Composer/Test/DependencyResolver/PoolTest.php +++ b/tests/Composer/Test/DependencyResolver/PoolTest.php @@ -22,90 +22,25 @@ class PoolTest extends TestCase public function testPool() { $pool = $this->createPool(); - $repo = new ArrayRepository; $package = $this->getPackage('foo', '1'); - $repo->addPackage($package); - $pool->addRepository($repo); + $pool->setPackages(array($package)); $this->assertEquals(array($package), $pool->whatProvides('foo')); $this->assertEquals(array($package), $pool->whatProvides('foo')); } - public function testPoolIgnoresIrrelevantPackages() - { - $pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE), array('bar' => BasePackage::STABILITY_BETA)); - $repo = new ArrayRepository; - $repo->addPackage($package = $this->getPackage('bar', '1')); - $repo->addPackage($betaPackage = $this->getPackage('bar', '1-beta')); - $repo->addPackage($alphaPackage = $this->getPackage('bar', '1-alpha')); - $repo->addPackage($package2 = $this->getPackage('foo', '1')); - $repo->addPackage($rcPackage2 = $this->getPackage('foo', '1rc')); - - $pool->addRepository($repo); - - $this->assertEquals(array($package, $betaPackage), $pool->whatProvides('bar')); - $this->assertEquals(array($package2), $pool->whatProvides('foo')); - } - - /** - * @expectedException \RuntimeException - */ - public function testGetPriorityForNotRegisteredRepository() - { - $pool = $this->createPool(); - $repository = new ArrayRepository; - - $pool->getPriority($repository); - } - - public function testGetPriorityWhenRepositoryIsRegistered() - { - $pool = $this->createPool(); - $firstRepository = new ArrayRepository; - $pool->addRepository($firstRepository); - $secondRepository = new ArrayRepository; - $pool->addRepository($secondRepository); - - $firstPriority = $pool->getPriority($firstRepository); - $secondPriority = $pool->getPriority($secondRepository); - - $this->assertEquals(0, $firstPriority); - $this->assertEquals(-1, $secondPriority); - } - - public function testWhatProvidesSamePackageForDifferentRepositories() - { - $pool = $this->createPool(); - $firstRepository = new ArrayRepository; - $secondRepository = new ArrayRepository; - - $firstPackage = $this->getPackage('foo', '1'); - $secondPackage = $this->getPackage('foo', '1'); - $thirdPackage = $this->getPackage('foo', '2'); - - $firstRepository->addPackage($firstPackage); - $secondRepository->addPackage($secondPackage); - $secondRepository->addPackage($thirdPackage); - - $pool->addRepository($firstRepository); - $pool->addRepository($secondRepository); - - $this->assertEquals(array($firstPackage, $secondPackage, $thirdPackage), $pool->whatProvides('foo')); - } - public function testWhatProvidesPackageWithConstraint() { $pool = $this->createPool(); - $repository = new ArrayRepository; $firstPackage = $this->getPackage('foo', '1'); $secondPackage = $this->getPackage('foo', '2'); - $repository->addPackage($firstPackage); - $repository->addPackage($secondPackage); - - $pool->addRepository($repository); + $pool->setPackages(array( + $firstPackage, + $secondPackage, + )); $this->assertEquals(array($firstPackage, $secondPackage), $pool->whatProvides('foo')); $this->assertEquals(array($secondPackage), $pool->whatProvides('foo', $this->getVersionConstraint('==', '2'))); @@ -114,11 +49,9 @@ class PoolTest extends TestCase public function testPackageById() { $pool = $this->createPool(); - $repository = new ArrayRepository; $package = $this->getPackage('foo', '1'); - $repository->addPackage($package); - $pool->addRepository($repository); + $pool->setPackages(array($package)); $this->assertSame($package, $pool->packageById(1)); } diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index e9753c848..9acd45455 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -22,13 +22,6 @@ use Composer\TestCase; class RuleSetTest extends TestCase { - protected $pool; - - public function setUp() - { - $this->pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE)); - } - public function testAdd() { $rules = array( @@ -146,9 +139,11 @@ class RuleSetTest extends TestCase public function testPrettyString() { - $repo = new ArrayRepository; - $repo->addPackage($p = $this->getPackage('foo', '2.1')); - $this->pool->addRepository($repo); + $pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE)); + $pool->setPackages(array( + $p = $this->getPackage('foo', '2.1'), + )); + $p->setId(1); $ruleSet = new RuleSet; $literal = $p->getId(); @@ -156,7 +151,7 @@ class RuleSetTest extends TestCase $ruleSet->add($rule, RuleSet::TYPE_JOB); - $this->assertContains('JOB : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($this->pool)); + $this->assertContains('JOB : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($pool)); } private function getRuleMock() diff --git a/tests/Composer/Test/DependencyResolver/RuleTest.php b/tests/Composer/Test/DependencyResolver/RuleTest.php index c9b3dbe1a..cb83266c8 100644 --- a/tests/Composer/Test/DependencyResolver/RuleTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleTest.php @@ -22,13 +22,6 @@ use Composer\TestCase; class RuleTest extends TestCase { - protected $pool; - - public function setUp() - { - $this->pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE)); - } - public function testGetHash() { $rule = new GenericRule(array(123), Rule::RULE_JOB_INSTALL, null); @@ -100,13 +93,16 @@ class RuleTest extends TestCase public function testPrettyString() { - $repo = new ArrayRepository; - $repo->addPackage($p1 = $this->getPackage('foo', '2.1')); - $repo->addPackage($p2 = $this->getPackage('baz', '1.1')); - $this->pool->addRepository($repo); + $pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE)); + $pool->setPackages(array( + $p1 = $this->getPackage('foo', '2.1'), + $p2 = $this->getPackage('baz', '1.1'), + )); + $p1->setId(1); + $p2->setId(2); $rule = new GenericRule(array($p1->getId(), -$p2->getId()), Rule::RULE_JOB_INSTALL, null); - $this->assertEquals('Install command rule (don\'t install baz 1.1|install foo 2.1)', $rule->getPrettyString($this->pool)); + $this->assertEquals('Install command rule (don\'t install baz 1.1|install foo 2.1)', $rule->getPrettyString($pool)); } } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index e4b02968f..0d221a8ab 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -20,6 +20,7 @@ use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Solver; use Composer\DependencyResolver\SolverProblemsException; use Composer\Package\Link; +use Composer\Repository\InstalledArrayRepository; use Composer\Repository\RepositorySet; use Composer\TestCase; use Composer\Semver\Constraint\MultiConstraint; @@ -31,12 +32,13 @@ class SolverTest extends TestCase protected $repoInstalled; protected $request; protected $policy; + protected $solver; public function setUp() { $this->repoSet = new RepositorySet(array()); $this->repo = new ArrayRepository; - $this->repoInstalled = new ArrayRepository; + $this->repoInstalled = new InstalledArrayRepository; $this->request = new Request($this->repoSet); $this->policy = new DefaultPolicy; @@ -71,6 +73,7 @@ class SolverTest extends TestCase $this->request->install('B', $this->getVersionConstraint('==', '1')); + $this->createSolver(); try { $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not result in exception.'); @@ -94,8 +97,6 @@ class SolverTest extends TestCase $this->repoSet->addRepository($repo1); $this->repoSet->addRepository($repo2); - $this->solver = new Solver($this->policy, $this->repoSet->createPool(), $this->repoInstalled, new NullIO()); - $this->request->install('foo'); $this->checkSolverResult(array( @@ -447,6 +448,7 @@ class SolverTest extends TestCase // must explicitly pick the provider, so error in this case $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException'); + $this->createSolver(); $this->solver->solve($this->request); } @@ -480,6 +482,7 @@ class SolverTest extends TestCase $this->request->install('A'); $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException'); + $this->createSolver(); $this->solver->solve($this->request); } @@ -652,6 +655,7 @@ class SolverTest extends TestCase $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException'); + $this->createSolver(); $this->solver->solve($this->request); } @@ -668,6 +672,7 @@ class SolverTest extends TestCase $this->request->install('A'); $this->request->install('B'); + $this->createSolver(); try { $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not result in exception.'); @@ -697,6 +702,7 @@ class SolverTest extends TestCase $this->request->install('A'); + $this->createSolver(); try { $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not result in exception.'); @@ -744,6 +750,7 @@ class SolverTest extends TestCase $this->request->install('A'); + $this->createSolver(); try { $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not result in exception.'); @@ -843,12 +850,16 @@ class SolverTest extends TestCase { $this->repoSet->addRepository($this->repoInstalled); $this->repoSet->addRepository($this->repo); + } - $this->solver = new Solver($this->policy, $this->repoSet->createPool(), $this->repoInstalled, new NullIO()); + protected function createSolver() + { + $this->solver = new Solver($this->policy, $this->repoSet->createPool($this->request), $this->repoInstalled, new NullIO()); } protected function checkSolverResult(array $expected) { + $this->createSolver(); $transaction = $this->solver->solve($this->request); $result = array(); diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 8614495ee..562a71518 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -30,7 +30,6 @@ use Symfony\Component\Console\Output\StreamOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Formatter\OutputFormatter; use Composer\TestCase; -use Composer\IO\BufferIO; class InstallerTest extends TestCase { From 96c812fb2479bdbb688ae750c637d7a66ad11b22 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 12 Sep 2018 13:27:10 +0200 Subject: [PATCH 09/11] Properly buffer installer test output to display as errors if necessary --- tests/Composer/Test/InstallerTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 562a71518..a244589af 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -14,6 +14,7 @@ namespace Composer\Test; use Composer\Installer; use Composer\Console\Application; +use Composer\IO\BufferIO; use Composer\Json\JsonFile; use Composer\Util\Filesystem; use Composer\Repository\ArrayRepository; @@ -56,7 +57,7 @@ class InstallerTest extends TestCase */ public function testInstaller(RootPackageInterface $rootPackage, $repositories, array $options) { - $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(); + $io = new BufferIO('', OutputInterface::VERBOSITY_NORMAL, new OutputFormatter(false)); $downloadManager = $this->getMockBuilder('Composer\Downloader\DownloadManager') ->setConstructorArgs(array($io)) @@ -81,7 +82,9 @@ class InstallerTest extends TestCase $installer = new Installer($io, $config, clone $rootPackage, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator); $result = $installer->run(); - $this->assertSame(0, $result); + + $output = str_replace("\r", '', $io->getOutput()); + $this->assertEquals(0, $result, $output); $expectedInstalled = isset($options['install']) ? $options['install'] : array(); $expectedUpdated = isset($options['update']) ? $options['update'] : array(); From 019ebee185a72745d284473dc9d27e50a186c69b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 12 Sep 2018 13:56:13 +0200 Subject: [PATCH 10/11] Add missing use statement to package event to fix install --no-dev --- src/Composer/Installer/PackageEvent.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Installer/PackageEvent.php b/src/Composer/Installer/PackageEvent.php index a563a91ba..d2f427a92 100644 --- a/src/Composer/Installer/PackageEvent.php +++ b/src/Composer/Installer/PackageEvent.php @@ -18,6 +18,7 @@ use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\PolicyInterface; use Composer\DependencyResolver\Request; use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositorySet; /** * The Package Event. From 261efe1e8e957151b910d433721e0cecd818354e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 12 Sep 2018 14:11:26 +0200 Subject: [PATCH 11/11] Implement loadPackages on Composer repositories with providers --- .../DependencyResolver/PoolBuilder.php | 5 ++++- src/Composer/Repository/BaseRepository.php | 4 ++-- .../Repository/ComposerRepository.php | 20 +++++++++++++++++++ .../Repository/RepositoryInterface.php | 3 ++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index 2ec6dd680..32e14e983 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -16,6 +16,7 @@ use Composer\Package\AliasPackage; use Composer\Package\BasePackage; use Composer\Package\PackageInterface; use Composer\Repository\AsyncRepositoryInterface; +use Composer\Repository\ComposerRepository; use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\LockArrayRepository; use Composer\Repository\PlatformRepository; @@ -83,9 +84,11 @@ class PoolBuilder } if ($repository instanceof AsyncRepositoryInterface) { + // TODO ispackageacceptablecallable in here? $packages = $repository->returnPackages($loadIds[$key]); } else { - $packages = $repository->loadPackages($loadNames); + // TODO should we really pass the callable into here? + $packages = $repository->loadPackages($loadNames, $this->isPackageAcceptableCallable); } foreach ($packages as $package) { diff --git a/src/Composer/Repository/BaseRepository.php b/src/Composer/Repository/BaseRepository.php index e9e97bb91..172fe16b8 100644 --- a/src/Composer/Repository/BaseRepository.php +++ b/src/Composer/Repository/BaseRepository.php @@ -25,13 +25,13 @@ use Composer\Package\Link; abstract class BaseRepository implements RepositoryInterface { // TODO should this stay here? some repos need a better implementation - public function loadPackages(array $packageNameMap) + public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable) { $packages = $this->getPackages(); $result = array(); foreach ($packages as $package) { - if (isset($packageNameMap[$package->getName()])) { + if (isset($packageNameMap[$package->getName()]) && call_user_func($isPackageAcceptableCallable, $package->getNames(), $package->getStability())) { $result[] = $package; } } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index ea583ede6..739027628 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -194,6 +194,26 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return parent::getPackages(); } + public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable) + { + if (!$this->hasProviders()) { + // TODO build more efficient version of this + return parent::loadPackages($packageNameMap, $isPackageAcceptableCallable); + } + + $packages = array(); + foreach ($packageNameMap as $name => $void) { + $matches = array(); + foreach ($this->whatProvides($name, false, $isPackageAcceptableCallable) as $match) { + if ($match->getName() === $name) { + $matches[] = $match; + } + } + $packages = array_merge($packages, $matches); + } + return $packages; + } + /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 703f92e35..55b76d33d 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -67,9 +67,10 @@ interface RepositoryInterface extends \Countable * Returns list of registered packages with the supplied name * * @param bool[] $packageNameMap + * @param $isPackageAcceptableCallable * @return PackageInterface[] */ - public function loadPackages(array $packageNameMap); + public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable); /** * Searches the repository for packages containing the query