Refactor the handling of conflict rules in the solver
Conflict rules are not added in the solver based on the packages loaded in the solver by require rules, instead of loading remote metadata for them. This has 2 benefits: - it reduces the number of conflict rules in the solver in case of conflict rules targetting packages which are not required - it fixes the behavior of replaces, which is meant to conflict with all versions of the replaced package, without introducing a performance regression (this behavior was changed when optimizing composer in the past).pull/7454/head
parent
734735c691
commit
e5b948c683
|
@ -317,12 +317,12 @@ class Pool implements \Countable
|
|||
* Checks if the package matches the given constraint directly or through
|
||||
* provided or replaced packages
|
||||
*
|
||||
* @param array|PackageInterface $candidate
|
||||
* @param PackageInterface $candidate
|
||||
* @param string $name Name of the package to be matched
|
||||
* @param ConstraintInterface $constraint The constraint to verify
|
||||
* @return int One of the MATCH* constants of this class or 0 if there is no match
|
||||
*/
|
||||
private function match($candidate, $name, ConstraintInterface $constraint = null, $bypassFilters)
|
||||
public function match($candidate, $name, ConstraintInterface $constraint = null, $bypassFilters)
|
||||
{
|
||||
$candidateName = $candidate->getName();
|
||||
$candidateVersion = $candidate->getVersion();
|
||||
|
|
|
@ -28,6 +28,9 @@ class RuleSetGenerator
|
|||
protected $installedMap;
|
||||
protected $whitelistedMap;
|
||||
protected $addedMap;
|
||||
protected $conflictAddedMap;
|
||||
protected $addedPackages;
|
||||
protected $addedPackagesByNames;
|
||||
|
||||
public function __construct(PolicyInterface $policy, Pool $pool)
|
||||
{
|
||||
|
@ -185,6 +188,7 @@ class RuleSetGenerator
|
|||
$workQueue->enqueue($package);
|
||||
|
||||
while (!$workQueue->isEmpty()) {
|
||||
/** @var PackageInterface $package */
|
||||
$package = $workQueue->dequeue();
|
||||
if (isset($this->addedMap[$package->id])) {
|
||||
continue;
|
||||
|
@ -192,6 +196,11 @@ class RuleSetGenerator
|
|||
|
||||
$this->addedMap[$package->id] = true;
|
||||
|
||||
$this->addedPackages[] = $package;
|
||||
foreach ($package->getNames() as $name) {
|
||||
$this->addedPackagesByNames[$name][] = $package;
|
||||
}
|
||||
|
||||
foreach ($package->getRequires() as $link) {
|
||||
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
|
||||
continue;
|
||||
|
@ -206,32 +215,6 @@ class RuleSetGenerator
|
|||
}
|
||||
}
|
||||
|
||||
foreach ($package->getConflicts() as $link) {
|
||||
$possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||
|
||||
foreach ($possibleConflicts as $conflict) {
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, $link));
|
||||
}
|
||||
}
|
||||
|
||||
// check obsoletes and implicit obsoletes of a package
|
||||
$isInstalled = isset($this->installedMap[$package->id]);
|
||||
|
||||
foreach ($package->getReplaces() as $link) {
|
||||
$obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||
|
||||
foreach ($obsoleteProviders as $provider) {
|
||||
if ($provider === $package) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
|
||||
$reason = $isInstalled ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$packageName = $package->getName();
|
||||
$obsoleteProviders = $this->pool->whatProvides($packageName, null);
|
||||
|
||||
|
@ -250,6 +233,49 @@ class RuleSetGenerator
|
|||
}
|
||||
}
|
||||
|
||||
protected function addConflictRules()
|
||||
{
|
||||
/** @var PackageInterface $package */
|
||||
foreach ($this->addedPackages as $package) {
|
||||
foreach ($package->getConflicts() as $link) {
|
||||
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var PackageInterface $possibleConflict */
|
||||
foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) {
|
||||
$conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true);
|
||||
|
||||
if ($conflictMatch === Pool::MATCH || $conflictMatch === Pool::MATCH_REPLACE) {
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $possibleConflict, Rule::RULE_PACKAGE_CONFLICT, $link));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// check obsoletes and implicit obsoletes of a package
|
||||
$isInstalled = isset($this->installedMap[$package->id]);
|
||||
|
||||
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 = $isInstalled ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
|
||||
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function obsoleteImpossibleForAlias($package, $provider)
|
||||
{
|
||||
$packageIsAlias = $package instanceof AliasPackage;
|
||||
|
@ -327,12 +353,20 @@ class RuleSetGenerator
|
|||
$this->pool->setWhitelist($this->whitelistedMap);
|
||||
|
||||
$this->addedMap = array();
|
||||
$this->conflictAddedMap = array();
|
||||
$this->addedPackages = array();
|
||||
$this->addedPackagesByNames = array();
|
||||
foreach ($this->installedMap as $package) {
|
||||
$this->addRulesForPackage($package, $ignorePlatformReqs);
|
||||
}
|
||||
|
||||
$this->addRulesForJobs($ignorePlatformReqs);
|
||||
|
||||
$this->addConflictRules();
|
||||
|
||||
// Remove references to packages
|
||||
$this->addedPackages = $this->addedPackagesByNames = null;
|
||||
|
||||
return $this->rules;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue