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

main
Jordi Boggiano 3 years ago
parent 62dfd0af23
commit e30a6b0b9b
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC

@ -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`.

@ -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);

@ -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)) {

@ -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;
}
}

@ -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,15 +808,16 @@ 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;
}
$rootRequires[$req] = $constraint;
}
$this->fixedRootPackage = clone $this->package;

@ -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--

@ -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",

Loading…
Cancel
Save