1
0
Fork 0

Fix PoolOptimizer should consider disjunctive MultiConstraints (#10579)

pull/10617/head
Yanick Witschi 2022-03-12 14:16:38 +01:00 committed by GitHub
parent b3f99faff7
commit ced24da7b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 7 deletions

View File

@ -110,21 +110,18 @@ class PoolOptimizer
// Extract requested package requirements
foreach ($request->getRequires() as $require => $constraint) {
$constraint = Intervals::compactConstraint($constraint);
$this->requireConstraintsPerPackage[$require][(string) $constraint] = $constraint;
$this->extractRequireConstraintsPerPackage($require, $constraint);
}
// First pass over all packages to extract information and mark package constraints irremovable
foreach ($pool->getPackages() as $package) {
// Extract package requirements
foreach ($package->getRequires() as $link) {
$constraint = Intervals::compactConstraint($link->getConstraint());
$this->requireConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint;
$this->extractRequireConstraintsPerPackage($link->getTarget(), $link->getConstraint());
}
// Extract package conflicts
foreach ($package->getConflicts() as $link) {
$constraint = Intervals::compactConstraint($link->getConstraint());
$this->conflictConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint;
$this->extractConflictConstraintsPerPackage($link->getTarget(), $link->getConstraint());
}
// Keep track of alias packages for every package so if either the alias or aliased is kept
@ -453,4 +450,55 @@ class PoolOptimizer
}
}
}
/**
* Disjunctive require constraints need to be considered in their own group. E.g. "^2.14 || ^3.3" needs to generate
* two require constraint groups in order for us to keep the best matching package for "^2.14" AND "^3.3" as otherwise, we'd
* only keep either one which can cause trouble (e.g. when using --prefer-lowest).
*
* @param string $package
* @param ConstraintInterface $constraint
* @return void
*/
private function extractRequireConstraintsPerPackage($package, ConstraintInterface $constraint)
{
foreach ($this->expandDisjunctiveMultiConstraints($constraint) as $expanded) {
$this->requireConstraintsPerPackage[$package][(string) $expanded] = $expanded;
}
}
/**
* Disjunctive conflict constraints need to be considered in their own group. E.g. "^2.14 || ^3.3" needs to generate
* two conflict constraint groups in order for us to keep the best matching package for "^2.14" AND "^3.3" as otherwise, we'd
* only keep either one which can cause trouble (e.g. when using --prefer-lowest).
*
* @param string $package
* @param ConstraintInterface $constraint
* @return void
*/
private function extractConflictConstraintsPerPackage($package, ConstraintInterface $constraint)
{
foreach ($this->expandDisjunctiveMultiConstraints($constraint) as $expanded) {
$this->conflictConstraintsPerPackage[$package][(string) $expanded] = $expanded;
}
}
/**
*
* @param ConstraintInterface $constraint
* @return ConstraintInterface[]
*/
private function expandDisjunctiveMultiConstraints(ConstraintInterface $constraint)
{
$constraint = Intervals::compactConstraint($constraint);
if ($constraint instanceof MultiConstraint && $constraint->isDisjunctive()) {
// No need to call ourselves recursively here because Intervals::compactConstraint() ensures that there
// are no nested disjunctive MultiConstraint instances possible
return $constraint->getConstraints();
}
// Regular constraints and conjunctive MultiConstraints
return array($constraint);
}
}

View File

@ -15,7 +15,7 @@ Test filters irrelevant package "package/b" in version 1.0.0
"name": "package/a",
"version": "1.0.0",
"require": {
"package/b": "^1.0"
"package/b": ">=1.0 <1.1 || ^1.2"
}
},
{
@ -25,6 +25,10 @@ Test filters irrelevant package "package/b" in version 1.0.0
{
"name": "package/b",
"version": "1.0.1"
},
{
"name": "package/b",
"version": "1.2.0"
}
]
@ -41,6 +45,10 @@ Test filters irrelevant package "package/b" in version 1.0.0
{
"name": "package/b",
"version": "1.0.1"
},
{
"name": "package/b",
"version": "1.2.0"
}
]

View File

@ -0,0 +1,55 @@
--TEST--
Test keeps package "package/b" in version 2.2.0 because for prefer-lowest either one might be relevant
--REQUEST--
{
"require": {
"package/a": "^1.0"
},
"preferLowest": true
}
--POOL-BEFORE--
[
{
"name": "package/a",
"version": "1.0.0",
"require": {
"package/b": "^1.0 || ^2.2"
}
},
{
"name": "package/b",
"version": "1.0.0"
},
{
"name": "package/b",
"version": "1.0.1"
},
{
"name": "package/b",
"version": "2.2.0"
}
]
--POOL-AFTER--
[
{
"name": "package/a",
"version": "1.0.0",
"require": {
"package/b": "^1.0"
}
},
{
"name": "package/b",
"version": "1.0.0"
},
{
"name": "package/b",
"version": "2.2.0"
}
]