From e85da00dff099370ef6a367ca836461a26243573 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Jun 2020 15:43:24 +0200 Subject: [PATCH] Add a way to ignore only some packages in --ignore-platform-reqs, and make the platform check ignore those packages which were ignored as requirements, fixes #8861 --- doc/03-cli.md | 24 ++++++++++--- src/Composer/Autoload/AutoloadGenerator.php | 35 +++++++++++++++++-- src/Composer/Command/CreateProjectCommand.php | 19 +++++----- src/Composer/Command/DumpAutoloadCommand.php | 6 ++++ src/Composer/Command/InitCommand.php | 20 ++++++----- src/Composer/Command/InstallCommand.php | 8 +++-- src/Composer/Command/RemoveCommand.php | 8 +++-- src/Composer/Command/RequireCommand.php | 10 ++++-- src/Composer/Command/UpdateCommand.php | 8 +++-- .../DependencyResolver/RuleSetGenerator.php | 9 +++-- src/Composer/DependencyResolver/Solver.php | 12 +++---- .../SolverProblemsException.php | 2 +- src/Composer/Installer.php | 17 +++++++-- .../Package/Version/VersionSelector.php | 12 +++---- tests/Composer/Test/InstallerTest.php | 12 +++++-- 15 files changed, 146 insertions(+), 56 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 161f2ee69..a29098249 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -114,7 +114,9 @@ resolution. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. ## update / u @@ -187,7 +189,9 @@ php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.* * **--apcu-autoloader:** Use APCu to cache found/not-found classes. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. @@ -233,7 +237,9 @@ If you do not specify a package, composer will prompt you to search for a packag * **--update-with-all-dependencies:** Also update dependencies of the newly required packages, including those that are root requirements. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. @@ -269,7 +275,9 @@ uninstalled. * **--update-with-dependencies:** Also update dependencies of the removed packages. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. @@ -724,7 +732,9 @@ By default the command checks for the packages on packagist.org. * **--no-install:** Disables installation of the vendors. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. ## dump-autoload (dumpautoload) @@ -748,6 +758,10 @@ performance. Implicitly enables `--optimize`. * **--apcu:** Use APCu to cache found/not-found classes. * **--no-dev:** Disables autoload-dev rules. +* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` + requirements and skip the [platform check](07-runtime.md#platform-check) for these. + You can also ignore specific packages only using `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. ## clear-cache / clearcache / cc diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index ef3d54573..b7c5cee8b 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -61,6 +61,11 @@ class AutoloadGenerator */ private $runScripts = false; + /** + * @var bool|array + */ + private $ignorePlatformReqs = false; + public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null) { $this->eventDispatcher = $eventDispatcher; @@ -103,6 +108,26 @@ class AutoloadGenerator $this->runScripts = (bool) $runScripts; } + /** + * Sets whether platform requirements should be ignored + * + * If this is set to true, the platform check file will not be generated + * If this is set to false, the platform check file will be generated with all requirements + * If this is set to string[], those packages will be ignored from the platform check file + * + * @param array|bool $ignorePlatformReqs + */ + public function setIgnorePlatformRequirements($ignorePlatformReqs) + { + if (is_array($ignorePlatformReqs)) { + $this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) { + return (bool) preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); + }); + } else { + $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + } + } + public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsrPackages = false, $suffix = '') { if ($this->classMapAuthoritative) { @@ -314,9 +339,9 @@ EOF; unlink($includeFilesFilePath); } $filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); - $checkPlatform = $config->get('platform-check'); + $checkPlatform = $config->get('platform-check') && $this->ignorePlatformReqs !== true; if ($checkPlatform) { - $platformCheckContent = $this->getPlatformCheck($packageMap); + $platformCheckContent = $this->getPlatformCheck($packageMap, $this->ignorePlatformReqs ?: array()); if (null === $platformCheckContent) { $checkPlatform = false; } @@ -575,7 +600,7 @@ EOF; return $baseDir . (($path !== false) ? var_export($path, true) : ""); } - protected function getPlatformCheck($packageMap) + protected function getPlatformCheck($packageMap, array $ignorePlatformReqs) { $lowestPhpVersion = Bound::zero(); $requiredExtensions = array(); @@ -593,6 +618,10 @@ EOF; foreach ($packageMap as $item) { list($package, $installPath) = $item; foreach ($package->getRequires() as $link) { + if (in_array($link->getTarget(), $ignorePlatformReqs, true)) { + continue; + } + if ('php' === $link->getTarget() && ($constraint = $link->getConstraint())) { if ($constraint->getLowerBound()->compareTo($lowestPhpVersion, '>')) { $lowestPhpVersion = $constraint->getLowerBound(); diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 7943e01ac..1960fe1ef 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -80,7 +80,7 @@ class CreateProjectCommand extends BaseCommand new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deleting the vcs folder.'), new InputOption('remove-vcs', null, InputOption::VALUE_NONE, 'Whether to force deletion of the vcs folder without prompting.'), new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), )) ->setHelp( <<setOption('no-plugins', true); } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + return $this->installProject( $io, $config, @@ -143,7 +147,7 @@ EOT $input->getOption('no-scripts'), $input->getOption('no-progress'), $input->getOption('no-install'), - $input->getOption('ignore-platform-reqs'), + $ignorePlatformReqs, !$input->getOption('no-secure-http'), $input->getOption('add-repository') ); @@ -336,19 +340,16 @@ EOT $repositorySet = new RepositorySet($stability); $repositorySet->addRepository($sourceRepo); - $platformRepo = null; - if (!$ignorePlatformReqs) { - $platformOverrides = $config->get('platform') ?: array(); - $platformRepo = new PlatformRepository(array(), $platformOverrides); - } + $platformOverrides = $config->get('platform') ?: array(); + $platformRepo = new PlatformRepository(array(), $platformOverrides); // find the latest version if there are multiple $versionSelector = new VersionSelector($repositorySet, $platformRepo); - $package = $versionSelector->findBestCandidate($name, $packageVersion, $stability); + $package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $ignorePlatformReqs); if (!$package) { $errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability"); - if ($platformRepo && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) { + if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) { throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version, PHP extensions and Composer version.'); } diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index 9627b2a88..240a6c558 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -35,6 +35,7 @@ class DumpAutoloadCommand extends BaseCommand new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'), new InputOption('apcu', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s) from the platform check.'), )) ->setHelp( <<getIO()->write('Generating autoload files'); } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $generator = $composer->getAutoloadGenerator(); $generator->setDevMode(!$input->getOption('no-dev')); $generator->setClassMapAuthoritative($authoritative); $generator->setApcu($apcu); $generator->setRunScripts(!$input->getOption('no-scripts')); + $generator->setIgnorePlatformRequirements($ignorePlatformReqs); $numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); if ($authoritative) { diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 7f00daa5f..3acad2fe5 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -723,26 +723,28 @@ EOT */ private function findBestVersionAndNameForPackage(InputInterface $input, $name, PlatformRepository $platformRepo = null, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) { - // ignore platform repo if platform requirements are ignored - $ignorePlatformReqs = $input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs'); - if ($ignorePlatformReqs) { - $platformRepo = null; + // handle ignore-platform-reqs flag if present + $ignorePlatformReqs = false; + if ($input->hasOption('ignore-platform-reqs')) { + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; } // find the latest version allowed in this repo set $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo); - $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability); + $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs); if (!$package) { // platform packages can not be found in the pool in versions other than the local platform's has // so if platform reqs are ignored we just take the user's word for it - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($name, $ignorePlatformReqs))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { return array($name, $requiredVersion ?: '*'); } // Check whether the PHP version was the problem - if ($platformRepo && $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true)) { + if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true)) { throw new \InvalidArgumentException(sprintf( 'Package %s at version %s has a PHP requirement incompatible with your PHP version, PHP extensions and Composer version', $name, @@ -750,7 +752,7 @@ EOT )); } // Check whether the required version was the problem - if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $preferredStability)) { + if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $preferredStability, $ignorePlatformReqs)) { throw new \InvalidArgumentException(sprintf( 'Could not find package %s in a version matching %s', $name, @@ -758,7 +760,7 @@ EOT )); } // Check whether the PHP version was the problem for all versions - if ($platformRepo && $versionSelector->findBestCandidate($name, null, $preferredStability, true)) { + if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, null, $preferredStability, true)) { throw new \InvalidArgumentException(sprintf( 'Could not find package %s in any version matching your PHP version, PHP extensions and Composer version', $name diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 5f5565248..4be349819 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -49,7 +49,7 @@ class InstallCommand extends BaseCommand new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp( @@ -103,6 +103,10 @@ EOT $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative'); $apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) @@ -114,7 +118,7 @@ EOT ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 3d6c56df5..ef77391a9 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -50,7 +50,7 @@ class RemoveCommand extends BaseCommand 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.'), new InputOption('unused', null, InputOption::VALUE_NONE, 'Remove all packages which are locked but not required by any other package.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), @@ -238,6 +238,10 @@ EOT $io->writeError('Running composer update '.implode(' ', $packages).$flags); + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setVerbose($input->getOption('verbose')) ->setDevMode($updateDevMode) @@ -248,7 +252,7 @@ EOT ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowList($packages) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setRunScripts(!$input->getOption('no-scripts')) ->setDryRun($dryRun) ; diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 250e99513..69bf1c528 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -69,7 +69,7 @@ class RequireCommand extends InitCommand new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'), new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-dependencies'), new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'), @@ -181,7 +181,7 @@ EOT $input, $output, $input->getArgument('packages'), - $input->getOption('ignore-platform-reqs') ? null : $platformRepo, + $platformRepo, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed') @@ -287,6 +287,10 @@ EOT $install = Installer::create($io, $composer); + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) @@ -300,7 +304,7 @@ EOT ->setUpdate(true) ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 3ca704620..2329d1c24 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -60,7 +60,7 @@ class UpdateCommand extends BaseCommand new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'), @@ -196,6 +196,10 @@ EOT $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) @@ -212,7 +216,7 @@ EOT ->setUpdateMirrors($updateMirrors) ->setUpdateAllowList($packages) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 02c3fd598..ffb88d56c 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -169,7 +169,7 @@ class RuleSetGenerator } foreach ($package->getRequires() as $link) { - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { continue; } @@ -193,7 +193,7 @@ class RuleSetGenerator continue; } - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { continue; } @@ -253,7 +253,7 @@ class RuleSetGenerator } foreach ($request->getRequires() as $packageName => $constraint) { - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { continue; } @@ -272,6 +272,9 @@ class RuleSetGenerator } } + /** + * @param bool|array $ignorePlatformReqs + */ public function getRulesFor(Request $request, $ignorePlatformReqs = false) { $this->rules = new RuleSet; diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index d0f90dc59..7c2d3a00c 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -164,13 +164,13 @@ class Solver } /** - * @param Request $request - * @param bool $ignorePlatformReqs + * @param Request $request + * @param bool|array $ignorePlatformReqs */ - protected function checkForRootRequireProblems($request, $ignorePlatformReqs) + protected function checkForRootRequireProblems(Request $request, $ignorePlatformReqs) { foreach ($request->getRequires() as $packageName => $constraint) { - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { continue; } @@ -183,8 +183,8 @@ class Solver } /** - * @param Request $request - * @param bool $ignorePlatformReqs + * @param Request $request + * @param bool|array $ignorePlatformReqs * @return LockTransaction */ public function solve(Request $request, $ignorePlatformReqs = false) diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index e6697fb05..dd1cb48b4 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -74,7 +74,7 @@ class SolverProblemsException extends \RuntimeException // TODO remove before 2.0 final if (!class_exists('PHPUnit\Framework\TestCase', false)) { if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match')) { - $hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs, but this will also ignore your PHP version and may result in bigger problems down the line."; + $hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs=composer-plugin-api, but this may result in broken plugins and bigger problems down the line."; } else { $hints[] = "You are using a snapshot build of Composer 2, which may be the cause of the problem. Run `composer self-update --stable` and then try again. In case it solves the problem, please report an issue mentioning Composer 2."; } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 2bca0618b..23421f666 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -308,6 +308,7 @@ class Installer $this->autoloadGenerator->setClassMapAuthoritative($this->classMapAuthoritative); $this->autoloadGenerator->setApcu($this->apcuAutoloader); $this->autoloadGenerator->setRunScripts($this->runScripts); + $this->autoloadGenerator->setIgnorePlatformRequirements($this->ignorePlatformReqs); $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); } @@ -737,7 +738,7 @@ class Installer $rootRequires = array(); foreach ($requires as $req => $constraint) { // skip platform requirements from the root package to avoid filtering out existing platform packages - if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { + if ((true === $this->ignorePlatformReqs || (is_array($this->ignorePlatformReqs) && in_array($req, $this->ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { continue; } if ($constraint instanceof Link) { @@ -1117,12 +1118,22 @@ class Installer /** * set ignore Platform Package requirements * - * @param bool $ignorePlatformReqs + * If this is set to true, all platform requirements are ignored + * If this is set to false, no platform requirements are ignored + * If this is set to string[], those packages will be ignored + * + * @param bool|array $ignorePlatformReqs * @return Installer */ public function setIgnorePlatformRequirements($ignorePlatformReqs = false) { - $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + if (is_array($ignorePlatformReqs)) { + $this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) { + return (bool) preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); + }); + } else { + $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + } return $this; } diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index 82d60dce7..d42c9f167 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -33,7 +33,7 @@ class VersionSelector { private $repositorySet; - private $platformConstraints; + private $platformConstraints = array(); private $parser; @@ -44,7 +44,6 @@ class VersionSelector { $this->repositorySet = $repositorySet; if ($platformRepo) { - $this->platformConstraints = array(); foreach ($platformRepo->getPackages() as $package) { $this->platformConstraints[$package->getName()][] = new Constraint('==', $package->getVersion()); } @@ -58,7 +57,7 @@ class VersionSelector * @param string $packageName * @param string $targetPackageVersion * @param string $preferredStability - * @param bool $ignorePlatformReqs + * @param bool|array $ignorePlatformReqs * @return PackageInterface|false */ public function findBestCandidate($packageName, $targetPackageVersion = null, $preferredStability = 'stable', $ignorePlatformReqs = false) @@ -71,13 +70,14 @@ class VersionSelector $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null; $candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint); - if ($this->platformConstraints && !$ignorePlatformReqs) { + if ($this->platformConstraints && true !== $ignorePlatformReqs) { $platformConstraints = $this->platformConstraints; - $candidates = array_filter($candidates, function ($pkg) use ($platformConstraints) { + $ignorePlatformReqs = $ignorePlatformReqs ?: array(); + $candidates = array_filter($candidates, function ($pkg) use ($platformConstraints, $ignorePlatformReqs) { $reqs = $pkg->getRequires(); foreach ($reqs as $name => $link) { - if (isset($platformConstraints[$name])) { + if (!in_array($name, $ignorePlatformReqs, true) && isset($platformConstraints[$name])) { foreach ($platformConstraints[$name] as $constraint) { if ($link->getConstraint()->matches($constraint)) { continue 2; diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 78c20a1be..1ba25847c 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -264,10 +264,14 @@ class InstallerTest extends TestCase $application = new Application; $application->get('install')->setCode(function ($input, $output) use ($installer) { + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $installer ->setDevMode(!$input->getOption('no-dev')) ->setDryRun($input->getOption('dry-run')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); + ->setIgnorePlatformRequirements($ignorePlatformReqs); return $installer->run(); }); @@ -287,6 +291,10 @@ class InstallerTest extends TestCase $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $installer ->setDevMode(!$input->getOption('no-dev')) ->setUpdate(true) @@ -297,7 +305,7 @@ class InstallerTest extends TestCase ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); + ->setIgnorePlatformRequirements($ignorePlatformReqs); return $installer->run(); });