diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index eb1757b47..49b9168a4 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -53,7 +53,7 @@ class RemoveCommand extends BaseCommand new InputOption('no-audit', null, InputOption::VALUE_NONE, 'Skip the audit step after updating the composer.lock file (can also be set via the COMPOSER_NO_AUDIT=1 env var).'), new InputOption('audit-format', null, InputOption::VALUE_REQUIRED, 'Audit output format. Must be "table", "plain", "json", or "summary".', Auditor::FORMAT_SUMMARY, Auditor::FORMATS), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), - new InputOption('update-with-dependencies', 'w', InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies. (Deprecrated, is now default behavior)'), + new InputOption('update-with-dependencies', 'w', InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies. (Deprecated, is now default behavior)'), new InputOption('update-with-all-dependencies', 'W', InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'), new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), new InputOption('no-update-with-dependencies', null, InputOption::VALUE_NONE, 'Does not allow inherited dependencies to be updated with explicit dependencies.'), diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 83c33aed6..5b4e1e3de 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -233,19 +233,26 @@ EOT } $pkg = PackageSorter::getMostCurrentVersion($this->getRepos()->findPackages($name)); - if ($pkg instanceof CompletePackageInterface && count(array_intersect($devTags, array_map('strtolower', $pkg->getKeywords()))) > 0) { - $devPackages[] = $name; + if ($pkg instanceof CompletePackageInterface) { + $pkgDevTags = array_intersect($devTags, array_map('strtolower', $pkg->getKeywords())); + if (count($pkgDevTags) > 0) { + $devPackages[] = $pkgDevTags; + } } } if (count($devPackages) === count($requirements)) { $plural = count($requirements) > 1 ? 's' : ''; $plural2 = count($requirements) > 1 ? 'are' : 'is'; - $io->warning('The package'.$plural.' you required '.$plural2.' recommended to be placed in require-dev but you did not use --dev.'); + $plural3 = count($requirements) > 1 ? 'they are' : 'it is'; + $pkgDevTags = array_unique(array_merge(...$devPackages)); + $io->warning('The package'.$plural.' you required '.$plural2.' recommended to be placed in require-dev (because '.$plural3.' tagged as "'.implode('", "', $pkgDevTags).'") but you did not use --dev.'); if ($io->askConfirmation('Do you want to re-run the command with --dev? [yes]? ')) { $input->setOption('dev', true); } } + + unset($devPackages, $pkgDevTags); } $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 2ec21c33f..47ef25de9 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -677,7 +677,12 @@ EOT */ protected function getRootRequires(): array { - $rootPackage = $this->requireComposer()->getPackage(); + $composer = $this->tryComposer(); + if ($composer === null) { + return []; + } + + $rootPackage = $composer->getPackage(); return array_map( 'strtolower', diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index afd735e7f..d415dd14e 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -223,7 +223,7 @@ class RuleSetGenerator if ($platformRequirementFilter->isIgnored($link->getTarget())) { continue; } elseif ($platformRequirementFilter instanceof IgnoreListPlatformRequirementFilter) { - $constraint = $platformRequirementFilter->filterConstraint($link->getTarget(), $constraint); + $constraint = $platformRequirementFilter->filterConstraint($link->getTarget(), $constraint, false); } $conflicts = $this->pool->whatProvides($link->getTarget(), $constraint); diff --git a/src/Composer/Filter/PlatformRequirementFilter/IgnoreListPlatformRequirementFilter.php b/src/Composer/Filter/PlatformRequirementFilter/IgnoreListPlatformRequirementFilter.php index 700795832..43cc33469 100644 --- a/src/Composer/Filter/PlatformRequirementFilter/IgnoreListPlatformRequirementFilter.php +++ b/src/Composer/Filter/PlatformRequirementFilter/IgnoreListPlatformRequirementFilter.php @@ -60,13 +60,16 @@ final class IgnoreListPlatformRequirementFilter implements PlatformRequirementFi return Preg::isMatch($this->ignoreRegex, $req); } - public function filterConstraint(string $req, ConstraintInterface $constraint): ConstraintInterface + /** + * @param bool $allowUpperBoundOverride For conflicts we do not want the upper bound to be skipped + */ + public function filterConstraint(string $req, ConstraintInterface $constraint, bool $allowUpperBoundOverride = true): ConstraintInterface { if (!PlatformRepository::isPlatformPackage($req)) { return $constraint; } - if (!Preg::isMatch($this->ignoreUpperBoundRegex, $req)) { + if (!$allowUpperBoundOverride || !Preg::isMatch($this->ignoreUpperBoundRegex, $req)) { return $constraint; } diff --git a/tests/Composer/Test/Command/ShowCommandTest.php b/tests/Composer/Test/Command/ShowCommandTest.php index 9c9880203..540faeaba 100644 --- a/tests/Composer/Test/Command/ShowCommandTest.php +++ b/tests/Composer/Test/Command/ShowCommandTest.php @@ -192,6 +192,20 @@ outdated/patch 1.0.0 ! 1.0.1', } } + public function testShowPlatformWorksWithoutComposerJson(): void + { + $this->initTempComposer([]); + unlink('./composer.json'); + unlink('./auth.json'); + + $appTester = $this->getApplicationTester(); + $appTester->run(['command' => 'show', '-p' => true]); + $output = trim($appTester->getDisplay(true)); + foreach (Regex::matchAll('{^(\w+)}m', $output)->matches as $m) { + self::assertTrue(PlatformRepository::isPlatformPackage((string) $m[1])); + } + } + public function testOutdatedWithZeroMajor(): void { $this->initTempComposer([ diff --git a/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirement-list-upper-bounds.test b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirement-list-upper-bounds.test index 0c7e776de..62d8fbb4b 100644 --- a/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirement-list-upper-bounds.test +++ b/tests/Composer/Test/Fixtures/installer/update-ignore-platform-package-requirement-list-upper-bounds.test @@ -7,13 +7,15 @@ Update with ignore-platform-req list ignoring upper bound of a dependency "type": "package", "package": [ { "name": "a/a", "version": "1.0.1", "require": { "ext-foo-bar": "3.*" } }, - { "name": "b/b", "version": "1.0.1", "require": { "ext-foo-bar": "10.*" } } + { "name": "b/b", "version": "1.0.1", "require": { "ext-foo-bar": "10.*" } }, + { "name": "c/c", "version": "1.0.1", "conflict": { "ext-foo-bar": "4.0.0 - 4.0.2", "php": "3.0.*" } } ] } ], "require": { "a/a": "1.0.*", "b/b": "1.0.*", + "c/c": "1.0.*", "php": "^4.3", "ext-foo-baz": "9.0.0" },