1
0
Fork 0

Add support for ignoring the upper bound of platform requirements using "name+" notation

pull/10318/head
Jordi Boggiano 2021-11-28 13:29:54 +01:00
parent 62dfd0af23
commit e30a6b0b9b
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
7 changed files with 131 additions and 14 deletions

View File

@ -117,7 +117,10 @@ resolution.
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement(`php`,
`hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine
does not fulfill it. Multiple requirements can be ignored via wildcard.
does not fulfill it. Multiple requirements can be ignored via wildcard. Appending
a `+` makes it only ignore the upper-bound of the requirements. For example, if a package
requires `php: ^7`, then the option `--ignore-platform-req=php+` would allow installing on PHP8,
but installation on PHP 5.6 would still fail.
## update / u
@ -202,7 +205,10 @@ php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.*
See also the [`platform`](06-config.md#platform) config option.
* **--ignore-platform-req:** ignore a specific platform requirement(`php`,
`hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine
does not fulfill it. Multiple requirements can be ignored via wildcard.
does not fulfill it. Multiple requirements can be ignored via wildcard. Appending
a `+` makes it only ignore the upper-bound of the requirements. For example, if a package
requires `php: ^7`, then the option `--ignore-platform-req=php+` would allow installing on PHP8,
but installation on PHP 5.6 would still fail.
* **--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`.

View File

@ -12,6 +12,7 @@
namespace Composer\DependencyResolver;
use Composer\Filter\PlatformRequirementFilter\IgnoreListPlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\Package\BasePackage;
@ -197,11 +198,14 @@ class RuleSetGenerator
}
foreach ($package->getRequires() as $link) {
$constraint = $link->getConstraint();
if ($platformRequirementFilter->isIgnored($link->getTarget())) {
continue;
} elseif ($platformRequirementFilter instanceof IgnoreListPlatformRequirementFilter) {
$constraint = $platformRequirementFilter->filterConstraint($link->getTarget(), $constraint);
}
$possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
$possibleRequires = $this->pool->whatProvides($link->getTarget(), $constraint);
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, $link));
@ -225,11 +229,14 @@ class RuleSetGenerator
continue;
}
$constraint = $link->getConstraint();
if ($platformRequirementFilter->isIgnored($link->getTarget())) {
continue;
} elseif ($platformRequirementFilter instanceof IgnoreListPlatformRequirementFilter) {
$constraint = $platformRequirementFilter->filterConstraint($link->getTarget(), $constraint);
}
$conflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
$conflicts = $this->pool->whatProvides($link->getTarget(), $constraint);
foreach ($conflicts as $conflict) {
// define the conflict rule for regular packages, for alias packages it's only needed if the name
@ -277,6 +284,8 @@ class RuleSetGenerator
foreach ($request->getRequires() as $packageName => $constraint) {
if ($platformRequirementFilter->isIgnored($packageName)) {
continue;
} elseif ($platformRequirementFilter instanceof IgnoreListPlatformRequirementFilter) {
$constraint = $platformRequirementFilter->filterConstraint($packageName, $constraint);
}
$packages = $this->pool->whatProvides($packageName, $constraint);

View File

@ -12,6 +12,7 @@
namespace Composer\DependencyResolver;
use Composer\Filter\PlatformRequirementFilter\IgnoreListPlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\IO\IOInterface;
@ -174,6 +175,8 @@ class Solver
foreach ($request->getRequires() as $packageName => $constraint) {
if ($platformRequirementFilter->isIgnored($packageName)) {
continue;
} elseif ($platformRequirementFilter instanceof IgnoreListPlatformRequirementFilter) {
$constraint = $platformRequirementFilter->filterConstraint($packageName, $constraint);
}
if (!$this->pool->whatProvides($packageName, $constraint)) {

View File

@ -5,20 +5,40 @@ namespace Composer\Filter\PlatformRequirementFilter;
use Composer\Package\BasePackage;
use Composer\Pcre\Preg;
use Composer\Repository\PlatformRepository;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Constraint\MatchAllConstraint;
use Composer\Semver\Constraint\MultiConstraint;
use Composer\Semver\Interval;
use Composer\Semver\Intervals;
final class IgnoreListPlatformRequirementFilter implements PlatformRequirementFilterInterface
{
/**
* @var string
*/
private $regexp;
private $ignoreRegex;
/**
* @var string
*/
private $ignoreUpperBoundRegex;
/**
* @param string[] $reqList
*/
public function __construct(array $reqList)
{
$this->regexp = BasePackage::packageNamesToRegexp($reqList);
$ignoreAll = $ignoreUpperBound = array();
foreach ($reqList as $req) {
if (substr($req, -1) === '+') {
$ignoreUpperBound[] = substr($req, 0, -1);
} else {
$ignoreAll[] = $req;
}
}
$this->ignoreRegex = BasePackage::packageNamesToRegexp($ignoreAll);
$this->ignoreUpperBoundRegex = BasePackage::packageNamesToRegexp($ignoreUpperBound);
}
/**
@ -31,6 +51,33 @@ final class IgnoreListPlatformRequirementFilter implements PlatformRequirementFi
return false;
}
return Preg::isMatch($this->regexp, $req);
return Preg::isMatch($this->ignoreRegex, $req);
}
/**
* @param string $req
* @return ConstraintInterface
*/
public function filterConstraint($req, ConstraintInterface $constraint)
{
if (!PlatformRepository::isPlatformPackage($req)) {
return $constraint;
}
if (!Preg::isMatch($this->ignoreUpperBoundRegex, $req)) {
return $constraint;
}
if (Preg::isMatch($this->ignoreRegex, $req)) {
return new MatchAllConstraint;
}
$intervals = Intervals::get($constraint);
$last = end($intervals['numeric']);
if ($last !== false && (string) $last->getEnd() !== (string) Interval::untilPositiveInfinity()) {
$constraint = new MultiConstraint(array($constraint, new Constraint('>=', $last->getEnd()->getVersion())), false);
}
return $constraint;
}
}

View File

@ -28,6 +28,7 @@ use Composer\DependencyResolver\SolverProblemsException;
use Composer\DependencyResolver\PolicyInterface;
use Composer\Downloader\DownloadManager;
use Composer\EventDispatcher\EventDispatcher;
use Composer\Filter\PlatformRequirementFilter\IgnoreListPlatformRequirementFilter;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
use Composer\Installer\InstallationManager;
@ -807,16 +808,17 @@ class Installer
$rootRequires = array();
foreach ($requires as $req => $constraint) {
if ($constraint instanceof Link) {
$constraint = $constraint->getConstraint();
}
// skip platform requirements from the root package to avoid filtering out existing platform packages
if ($this->platformRequirementFilter->isIgnored($req)) {
continue;
} elseif ($this->platformRequirementFilter instanceof IgnoreListPlatformRequirementFilter) {
$constraint = $this->platformRequirementFilter->filterConstraint($req, $constraint);
}
if ($constraint instanceof Link) {
$rootRequires[$req] = $constraint->getConstraint();
} else {
$rootRequires[$req] = $constraint;
}
}
$this->fixedRootPackage = clone $this->package;
$this->fixedRootPackage->setRequires(array());

View File

@ -0,0 +1,50 @@
--TEST--
Update with ignore-platform-req list ignoring upper bound of a dependency
--COMPOSER--
{
"repositories": [
{
"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.*" } }
]
}
],
"require": {
"a/a": "1.0.*",
"b/b": "1.0.*",
"php": "^4.3",
"ext-foo-baz": "9.0.0"
},
"provide": {
"ext-foo-bar": "5.0.3"
}
}
--INSTALLED--
[
{ "name": "a/a", "version": "1.0.0" }
]
--RUN--
update --ignore-platform-req=php+ --ignore-platform-req=ext-foo-bar+ --ignore-platform-req=ext-foo-baz+
--EXPECT-EXIT-CODE--
2
--EXPECT-OUTPUT--
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Root composer.json requires PHP extension ext-foo-baz [== 9.0.0.0 || >= 9.0.0.0] but it is missing from your system. Install or enable PHP's foo-baz extension.
Problem 2
- Root composer.json requires b/b 1.0.* -> satisfiable by b/b[1.0.1].
- b/b 1.0.1 requires ext-foo-bar 10.* -> it has the wrong version (5.0.3 provided by __root__ 1.0.0+no-version-set) installed. Install or enable PHP's foo-bar extension.
To enable extensions, verify that they are enabled in your .ini files:
__inilist__
You can also run `php --ini` in a terminal to see which files are used by PHP in CLI mode.
Alternatively, you can run Composer with `--ignore-platform-req=ext-foo-baz --ignore-platform-req=ext-foo-bar` to temporarily ignore these required extensions.
--EXPECT--

View File

@ -77,12 +77,12 @@
{
"location": "Composer\\Test\\InstallerTest::testIntegrationWithRawPool",
"message": "preg_match(): Passing null to parameter #4 ($flags) of type int is deprecated",
"count": 1800
"count": 1820
},
{
"location": "Composer\\Test\\InstallerTest::testIntegrationWithPoolOptimizer",
"message": "preg_match(): Passing null to parameter #4 ($flags) of type int is deprecated",
"count": 1800
"count": 1820
},
{
"location": "Composer\\Test\\Package\\Archiver\\ArchivableFilesFinderTest::testManualExcludes",