diff --git a/src/Composer/Command/BaseDependencyCommand.php b/src/Composer/Command/BaseDependencyCommand.php
index 2fb363979..bb2a64233 100644
--- a/src/Composer/Command/BaseDependencyCommand.php
+++ b/src/Composer/Command/BaseDependencyCommand.php
@@ -13,6 +13,7 @@
namespace Composer\Command;
use Composer\Package\Link;
+use Composer\Package\Package;
use Composer\Package\PackageInterface;
use Composer\Package\CompletePackageInterface;
use Composer\Package\RootPackage;
@@ -24,6 +25,8 @@ use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
+use Composer\Semver\Constraint\Bound;
+use Composer\Util\Platform;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Composer\Package\Version\VersionParser;
@@ -102,13 +105,27 @@ abstract class BaseDependencyCommand extends BaseCommand
// If the version we ask for is not installed then we need to locate it in remote repos and add it.
// This is needed for why-not to resolve conflicts from an uninstalled version against installed packages.
- if (!$installedRepo->findPackage($needle, $textConstraint)) {
+ $matchedPackage = $installedRepo->findPackage($needle, $textConstraint);
+ if (!$matchedPackage) {
$defaultRepos = new CompositeRepository(RepositoryFactory::defaultRepos($this->getIO(), $composer->getConfig(), $composer->getRepositoryManager()));
if ($match = $defaultRepos->findPackage($needle, $textConstraint)) {
$installedRepo->addRepository(new InstalledArrayRepository([clone $match]));
+ } elseif (PlatformRepository::isPlatformPackage($needle)) {
+ $parser = new VersionParser();
+ $constraint = $parser->parseConstraints($textConstraint);
+ if ($constraint->getLowerBound() !== Bound::zero()) {
+ $tempPlatformPkg = new Package($needle, $constraint->getLowerBound()->getVersion(), $constraint->getLowerBound()->getVersion());
+ $installedRepo->addRepository(new InstalledArrayRepository([$tempPlatformPkg]));
+ }
} else {
$this->getIO()->writeError('Package "'.$needle.'" could not be found with constraint "'.$textConstraint.'", results below will most likely be incomplete.');
}
+ } elseif (PlatformRepository::isPlatformPackage($needle)) {
+ $extraNotice = '';
+ if (($matchedPackage->getExtra()['config.platform'] ?? false) === true) {
+ $extraNotice = ' (version provided by config.platform)';
+ }
+ $this->getIO()->writeError('Package "'.$needle.' '.$textConstraint.'" found in version "'.$matchedPackage->getPrettyVersion().'"'.$extraNotice.'.');
}
// Include replaced packages for inverted lookups as they are then the actual starting point to consider
@@ -154,7 +171,7 @@ abstract class BaseDependencyCommand extends BaseCommand
$this->printTable($output, $results);
}
- if ($inverted && $input->hasArgument(self::ARGUMENT_CONSTRAINT)) {
+ if ($inverted && $input->hasArgument(self::ARGUMENT_CONSTRAINT) && !PlatformRepository::isPlatformPackage($needle)) {
$composerCommand = 'update';
foreach ($composer->getPackage()->getRequires() as $rootRequirement) {
diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php
index 15bc35885..d3fbc521f 100644
--- a/src/Composer/DependencyResolver/PoolBuilder.php
+++ b/src/Composer/DependencyResolver/PoolBuilder.php
@@ -616,6 +616,8 @@ class PoolBuilder
private function warnAboutNonMatchingUpdateAllowList(Request $request): void
{
foreach ($this->updateAllowList as $pattern) {
+ $matchedPlatformPackage = false;
+
$patternRegexp = BasePackage::packageNameToRegexp($pattern);
// update pattern matches a locked package? => all good
foreach ($request->getLockedRepository()->getPackages() as $package) {
@@ -626,10 +628,16 @@ class PoolBuilder
// update pattern matches a root require? => all good, probably a new package
foreach ($request->getRequires() as $packageName => $constraint) {
if (Preg::isMatch($patternRegexp, $packageName)) {
+ if (PlatformRepository::isPlatformPackage($packageName)) {
+ $matchedPlatformPackage = true;
+ continue;
+ }
continue 2;
}
}
- if (strpos($pattern, '*') !== false) {
+ if ($matchedPlatformPackage) {
+ $this->io->writeError('Pattern "' . $pattern . '" listed for update matches platform packages, but these cannot be updated by Composer.');
+ } elseif (strpos($pattern, '*') !== false) {
$this->io->writeError('Pattern "' . $pattern . '" listed for update does not match any locked packages.');
} else {
$this->io->writeError('Package "' . $pattern . '" listed for update is not locked.');
diff --git a/tests/Composer/Test/Command/BaseDependencyCommandTest.php b/tests/Composer/Test/Command/BaseDependencyCommandTest.php
index c663b70f7..0fad665ae 100644
--- a/tests/Composer/Test/Command/BaseDependencyCommandTest.php
+++ b/tests/Composer/Test/Command/BaseDependencyCommandTest.php
@@ -15,6 +15,7 @@ namespace Composer\Test\Command;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\MatchAllConstraint;
+use Composer\Semver\Constraint\MultiConstraint;
use Symfony\Component\Console\Command\Command;
use UnexpectedValueException;
use InvalidArgumentException;
@@ -374,19 +375,25 @@ OUTPUT
'package' => [
['name' => 'vendor1/package1', 'version' => '1.3.0'],
['name' => 'vendor2/package1', 'version' => '2.0.0'],
- ['name' => 'vendor2/package2', 'version' => '1.0.0', 'require' => ['vendor2/package3' => '1.4.*']],
+ ['name' => 'vendor2/package2', 'version' => '1.0.0', 'require' => ['vendor2/package3' => '1.4.*', 'php' => '^8.2']],
['name' => 'vendor2/package3', 'version' => '1.4.0'],
['name' => 'vendor2/package3', 'version' => '1.5.0']
],
],
],
'require' => [
- 'vendor1/package1' => '1.*'
+ 'vendor1/package1' => '1.*',
+ 'php' => '^8',
],
'require-dev' => [
'vendor2/package1' => '2.*',
'vendor2/package2' => '^1'
- ]
+ ],
+ 'config' => [
+ 'platform' => [
+ 'php' => '8.3.2',
+ ],
+ ],
]);
$someRequiredPackage = self::getPackage('vendor1/package1', '1.3.0');
@@ -399,7 +406,14 @@ OUTPUT
new MatchAllConstraint(),
Link::TYPE_REQUIRE,
'1.4.*'
- )
+ ),
+ 'php' => new Link(
+ 'vendor2/package2',
+ 'php',
+ new MultiConstraint([self::getVersionConstraint('>=', '8.2.0.0'), self::getVersionConstraint('<', '9.0.0.0-dev')]),
+ Link::TYPE_REQUIRE,
+ '^8.2'
+ ),
]);
$secondDevNestedRequiredPackage = self::getPackage('vendor2/package3', '1.4.0');
@@ -466,6 +480,26 @@ OUTPUT
vendor2/package2 1.0.0 requires vendor2/package3 (1.4.*)
Not finding what you were looking for? Try calling `composer update "vendor2/package3:1.5.0" --dry-run` to get another view on the problem.
OUTPUT
+,
+ 1
+ ];
+
+ yield 'all compatible with the inspected platform package (range matching installed)' => [
+ ['package' => 'php', 'version' => '^8'],
+ <<