diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index 7088b3d50..0ab1b67e4 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -87,20 +87,20 @@ class Pool implements \Countable * packages must match or null to return all * @return PackageInterface[] A set of packages */ - public function whatProvides($name, ConstraintInterface $constraint = null, $allowProvide = true) + public function whatProvides($name, ConstraintInterface $constraint = null) { - $key = ((int) $allowProvide).$constraint; + $key = (string) $constraint; if (isset($this->providerCache[$name][$key])) { return $this->providerCache[$name][$key]; } - return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint, $allowProvide); + return $this->providerCache[$name][$key] = $this->computeWhatProvides($name, $constraint); } /** * @see whatProvides */ - private function computeWhatProvides($name, $constraint, $allowProvide = true) + private function computeWhatProvides($name, $constraint) { if (!isset($this->packageByName[$name])) { return array(); @@ -113,14 +113,9 @@ class Pool implements \Countable case self::MATCH_NONE: break; - case self::MATCH_PROVIDE: - if ($allowProvide) { - $matches[] = $candidate; - } - break; - case self::MATCH: case self::MATCH_REPLACE: + case self::MATCH_PROVIDE: $matches[] = $candidate; break; diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index fe1afd12e..7885efda3 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -29,10 +29,7 @@ abstract class Rule const RULE_FIXED = 3; const RULE_PACKAGE_CONFLICT = 6; const RULE_PACKAGE_REQUIRES = 7; - const RULE_PACKAGE_OBSOLETES = 8; - const RULE_INSTALLED_PACKAGE_OBSOLETES = 9; const RULE_PACKAGE_SAME_NAME = 10; - const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11; const RULE_LEARNED = 12; const RULE_PACKAGE_ALIAS = 13; @@ -191,83 +188,50 @@ abstract class Rule return $text; - case self::RULE_PACKAGE_OBSOLETES: - if (count($literals) === 2 && $literals[0] < 0 && $literals[1] < 0) { - $package1 = $pool->literalToPackage($literals[0]); - $package2 = $pool->literalToPackage($literals[1]); - - $replaces1 = $this->getReplacedNames($package1); - $replaces2 = $this->getReplacedNames($package2); - - $reason = null; - if ($conflictingNames = array_values(array_intersect($replaces1, $replaces2))) { - $reason = 'They both replace '.(count($conflictingNames) > 1 ? '['.implode(', ', $conflictingNames).']' : $conflictingNames[0]).' and thus cannot coexist.'; - } elseif (in_array($package1->getName(), $replaces2, true)) { - $reason = $package2->getName().' replaces '.$package1->getName().' and thus cannot coexist with it.'; - } elseif (in_array($package2->getName(), $replaces1, true)) { - $reason = $package1->getName().' replaces '.$package2->getName().' and thus cannot coexist with it.'; - } - - if ($reason) { - if (isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) { - // swap vars so the if below passes - $tmp = $package2; - $package2 = $package1; - $package1 = $tmp; - } - if (!isset($installedMap[$package1->id]) && isset($installedMap[$package2->id])) { - return $package1->getPrettyString().' cannot be installed as that would require removing '.$package2->getPrettyString().'. '.$reason; - } - - if (!isset($installedMap[$package1->id]) && !isset($installedMap[$package2->id])) { - return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'. '.$reason; - } - } - - return 'Only one of these can be installed: '.$package1->getPrettyString().', '.$package2->getPrettyString().'.'; - } - - return $ruleText; - case self::RULE_INSTALLED_PACKAGE_OBSOLETES: - return $ruleText; case self::RULE_PACKAGE_SAME_NAME: - $replacedNames = null; $packageNames = array(); foreach ($literals as $literal) { $package = $pool->literalToPackage($literal); - $pkgReplaces = $this->getReplacedNames($package); - if ($pkgReplaces) { - if ($replacedNames === null) { - $replacedNames = $this->getReplacedNames($package); + $packageNames[$package->getName()] = true; + } + $replacedName = $this->reasonData; + + if (count($packageNames) > 1) { + $reason = null; + + if (!isset($packageNames[$replacedName])) { + $reason = 'They '.(count($literals) == 2 ? 'both' : 'all').' replace '.$replacedName.' and thus cannot coexist.'; + } else { + $replacerNames = $packageNames; + unset($replacerNames[$replacedName]); + $replacerNames = array_keys($replacerNames); + + if (count($replacerNames) == 1) { + $reason = $replacerNames[0] . ' replaces '; } else { - $replacedNames = array_intersect($replacedNames, $this->getReplacedNames($package)); + $reason = '['.implode(', ', $replacerNames).'] replace '; } + $reason .= $replacedName.' and thus cannot coexist with it.'; } - $packageNames[$package->getName()] = true; - } - if ($replacedNames) { - $replacedNames = array_values(array_intersect(array_keys($packageNames), $replacedNames)); - } - if ($replacedNames && count($packageNames) > 1) { - $replacer = null; + $installedPackages = array(); + $removablePackages = array(); foreach ($literals as $literal) { - $package = $pool->literalToPackage($literal); - if (array_intersect($replacedNames, $this->getReplacedNames($package))) { - $replacer = $package; - break; + if (isset($installedMap[abs($literal)])) { + $installedPackages[] = $pool->literalToPackage($literal); + } else { + $removablePackages[] = $pool->literalToPackage($literal); } } - $replacedNames = count($replacedNames) > 1 ? '['.implode(', ', $replacedNames).']' : $replacedNames[0]; - if ($replacer) { - return 'Only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '. '.$replacer->getName().' replaces '.$replacedNames.' and thus cannot coexist with it.'; + if ($installedPackages && $removablePackages) { + return $this->formatPackagesUnique($pool, $removablePackages).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages).'. '.$reason; } + + return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals).'. '.$reason; } return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '.'; - case self::RULE_PACKAGE_IMPLICIT_OBSOLETES: - return $ruleText; case self::RULE_LEARNED: if (isset($learnedPool[$this->reasonData])) { $learnedString = ', learned rules:'."\n - "; diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index ca10702a2..e6ac7ac31 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -160,8 +160,12 @@ class RuleSetGenerator $this->addedMap[$package->id] = true; $this->addedPackages[] = $package; - foreach ($package->getNames(false) as $name) { - $this->addedPackagesByNames[$name][] = $package; + if (!$package instanceof AliasPackage) { + foreach ($package->getNames(false) as $name) { + $this->addedPackagesByNames[$name][] = $package; + } + } else { + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package)); } foreach ($package->getRequires() as $link) { @@ -177,29 +181,6 @@ class RuleSetGenerator $workQueue->enqueue($require); } } - - $packageName = $package->getName(); - $obsoleteProviders = $this->pool->whatProvides($packageName, null, false); - - foreach ($obsoleteProviders as $provider) { - if ($provider === $package) { - continue; - } - - if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package)); - } else { - if (!isset($this->conflictsForName[$packageName])) { - $this->conflictsForName[$packageName] = array(); - } - if (!$package instanceof AliasPackage) { - $this->conflictsForName[$packageName][$package->id] = $package; - } - if (!$provider instanceof AliasPackage) { - $this->conflictsForName[$packageName][$provider->id] = $provider; - } - } - } } } @@ -218,39 +199,17 @@ class RuleSetGenerator /** @var PackageInterface $possibleConflict */ foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) { - $conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint()); - - if ($conflictMatch === Pool::MATCH || $conflictMatch === Pool::MATCH_REPLACE) { + if ($this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint())) { $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $possibleConflict, Rule::RULE_PACKAGE_CONFLICT, $link)); } - - } - } - - // check obsoletes and implicit obsoletes of a package - foreach ($package->getReplaces() as $link) { - if (!isset($this->addedPackagesByNames[$link->getTarget()])) { - continue; - } - - /** @var PackageInterface $possibleConflict */ - foreach ($this->addedPackagesByNames[$link->getTarget()] as $provider) { - if ($provider === $package) { - continue; - } - - if (!$this->obsoleteImpossibleForAlias($package, $provider)) { - $reason = Rule::RULE_PACKAGE_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link)); - } } } } - foreach ($this->conflictsForName as $name => $packages) { + foreach ($this->addedPackagesByNames as $name => $packages) { if (count($packages) > 1) { $reason = Rule::RULE_PACKAGE_SAME_NAME; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createMultiConflictRule($packages, $reason, null)); + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createMultiConflictRule($packages, $reason, $name)); } } } diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts.test index 3a9700103..a73dd3dc9 100644 --- a/tests/Composer/Test/Fixtures/installer/provider-conflicts.test +++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts.test @@ -42,7 +42,7 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - __root__ is present at version 1.2.3 and cannot be modified by Composer - - provider/pkg 1.0.0 cannot be installed as that would require removing __root__ 1.2.3. They both replace root-replaced/transitive-replaced and thus cannot coexist. + - provider/pkg[1.0.0] cannot be installed as that would require removing __root__[1.2.3]. They both replace root-replaced/transitive-replaced and thus cannot coexist. - Root composer.json requires provider/pkg * -> satisfiable by provider/pkg[1.0.0]. --EXPECT--