Add support for setting platform packages to false to disable them (#10308)
Fixes #9664pull/10312/head
parent
2c1ff41f8f
commit
91548d178b
|
@ -191,6 +191,9 @@ you may ignore it instead by passing `--ignore-platform-req=ext-foo` to `update`
|
|||
extensions as if you ignore one now and a new package you add a month later also
|
||||
requires it, you may introduce issues in production unknowingly.
|
||||
|
||||
If you have an extension installed locally but *not* on production, you may want
|
||||
to artificially hide it from Composer using `{"ext-foo": false}`.
|
||||
|
||||
## vendor-dir
|
||||
|
||||
Defaults to `vendor`. You can install dependencies into a different directory if
|
||||
|
|
|
@ -203,7 +203,7 @@
|
|||
"type": "object",
|
||||
"description": "This is a hash of package name (keys) and version (values) that will be used to mock the platform packages on this machine.",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
"type": ["string", "boolean"]
|
||||
}
|
||||
},
|
||||
"vendor-dir": {
|
||||
|
|
|
@ -704,7 +704,7 @@ EOT
|
|||
return 0;
|
||||
}
|
||||
|
||||
$this->configSource->addConfigSetting($settingKey, $values[0]);
|
||||
$this->configSource->addConfigSetting($settingKey, $values[0] === 'false' ? false : $values[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -996,7 +996,11 @@ EOT
|
|||
}
|
||||
$platformPkg = $platformRepo->findPackage($link->getTarget(), '*');
|
||||
if (!$platformPkg) {
|
||||
$details[] = $candidate->getPrettyName().' '.$candidate->getPrettyVersion().' requires '.$link->getTarget().' '.$link->getPrettyConstraint().' but it is not present.';
|
||||
if ($platformRepo->isPlatformPackageDisabled($link->getTarget())) {
|
||||
$details[] = $candidate->getPrettyName().' '.$candidate->getPrettyVersion().' requires '.$link->getTarget().' '.$link->getPrettyConstraint().' but it is disabled by your platform config. Enable it again with "composer config platform.'.$link->getTarget().' --unset".';
|
||||
} else {
|
||||
$details[] = $candidate->getPrettyName().' '.$candidate->getPrettyVersion().' requires '.$link->getTarget().' '.$link->getPrettyConstraint().' but it is not present.';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!$link->getConstraint()->matches(new Constraint('==', $platformPkg->getVersion()))) {
|
||||
|
|
|
@ -20,6 +20,7 @@ use Composer\Package\BasePackage;
|
|||
use Composer\Package\CompletePackageInterface;
|
||||
use Composer\Package\Link;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\Package;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Package\Version\VersionSelector;
|
||||
|
@ -370,6 +371,11 @@ EOT
|
|||
}
|
||||
}
|
||||
}
|
||||
if ($repo === $platformRepo) {
|
||||
foreach ($platformRepo->getDisabledPackages() as $name => $package) {
|
||||
$packages[$type][$name] = $package;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ use Composer\Repository\LockArrayRepository;
|
|||
use Composer\Semver\Constraint\Constraint;
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
|
||||
/**
|
||||
* Represents a problem detected while solving dependencies
|
||||
|
@ -209,46 +210,62 @@ class Problem
|
|||
*/
|
||||
public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, $packageName, ConstraintInterface $constraint = null)
|
||||
{
|
||||
// handle php/hhvm
|
||||
if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') {
|
||||
$version = self::getPlatformPackageVersion($pool, $packageName, phpversion());
|
||||
if (PlatformRepository::isPlatformPackage($packageName)) {
|
||||
// handle php/php-*/hhvm
|
||||
if (0 === stripos($packageName, 'php') || $packageName === 'hhvm') {
|
||||
$version = self::getPlatformPackageVersion($pool, $packageName, phpversion());
|
||||
|
||||
$msg = "- Root composer.json requires ".$packageName.self::constraintToText($constraint).' but ';
|
||||
$msg = "- Root composer.json requires ".$packageName.self::constraintToText($constraint).' but ';
|
||||
|
||||
if (defined('HHVM_VERSION') || ($packageName === 'hhvm' && count($pool->whatProvides($packageName)) > 0)) {
|
||||
return array($msg, 'your HHVM version does not satisfy that requirement.');
|
||||
if (defined('HHVM_VERSION') || ($packageName === 'hhvm' && count($pool->whatProvides($packageName)) > 0)) {
|
||||
return array($msg, 'your HHVM version does not satisfy that requirement.');
|
||||
}
|
||||
|
||||
if ($packageName === 'hhvm') {
|
||||
return array($msg, 'HHVM was not detected on this machine, make sure it is in your PATH.');
|
||||
}
|
||||
|
||||
if (null === $version) {
|
||||
return array($msg, 'the '.$packageName.' package is disabled by your platform config. Enable it again with "composer config platform.'.$packageName.' --unset".');
|
||||
}
|
||||
|
||||
return array($msg, 'your '.$packageName.' version ('. $version .') does not satisfy that requirement.');
|
||||
}
|
||||
|
||||
if ($packageName === 'hhvm') {
|
||||
return array($msg, 'HHVM was not detected on this machine, make sure it is in your PATH.');
|
||||
// handle php extensions
|
||||
if (0 === stripos($packageName, 'ext-')) {
|
||||
if (false !== strpos($packageName, ' ')) {
|
||||
return array('- ', "PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.');
|
||||
}
|
||||
|
||||
$ext = substr($packageName, 4);
|
||||
$msg = "- Root composer.json requires PHP extension ".$packageName.self::constraintToText($constraint).' but ';
|
||||
|
||||
if (extension_loaded($ext)) {
|
||||
$version = self::getPlatformPackageVersion($pool, $packageName, phpversion($ext) ?: '0');
|
||||
|
||||
if (null === $version) {
|
||||
return array($msg, 'the '.$packageName.' package is disabled by your platform config. Enable it again with "composer config platform.'.$packageName.' --unset".');
|
||||
}
|
||||
|
||||
$error = 'it has the wrong version ('.$version.') installed';
|
||||
} else {
|
||||
$error = 'it is missing from your system';
|
||||
}
|
||||
|
||||
return array($msg, $error.'. Install or enable PHP\'s '.$ext.' extension.');
|
||||
}
|
||||
|
||||
return array($msg, 'your '.$packageName.' version ('. $version .') does not satisfy that requirement.');
|
||||
}
|
||||
// handle linked libs
|
||||
if (0 === stripos($packageName, 'lib-')) {
|
||||
if (strtolower($packageName) === 'lib-icu') {
|
||||
$error = extension_loaded('intl') ? 'it has the wrong version installed, try upgrading the intl extension.' : 'it is missing from your system, make sure the intl extension is loaded.';
|
||||
|
||||
// handle php extensions
|
||||
if (0 === stripos($packageName, 'ext-')) {
|
||||
if (false !== strpos($packageName, ' ')) {
|
||||
return array('- ', "PHP extension ".$packageName.' should be required as '.str_replace(' ', '-', $packageName).'.');
|
||||
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', $error);
|
||||
}
|
||||
|
||||
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.');
|
||||
}
|
||||
|
||||
$ext = substr($packageName, 4);
|
||||
$version = self::getPlatformPackageVersion($pool, $packageName, phpversion($ext) ?: '0');
|
||||
|
||||
$error = extension_loaded($ext) ? 'it has the wrong version ('.$version.') installed' : 'it is missing from your system';
|
||||
|
||||
return array("- Root composer.json requires PHP extension ".$packageName.self::constraintToText($constraint).' but ', $error.'. Install or enable PHP\'s '.$ext.' extension.');
|
||||
}
|
||||
|
||||
// handle linked libs
|
||||
if (0 === stripos($packageName, 'lib-')) {
|
||||
if (strtolower($packageName) === 'lib-icu') {
|
||||
$error = extension_loaded('intl') ? 'it has the wrong version installed, try upgrading the intl extension.' : 'it is missing from your system, make sure the intl extension is loaded.';
|
||||
|
||||
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', $error);
|
||||
}
|
||||
|
||||
return array("- Root composer.json requires linked library ".$packageName.self::constraintToText($constraint).' but ', 'it has the wrong version installed or is missing from your system, make sure to load the extension providing it.');
|
||||
}
|
||||
|
||||
$lockedPackage = null;
|
||||
|
@ -404,9 +421,9 @@ class Problem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $version
|
||||
* @param string $packageName
|
||||
* @return string
|
||||
* @param string $version the effective runtime version of the platform package
|
||||
* @return ?string a version string or null if it appears the package was artificially disabled
|
||||
*/
|
||||
private static function getPlatformPackageVersion(Pool $pool, $packageName, $version)
|
||||
{
|
||||
|
@ -419,6 +436,8 @@ class Problem
|
|||
if ($firstAvailable instanceof CompletePackageInterface && isset($extra['config.platform']) && $extra['config.platform'] === true) {
|
||||
$version .= '; ' . str_replace('Package ', '', $firstAvailable->getDescription());
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $version;
|
||||
|
|
|
@ -80,6 +80,13 @@ class ValidatingArrayLoader implements LoaderInterface
|
|||
|
||||
if (!empty($this->config['config']['platform'])) {
|
||||
foreach ((array) $this->config['config']['platform'] as $key => $platform) {
|
||||
if (false === $platform) {
|
||||
continue;
|
||||
}
|
||||
if (!is_string($platform)) {
|
||||
$this->errors[] = 'config.platform.' . $key . ' : invalid value ('.gettype($platform).' '.var_export($platform, true).'): expected string or false';
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$this->versionParser->normalize($platform);
|
||||
} catch (\Exception $e) {
|
||||
|
|
|
@ -48,23 +48,36 @@ class PlatformRepository extends ArrayRepository
|
|||
*
|
||||
* Keyed by package name (lowercased)
|
||||
*
|
||||
* @var array<string, array{name: string, version: string}>
|
||||
* @var array<string, array{name: string, version: string|false}>
|
||||
*/
|
||||
private $overrides = array();
|
||||
|
||||
/**
|
||||
* Stores which packages have been disabled and their actual version
|
||||
*
|
||||
* @var array<string, CompletePackageInterface>
|
||||
*/
|
||||
private $disabledPackages = array();
|
||||
|
||||
/** @var Runtime */
|
||||
private $runtime;
|
||||
/** @var HhvmDetector */
|
||||
private $hhvmDetector;
|
||||
|
||||
/**
|
||||
* @param array<string, string> $overrides
|
||||
* @param array<string, string|false> $overrides
|
||||
*/
|
||||
public function __construct(array $packages = array(), array $overrides = array(), Runtime $runtime = null, HhvmDetector $hhvmDetector = null)
|
||||
{
|
||||
$this->runtime = $runtime ?: new Runtime();
|
||||
$this->hhvmDetector = $hhvmDetector ?: new HhvmDetector();
|
||||
foreach ($overrides as $name => $version) {
|
||||
if (!is_string($version) && false !== $version) { // @phpstan-ignore-line
|
||||
throw new \UnexpectedValueException('config.platform.'.$name.' should be a string or false, but got '.gettype($version).' '.var_export($version, true));
|
||||
}
|
||||
if ($name === 'php' && $version === false) {
|
||||
throw new \UnexpectedValueException('config.platform.'.$name.' cannot be set to false as you cannot disable php entirely.');
|
||||
}
|
||||
$this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version);
|
||||
}
|
||||
parent::__construct($packages);
|
||||
|
@ -75,6 +88,23 @@ class PlatformRepository extends ArrayRepository
|
|||
return 'platform repo';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return boolean
|
||||
*/
|
||||
public function isPlatformPackageDisabled($name)
|
||||
{
|
||||
return isset($this->disabledPackages[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, CompletePackageInterface>
|
||||
*/
|
||||
public function getDisabledPackages()
|
||||
{
|
||||
return $this->disabledPackages;
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
@ -89,7 +119,9 @@ class PlatformRepository extends ArrayRepository
|
|||
throw new \InvalidArgumentException('Invalid platform package name in config.platform: '.$override['name']);
|
||||
}
|
||||
|
||||
$this->addOverriddenPackage($override);
|
||||
if ($override['version'] !== false) {
|
||||
$this->addOverriddenPackage($override);
|
||||
}
|
||||
}
|
||||
|
||||
$prettyVersion = PluginInterface::PLUGIN_API_VERSION;
|
||||
|
@ -494,8 +526,17 @@ class PlatformRepository extends ArrayRepository
|
|||
*/
|
||||
public function addPackage(PackageInterface $package)
|
||||
{
|
||||
if (!$package instanceof CompletePackage) {
|
||||
throw new \UnexpectedValueException('Expected CompletePackage but got '.get_class($package));
|
||||
}
|
||||
|
||||
// Skip if overridden
|
||||
if (isset($this->overrides[$package->getName()])) {
|
||||
if ($this->overrides[$package->getName()]['version'] === false) {
|
||||
$this->addDisabledPackage($package);
|
||||
return;
|
||||
}
|
||||
|
||||
$overrider = $this->findPackage($package->getName(), '*');
|
||||
if ($package->getVersion() === $overrider->getVersion()) {
|
||||
$actualText = 'same as actual';
|
||||
|
@ -511,6 +552,11 @@ class PlatformRepository extends ArrayRepository
|
|||
|
||||
// Skip if PHP is overridden and we are adding a php-* package
|
||||
if (isset($this->overrides['php']) && 0 === strpos($package->getName(), 'php-')) {
|
||||
if (isset($this->overrides[$package->getName()]) && $this->overrides[$package->getName()]['version'] === false) {
|
||||
$this->addDisabledPackage($package);
|
||||
return;
|
||||
}
|
||||
|
||||
$overrider = $this->addOverriddenPackage($this->overrides['php'], $package->getPrettyName());
|
||||
if ($package->getVersion() === $overrider->getVersion()) {
|
||||
$actualText = 'same as actual';
|
||||
|
@ -546,6 +592,17 @@ class PlatformRepository extends ArrayRepository
|
|||
return $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function addDisabledPackage(CompletePackage $package)
|
||||
{
|
||||
$package->setDescription($package->getDescription().'. <warning>Package disabled via config.platform</warning>');
|
||||
$package->setExtra(array('config.platform' => true));
|
||||
|
||||
$this->disabledPackages[$package->getName()] = $package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the version and adds a new package to the repository
|
||||
*
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
--TEST--
|
||||
Test the error output of solver problems for disabled platform packages. ext/php are well reported if present but disabled, lib packages are currently not handled as it is too complex.
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "dependency/pkg", "version": "1.0.0", "require": {"php-ipv6": "^8"} },
|
||||
{ "name": "dependency/pkg2", "version": "1.0.0", "require": {"php-64bit": "^8"} },
|
||||
{ "name": "dependency/pkg3", "version": "1.0.0", "require": {"lib-xml": "1002.*"} },
|
||||
{ "name": "dependency/pkg4", "version": "1.0.0", "require": {"lib-icu": "1001.*"} },
|
||||
{ "name": "dependency/pkg5", "version": "1.0.0", "require": {"ext-foobar": "1.0.0"} },
|
||||
{ "name": "dependency/pkg6", "version": "1.0.0", "require": {"ext-pcre": "^8"} }
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"dependency/pkg": "1.*",
|
||||
"dependency/pkg2": "1.*",
|
||||
"dependency/pkg3": "1.*",
|
||||
"dependency/pkg4": "1.*",
|
||||
"dependency/pkg5": "1.*",
|
||||
"dependency/pkg6": "1.*",
|
||||
"php-64bit": "^8",
|
||||
"php-ipv6": "^8",
|
||||
"lib-xml": "1002.*",
|
||||
"lib-icu": "1001.*",
|
||||
"ext-foobar": "1.0.0",
|
||||
"ext-pcre": "^8"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php-64bit": false,
|
||||
"php-ipv6": "8.0.3",
|
||||
"lib-xml": false,
|
||||
"lib-icu": false,
|
||||
"ext-foobar": false,
|
||||
"ext-pcre": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--RUN--
|
||||
update
|
||||
|
||||
--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-64bit ^8 but the php-64bit package is disabled by your platform config. Enable it again with "composer config platform.php-64bit --unset".
|
||||
Problem 2
|
||||
- Root composer.json requires linked library lib-xml 1002.* but it has the wrong version installed or is missing from your system, make sure to load the extension providing it.
|
||||
Problem 3
|
||||
- Root composer.json requires linked library lib-icu 1001.* but it has the wrong version installed, try upgrading the intl extension.
|
||||
Problem 4
|
||||
- Root composer.json requires PHP extension ext-foobar 1.0.0 but it is missing from your system. Install or enable PHP's foobar extension.
|
||||
Problem 5
|
||||
- Root composer.json requires PHP extension ext-pcre ^8 but the ext-pcre package is disabled by your platform config. Enable it again with "composer config platform.ext-pcre --unset".
|
||||
Problem 6
|
||||
- Root composer.json requires dependency/pkg2 1.* -> satisfiable by dependency/pkg2[1.0.0].
|
||||
- dependency/pkg2 1.0.0 requires php-64bit ^8 -> the php-64bit package is disabled by your platform config. Enable it again with "composer config platform.php-64bit --unset".
|
||||
Problem 7
|
||||
- Root composer.json requires dependency/pkg3 1.* -> satisfiable by dependency/pkg3[1.0.0].
|
||||
- dependency/pkg3 1.0.0 requires lib-xml 1002.* -> it has the wrong version installed or is missing from your system, make sure to load the extension providing it.
|
||||
Problem 8
|
||||
- Root composer.json requires dependency/pkg4 1.* -> satisfiable by dependency/pkg4[1.0.0].
|
||||
- dependency/pkg4 1.0.0 requires lib-icu 1001.* -> it has the wrong version installed, try upgrading the intl extension.
|
||||
Problem 9
|
||||
- Root composer.json requires dependency/pkg5 1.* -> satisfiable by dependency/pkg5[1.0.0].
|
||||
- dependency/pkg5 1.0.0 requires ext-foobar 1.0.0 -> it is missing from your system. Install or enable PHP's foobar extension.
|
||||
Problem 10
|
||||
- Root composer.json requires dependency/pkg6 1.* -> satisfiable by dependency/pkg6[1.0.0].
|
||||
- dependency/pkg6 1.0.0 requires ext-pcre ^8 -> the ext-pcre package is disabled by your platform config. Enable it again with "composer config platform.ext-pcre --unset".
|
||||
|
||||
To enable extensions, verify that they are enabled in your .ini files:
|
||||
__inilist__
|
||||
You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.
|
||||
|
||||
--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": 1776
|
||||
"count": 1784
|
||||
},
|
||||
{
|
||||
"location": "Composer\\Test\\InstallerTest::testIntegrationWithPoolOptimizer",
|
||||
"message": "preg_match(): Passing null to parameter #4 ($flags) of type int is deprecated",
|
||||
"count": 1776
|
||||
"count": 1784
|
||||
},
|
||||
{
|
||||
"location": "Composer\\Test\\Package\\Archiver\\ArchivableFilesFinderTest::testManualExcludes",
|
||||
|
|
Loading…
Reference in New Issue