diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index b6447223b..5eef7fd32 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -75,7 +75,7 @@ class DefaultPolicy implements PolicyInterface foreach ($packages as &$literals) { $policy = $this; usort($literals, function ($a, $b) use ($policy, $pool, $installed) { - return $policy->compareByPriorityPreferInstalled($pool, $installed, $a->getPackage(), $b->getPackage()); + return $policy->compareByPriorityPreferInstalled($pool, $installed, $a->getPackage(), $b->getPackage(), true); }); } @@ -87,6 +87,11 @@ class DefaultPolicy implements PolicyInterface $selected = call_user_func_array('array_merge', $packages); + // now sort the result across all packages to respect replaces across packages + usort($selected, function ($a, $b) use ($policy, $pool, $installed) { + return $policy->compareByPriorityPreferInstalled($pool, $installed, $a->getPackage(), $b->getPackage()); + }); + return $selected; } @@ -110,10 +115,26 @@ class DefaultPolicy implements PolicyInterface return $packages; } - public function compareByPriorityPreferInstalled(Pool $pool, RepositoryInterface $installed, PackageInterface $a, PackageInterface $b) + public function compareByPriorityPreferInstalled(Pool $pool, RepositoryInterface $installed, PackageInterface $a, PackageInterface $b, $ignoreReplace = false) { if ($a->getRepository() === $b->getRepository()) { - return 0; + + if (!$ignoreReplace) { + // return original, not replaced + if ($this->replaces($a, $b)) { + return 1; // use b + } + if ($this->replaces($b, $a)) { + return -1; // use a + } + } + + // priority equal, sort by package id to make reproducible + if ($a->getId() === $b->getId()) { + return 0; + } + + return ($a->getId() < $b->getId()) ? -1 : 1; } if ($a->getRepository() === $installed) { @@ -127,6 +148,19 @@ class DefaultPolicy implements PolicyInterface return ($this->getPriority($pool, $a) > $this->getPriority($pool, $b)) ? -1 : 1; } + protected function replaces(PackageInterface $source, PackageInterface $target) + { + foreach ($source->getReplaces() as $link) { + if ($link->getTarget() === $target->getName() && + (null === $link->getConstraint() || + $link->getConstraint()->matches(new VersionConstraint('==', $target->getVersion())))) { + return true; + } + } + + return false; + } + protected function pruneToBestVersion($literals) { $bestLiterals = array($literals[0]); diff --git a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php index d9f9da05b..b294bf93d 100644 --- a/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php +++ b/tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php @@ -135,9 +135,8 @@ class DefaultPolicyTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $selected); } - public function testSelectNonReplacingFromSameRepo() + public function testPreferNonReplacingFromSameRepo() { - $this->markTestIncomplete(); $this->repo->addPackage($packageA = new MemoryPackage('A', '1.0')); $this->repo->addPackage($packageB = new MemoryPackage('B', '2.0')); @@ -147,7 +146,7 @@ class DefaultPolicyTest extends \PHPUnit_Framework_TestCase $this->pool->addRepository($this->repo); $literals = array(new Literal($packageA, true), new Literal($packageB, true)); - $expected = array(new Literal($packageA, true)); + $expected = array(new Literal($packageA, true), new Literal($packageB, true)); $selected = $this->policy->selectPreferedPackages($this->pool, $this->repoInstalled, $literals); diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index d742de9c7..911296849 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -209,7 +209,7 @@ class SolverTest extends \PHPUnit_Framework_TestCase $this->request->install('A'); $this->checkSolverResult(array( - array('job' => 'install', 'package' => $packageB), + array('job' => 'install', 'package' => $packageA), )); }