From 6de9cacfd874c4d6112aa7b52ba686503bdbf6b4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 13 Jan 2022 11:34:20 +0100 Subject: [PATCH 1/9] Remove unnecessary sprintf --- src/Composer/Util/AuthHelper.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Composer/Util/AuthHelper.php b/src/Composer/Util/AuthHelper.php index f49373397..28c4aba68 100644 --- a/src/Composer/Util/AuthHelper.php +++ b/src/Composer/Util/AuthHelper.php @@ -96,10 +96,7 @@ class AuthHelper if ($requiresSso) { $ssoUrl = $gitHubUtil->getSsoUrl($headers); - $message = sprintf( - 'GitHub API token requires SSO authorization. Authorize this token at ' . $ssoUrl, - $ssoUrl - ) . "\n"; + $message = 'GitHub API token requires SSO authorization. Authorize this token at ' . $ssoUrl . "\n"; $this->io->writeError($message); if (!$this->io->isInteractive()) { throw new TransportException('Could not authenticate against ' . $origin, 403); From 6a70161c0175ce178a5abd9edea891ab03298e03 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 17 Jan 2022 15:28:07 +0100 Subject: [PATCH 2/9] Disable "composer/package-versions-deprecated" by default (#10458) --- src/Composer/Plugin/PluginManager.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 2895c3ca8..905a6cc53 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -699,6 +699,10 @@ class PluginManager } } + if ($package === 'composer/package-versions-deprecated') { + return false; + } + if (!isset($warned[$package])) { if ($this->io->isInteractive()) { $composer = $isGlobalPlugin && $this->globalComposer !== null ? $this->globalComposer : $this->composer; From db64534b26189bacc95956222677fc34fb62e845 Mon Sep 17 00:00:00 2001 From: John Stevenson Date: Fri, 21 Jan 2022 09:09:58 +0000 Subject: [PATCH 3/9] Fixed bin proxies on PHP < 8 to support stream_seek (#10468) --- src/Composer/Installer/BinaryInstaller.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Composer/Installer/BinaryInstaller.php b/src/Composer/Installer/BinaryInstaller.php index 03bd0839f..1f6f14d78 100644 --- a/src/Composer/Installer/BinaryInstaller.php +++ b/src/Composer/Installer/BinaryInstaller.php @@ -333,6 +333,16 @@ if (PHP_VERSION_ID < 80000) { return \$operation ? flock(\$this->handle, \$operation) : true; } + public function stream_seek(\$offset, \$whence) + { + if (0 === fseek(\$this->handle, \$offset, \$whence)) { + \$this->position = ftell(\$this->handle); + return true; + } + + return false; + } + public function stream_tell() { return \$this->position; From e103ee0249ca2b0e513a79280ab19cda87778c99 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 21 Jan 2022 10:13:59 +0100 Subject: [PATCH 4/9] Remove getenv workaround on fixed PHP versions --- bin/composer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/composer b/bin/composer index 9bb3559a4..55391b9b6 100755 --- a/bin/composer +++ b/bin/composer @@ -67,7 +67,7 @@ if (function_exists('ini_set')) { // Workaround PHP bug on Windows where env vars containing Unicode chars are mangled in $_SERVER // see https://github.com/php/php-src/issues/7896 -if (PHP_VERSION_ID >= 70113 && Platform::isWindows()) { +if (PHP_VERSION_ID >= 70113 && (PHP_VERSION_ID < 80016 || (PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80103)) && Platform::isWindows()) { foreach ($_SERVER as $serverVar => $serverVal) { if (($serverVal = getenv($serverVar)) !== false) { $_SERVER[$serverVar] = $serverVal; From e3d99cac5928d72859fac8300255c9566a26e9d5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 21 Jan 2022 12:48:16 +0100 Subject: [PATCH 5/9] Fix phpstan issues, update baseline and pin PHPStan version in 2.2 branch (#10474) --- .github/workflows/phpstan.yml | 2 +- composer.json | 2 +- phpstan/baseline.neon | 163 ++++-------------- src/Composer/Config.php | 8 +- src/Composer/DependencyResolver/RuleSet.php | 2 +- src/Composer/DependencyResolver/Solver.php | 6 +- src/Composer/IO/BaseIO.php | 2 +- src/Composer/IO/IOInterface.php | 2 +- src/Composer/Installer.php | 2 +- src/Composer/Json/JsonFile.php | 2 +- src/Composer/Package/Comparer/Comparer.php | 5 +- .../Package/Version/VersionGuesser.php | 2 +- src/Composer/Plugin/PluginManager.php | 28 +-- src/Composer/Repository/ArrayRepository.php | 5 +- .../Repository/ComposerRepository.php | 12 +- src/Composer/Repository/Vcs/GitDriver.php | 6 +- src/Composer/Repository/Vcs/GitLabDriver.php | 2 +- .../Repository/Vcs/VcsDriverInterface.php | 4 +- src/Composer/Repository/VcsRepository.php | 2 + src/Composer/Util/Http/CurlDownloader.php | 3 + src/Composer/Util/NoProxyPattern.php | 15 ++ src/Composer/Util/Svn.php | 18 +- tests/Composer/Test/IO/NullIOTest.php | 2 +- .../Test/Plugin/PluginInstallerTest.php | 5 +- 24 files changed, 118 insertions(+), 182 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 40877dc5a..5881fb0b7 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -51,7 +51,7 @@ jobs: - name: "Install PHPStan" # Locked to phpunit 7.5 here as newer ones have void return types which break inheritance - run: "bin/composer require --dev phpstan/phpstan:^1.0 phpstan/phpstan-phpunit:^1.0 phpstan/phpstan-deprecation-rules:^1 phpstan/phpstan-strict-rules:^1 phpunit/phpunit:^7.5.20 --with-all-dependencies ${{ env.COMPOSER_FLAGS }}" + run: "bin/composer require --dev phpstan/phpstan:1.4.* phpstan/phpstan-phpunit:1.0.* phpstan/phpstan-deprecation-rules:1.0.* phpstan/phpstan-strict-rules:1.1.* phpunit/phpunit:^7.5.20 --with-all-dependencies ${{ env.COMPOSER_FLAGS }}" - name: "Run PHPStan" run: "vendor/bin/phpstan analyse --configuration=phpstan/config.neon" diff --git a/composer.json b/composer.json index b772f9546..308b01100 100644 --- a/composer.json +++ b/composer.json @@ -83,7 +83,7 @@ "phpstan-setup": [ "@composer config platform --unset", "@composer update", - "@composer require --dev phpstan/phpstan:^1.0 phpstan/phpstan-phpunit:^1.0 phpstan/phpstan-deprecation-rules:^1 phpstan/phpstan-strict-rules:^1 phpunit/phpunit:^7.5.20 --with-all-dependencies", + "@composer require --dev phpstan/phpstan:1.4.* phpstan/phpstan-phpunit:1.0.* phpstan/phpstan-deprecation-rules:1.0.* phpstan/phpstan-strict-rules:1.1.* phpunit/phpunit:^7.5.20 --with-all-dependencies", "git checkout composer.json composer.lock" ], "phpstan": "@php vendor/bin/phpstan analyse --configuration=phpstan/config.neon" diff --git a/phpstan/baseline.neon b/phpstan/baseline.neon index af31ad97d..5c33d1b38 100644 --- a/phpstan/baseline.neon +++ b/phpstan/baseline.neon @@ -1,10 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Argument of an invalid type array\\\\|string supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../src/Composer/Autoload/AutoloadGenerator.php - - message: "#^Binary operation \"\\.\" between non\\-empty\\-string and array\\|string\\|null results in an error\\.$#" count: 1 @@ -110,11 +105,6 @@ parameters: count: 3 path: ../src/Composer/Autoload/AutoloadGenerator.php - - - message: "#^Parameter \\#1 \\$files of method Composer\\\\Autoload\\\\AutoloadGenerator\\:\\:getIncludeFilesFile\\(\\) expects array\\, array\\\\|string\\> given\\.$#" - count: 1 - path: ../src/Composer/Autoload/AutoloadGenerator.php - - message: "#^Parameter \\#1 \\$from of method Composer\\\\Util\\\\Filesystem\\:\\:findShortestPathCode\\(\\) expects string, string\\|false given\\.$#" count: 5 @@ -165,11 +155,6 @@ parameters: count: 1 path: ../src/Composer/Autoload/AutoloadGenerator.php - - - message: "#^Parameter \\#6 \\$namespaceFilter of method Composer\\\\Autoload\\\\AutoloadGenerator\\:\\:addClassMapCode\\(\\) expects string\\|null, int\\|string given\\.$#" - count: 1 - path: ../src/Composer/Autoload/AutoloadGenerator.php - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" count: 2 @@ -1175,6 +1160,11 @@ parameters: count: 1 path: ../src/Composer/Command/InitCommand.php + - + message: "#^Property Composer\\\\Command\\\\InitCommand\\:\\:\\$gitConfig \\(array\\\\) does not accept array\\\\.$#" + count: 1 + path: ../src/Composer/Command/InitCommand.php + - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" count: 15 @@ -2485,11 +2475,6 @@ parameters: count: 1 path: ../src/Composer/Config.php - - - message: "#^Array \\(array\\\\) does not accept key 0\\|string\\.$#" - count: 1 - path: ../src/Composer/Config.php - - message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#" count: 1 @@ -3005,11 +2990,6 @@ parameters: count: 2 path: ../src/Composer/DependencyResolver/Rule.php - - - message: "#^Array \\(array\\\\|Composer\\\\DependencyResolver\\\\Rule\\>\\) does not accept key int\\|string\\.$#" - count: 2 - path: ../src/Composer/DependencyResolver/RuleSet.php - - message: "#^Method Composer\\\\DependencyResolver\\\\RuleSet\\:\\:getTypes\\(\\) should return array\\{0, 1, 4\\} but returns array\\\\.$#" count: 1 @@ -3080,11 +3060,6 @@ parameters: count: 1 path: ../src/Composer/DependencyResolver/Solver.php - - - message: "#^Casting to int something that's already int\\.$#" - count: 2 - path: ../src/Composer/DependencyResolver/Solver.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 2 @@ -3100,11 +3075,6 @@ parameters: count: 1 path: ../src/Composer/DependencyResolver/Solver.php - - - message: "#^Only booleans are allowed in &&, int given on the left side\\.$#" - count: 1 - path: ../src/Composer/DependencyResolver/Solver.php - - message: "#^Only booleans are allowed in &&, int\\<0, max\\> given on the right side\\.$#" count: 2 @@ -3127,7 +3097,7 @@ parameters: - message: "#^Only booleans are allowed in a negated boolean, int given\\.$#" - count: 3 + count: 1 path: ../src/Composer/DependencyResolver/Solver.php - @@ -3980,11 +3950,6 @@ parameters: count: 5 path: ../src/Composer/Factory.php - - - message: "#^Array \\(array\\\\) does not accept array\\{username\\: string, password\\: string\\|null\\}\\.$#" - count: 1 - path: ../src/Composer/IO/BaseIO.php - - message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" count: 1 @@ -4050,11 +4015,6 @@ parameters: count: 1 path: ../src/Composer/IO/ConsoleIO.php - - - message: "#^Array \\(array\\\\|bool\\|string\\>\\|bool\\|string\\>\\>\\>\\) does not accept key \\(int\\|string\\)\\.$#" - count: 1 - path: ../src/Composer/InstalledVersions.php - - message: "#^Call to function method_exists\\(\\) with 'Composer\\\\\\\\Autoload…' and 'getRegisteredLoaders' will always evaluate to true\\.$#" count: 1 @@ -4085,11 +4045,6 @@ parameters: count: 15 path: ../src/Composer/Installer.php - - - message: "#^Method Composer\\\\Installer\\:\\:run\\(\\) should return 0\\|1\\|2\\|3\\|4 but returns int\\\\|int\\<1, max\\>\\.$#" - count: 1 - path: ../src/Composer/Installer.php - - message: "#^Only booleans are allowed in &&, array\\\\> given on the right side\\.$#" count: 1 @@ -4150,11 +4105,6 @@ parameters: count: 1 path: ../src/Composer/Installer.php - - - message: "#^Only booleans are allowed in an if condition, int given\\.$#" - count: 1 - path: ../src/Composer/Installer.php - - message: "#^Only booleans are allowed in \\|\\|, array\\\\|null given on the left side\\.$#" count: 2 @@ -4235,6 +4185,11 @@ parameters: count: 2 path: ../src/Composer/Installer/InstallationManager.php + - + message: "#^Constant PHP_WINDOWS_EVENT_CTRL_C not found\\.$#" + count: 1 + path: ../src/Composer/Installer/InstallationManager.php + - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 @@ -4245,6 +4200,11 @@ parameters: count: 2 path: ../src/Composer/Installer/InstallationManager.php + - + message: "#^Function sapi_windows_set_ctrl_handler not found\\.$#" + count: 3 + path: ../src/Composer/Installer/InstallationManager.php + - message: "#^Only booleans are allowed in &&, Composer\\\\EventDispatcher\\\\EventDispatcher\\|null given on the right side\\.$#" count: 2 @@ -4450,11 +4410,6 @@ parameters: count: 1 path: ../src/Composer/Json/JsonFile.php - - - message: "#^Only booleans are allowed in an if condition, int given\\.$#" - count: 1 - path: ../src/Composer/Json/JsonFile.php - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" count: 1 @@ -4625,11 +4580,6 @@ parameters: count: 1 path: ../src/Composer/Package/BasePackage.php - - - message: "#^Method Composer\\\\Package\\\\Comparer\\\\Comparer\\:\\:getChanged\\(\\) should return array\\{changed\\?\\: array\\, removed\\?\\: array\\, added\\?\\: array\\\\}\\|string\\|false but returns \\(non\\-empty\\-array\\\\>\\)\\|string\\.$#" - count: 1 - path: ../src/Composer/Package/Comparer/Comparer.php - - message: "#^Only booleans are allowed in &&, int\\<0, max\\>\\|false given on the right side\\.$#" count: 1 @@ -5115,11 +5065,6 @@ parameters: count: 1 path: ../src/Composer/Platform/Runtime.php - - - message: "#^Array \\(array\\\\) does not accept object\\.$#" - count: 2 - path: ../src/Composer/Plugin/PluginManager.php - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" count: 1 @@ -5195,21 +5140,11 @@ parameters: count: 1 path: ../src/Composer/Plugin/PluginManager.php - - - message: "#^Parameter \\#1 \\$installer of method Composer\\\\Installer\\\\InstallationManager\\:\\:addInstaller\\(\\) expects Composer\\\\Installer\\\\InstallerInterface, object given\\.$#" - count: 1 - path: ../src/Composer/Plugin/PluginManager.php - - message: "#^Parameter \\#1 \\$path of function dirname expects string, string\\|false given\\.$#" count: 1 path: ../src/Composer/Plugin/PluginManager.php - - - message: "#^Parameter \\#1 \\$plugin of method Composer\\\\Plugin\\\\PluginManager\\:\\:addPlugin\\(\\) expects Composer\\\\Plugin\\\\PluginInterface, object given\\.$#" - count: 1 - path: ../src/Composer/Plugin/PluginManager.php - - message: "#^Parameter \\#3 \\$subject of static method Composer\\\\Pcre\\\\Preg\\:\\:replace\\(\\) expects string, string\\|false given\\.$#" count: 1 @@ -5275,11 +5210,6 @@ parameters: count: 3 path: ../src/Composer/Repository/ComposerRepository.php - - - message: "#^Argument of an invalid type array\\\\|null supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: ../src/Composer/Repository/ComposerRepository.php - - message: "#^Cannot access offset 'path' on array\\{scheme\\?\\: string, host\\?\\: string, port\\?\\: int, user\\?\\: string, pass\\?\\: string, path\\?\\: string, query\\?\\: string, fragment\\?\\: string\\}\\|false\\.$#" count: 1 @@ -5420,11 +5350,6 @@ parameters: count: 1 path: ../src/Composer/Repository/ComposerRepository.php - - - message: "#^Only booleans are allowed in an if condition, int given\\.$#" - count: 1 - path: ../src/Composer/Repository/ComposerRepository.php - - message: "#^Only booleans are allowed in an if condition, string\\|false given\\.$#" count: 3 @@ -5445,11 +5370,6 @@ parameters: count: 1 path: ../src/Composer/Repository/ComposerRepository.php - - - message: "#^Parameter \\#1 \\$input of function array_values expects array, array\\\\|null given\\.$#" - count: 1 - path: ../src/Composer/Repository/ComposerRepository.php - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" count: 3 @@ -5920,11 +5840,6 @@ parameters: count: 1 path: ../src/Composer/Repository/Vcs/GitDriver.php - - - message: "#^Property Composer\\\\Repository\\\\Vcs\\\\GitDriver\\:\\:\\$branches \\(array\\\\) does not accept array\\\\.$#" - count: 1 - path: ../src/Composer/Repository/Vcs/GitDriver.php - - message: "#^Call to function array_search\\(\\) requires parameter \\#3 to be set\\.$#" count: 2 @@ -6725,11 +6640,6 @@ parameters: count: 2 path: ../src/Composer/Util/Hg.php - - - message: "#^Array \\(array\\\\) does not accept array\\{url\\: string, origin\\: string, attributes\\: non\\-empty\\-array\\, options\\: array, progress\\: array, curlHandle\\: resource, filename\\: string\\|null, headerHandle\\: resource\\|false, \\.\\.\\.\\}\\.$#" - count: 1 - path: ../src/Composer/Util/Http/CurlDownloader.php - - message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" count: 1 @@ -6920,6 +6830,11 @@ parameters: count: 1 path: ../src/Composer/Util/Http/CurlDownloader.php + - + message: "#^Property Composer\\\\Util\\\\Http\\\\CurlDownloader\\:\\:\\$jobs \\(array\\\\) does not accept non\\-empty\\-array\\, options\\: array, progress\\: array, curlHandle\\: resource, filename\\: string\\|false\\|null, headerHandle\\: resource, \\.\\.\\.\\}\\>\\.$#" + count: 1 + path: ../src/Composer/Util/Http/CurlDownloader.php + - message: "#^Property Composer\\\\Util\\\\Http\\\\CurlDownloader\\:\\:\\$multiHandle \\(resource\\|null\\) does not accept resource\\|false\\.$#" count: 1 @@ -7155,11 +7070,6 @@ parameters: count: 2 path: ../src/Composer/Util/Loop.php - - - message: "#^Cannot access offset int\\ on array\\|false\\.$#" - count: 6 - path: ../src/Composer/Util/NoProxyPattern.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 2 @@ -7345,11 +7255,6 @@ parameters: count: 1 path: ../src/Composer/Util/ProcessExecutor.php - - - message: "#^Only booleans are allowed in &&, Composer\\\\IO\\\\IOInterface\\|null given on the left side\\.$#" - count: 2 - path: ../src/Composer/Util/ProcessExecutor.php - - message: "#^Only booleans are allowed in &&, string\\|false given on the right side\\.$#" count: 2 @@ -7370,9 +7275,14 @@ parameters: count: 1 path: ../src/Composer/Util/ProcessExecutor.php + - + message: "#^Property Composer\\\\Util\\\\ProcessExecutor\\:\\:\\$jobs \\(array\\\\>\\) does not accept array\\\\>\\.$#" + count: 1 + path: ../src/Composer/Util/ProcessExecutor.php + - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 2 + count: 1 path: ../src/Composer/Util/ProcessExecutor.php - @@ -8226,16 +8136,10 @@ parameters: path: ../tests/Composer/Test/EventDispatcher/EventDispatcherTest.php - - message: "#^Call to function is_array\\(\\) with array\\ will always evaluate to true\\.$#" - count: 1 - path: ../tests/Composer/Test/IO/NullIOTest.php - - - - message: - """ - #^Call to deprecated method getRawData\\(\\) of class Composer\\\\InstalledVersions\\: - Use getAllRawData\\(\\) instead which returns all datasets for all autoloaders present in the process\\. getRawData only returns the first dataset loaded, which may not be what you expect\\.$# - """ + message: """ + #^Call to deprecated method getRawData\\(\\) of class Composer\\\\InstalledVersions\\: + Use getAllRawData\\(\\) instead which returns all datasets for all autoloaders present in the process\\. getRawData only returns the first dataset loaded, which may not be what you expect\\.$# + """ count: 1 path: ../tests/Composer/Test/InstalledVersionsTest.php @@ -8684,11 +8588,6 @@ parameters: count: 1 path: ../tests/Composer/Test/Platform/VersionTest.php - - - message: "#^Array \\(array\\\\) does not accept Composer\\\\Package\\\\CompleteAliasPackage\\|Composer\\\\Package\\\\CompletePackage\\.$#" - count: 1 - path: ../tests/Composer/Test/Plugin/PluginInstallerTest.php - - message: "#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with 'Composer\\\\\\\\Command…' and Composer\\\\Command\\\\BaseCommand will always evaluate to true\\.$#" count: 1 diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 6b00ba46c..0d927f190 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -606,10 +606,12 @@ class Config } if ($io) { $host = parse_url($url, PHP_URL_HOST); - if (!isset($this->warnedHosts[$host])) { - $io->writeError("Warning: Accessing $host over $scheme which is an insecure protocol."); + if (is_string($host)) { + if (!isset($this->warnedHosts[$host])) { + $io->writeError("Warning: Accessing $host over $scheme which is an insecure protocol."); + } + $this->warnedHosts[$host] = true; } - $this->warnedHosts[$host] = true; } } } diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index b754046a6..0ad928098 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -45,7 +45,7 @@ class RuleSet implements \IteratorAggregate, \Countable /** @var int */ protected $nextRuleId = 0; - /** @var array */ + /** @var array */ protected $rulesByHash = array(); public function __construct() diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 28b9e40da..b0a3eb00f 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -324,7 +324,7 @@ class Solver if ($newLevel <= 0 || $newLevel >= $level) { throw new SolverBugException( - "Trying to revert to invalid level ".(int) $newLevel." from level ".(int) $level."." + "Trying to revert to invalid level ".$newLevel." from level ".$level."." ); } @@ -423,7 +423,7 @@ class Solver while ($l1retry) { $l1retry = false; - if (!$num && !--$l1num) { + if (0 === $num && 0 === --$l1num) { // all level 1 literals done break 2; } @@ -447,7 +447,7 @@ class Solver unset($seen[abs($literal)]); - if ($num && 0 === --$num) { + if (0 !== $num && 0 === --$num) { if ($literal < 0) { $this->testFlagLearnedPositiveLiteral = true; } diff --git a/src/Composer/IO/BaseIO.php b/src/Composer/IO/BaseIO.php index 24a0c3b62..d03eab77d 100644 --- a/src/Composer/IO/BaseIO.php +++ b/src/Composer/IO/BaseIO.php @@ -19,7 +19,7 @@ use Psr\Log\LogLevel; abstract class BaseIO implements IOInterface { - /** @var array */ + /** @var array */ protected $authentications = array(); /** diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php index cf19321c2..4650e1ed9 100644 --- a/src/Composer/IO/IOInterface.php +++ b/src/Composer/IO/IOInterface.php @@ -198,7 +198,7 @@ interface IOInterface extends LoggerInterface /** * Get all authentication information entered. * - * @return array The map of authentication data + * @return array The map of authentication data */ public function getAuthentications(); diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index c4571ef85..8bd88e5b9 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -354,7 +354,7 @@ class Installer $fundingCount++; } } - if ($fundingCount) { + if ($fundingCount > 0) { $this->io->writeError(array( sprintf( "%d package%s you are using %s looking for funding.", diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 5eba52339..2202530d9 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -149,7 +149,7 @@ class JsonFile $this->filePutContentsIfModified($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : '')); break; } catch (\Exception $e) { - if ($retries) { + if ($retries > 0) { usleep(500000); continue; } diff --git a/src/Composer/Package/Comparer/Comparer.php b/src/Composer/Package/Comparer/Comparer.php index 06ebf7daa..b24661ad3 100644 --- a/src/Composer/Package/Comparer/Comparer.php +++ b/src/Composer/Package/Comparer/Comparer.php @@ -67,12 +67,13 @@ class Comparer } if ($toString) { + $strings = array(); foreach ($changed as $sectionKey => $itemSection) { foreach ($itemSection as $itemKey => $item) { - $changed['string'][] = $item."\r\n"; + $strings[] = $item."\r\n"; } } - $changed = implode("\r\n", $changed['string']); + $changed = implode("\r\n", $strings); } return $changed; diff --git a/src/Composer/Package/Version/VersionGuesser.php b/src/Composer/Package/Version/VersionGuesser.php index d9c18a823..797a2c0ba 100644 --- a/src/Composer/Package/Version/VersionGuesser.php +++ b/src/Composer/Package/Version/VersionGuesser.php @@ -251,7 +251,7 @@ class VersionGuesser // re-use the HgDriver to fetch branches (this properly includes bookmarks) $io = new NullIO(); $driver = new HgDriver(array('url' => $path), $io, $this->config, new HttpDownloader($io, $this->config), $this->process); - $branches = array_keys($driver->getBranches()); + $branches = array_map('strval', array_keys($driver->getBranches())); // try to find the best (nearest) version branch to assume this feature's version $result = $this->guessFeatureVersion($packageConfig, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"', $path); diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 905a6cc53..0f0257752 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -14,6 +14,7 @@ namespace Composer\Plugin; use Composer\Composer; use Composer\EventDispatcher\EventSubscriberInterface; +use Composer\Installer\InstallerInterface; use Composer\IO\IOInterface; use Composer\Package\BasePackage; use Composer\Package\CompletePackage; @@ -50,7 +51,7 @@ class PluginManager /** @var array */ protected $plugins = array(); - /** @var array */ + /** @var array */ protected $registeredPlugins = array(); /** @@ -284,11 +285,17 @@ class PluginManager } if ($oldInstallerPlugin) { + if (!is_a($class, 'Composer\Installer\InstallerInterface', true)) { + throw new \RuntimeException('Could not activate plugin "'.$package->getName().'" as "'.$class.'" does not implement Composer\Installer\InstallerInterface'); + } $this->io->writeError('Loading "'.$package->getName() . '" '.($isGlobalPlugin ? '(installed globally) ' : '').'which is a legacy composer-installer built for Composer 1.x, it is likely to cause issues as you are running Composer 2.x.'); $installer = new $class($this->io, $this->composer); $this->composer->getInstallationManager()->addInstaller($installer); $this->registeredPlugins[$package->getName()] = $installer; } elseif (class_exists($class)) { + if (!is_a($class, 'Composer\Plugin\PluginInterface', true)) { + throw new \RuntimeException('Could not activate plugin "'.$package->getName().'" as "'.$class.'" does not implement Composer\Plugin\PluginInterface'); + } $plugin = new $class(); $this->addPlugin($plugin, $isGlobalPlugin, $package); $this->registeredPlugins[$package->getName()] = $plugin; @@ -316,20 +323,15 @@ class PluginManager return; } - $oldInstallerPlugin = ($package->getType() === 'composer-installer'); - if (!isset($this->registeredPlugins[$package->getName()])) { return; } - if ($oldInstallerPlugin) { - /** @var \Composer\Installer\InstallerInterface $installer */ - $installer = $this->registeredPlugins[$package->getName()]; - unset($this->registeredPlugins[$package->getName()]); - $this->composer->getInstallationManager()->removeInstaller($installer); + $plugin = $this->registeredPlugins[$package->getName()]; + unset($this->registeredPlugins[$package->getName()]); + if ($plugin instanceof InstallerInterface) { + $this->composer->getInstallationManager()->removeInstaller($plugin); } else { - $plugin = $this->registeredPlugins[$package->getName()]; - unset($this->registeredPlugins[$package->getName()]); $this->removePlugin($plugin); } } @@ -352,16 +354,14 @@ class PluginManager return; } - $oldInstallerPlugin = ($package->getType() === 'composer-installer'); - if (!isset($this->registeredPlugins[$package->getName()])) { return; } - if ($oldInstallerPlugin) { + $plugin = $this->registeredPlugins[$package->getName()]; + if ($plugin instanceof InstallerInterface) { $this->deactivatePackage($package); } else { - $plugin = $this->registeredPlugins[$package->getName()]; unset($this->registeredPlugins[$package->getName()]); $this->removePlugin($plugin); $this->uninstallPlugin($plugin); diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 3d4e15abd..6a4073ca7 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -210,12 +210,13 @@ class ArrayRepository implements RepositoryInterface /** * Adds a new package to the repository * - * @param PackageInterface $package - * * @return void */ public function addPackage(PackageInterface $package) { + if (!$package instanceof BasePackage) { + throw new \InvalidArgumentException('Only subclasses of BasePackage are supported'); + } if (null === $this->packages) { $this->initialize(); } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 8c30935f3..cbe7cb3f9 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -102,7 +102,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito private $rootData; /** @var bool */ private $hasPartialPackages = false; - /** @var ?array */ + /** @var ?array */ private $partialPackagesByName = null; /** @@ -323,7 +323,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } if ($this->hasPartialPackages()) { - return array_values($this->partialPackagesByName); + if (!is_array($this->partialPackagesByName)) { + throw new \LogicException('hasPartialPackages failed to initialize $this->partialPackagesByName'); + } + return $this->createPackages($this->partialPackagesByName, 'packages.json inline packages'); } throw new \LogicException('Composer repositories that have lazy providers and no available-packages list can not load the complete list of packages, use getPackageNames instead.'); @@ -614,6 +617,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } if ($this->hasPartialPackages()) { + if (!is_array($this->partialPackagesByName)) { + throw new \LogicException('hasPartialPackages failed to initialize $this->partialPackagesByName'); + } foreach ($this->partialPackagesByName as $versions) { foreach ($versions as $candidate) { if (isset($result[$candidate['name']]) || !isset($candidate['provide'][$packageName])) { @@ -1327,7 +1333,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $filename = str_replace('http://', 'https://', $filename); } - if ($retries) { + if ($retries > 0) { usleep(100000); continue; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 6b5cb2420..b91508acd 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -26,9 +26,9 @@ use Composer\Config; */ class GitDriver extends VcsDriver { - /** @var array Map of tag name to identifier */ + /** @var array Map of tag name (can be turned to an int by php if it is a numeric name) to identifier */ protected $tags; - /** @var array Map of branch name to identifier */ + /** @var array Map of branch name (can be turned to an int by php if it is a numeric name) to identifier */ protected $branches; /** @var string */ protected $rootIdentifier; @@ -172,7 +172,7 @@ class GitDriver extends VcsDriver $this->process->execute('git show-ref --tags --dereference', $output, $this->repoDir); foreach ($output = $this->process->splitLines($output) as $tag) { if ($tag && Preg::isMatch('{^([a-f0-9]{40}) refs/tags/(\S+?)(\^\{\})?$}', $tag, $match)) { - $this->tags[$match[2]] = $match[1]; + $this->tags[$match[2]] = (string) $match[1]; } } } diff --git a/src/Composer/Repository/Vcs/GitLabDriver.php b/src/Composer/Repository/Vcs/GitLabDriver.php index dd7cb65b2..3c45b4e73 100644 --- a/src/Composer/Repository/Vcs/GitLabDriver.php +++ b/src/Composer/Repository/Vcs/GitLabDriver.php @@ -46,7 +46,7 @@ class GitLabDriver extends VcsDriver private $project; /** - * @var array Keeps commits returned by GitLab API + * @var array Keeps commits returned by GitLab API as commit id => info */ private $commits = array(); diff --git a/src/Composer/Repository/Vcs/VcsDriverInterface.php b/src/Composer/Repository/Vcs/VcsDriverInterface.php index cdddd022b..b8c5e842d 100644 --- a/src/Composer/Repository/Vcs/VcsDriverInterface.php +++ b/src/Composer/Repository/Vcs/VcsDriverInterface.php @@ -62,14 +62,14 @@ interface VcsDriverInterface /** * Return list of branches in the repository * - * @return array Branch names as keys, identifiers as values + * @return array Branch names as keys, identifiers as values */ public function getBranches(); /** * Return list of tags in the repository * - * @return array Tag names as keys, identifiers as values + * @return array Tag names as keys, identifiers as values */ public function getTags(); diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 993343188..65b4dc281 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -223,6 +223,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt } foreach ($driver->getTags() as $tag => $identifier) { + $tag = (string) $tag; $msg = 'Reading composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $tag . ')'; if ($isVeryVerbose) { $this->io->writeError($msg); @@ -330,6 +331,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt } foreach ($branches as $branch => $identifier) { + $branch = (string) $branch; $msg = 'Reading composer.json of ' . ($this->packageName ?: $this->url) . ' (' . $branch . ')'; if ($isVeryVerbose) { $this->io->writeError($msg); diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index b5d4f35d9..4a2a0d0c3 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -174,6 +174,9 @@ class CurlDownloader $curlHandle = curl_init(); $headerHandle = fopen('php://temp/maxmemory:32768', 'w+b'); + if (false === $headerHandle) { + throw new \RuntimeException('Failed to open a temp stream to store curl headers'); + } if ($copyTo) { $errorMessage = ''; diff --git a/src/Composer/Util/NoProxyPattern.php b/src/Composer/Util/NoProxyPattern.php index d06529eed..e8f14997b 100644 --- a/src/Composer/Util/NoProxyPattern.php +++ b/src/Composer/Util/NoProxyPattern.php @@ -159,6 +159,15 @@ class NoProxyPattern $net = unpack('C*', $network->ip); $mask = unpack('C*', $network->netmask); $ip = unpack('C*', $target->ip); + if (false === $net) { + throw new \RuntimeException('Could not parse network IP '.$network->ip); + } + if (false === $mask) { + throw new \RuntimeException('Could not parse netmask '.$network->netmask); + } + if (false === $ip) { + throw new \RuntimeException('Could not parse target IP '.$target->ip); + } for ($i = 1; $i < 17; ++$i) { if (($net[$i] & $mask[$i]) !== ($ip[$i] & $mask[$i])) { @@ -304,6 +313,12 @@ class NoProxyPattern $mask = unpack('C*', $netmask); $ip = unpack('C*', $rangeIp); $net = ''; + if (false === $mask) { + throw new \RuntimeException('Could not parse netmask '.$netmask); + } + if (false === $ip) { + throw new \RuntimeException('Could not parse range IP '.$rangeIp); + } for ($i = 1; $i < 17; ++$i) { $net .= chr($ip[$i] & $mask[$i]); diff --git a/src/Composer/Util/Svn.php b/src/Composer/Util/Svn.php index 333802035..d6b6d044c 100644 --- a/src/Composer/Util/Svn.php +++ b/src/Composer/Util/Svn.php @@ -217,8 +217,10 @@ class Svn $this->io->writeError("The Subversion server ({$this->url}) requested credentials:"); $this->hasAuth = true; - $this->credentials['username'] = $this->io->ask("Username: "); - $this->credentials['password'] = $this->io->askAndHideAnswer("Password: "); + $this->credentials = array( + 'username' => (string) $this->io->ask("Username: ", ''), + 'password' => (string) $this->io->askAndHideAnswer("Password: "), + ); $this->cacheCredentials = $this->io->askConfirmation("Should Subversion cache these credentials? (yes/no) "); @@ -345,8 +347,10 @@ class Svn $host = parse_url($this->url, PHP_URL_HOST); if (isset($authConfig[$host])) { - $this->credentials['username'] = $authConfig[$host]['username']; - $this->credentials['password'] = $authConfig[$host]['password']; + $this->credentials = array( + 'username' => $authConfig[$host]['username'], + 'password' => $authConfig[$host]['password'], + ); return $this->hasAuth = true; } @@ -366,8 +370,10 @@ class Svn return $this->hasAuth = false; } - $this->credentials['username'] = $uri['user']; - $this->credentials['password'] = !empty($uri['pass']) ? $uri['pass'] : ''; + $this->credentials = array( + 'username' => $uri['user'], + 'password' => !empty($uri['pass']) ? $uri['pass'] : '', + ); return $this->hasAuth = true; } diff --git a/tests/Composer/Test/IO/NullIOTest.php b/tests/Composer/Test/IO/NullIOTest.php index 18c5b8361..eecdc0aec 100644 --- a/tests/Composer/Test/IO/NullIOTest.php +++ b/tests/Composer/Test/IO/NullIOTest.php @@ -42,7 +42,7 @@ class NullIOTest extends TestCase { $io = new NullIO(); - $this->assertTrue(is_array($io->getAuthentications())); + $this->assertTrue(is_array($io->getAuthentications())); // @phpstan-ignore-line $this->assertEmpty($io->getAuthentications()); $this->assertEquals(array('username' => null, 'password' => null), $io->getAuthentication('foo')); } diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php index 5091617a7..cc225c3a8 100644 --- a/tests/Composer/Test/Plugin/PluginInstallerTest.php +++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php @@ -15,6 +15,7 @@ namespace Composer\Test\Plugin; use Composer\Composer; use Composer\Config; use Composer\Installer\PluginInstaller; +use Composer\Package\CompleteAliasPackage; use Composer\Package\CompletePackage; use Composer\Package\Loader\JsonLoader; use Composer\Package\Loader\ArrayLoader; @@ -44,7 +45,7 @@ class PluginInstallerTest extends TestCase protected $autoloadGenerator; /** - * @var CompletePackage[] + * @var array */ protected $packages; @@ -274,7 +275,7 @@ class PluginInstallerTest extends TestCase /** * @param string $newPluginApiVersion - * @param CompletePackage[] $plugins + * @param array $plugins * * @return void */ From 0228e5b47df0e7cd019a1355ccf9bcd5334e29bf Mon Sep 17 00:00:00 2001 From: John Stevenson Date: Fri, 21 Jan 2022 11:55:42 +0000 Subject: [PATCH 6/9] Clean up properly if self-update fails (#10475) --- src/Composer/Command/SelfUpdateCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 541636c1e..daf1b55ff 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -468,6 +468,7 @@ TAGSPUBKEY return $this->tryAsWindowsAdmin($localFilename, $newFilename); } + @unlink($newFilename); $action = 'Composer '.($backupTarget ? 'update' : 'rollback'); throw new FilesystemException($action.' failed: "'.$localFilename.'" could not be written.'.PHP_EOL.$e->getMessage()); } @@ -620,7 +621,7 @@ EOT; exec('"'.$script.'"'); @unlink($script); - // see if the file was moved and is still accessible + // see if the file was copied and is still accessible if ($result = Filesystem::isReadable($localFilename) && (hash_file('sha256', $localFilename) === $checksum)) { $io->writeError('Operation succeeded.'); @unlink($newFilename); From af6013093f16abf9e75820c971a7a0f00e17535d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 21 Jan 2022 13:21:51 +0100 Subject: [PATCH 7/9] Ignore phar files in artifact repo, fixes #10406 --- src/Composer/Repository/ArtifactRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/ArtifactRepository.php b/src/Composer/Repository/ArtifactRepository.php index 715075dea..e8140162c 100644 --- a/src/Composer/Repository/ArtifactRepository.php +++ b/src/Composer/Repository/ArtifactRepository.php @@ -79,7 +79,7 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito $directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS); $iterator = new \RecursiveIteratorIterator($directory); - $regex = new \RegexIterator($iterator, '/^.+\.(zip|phar|tar|gz|tgz)$/i'); + $regex = new \RegexIterator($iterator, '/^.+\.(zip|tar|gz|tgz)$/i'); foreach ($regex as $file) { /* @var $file \SplFileInfo */ if (!$file->isFile()) { From 6b8f1409e4e7b5f70173104b4616be04050007aa Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Fri, 21 Jan 2022 14:52:39 +0100 Subject: [PATCH 8/9] report error if binary is a directory (#10463) file_exists is true also for directory and symlink. but later in generateUnixyProxyCode we call `file_get_contents` on the binary, which fails with `file_get_contents(): read of 8192 bytes failed with errno=21 Is a directory` if the binary is a directory. --- src/Composer/Installer/BinaryInstaller.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Installer/BinaryInstaller.php b/src/Composer/Installer/BinaryInstaller.php index 1f6f14d78..7d5db0d2b 100644 --- a/src/Composer/Installer/BinaryInstaller.php +++ b/src/Composer/Installer/BinaryInstaller.php @@ -77,6 +77,10 @@ class BinaryInstaller $this->io->writeError(' Skipped installation of bin '.$bin.' for package '.$package->getName().': file not found in package'); continue; } + if (is_dir($binPath)) { + $this->io->writeError(' Skipped installation of bin '.$bin.' for package '.$package->getName().': found a directory at that path'); + continue; + } if (!$this->filesystem->isAbsolutePath($binPath)) { // in case a custom installer returned a relative path for the // $package, we can now safely turn it into a absolute path (as we From 3b4afaa9e35cc6c028600b70b252587b61e82e70 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 21 Jan 2022 13:57:28 +0000 Subject: [PATCH 9/9] ArrayLoader/ValidatingArrayLoader: handle non-string values for version/version_normalized (#10470) Co-authored-by: Jordi Boggiano --- src/Composer/Package/Loader/ArrayLoader.php | 7 +++++-- .../Package/Loader/ValidatingArrayLoader.php | 17 ++++++++++++----- .../Test/Package/Loader/ArrayLoaderTest.php | 11 +++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index f7f677e1b..bc4a14139 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -113,12 +113,15 @@ class ArrayLoader implements LoaderInterface if (!isset($config['name'])) { throw new \UnexpectedValueException('Unknown package has no name defined ('.json_encode($config).').'); } - if (!isset($config['version'])) { + if (!isset($config['version']) || !is_scalar($config['version'])) { throw new \UnexpectedValueException('Package '.$config['name'].' has no version defined.'); } + if (!is_string($config['version'])) { + $config['version'] = (string) $config['version']; + } // handle already normalized versions - if (isset($config['version_normalized'])) { + if (isset($config['version_normalized']) && is_string($config['version_normalized'])) { $version = $config['version_normalized']; // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index 3b180139d..7fb2c3b79 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -71,11 +71,18 @@ class ValidatingArrayLoader implements LoaderInterface } if (!empty($this->config['version'])) { - try { - $this->versionParser->normalize($this->config['version']); - } catch (\Exception $e) { - $this->errors[] = 'version : invalid value ('.$this->config['version'].'): '.$e->getMessage(); - unset($this->config['version']); + if (!is_scalar($this->config['version'])) { + $this->validateString('version'); + } else { + if (!is_string($this->config['version'])) { + $this->config['version'] = (string) $this->config['version']; + } + try { + $this->versionParser->normalize($this->config['version']); + } catch (\Exception $e) { + $this->errors[] = 'version : invalid value ('.$this->config['version'].'): '.$e->getMessage(); + unset($this->config['version']); + } } } diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 829fc633e..d1f42d11b 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -314,4 +314,15 @@ class ArrayLoaderTest extends TestCase $this->assertArrayHasKey('composer-plugin-api', $links); $this->assertSame('6.6.6', $links['composer-plugin-api']->getConstraint()->getPrettyString()); } + + public function testNoneStringVersion() + { + $config = array( + 'name' => 'acme/package', + 'version' => 1, + ); + + $package = $this->loader->load($config); + $this->assertSame('1', $package->getPrettyVersion()); + } }