1
0
Fork 0

Refactor ignore platform reqs checks (#10079)

Introduces a `PlatformRequirementFilter` with methods that help to decide if a requirement is ignored or not as discussed in #10045 but without changing behaviour.
pull/10083/head
Martin Herndl 2021-11-11 12:24:12 +01:00 committed by GitHub
parent 68847ed609
commit 3013674c92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 419 additions and 98 deletions

View File

@ -14,13 +14,15 @@ namespace Composer\Autoload;
use Composer\Config;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\Installer\InstallationManager;
use Composer\IO\IOInterface;
use Composer\Package\AliasPackage;
use Composer\Package\PackageInterface;
use Composer\Package\RootPackageInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Repository\PlatformRepository;
use Composer\Semver\Constraint\Bound;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
@ -70,14 +72,16 @@ class AutoloadGenerator
private $runScripts = false;
/**
* @var bool|string[]
* @var PlatformRequirementFilterInterface
*/
private $ignorePlatformReqs = false;
private $platformRequirementFilter;
public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null)
{
$this->eventDispatcher = $eventDispatcher;
$this->io = $io;
$this->platformRequirementFilter = PlatformRequirementFilterFactory::ignoreNothing();
}
/**
@ -133,16 +137,22 @@ class AutoloadGenerator
*
* @param bool|string[] $ignorePlatformReqs
* @return void
*
* @deprecated use setPlatformRequirementFilter instead
*/
public function setIgnorePlatformRequirements($ignorePlatformReqs)
{
if (is_array($ignorePlatformReqs)) {
$this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) {
return PlatformRepository::isPlatformPackage($req);
});
} else {
$this->ignorePlatformReqs = (bool) $ignorePlatformReqs;
trigger_error('AutoloadGenerator::setIgnorePlatformRequirements is deprecated since Composer 2.2, use setPlatformRequirementFilter instead.', E_USER_DEPRECATED);
$this->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
}
/**
* @return void
*/
public function setPlatformRequirementFilter(PlatformRequirementFilterInterface $platformRequirementFilter)
{
$this->platformRequirementFilter = $platformRequirementFilter;
}
/**
@ -391,10 +401,10 @@ EOF;
unlink($includeFilesFilePath);
}
$filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion));
$checkPlatform = $config->get('platform-check') && $this->ignorePlatformReqs !== true;
$checkPlatform = $config->get('platform-check') && !($this->platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter);
$platformCheckContent = null;
if ($checkPlatform) {
$platformCheckContent = $this->getPlatformCheck($packageMap, $this->ignorePlatformReqs ?: array(), $config->get('platform-check'), $devPackageNames);
$platformCheckContent = $this->getPlatformCheck($packageMap, $config->get('platform-check'), $devPackageNames);
if (null === $platformCheckContent) {
$checkPlatform = false;
}
@ -740,12 +750,11 @@ EOF;
/**
* @param array<int, array{0: PackageInterface, 1: string}> $packageMap
* @param string[] $ignorePlatformReqs
* @param bool $checkPlatform
* @param string[] $devPackageNames
* @return ?string
*/
protected function getPlatformCheck(array $packageMap, array $ignorePlatformReqs, $checkPlatform, array $devPackageNames)
protected function getPlatformCheck(array $packageMap, $checkPlatform, array $devPackageNames)
{
$lowestPhpVersion = Bound::zero();
$requiredExtensions = array();
@ -768,7 +777,7 @@ EOF;
}
foreach ($package->getRequires() as $link) {
if (in_array($link->getTarget(), $ignorePlatformReqs, true)) {
if ($this->platformRequirementFilter->isIgnored($link->getTarget())) {
continue;
}

View File

@ -14,6 +14,9 @@ namespace Composer\Command;
use Composer\Config;
use Composer\Factory;
use Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\Installer;
use Composer\Installer\ProjectInstaller;
use Composer\Installer\SuggestedPackagesReporter;
@ -159,7 +162,7 @@ EOT
$input->getOption('no-scripts'),
$input->getOption('no-progress'),
$input->getOption('no-install'),
$ignorePlatformReqs,
PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs),
!$input->getOption('no-secure-http'),
$input->getOption('add-repository')
);
@ -178,14 +181,13 @@ EOT
* @param bool $noScripts
* @param bool $noProgress
* @param bool $noInstall
* @param bool $ignorePlatformReqs
* @param bool $secureHttp
* @param bool $addRepository
*
* @return int
* @throws \Exception
*/
public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName = null, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true, $addRepository = false)
public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName = null, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, PlatformRequirementFilterInterface $platformRequirementFilter = null, $secureHttp = true, $addRepository = false)
{
$oldCwd = getcwd();
@ -193,13 +195,15 @@ EOT
$repositories = (array) $repositories;
}
$platformRequirementFilter = $platformRequirementFilter ?: PlatformRequirementFilterFactory::ignoreNothing();
// we need to manually load the configuration to pass the auth credentials to the io interface!
$io->loadConfiguration($config);
$this->suggestedPackagesReporter = new SuggestedPackagesReporter($io);
if ($packageName !== null) {
$installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositories, $disablePlugins, $noScripts, $noProgress, $ignorePlatformReqs, $secureHttp);
$installedFromVcs = $this->installRootPackage($io, $config, $packageName, $platformRequirementFilter, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositories, $disablePlugins, $noScripts, $noProgress, $secureHttp);
} else {
$installedFromVcs = false;
}
@ -250,7 +254,7 @@ EOT
$installer->setPreferSource($preferSource)
->setPreferDist($preferDist)
->setDevMode($installDevPackages)
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setPlatformRequirementFilter($platformRequirementFilter)
->setSuggestedPackagesReporter($this->suggestedPackagesReporter)
->setOptimizeAutoloader($config->get('optimize-autoloader'))
->setClassMapAuthoritative($config->get('classmap-authoritative'))
@ -342,13 +346,12 @@ EOT
* @param bool $disablePlugins
* @param bool $noScripts
* @param bool $noProgress
* @param bool $ignorePlatformReqs
* @param bool $secureHttp
*
* @return bool
* @throws \Exception
*/
protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, array $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true)
protected function installRootPackage(IOInterface $io, Config $config, $packageName, PlatformRequirementFilterInterface $platformRequirementFilter, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, array $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $secureHttp = true)
{
if (!$secureHttp) {
$config->merge(array('config' => array('secure-http' => false)));
@ -425,11 +428,11 @@ EOT
// find the latest version if there are multiple
$versionSelector = new VersionSelector($repositorySet, $platformRepo);
$package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $ignorePlatformReqs);
$package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $platformRequirementFilter);
if (!$package) {
$errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability");
if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) {
if (!($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter) && $versionSelector->findBestCandidate($name, $packageVersion, $stability, PlatformRequirementFilterFactory::ignoreAll())) {
throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version, PHP extensions and Composer version.');
}

View File

@ -12,6 +12,7 @@
namespace Composer\Command;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Symfony\Component\Console\Input\InputInterface;
@ -97,7 +98,7 @@ EOT
$generator->setClassMapAuthoritative($authoritative);
$generator->setRunScripts(true);
$generator->setApcu($apcu, $apcuPrefix);
$generator->setIgnorePlatformRequirements($ignorePlatformReqs);
$generator->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
$numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
if ($authoritative) {

View File

@ -13,6 +13,8 @@
namespace Composer\Command;
use Composer\Factory;
use Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Json\JsonFile;
use Composer\Json\JsonValidationException;
use Composer\Package\BasePackage;
@ -890,22 +892,23 @@ EOT
if ($input->hasOption('ignore-platform-reqs') && $input->hasOption('ignore-platform-req')) {
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
}
$platformRequirementFilter = PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs);
// find the latest version allowed in this repo set
$versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo);
$effectiveMinimumStability = $minimumStability ?: $this->getMinimumStability($input);
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs);
$package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter);
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 ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($name, $ignorePlatformReqs))) && PlatformRepository::isPlatformPackage($name)) {
if ($platformRequirementFilter->isIgnored($name)) {
return array($name, $requiredVersion ?: '*');
}
// Check whether the package requirements were the problem
if (true !== $ignorePlatformReqs && ($candidate = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true))) {
if (!($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter) && ($candidate = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, PlatformRequirementFilterFactory::ignoreAll()))) {
throw new \InvalidArgumentException(sprintf(
'Package %s%s has requirements incompatible with your PHP version, PHP extensions and Composer version' . $this->getPlatformExceptionDetails($candidate, $platformRepo),
$name,
@ -913,9 +916,9 @@ EOT
));
}
// Check whether the minimum stability was the problem but the package exists
if ($package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
if ($package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) {
// we must first verify if a valid package would be found in a lower priority repository
if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
throw new \InvalidArgumentException(
'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages with higher priority do not match your minimum-stability and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.'
);
@ -928,9 +931,9 @@ EOT
));
}
// Check whether the required version was the problem
if ($requiredVersion && $package = $versionSelector->findBestCandidate($name, null, $preferredStability, $ignorePlatformReqs)) {
if ($requiredVersion && $package = $versionSelector->findBestCandidate($name, null, $preferredStability, $platformRequirementFilter)) {
// we must first verify if a valid package would be found in a lower priority repository
if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, false, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, PlatformRequirementFilterFactory::ignoreNothing(), RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) {
throw new \InvalidArgumentException(
'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.'
);
@ -943,9 +946,9 @@ EOT
));
}
// Check whether the PHP version was the problem for all versions
if (true !== $ignorePlatformReqs && ($candidate = $versionSelector->findBestCandidate($name, null, $preferredStability, true, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES))) {
if (!($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter) && ($candidate = $versionSelector->findBestCandidate($name, null, $preferredStability, PlatformRequirementFilterFactory::ignoreAll(), RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES))) {
$additional = '';
if (false === $versionSelector->findBestCandidate($name, null, $preferredStability, true)) {
if (false === $versionSelector->findBestCandidate($name, null, $preferredStability, PlatformRequirementFilterFactory::ignoreAll())) {
$additional = PHP_EOL.PHP_EOL.'Additionally, the package was only found with a stability of "'.$candidate->getStability().'" while your minimum stability is "'.$effectiveMinimumStability.'".';
}

View File

@ -12,6 +12,7 @@
namespace Composer\Command;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Installer;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
@ -130,7 +131,7 @@ EOT
->setOptimizeAutoloader($optimize)
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu, $apcuPrefix)
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs))
;
if ($input->getOption('no-plugins')) {

View File

@ -15,6 +15,7 @@ namespace Composer\Command;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Transaction;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Package\AliasPackage;
use Composer\Package\BasePackage;
use Composer\Plugin\CommandEvent;
@ -158,7 +159,7 @@ EOT
$generator = $composer->getAutoloadGenerator();
$generator->setClassMapAuthoritative($authoritative);
$generator->setApcu($apcu, $apcuPrefix);
$generator->setIgnorePlatformRequirements($ignorePlatformReqs);
$generator->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
$generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
}

View File

@ -14,6 +14,7 @@ namespace Composer\Command;
use Composer\Config\JsonConfigSource;
use Composer\DependencyResolver\Request;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Installer;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
@ -265,7 +266,7 @@ EOT
->setUpdate(true)
->setInstall(!$input->getOption('no-install'))
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs))
->setDryRun($dryRun)
;

View File

@ -13,6 +13,7 @@
namespace Composer\Command;
use Composer\DependencyResolver\Request;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Util\Filesystem;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
@ -413,7 +414,7 @@ EOT
->setUpdate(true)
->setInstall(!$input->getOption('no-install'))
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs))
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
;

View File

@ -14,6 +14,7 @@ namespace Composer\Command;
use Composer\Composer;
use Composer\DependencyResolver\Request;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Installer;
use Composer\IO\IOInterface;
use Composer\Package\Loader\RootPackageLoader;
@ -236,7 +237,7 @@ EOT
->setUpdateMirrors($updateMirrors)
->setUpdateAllowList($packages)
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setIgnorePlatformRequirements($ignorePlatformReqs)
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs))
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
;

View File

@ -12,6 +12,8 @@
namespace Composer\DependencyResolver;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\Package\BasePackage;
use Composer\Package\AliasPackage;
use Composer\Repository\PlatformRepository;
@ -160,10 +162,9 @@ class RuleSetGenerator
}
/**
* @param bool|string[] $ignorePlatformReqs
* @return void
*/
protected function addRulesForPackage(BasePackage $package, $ignorePlatformReqs)
protected function addRulesForPackage(BasePackage $package, PlatformRequirementFilterInterface $platformRequirementFilter)
{
/** @var \SplQueue<BasePackage> */
$workQueue = new \SplQueue;
@ -196,7 +197,7 @@ class RuleSetGenerator
}
foreach ($package->getRequires() as $link) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($link->getTarget())) {
if ($platformRequirementFilter->isIgnored($link->getTarget())) {
continue;
}
@ -212,10 +213,9 @@ class RuleSetGenerator
}
/**
* @param bool|string[] $ignorePlatformReqs
* @return void
*/
protected function addConflictRules($ignorePlatformReqs = false)
protected function addConflictRules(PlatformRequirementFilterInterface $platformRequirementFilter)
{
/** @var BasePackage $package */
foreach ($this->addedMap as $package) {
@ -225,7 +225,7 @@ class RuleSetGenerator
continue;
}
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($link->getTarget())) {
if ($platformRequirementFilter->isIgnored($link->getTarget())) {
continue;
}
@ -251,10 +251,9 @@ class RuleSetGenerator
}
/**
* @param bool|string[] $ignorePlatformReqs
* @return void
*/
protected function addRulesForRequest(Request $request, $ignorePlatformReqs)
protected function addRulesForRequest(Request $request, PlatformRequirementFilterInterface $platformRequirementFilter)
{
foreach ($request->getFixedPackages() as $package) {
if ($package->id == -1) {
@ -267,7 +266,7 @@ class RuleSetGenerator
throw new \LogicException("Fixed package ".$package->getPrettyString()." was not added to solver pool.");
}
$this->addRulesForPackage($package, $ignorePlatformReqs);
$this->addRulesForPackage($package, $platformRequirementFilter);
$rule = $this->createInstallOneOfRule(array($package), Rule::RULE_FIXED, array(
'package' => $package,
@ -276,14 +275,14 @@ class RuleSetGenerator
}
foreach ($request->getRequires() as $packageName => $constraint) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($packageName)) {
if ($platformRequirementFilter->isIgnored($packageName)) {
continue;
}
$packages = $this->pool->whatProvides($packageName, $constraint);
if ($packages) {
foreach ($packages as $package) {
$this->addRulesForPackage($package, $ignorePlatformReqs);
$this->addRulesForPackage($package, $platformRequirementFilter);
}
$rule = $this->createInstallOneOfRule($packages, Rule::RULE_ROOT_REQUIRE, array(
@ -296,10 +295,9 @@ class RuleSetGenerator
}
/**
* @param bool|string[] $ignorePlatformReqs
* @return void
*/
protected function addRulesForRootAliases($ignorePlatformReqs)
protected function addRulesForRootAliases(PlatformRequirementFilterInterface $platformRequirementFilter)
{
foreach ($this->pool->getPackages() as $package) {
// ensure that rules for root alias packages and aliases of packages which were loaded are also loaded
@ -309,22 +307,23 @@ class RuleSetGenerator
$package instanceof AliasPackage &&
($package->isRootPackageAlias() || isset($this->addedMap[$package->getAliasOf()->id]))
) {
$this->addRulesForPackage($package, $ignorePlatformReqs);
$this->addRulesForPackage($package, $platformRequirementFilter);
}
}
}
/**
* @param bool|string[] $ignorePlatformReqs
* @return RuleSet
*/
public function getRulesFor(Request $request, $ignorePlatformReqs = false)
public function getRulesFor(Request $request, PlatformRequirementFilterInterface $platformRequirementFilter = null)
{
$this->addRulesForRequest($request, $ignorePlatformReqs);
$platformRequirementFilter = $platformRequirementFilter ?: PlatformRequirementFilterFactory::ignoreNothing();
$this->addRulesForRootAliases($ignorePlatformReqs);
$this->addRulesForRequest($request, $platformRequirementFilter);
$this->addConflictRules($ignorePlatformReqs);
$this->addRulesForRootAliases($platformRequirementFilter);
$this->addConflictRules($platformRequirementFilter);
// Remove references to packages
$this->addedMap = $this->addedPackagesByNames = array();

View File

@ -12,9 +12,10 @@
namespace Composer\DependencyResolver;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\IO\IOInterface;
use Composer\Package\BasePackage;
use Composer\Repository\PlatformRepository;
/**
* @author Nils Adermann <naderman@naderman.de>
@ -166,13 +167,12 @@ class Solver
}
/**
* @param bool|string[] $ignorePlatformReqs
* @return void
*/
protected function checkForRootRequireProblems(Request $request, $ignorePlatformReqs)
protected function checkForRootRequireProblems(Request $request, PlatformRequirementFilterInterface $platformRequirementFilter)
{
foreach ($request->getRequires() as $packageName => $constraint) {
if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($packageName)) {
if ($platformRequirementFilter->isIgnored($packageName)) {
continue;
}
@ -185,18 +185,19 @@ class Solver
}
/**
* @param bool|string[] $ignorePlatformReqs
* @return LockTransaction
*/
public function solve(Request $request, $ignorePlatformReqs = false)
public function solve(Request $request, PlatformRequirementFilterInterface $platformRequirementFilter = null)
{
$platformRequirementFilter = $platformRequirementFilter ?: PlatformRequirementFilterFactory::ignoreNothing();
$this->setupFixedMap($request);
$this->io->writeError('Generating rules', true, IOInterface::DEBUG);
$ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool);
$this->rules = $ruleSetGenerator->getRulesFor($request, $ignorePlatformReqs);
$this->rules = $ruleSetGenerator->getRulesFor($request, $platformRequirementFilter);
unset($ruleSetGenerator);
$this->checkForRootRequireProblems($request, $ignorePlatformReqs);
$this->checkForRootRequireProblems($request, $platformRequirementFilter);
$this->decisions = new Decisions($this->pool);
$this->watchGraph = new RuleWatchGraph;

View File

@ -0,0 +1,17 @@
<?php
namespace Composer\Filter\PlatformRequirementFilter;
use Composer\Repository\PlatformRepository;
final class IgnoreAllPlatformRequirementFilter implements PlatformRequirementFilterInterface
{
/**
* @param string $req
* @return bool
*/
public function isIgnored($req)
{
return PlatformRepository::isPlatformPackage($req);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Composer\Filter\PlatformRequirementFilter;
use Composer\Repository\PlatformRepository;
final class IgnoreListPlatformRequirementFilter implements PlatformRequirementFilterInterface
{
/**
* @var string[]
*/
private $reqList;
/**
* @param string[] $reqList
*/
public function __construct(array $reqList)
{
$this->reqList = $reqList;
}
/**
* @param string $req
* @return bool
*/
public function isIgnored($req)
{
if (!PlatformRepository::isPlatformPackage($req)) {
return false;
}
return in_array($req, $this->reqList, true);
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Composer\Filter\PlatformRequirementFilter;
final class IgnoreNothingPlatformRequirementFilter implements PlatformRequirementFilterInterface
{
/**
* @param string $req
* @return false
*/
public function isIgnored($req)
{
return false;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Composer\Filter\PlatformRequirementFilter;
final class PlatformRequirementFilterFactory
{
/**
* @param mixed $boolOrList
*
* @return PlatformRequirementFilterInterface
*/
public static function fromBoolOrList($boolOrList)
{
if (is_bool($boolOrList)) {
return $boolOrList ? self::ignoreAll() : self::ignoreNothing();
}
if (is_array($boolOrList)) {
return new IgnoreListPlatformRequirementFilter($boolOrList);
}
throw new \InvalidArgumentException(
sprintf(
'PlatformRequirementFilter: Unknown $boolOrList parameter %s. Please report at https://github.com/composer/composer/issues/new.',
gettype($boolOrList)
)
);
}
/**
* @return PlatformRequirementFilterInterface
*/
public static function ignoreAll()
{
return new IgnoreAllPlatformRequirementFilter();
}
/**
* @return PlatformRequirementFilterInterface
*/
public static function ignoreNothing()
{
return new IgnoreNothingPlatformRequirementFilter();
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace Composer\Filter\PlatformRequirementFilter;
interface PlatformRequirementFilterInterface
{
/**
* @param string $req
* @return bool
*/
public function isIgnored($req);
}

View File

@ -27,6 +27,8 @@ use Composer\DependencyResolver\SolverProblemsException;
use Composer\DependencyResolver\PolicyInterface;
use Composer\Downloader\DownloadManager;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\Installer\InstallationManager;
use Composer\Installer\InstallerEvents;
use Composer\Installer\SuggestedPackagesReporter;
@ -150,8 +152,6 @@ class Installer
protected $dumpAutoloader = true;
/** @var bool */
protected $runScripts = true;
/** @var bool|string[] */
protected $ignorePlatformReqs = false;
/** @var bool */
protected $preferStable = false;
/** @var bool */
@ -177,6 +177,11 @@ class Installer
*/
protected $suggestedPackagesReporter;
/**
* @var PlatformRequirementFilterInterface
*/
protected $platformRequirementFilter;
/**
* @var ?RepositoryInterface
*/
@ -207,6 +212,7 @@ class Installer
$this->eventDispatcher = $eventDispatcher;
$this->autoloadGenerator = $autoloadGenerator;
$this->suggestedPackagesReporter = new SuggestedPackagesReporter($this->io);
$this->platformRequirementFilter = PlatformRequirementFilterFactory::ignoreNothing();
$this->writeLock = $config->get('lock');
}
@ -329,7 +335,7 @@ class Installer
$this->autoloadGenerator->setClassMapAuthoritative($this->classMapAuthoritative);
$this->autoloadGenerator->setApcu($this->apcuAutoloader, $this->apcuAutoloaderPrefix);
$this->autoloadGenerator->setRunScripts($this->runScripts);
$this->autoloadGenerator->setIgnorePlatformRequirements($this->ignorePlatformReqs);
$this->autoloadGenerator->setPlatformRequirementFilter($this->platformRequirementFilter);
$this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
}
@ -431,7 +437,7 @@ class Installer
// solve dependencies
$solver = new Solver($policy, $pool, $this->io);
try {
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$lockTransaction = $solver->solve($request, $this->platformRequirementFilter);
$ruleSetSize = $solver->getRuleSetSize();
$solver = null;
} catch (SolverProblemsException $e) {
@ -609,7 +615,7 @@ class Installer
$solver = new Solver($policy, $pool, $this->io);
try {
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$nonDevLockTransaction = $solver->solve($request, $this->platformRequirementFilter);
$solver = null;
} catch (SolverProblemsException $e) {
$err = 'Unable to find a compatible set of packages based on your non-dev requirements alone.';
@ -674,7 +680,7 @@ class Installer
// solve dependencies
$solver = new Solver($policy, $pool, $this->io);
try {
$lockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$lockTransaction = $solver->solve($request, $this->platformRequirementFilter);
$solver = null;
// installing the locked packages on this platform resulted in lock modifying operations, there wasn't a conflict, but the lock file as-is seems to not work on this system
@ -799,7 +805,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 ((true === $this->ignorePlatformReqs || (is_array($this->ignorePlatformReqs) && in_array($req, $this->ignorePlatformReqs, true))) && PlatformRepository::isPlatformPackage($req)) {
if ($this->platformRequirementFilter->isIgnored($req)) {
continue;
}
if ($constraint instanceof Link) {
@ -1242,17 +1248,24 @@ class Installer
* @param bool|string[] $ignorePlatformReqs
*
* @return Installer
*
* @deprecated use setPlatformRequirementFilter instead
*/
public function setIgnorePlatformRequirements($ignorePlatformReqs)
{
if (is_array($ignorePlatformReqs)) {
$this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) {
return PlatformRepository::isPlatformPackage($req);
});
} else {
$this->ignorePlatformReqs = (bool) $ignorePlatformReqs;
trigger_error('Installer::setIgnorePlatformRequirements is deprecated since Composer 2.2, use setPlatformRequirementFilter instead.', E_USER_DEPRECATED);
return $this->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
}
/**
* @param PlatformRequirementFilterInterface $platformRequirementFilter
* @return Installer
*/
public function setPlatformRequirementFilter(PlatformRequirementFilterInterface $platformRequirementFilter)
{
$this->platformRequirementFilter = $platformRequirementFilter;
return $this;
}

View File

@ -12,6 +12,9 @@
namespace Composer\Package\Version;
use Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\Package\BasePackage;
use Composer\Package\AliasPackage;
use Composer\Package\PackageInterface;
@ -60,29 +63,34 @@ class VersionSelector
* @param string $packageName
* @param string $targetPackageVersion
* @param string $preferredStability
* @param bool|string[] $ignorePlatformReqs
* @param int $repoSetFlags
*
* @param PlatformRequirementFilterInterface|bool|string[] $platformRequirementFilter
* @param int $repoSetFlags*
* @return PackageInterface|false
*/
public function findBestCandidate($packageName, $targetPackageVersion = null, $preferredStability = 'stable', $ignorePlatformReqs = false, $repoSetFlags = 0)
public function findBestCandidate($packageName, $targetPackageVersion = null, $preferredStability = 'stable', $platformRequirementFilter = null, $repoSetFlags = 0)
{
if (!isset(BasePackage::$stabilities[$preferredStability])) {
// If you get this, maybe you are still relying on the Composer 1.x signature where the 3rd arg was the php version
throw new \UnexpectedValueException('Expected a valid stability name as 3rd argument, got '.$preferredStability);
}
if (null === $platformRequirementFilter) {
$platformRequirementFilter = PlatformRequirementFilterFactory::ignoreNothing();
} elseif (!($platformRequirementFilter instanceof PlatformRequirementFilterInterface)) {
trigger_error('VersionSelector::findBestCandidate with ignored platform reqs as bool|array is deprecated since Composer 2.2, use an instance of PlatformRequirementFilterInterface instead.', E_USER_DEPRECATED);
$platformRequirementFilter = PlatformRequirementFilterFactory::fromBoolOrList($platformRequirementFilter);
}
$constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null;
$candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint, $repoSetFlags);
if ($this->platformConstraints && true !== $ignorePlatformReqs) {
if ($this->platformConstraints && !($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter)) {
$platformConstraints = $this->platformConstraints;
$ignorePlatformReqs = $ignorePlatformReqs ?: array();
$candidates = array_filter($candidates, function ($pkg) use ($platformConstraints, $ignorePlatformReqs) {
$candidates = array_filter($candidates, function ($pkg) use ($platformConstraints, $platformRequirementFilter) {
$reqs = $pkg->getRequires();
foreach ($reqs as $name => $link) {
if (!in_array($name, $ignorePlatformReqs, true)) {
if (!$platformRequirementFilter->isIgnored($name)) {
if (isset($platformConstraints[$name])) {
foreach ($platformConstraints[$name] as $constraint) {
if ($link->getConstraint()->matches($constraint)) {

View File

@ -13,6 +13,7 @@
namespace Composer\Test\Autoload;
use Composer\Autoload\AutoloadGenerator;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Package\Link;
use Composer\Package\Version\VersionParser;
use Composer\Semver\Constraint\Constraint;
@ -1731,7 +1732,7 @@ EOF;
->method('getCanonicalPackages')
->will($this->returnValue(array()));
$this->generator->setIgnorePlatformRequirements($ignorePlatformReqs);
$this->generator->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1');
if (null === $expectedFixture) {

View File

@ -0,0 +1,33 @@
<?php
namespace Composer\Test\Filter\PlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter;
use Composer\Test\TestCase;
final class IgnoreAllPlatformRequirementFilterTest extends TestCase
{
/**
* @dataProvider dataIsIgnored
*
* @param string $req
* @param bool $expectIgnored
*/
public function testIsIgnored($req, $expectIgnored)
{
$platformRequirementFilter = new IgnoreAllPlatformRequirementFilter();
$this->assertSame($expectIgnored, $platformRequirementFilter->isIgnored($req));
}
/**
* @return array<string, mixed[]>
*/
public function dataIsIgnored()
{
return array(
'php is ignored' => array('php', true),
'monolog/monolog is not ignored' => array('monolog/monolog', false),
);
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Composer\Test\Filter\PlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\IgnoreListPlatformRequirementFilter;
use Composer\Test\TestCase;
final class IgnoreListPlatformRequirementFilterTest extends TestCase
{
/**
* @dataProvider dataIsIgnored
*
* @param string[] $reqList
* @param string $req
* @param bool $expectIgnored
*/
public function testIsIgnored(array $reqList, $req, $expectIgnored)
{
$platformRequirementFilter = new IgnoreListPlatformRequirementFilter($reqList);
$this->assertSame($expectIgnored, $platformRequirementFilter->isIgnored($req));
}
/**
* @return array<string, mixed[]>
*/
public function dataIsIgnored()
{
return array(
'ext-json is ignored if listed' => array(array('ext-json', 'monolog/monolog'), 'ext-json', true),
'php is not ignored if not listed' => array(array('ext-json', 'monolog/monolog'), 'php', false),
'monolog/monolog is not ignored even if listed' => array(array('ext-json', 'monolog/monolog'), 'monolog/monolog', false),
);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Composer\Test\Filter\PlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\IgnoreNothingPlatformRequirementFilter;
use Composer\Test\TestCase;
final class IgnoreNothingPlatformRequirementFilterTest extends TestCase
{
/**
* @dataProvider dataIsIgnored
*
* @param string $req
*/
public function testIsIgnored($req)
{
$platformRequirementFilter = new IgnoreNothingPlatformRequirementFilter();
$this->assertFalse($platformRequirementFilter->isIgnored($req));
}
/**
* @return array<string, mixed[]>
*/
public function dataIsIgnored()
{
return array(
'php is not ignored' => array('php'),
'monolog/monolog is not ignored' => array('monolog/monolog'),
);
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Composer\Test\Filter\PlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Test\TestCase;
final class PlatformRequirementFilterFactoryTest extends TestCase
{
/**
* @dataProvider dataFromBoolOrList
*
* @param mixed $boolOrList
* @param class-string $expectedInstance
*/
public function testFromBoolOrList($boolOrList, $expectedInstance)
{
$this->assertInstanceOf($expectedInstance, PlatformRequirementFilterFactory::fromBoolOrList($boolOrList));
}
/**
* @return array<string, mixed[]>
*/
public function dataFromBoolOrList()
{
return array(
'true creates IgnoreAllFilter' => array(true, 'Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter'),
'false creates IgnoreNothingFilter' => array(false, 'Composer\Filter\PlatformRequirementFilter\IgnoreNothingPlatformRequirementFilter'),
'list creates IgnoreListFilter' => array(array('php', 'ext-json'), 'Composer\Filter\PlatformRequirementFilter\IgnoreListPlatformRequirementFilter'),
);
}
public function testFromBoolThrowsExceptionIfTypeIsUnknown()
{
$this->setExpectedException('InvalidArgumentException', 'PlatformRequirementFilter: Unknown $boolOrList parameter NULL. Please report at https://github.com/composer/composer/issues/new.');
PlatformRequirementFilterFactory::fromBoolOrList(null);
}
public function testIgnoreAll()
{
$platformRequirementFilter = PlatformRequirementFilterFactory::ignoreAll();
$this->assertInstanceOf('Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter', $platformRequirementFilter);
}
public function testIgnoreNothing()
{
$platformRequirementFilter = PlatformRequirementFilterFactory::ignoreNothing();
$this->assertInstanceOf('Composer\Filter\PlatformRequirementFilter\IgnoreNothingPlatformRequirementFilter', $platformRequirementFilter);
}
}

View File

@ -13,6 +13,7 @@
namespace Composer\Test;
use Composer\DependencyResolver\Request;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Installer;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
@ -340,7 +341,7 @@ class InstallerTest extends TestCase
$installer
->setDevMode(!$input->getOption('no-dev'))
->setDryRun($input->getOption('dry-run'))
->setIgnorePlatformRequirements($ignorePlatformReqs);
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
return $installer->run();
});
@ -385,7 +386,7 @@ class InstallerTest extends TestCase
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
->setPreferStable($input->getOption('prefer-stable'))
->setPreferLowest($input->getOption('prefer-lowest'))
->setIgnorePlatformRequirements($ignorePlatformReqs);
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
return $installer->run();
});

View File

@ -12,6 +12,7 @@
namespace Composer\Test\Package\Version;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Package\Version\VersionSelector;
use Composer\Package\Package;
use Composer\Package\Link;
@ -70,7 +71,7 @@ class VersionSelectorTest extends TestCase
$best = $versionSelector->findBestCandidate($packageName);
$this->assertSame($package1, $best, 'Latest version supporting php 5.5 should be returned (1.0.0)');
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', true);
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', PlatformRequirementFilterFactory::ignoreAll());
$this->assertSame($package2, $best, 'Latest version should be returned when ignoring platform reqs (2.0.0)');
}
@ -96,7 +97,7 @@ class VersionSelectorTest extends TestCase
$best = $versionSelector->findBestCandidate($packageName);
$this->assertSame($package1, $best, 'Latest version supporting ext-zip 5.3.0 should be returned (1.0.0)');
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', true);
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', PlatformRequirementFilterFactory::ignoreAll());
$this->assertSame($package2, $best, 'Latest version should be returned when ignoring platform reqs (2.0.0)');
}
@ -121,7 +122,7 @@ class VersionSelectorTest extends TestCase
$best = $versionSelector->findBestCandidate($packageName);
$this->assertSame($package1, $best, 'Latest version not requiring ext-barfoo should be returned (1.0.0)');
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', true);
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', PlatformRequirementFilterFactory::ignoreAll());
$this->assertSame($package2, $best, 'Latest version should be returned when ignoring platform reqs (2.0.0)');
}
@ -147,7 +148,7 @@ class VersionSelectorTest extends TestCase
$best = $versionSelector->findBestCandidate($packageName);
$this->assertSame($package1, $best, 'Latest version supporting composer 1 should be returned (1.0.0)');
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', true);
$best = $versionSelector->findBestCandidate($packageName, null, 'stable', PlatformRequirementFilterFactory::ignoreAll());
$this->assertSame($package2, $best, 'Latest version should be returned when ignoring platform reqs (1.1.0)');
}