diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 92444eaac..35b97e6ae 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -518,15 +518,19 @@ class Solver * @param Problem $problem * @param Rule $conflictRule */ - private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule) + private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule, &$ruleSeen) { + $ruleSeen[spl_object_hash($conflictRule)] = true; + if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) { $why = spl_object_hash($conflictRule); $learnedWhy = $this->learnedWhy[$why]; $problemRules = $this->learnedPool[$learnedWhy]; foreach ($problemRules as $problemRule) { - $this->analyzeUnsolvableRule($problem, $problemRule); + if (!isset($ruleSeen[spl_object_hash($problemRule)])) { + $this->analyzeUnsolvableRule($problem, $problemRule, $ruleSeen); + } } return; @@ -550,7 +554,9 @@ class Solver $problem = new Problem(); $problem->addRule($conflictRule); - $this->analyzeUnsolvableRule($problem, $conflictRule); + $ruleSeen = array(); + + $this->analyzeUnsolvableRule($problem, $conflictRule, $ruleSeen); $this->problems[] = $problem; @@ -576,7 +582,7 @@ class Solver $why = $decision[Decisions::DECISION_REASON]; $problem->addRule($why); - $this->analyzeUnsolvableRule($problem, $why); + $this->analyzeUnsolvableRule($problem, $why, $ruleSeen); $literals = $why->getLiterals(); diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-7051.test b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test index 9c39c1441..7683c7482 100644 --- a/tests/Composer/Test/Fixtures/installer/github-issues-7051.test +++ b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test @@ -116,13 +116,13 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5 (conflict analysis result) - - Conclusion: don't install symfony/console v3.4.29 (conflict analysis result) - - Conclusion: don't install symfony/console v2.8.8 (conflict analysis result) - - Conclusion: don't install symfony/console v3.4.28 (conflict analysis result) + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Conclusion: don't install symfony/console v2.8.7 (conflict analysis result) - - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Conclusion: don't install symfony/console v3.4.28 (conflict analysis result) + - Conclusion: don't install symfony/console v2.8.8 (conflict analysis result) + - Conclusion: don't install symfony/console v3.4.29 (conflict analysis result) - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-9012.test b/tests/Composer/Test/Fixtures/installer/github-issues-9012.test new file mode 100644 index 000000000..f118584d1 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/github-issues-9012.test @@ -0,0 +1,325 @@ +--TEST-- + +See Github issue #9012 ( https://github.com/composer/composer/issues/9012 and https://gist.github.com/Seldaek/4e2dbc2cea4b4fd7a8207bb310ec8e34). + +Recursive analysis of the same learnt rules can lead to infinite recursion in solver. + +--COMPOSER-- +{ + "require": { + "php": ">=7.1.7", + "laravel/framework": "^6.0", + "nunomaduro/collision": "^4.0" + }, + "config": { + "preferred-install": "dist", + "sort-packages": true, + "optimize-autoloader": true, + "process-timeout": 0 + }, + "repositories": { + "laravel/framework": { + "type": "package", + "package": [ + { + "name": "laravel/framework", + "version": "v6.0.0", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + } + }, + "require": { + "symfony/console": "^4.3.4" + } + } + ] + }, + "nunomaduro/collision": { + "type": "package", + "package": [ + { + "name": "nunomaduro/collision", + "version": "v4.2.0", + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "require": { + "symfony/console": "^5.0" + } + } + ] + }, + "symfony/console": { + "type": "package", + "package": [ + { + "name": "symfony/console", + "version": "v5.1.7", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.6", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.5", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.4", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.3", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.2", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.1", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.0", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.0-RC2", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.0-RC1", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.1.0-BETA1", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.11", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.10", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.9", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.8", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.7", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.6", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.5", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.4", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.3", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.2", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.1", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v5.0.0", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.15", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.14", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.13", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.12", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.11", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.10", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.9", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.8", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.7", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.6", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.5", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.4", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.3", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.2", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.1", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.0", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.0-RC1", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.0-BETA2", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.4.0-BETA1", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.11", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.10", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.9", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.8", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.7", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.6", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.5", + "type": "library" + }, + { + "name": "symfony/console", + "version": "v4.3.4", + "type": "library" + } + ] + } + } +} + +--RUN-- +update + +--EXPECT-- + +--EXPECT-OUTPUT-- + +--EXPECT-EXIT-CODE-- +2 diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test index de0744b53..213b1e1e1 100644 --- a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test +++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test @@ -37,9 +37,9 @@ Updating dependencies Your requirements could not be resolved to an installable set of packages. Problem 1 - - Conclusion: don't install regular/pkg 1.0.3 (conflict analysis result) - - Conclusion: don't install regular/pkg 1.0.2 (conflict analysis result) - Conclusion: don't install regular/pkg 1.0.1 (conflict analysis result) + - Conclusion: don't install regular/pkg 1.0.2 (conflict analysis result) + - Conclusion: don't install regular/pkg 1.0.3 (conflict analysis result) - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3]. - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].