From 5c98a2cf8e3df53172ed34130302a3d362f90631 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 16 Feb 2022 13:24:57 +0100 Subject: [PATCH] Add phpstan-symfony to get type info about console InputInterface, fix many errors (#10476) Extract common init/require commands functionality into PackageDiscoveryTrait Extract some helper methods into BaseCommand for better types --- composer.json | 9 +- composer.lock | 74 +- doc/articles/plugins.md | 4 +- phpstan/baseline-8.1.neon | 12 +- phpstan/baseline.neon | 970 ++---------------- phpstan/config.neon | 5 + src/Composer/Command/AboutCommand.php | 2 +- src/Composer/Command/ArchiveCommand.php | 30 +- src/Composer/Command/BaseCommand.php | 106 +- .../Command/BaseDependencyCommand.php | 2 +- .../Command/CheckPlatformReqsCommand.php | 7 +- src/Composer/Command/ClearCacheCommand.php | 5 +- src/Composer/Command/ConfigCommand.php | 5 +- src/Composer/Command/CreateProjectCommand.php | 17 +- src/Composer/Command/DependsCommand.php | 7 +- src/Composer/Command/DiagnoseCommand.php | 5 +- src/Composer/Command/DumpAutoloadCommand.php | 9 +- src/Composer/Command/ExecCommand.php | 7 +- src/Composer/Command/FundCommand.php | 7 +- src/Composer/Command/HomeCommand.php | 11 +- src/Composer/Command/InitCommand.php | 444 +------- src/Composer/Command/InstallCommand.php | 9 +- src/Composer/Command/LicensesCommand.php | 7 +- src/Composer/Command/OutdatedCommand.php | 10 +- .../Command/PackageDiscoveryTrait.php | 424 ++++++++ src/Composer/Command/ProhibitsCommand.php | 7 +- src/Composer/Command/ReinstallCommand.php | 8 +- src/Composer/Command/RemoveCommand.php | 13 +- src/Composer/Command/RequireCommand.php | 27 +- src/Composer/Command/RunScriptCommand.php | 11 +- src/Composer/Command/ScriptAliasCommand.php | 7 +- src/Composer/Command/SearchCommand.php | 4 +- src/Composer/Command/SelfUpdateCommand.php | 3 +- src/Composer/Command/ShowCommand.php | 131 +-- src/Composer/Command/StatusCommand.php | 9 +- src/Composer/Command/SuggestsCommand.php | 7 +- src/Composer/Command/UpdateCommand.php | 12 +- src/Composer/Command/ValidateCommand.php | 5 +- src/Composer/Config/ConfigSourceInterface.php | 2 +- src/Composer/Config/JsonConfigSource.php | 45 +- .../EventDispatcher/EventDispatcher.php | 2 +- src/Composer/IO/IOInterface.php | 4 +- src/Composer/Util/Git.php | 2 +- src/Composer/Util/Http/CurlDownloader.php | 27 +- tests/Composer/Test/ApplicationTest.php | 4 +- .../Test/Command/ArchiveCommandTest.php | 11 +- .../Test/Command/RunScriptCommandTest.php | 4 +- .../Composer/Test/Mock/HttpDownloaderMock.php | 17 +- .../Test/Mock/ProcessExecutorMock.php | 21 +- .../plugin-v8/Installer/CommandProvider.php | 2 +- tests/console-application.php | 5 + 51 files changed, 937 insertions(+), 1641 deletions(-) create mode 100644 src/Composer/Command/PackageDiscoveryTrait.php create mode 100644 tests/console-application.php diff --git a/composer.json b/composer.json index 713c6cce5..98fe72ab9 100644 --- a/composer.json +++ b/composer.json @@ -37,14 +37,17 @@ "symfony/finder": "^5.4 || ^6.0", "symfony/process": "^5.4 || ^6.0", "react/promise": "^2.8", - "composer/pcre": "^1.0" + "composer/pcre": "^1.0", + "symfony/polyfill-php73": "^1.24", + "symfony/polyfill-php80": "^1.24" }, "require-dev": { "symfony/phpunit-bridge": "^6.0", "phpstan/phpstan": "^1.4.1", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-strict-rules": "^1" + "phpstan/phpstan-strict-rules": "^1", + "phpstan/phpstan-symfony": "^1.1" }, "suggest": { "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", @@ -83,7 +86,7 @@ "scripts-descriptions": { "compile": "Compile composer.phar", "test": "Run all tests", - "phpstan": "Runs PHPStan (after phpstan-setup was executed, must be run with PHP7.4)" + "phpstan": "Runs PHPStan" }, "support": { "issues": "https://github.com/composer/composer/issues", diff --git a/composer.lock b/composer.lock index 267d9f0a6..7e00d4f9c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "36248b1f2f20c084208c57c0758261e8", + "content-hash": "03b82664ef6f80651118f0c06a49280c", "packages": [ { "name": "composer/ca-bundle", @@ -2042,6 +2042,78 @@ }, "time": "2021-11-18T09:30:29+00:00" }, + { + "name": "phpstan/phpstan-symfony", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-symfony.git", + "reference": "4e20d2e6b65a6eb2d0b8ff285c6c8c049faff03d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/4e20d2e6b65a6eb2d0b8ff285c6c8c049faff03d", + "reference": "4e20d2e6b65a6eb2d0b8ff285c6c8c049faff03d", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "php": "^7.1 || ^8.0", + "phpstan/phpstan": "^1.4" + }, + "conflict": { + "symfony/framework-bundle": "<3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/config": "^4.2 || ^5.0", + "symfony/console": "^4.0 || ^5.0", + "symfony/form": "^4.0 || ^5.0", + "symfony/framework-bundle": "^4.4 || ^5.0", + "symfony/http-foundation": "^5.1", + "symfony/messenger": "^4.2 || ^5.0", + "symfony/polyfill-php80": "^1.24", + "symfony/serializer": "^4.0 || ^5.0" + }, + "type": "phpstan-extension", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lukáš Unger", + "email": "looky.msc@gmail.com", + "homepage": "https://lookyman.net" + } + ], + "description": "Symfony Framework extensions and rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-symfony/issues", + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.1.5" + }, + "time": "2022-02-05T13:48:27+00:00" + }, { "name": "symfony/phpunit-bridge", "version": "v6.0.3", diff --git a/doc/articles/plugins.md b/doc/articles/plugins.md index 065ef5c58..fa783679b 100644 --- a/doc/articles/plugins.md +++ b/doc/articles/plugins.md @@ -263,12 +263,12 @@ class CommandProvider implements CommandProviderCapability class Command extends BaseCommand { - protected function configure() + protected function configure(): void { $this->setName('custom-plugin-command'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Executing'); } diff --git a/phpstan/baseline-8.1.neon b/phpstan/baseline-8.1.neon index c44b89729..792be138f 100644 --- a/phpstan/baseline-8.1.neon +++ b/phpstan/baseline-8.1.neon @@ -30,11 +30,6 @@ parameters: count: 1 path: ../src/Composer/Command/ExecCommand.php - - - message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\<0, max\\>\\|false given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - message: "#^Parameter \\#1 \\$from of function rename expects string, string\\|false given\\.$#" count: 1 @@ -247,7 +242,7 @@ parameters: - message: "#^Parameter \\#1 \\$string of function rawurlencode expects string, string\\|null given\\.$#" - count: 17 + count: 15 path: ../src/Composer/Util/Git.php - @@ -315,6 +310,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\\\\.$#" + count: 1 + path: ../src/Composer/Util/Http/CurlDownloader.php + - message: "#^Property Composer\\\\Util\\\\Http\\\\CurlDownloader\\:\\:\\$multiHandle \\(resource\\|null\\) does not accept CurlMultiHandle\\.$#" count: 1 diff --git a/phpstan/baseline.neon b/phpstan/baseline.neon index d9b63cbe8..220027402 100644 --- a/phpstan/baseline.neon +++ b/phpstan/baseline.neon @@ -295,11 +295,6 @@ parameters: count: 1 path: ../src/Composer/Cache.php - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ArchiveCommand.php - - message: "#^Only booleans are allowed in &&, Composer\\\\Composer\\|null given on the right side\\.$#" count: 1 @@ -335,66 +330,16 @@ parameters: count: 1 path: ../src/Composer/Command/ArchiveCommand.php - - - message: "#^Instanceof between Composer\\\\Console\\\\Application and Composer\\\\Console\\\\Application will always evaluate to true\\.$#" - count: 2 - path: ../src/Composer/Command/BaseCommand.php - - - - message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#" - count: 3 - path: ../src/Composer/Command/BaseCommand.php - - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/BaseCommand.php - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Composer\\|null given\\.$#" count: 1 path: ../src/Composer/Command/BaseCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/BaseCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 2 - path: ../src/Composer/Command/BaseCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 1 - path: ../src/Composer/Command/BaseCommand.php - - message: "#^Parameter \\#3 \\$command of class Composer\\\\Plugin\\\\PreCommandRunEvent constructor expects string, string\\|null given\\.$#" count: 1 path: ../src/Composer/Command/BaseCommand.php - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/BaseDependencyCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/BaseDependencyCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/BaseDependencyCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/BaseDependencyCommand.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 3 @@ -410,11 +355,6 @@ parameters: count: 1 path: ../src/Composer/Command/BaseDependencyCommand.php - - - message: "#^Only booleans are allowed in an elseif condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/BaseDependencyCommand.php - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Package\\\\BasePackage\\|null given\\.$#" count: 1 @@ -430,16 +370,6 @@ parameters: count: 1 path: ../src/Composer/Command/BaseDependencyCommand.php - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 1 - path: ../src/Composer/Command/BaseDependencyCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 1 - path: ../src/Composer/Command/BaseDependencyCommand.php - - message: "#^Parameter \\#1 \\$results of method Composer\\\\Command\\\\BaseDependencyCommand\\:\\:printTree\\(\\) expects array\\, non\\-empty\\-array\\|true given\\.$#" count: 1 @@ -470,21 +400,6 @@ parameters: count: 2 path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - - message: "#^Cannot call method getLocker\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 2 - path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 2 - path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Semver\\\\Constraint\\\\ConstraintInterface\\|null given\\.$#" count: 1 @@ -495,31 +410,16 @@ parameters: count: 1 path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 3 - path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - message: "#^Only booleans are allowed in a ternary operator condition, Composer\\\\Package\\\\Link\\|null given\\.$#" count: 1 path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 3 - path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - message: "#^Only booleans are allowed in an if condition, array\\ given\\.$#" count: 1 path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/CheckPlatformReqsCommand.php - - message: "#^Only booleans are allowed in a negated boolean, string\\|false given\\.$#" count: 1 @@ -530,19 +430,14 @@ parameters: count: 3 path: ../src/Composer/Command/ConfigCommand.php - - - message: "#^Only booleans are allowed in &&, mixed given on the left side\\.$#" - count: 6 - path: ../src/Composer/Command/ConfigCommand.php - - message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#" - count: 5 + count: 2 path: ../src/Composer/Command/ConfigCommand.php - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 2 + count: 1 path: ../src/Composer/Command/ConfigCommand.php - @@ -550,21 +445,11 @@ parameters: count: 1 path: ../src/Composer/Command/ConfigCommand.php - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 4 - path: ../src/Composer/Command/ConfigCommand.php - - message: "#^Only booleans are allowed in an elseif condition, int\\<0, max\\>\\|false given\\.$#" count: 1 path: ../src/Composer/Command/ConfigCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 11 - path: ../src/Composer/Command/ConfigCommand.php - - message: "#^Only booleans are allowed in an if condition, string given\\.$#" count: 1 @@ -610,21 +495,11 @@ parameters: count: 1 path: ../src/Composer/Command/CreateProjectCommand.php - - - message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#" - count: 1 - path: ../src/Composer/Command/CreateProjectCommand.php - - message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Package\\\\PackageInterface\\|false given\\.$#" count: 1 path: ../src/Composer/Command/CreateProjectCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 3 - path: ../src/Composer/Command/CreateProjectCommand.php - - message: "#^Only booleans are allowed in a negated boolean, string\\|null given\\.$#" count: 1 @@ -635,21 +510,11 @@ parameters: count: 1 path: ../src/Composer/Command/CreateProjectCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/CreateProjectCommand.php - - message: "#^Only booleans are allowed in an if condition, string\\|false given\\.$#" count: 2 path: ../src/Composer/Command/CreateProjectCommand.php - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 1 - path: ../src/Composer/Command/CreateProjectCommand.php - - message: "#^Parameter \\#1 \\$directory of function chdir expects string, string\\|false given\\.$#" count: 1 @@ -672,7 +537,7 @@ parameters: - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 5 + count: 3 path: ../src/Composer/Command/CreateProjectCommand.php - @@ -790,88 +655,23 @@ parameters: count: 3 path: ../src/Composer/Command/DiagnoseCommand.php - - - message: "#^Cannot call method getAutoloadGenerator\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 3 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 2 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 4 - path: ../src/Composer/Command/DumpAutoloadCommand.php - - - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 2 + count: 3 path: ../src/Composer/Command/DumpAutoloadCommand.php - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ExecCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ExecCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ExecCommand.php - - message: "#^Only booleans are allowed in a negated boolean, array\\ given\\.$#" count: 1 path: ../src/Composer/Command/ExecCommand.php - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/ExecCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, array\\\\|false given\\.$#" count: 1 path: ../src/Composer/Command/ExecCommand.php - - message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, array\\\\|false given\\.$#" + message: "#^Parameter \\#2 \\$listener of method Composer\\\\EventDispatcher\\\\EventDispatcher\\:\\:addListener\\(\\) expects callable\\(\\)\\: mixed, string given\\.$#" count: 1 path: ../src/Composer/Command/ExecCommand.php @@ -880,11 +680,6 @@ parameters: count: 1 path: ../src/Composer/Command/FundCommand.php - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 2 - path: ../src/Composer/Command/FundCommand.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 2 @@ -910,16 +705,6 @@ parameters: count: 1 path: ../src/Composer/Command/GlobalCommand.php - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/HomeCommand.php - - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/HomeCommand.php - - message: "#^Only booleans are allowed in a negated boolean, string\\|false given\\.$#" count: 1 @@ -930,11 +715,6 @@ parameters: count: 2 path: ../src/Composer/Command/HomeCommand.php - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/HomeCommand.php - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Composer\\|null given\\.$#" count: 1 @@ -960,19 +740,9 @@ parameters: count: 4 path: ../src/Composer/Command/InitCommand.php - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Cannot call method search\\(\\) on Composer\\\\Repository\\\\CompositeRepository\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 9 + count: 8 path: ../src/Composer/Command/InitCommand.php - @@ -986,350 +756,85 @@ parameters: path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in &&, Composer\\\\Package\\\\PackageInterface\\|false given on the right side\\.$#" - count: 3 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in &&, string\\|null given on the left side\\.$#" + message: "#^Only booleans are allowed in a negated boolean, string given\\.$#" count: 1 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Package\\\\BasePackage\\|null given\\.$#" + message: "#^Only booleans are allowed in an elseif condition, string given\\.$#" count: 1 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Package\\\\PackageInterface\\|false given\\.$#" + message: "#^Parameter \\#1 \\$author of method Composer\\\\Command\\\\InitCommand\\:\\:formatAuthors\\(\\) expects string, array\\|float\\|int\\\\|int\\<1, max\\>\\|string\\|true given\\.$#" count: 1 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Repository\\\\CompositeRepository\\|null given\\.$#" + message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|false given\\.$#" count: 1 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Repository\\\\PlatformRepository\\|null given\\.$#" + message: "#^Parameter \\#1 \\$path of function basename expects string, string\\|false given\\.$#" count: 1 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, array\\ given\\.$#" + message: "#^Parameter \\#2 \\$subject of static method Composer\\\\Pcre\\\\Preg\\:\\:isMatch\\(\\) expects string, array\\|float\\|int\\\\|int\\<1, max\\>\\|string\\|true given\\.$#" count: 1 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" + message: "#^Property Composer\\\\Command\\\\InitCommand\\:\\:\\$gitConfig \\(array\\\\) does not accept array\\\\.$#" count: 1 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, string given\\.$#" - count: 1 + message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" + count: 8 path: ../src/Composer/Command/InitCommand.php - - message: "#^Only booleans are allowed in a negated boolean, true\\|null given\\.$#" + message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Package\\\\Locker\\|null given\\.$#" count: 1 - path: ../src/Composer/Command/InitCommand.php + path: ../src/Composer/Command/InstallCommand.php - - message: "#^Only booleans are allowed in a ternary operator condition, Composer\\\\Composer\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php + message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" + count: 3 + path: ../src/Composer/Command/InstallCommand.php - - message: "#^Only booleans are allowed in a ternary operator condition, bool\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in a ternary operator condition, string\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in an elseif condition, string given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Package\\\\BasePackage\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Package\\\\PackageInterface\\|false given\\.$#" - count: 3 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Repository\\\\InstalledRepositoryInterface\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in an if condition, array\\ given\\.$#" - count: 2 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in an if condition, int\\<0, max\\> given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 2 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Parameter \\#1 \\$author of method Composer\\\\Command\\\\InitCommand\\:\\:formatAuthors\\(\\) expects string, array\\|float\\|int\\\\|int\\<1, max\\>\\|string\\|true given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|false given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Parameter \\#1 \\$path of function basename expects string, string\\|false given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Parameter \\#2 \\$subject of static method Composer\\\\Pcre\\\\Preg\\:\\:isMatch\\(\\) expects string, array\\|float\\|int\\\\|int\\<1, max\\>\\|string\\|true given\\.$#" - count: 1 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Parameter \\#3 \\$length of function substr expects int, int\\<0, max\\>\\|false given\\.$#" - 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 - path: ../src/Composer/Command/InitCommand.php - - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Cannot call method getLocker\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 2 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Only booleans are allowed in a negated boolean, Composer\\\\Package\\\\Locker\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 3 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 5 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 2 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 4 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Parameter \\#2 \\$composer of static method Composer\\\\Installer\\:\\:create\\(\\) expects Composer\\\\Composer, Composer\\\\Composer\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 2 - path: ../src/Composer/Command/InstallCommand.php - - - - message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" - count: 2 - path: ../src/Composer/Command/LicensesCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/LicensesCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/LicensesCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/LicensesCommand.php + message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" + count: 2 + path: ../src/Composer/Command/LicensesCommand.php - message: "#^Foreach overwrites \\$package with its value variable\\.$#" count: 1 path: ../src/Composer/Command/LicensesCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/LicensesCommand.php - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" count: 2 path: ../src/Composer/Command/LicensesCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/OutdatedCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 8 - path: ../src/Composer/Command/OutdatedCommand.php - - - - message: "#^Cannot call method getAutoloadGenerator\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Cannot call method getDownloadManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - message: "#^Only booleans are allowed in a negated boolean, array\\ given\\.$#" count: 1 path: ../src/Composer/Command/ReinstallCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 2 - path: ../src/Composer/Command/ReinstallCommand.php - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 4 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 2 - path: ../src/Composer/Command/ReinstallCommand.php - - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" count: 3 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RemoveCommand.php + path: ../src/Composer/Command/ReinstallCommand.php - message: "#^Cannot call method getLockedRepository\\(\\) on Composer\\\\Package\\\\Locker\\|null\\.$#" count: 1 path: ../src/Composer/Command/RemoveCommand.php - - - message: "#^Cannot call method getLocker\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 2 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 3 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RemoveCommand.php - - message: "#^Cannot call method isLocked\\(\\) on Composer\\\\Package\\\\Locker\\|null\\.$#" count: 2 @@ -1345,21 +850,6 @@ parameters: count: 2 path: ../src/Composer/Command/RemoveCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 6 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Only booleans are allowed in an elseif condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/RemoveCommand.php - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Composer\\|null given\\.$#" count: 1 @@ -1370,19 +860,9 @@ parameters: count: 1 path: ../src/Composer/Command/RemoveCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 8 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 3 - path: ../src/Composer/Command/RemoveCommand.php - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 5 + count: 3 path: ../src/Composer/Command/RemoveCommand.php - @@ -1391,50 +871,10 @@ parameters: path: ../src/Composer/Command/RemoveCommand.php - - message: "#^Parameter \\#2 \\$composer of static method Composer\\\\Installer\\:\\:create\\(\\) expects Composer\\\\Composer, Composer\\\\Composer\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 2 - path: ../src/Composer/Command/RemoveCommand.php - - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 6 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 2 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Cannot call method getLocker\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" + message: "#^Cannot call method getRepoName\\(\\) on Composer\\\\Repository\\\\RepositoryInterface\\|null\\.$#" count: 4 path: ../src/Composer/Command/RequireCommand.php - - - message: "#^Cannot call method getPluginManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RequireCommand.php - - message: "#^Cannot call method isLocked\\(\\) on Composer\\\\Package\\\\Locker\\|null\\.$#" count: 1 @@ -1442,7 +882,7 @@ parameters: - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 4 + count: 3 path: ../src/Composer/Command/RequireCommand.php - @@ -1457,17 +897,7 @@ parameters: - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 6 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 4 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 3 + count: 1 path: ../src/Composer/Command/RequireCommand.php - @@ -1475,14 +905,9 @@ parameters: count: 2 path: ../src/Composer/Command/RequireCommand.php - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 5 - path: ../src/Composer/Command/RequireCommand.php - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 7 + count: 4 path: ../src/Composer/Command/RequireCommand.php - @@ -1490,11 +915,6 @@ parameters: count: 1 path: ../src/Composer/Command/RequireCommand.php - - - message: "#^Parameter \\#2 \\$composer of static method Composer\\\\Installer\\:\\:create\\(\\) expects Composer\\\\Composer, Composer\\\\Composer\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/RequireCommand.php - - message: "#^Property Composer\\\\Command\\\\RequireCommand\\:\\:\\$composerBackup \\(string\\) does not accept string\\|false\\.$#" count: 1 @@ -1503,57 +923,22 @@ parameters: - message: "#^Property Composer\\\\Command\\\\RequireCommand\\:\\:\\$lockBackup \\(string\\|null\\) does not accept string\\|false\\|null\\.$#" count: 1 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 3 - path: ../src/Composer/Command/RequireCommand.php - - - - message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" - count: 1 - path: ../src/Composer/Command/RunScriptCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 2 - path: ../src/Composer/Command/RunScriptCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/RunScriptCommand.php - - - - message: "#^Only booleans are allowed in a negated boolean, int\\<0, max\\> given\\.$#" - count: 1 - path: ../src/Composer/Command/RunScriptCommand.php - - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/RunScriptCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/RunScriptCommand.php + path: ../src/Composer/Command/RequireCommand.php - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" + message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" count: 1 - path: ../src/Composer/Command/RunScriptCommand.php + path: ../src/Composer/Command/RequireCommand.php - - message: "#^Parameter \\#2 \\$composer of class Composer\\\\Script\\\\Event constructor expects Composer\\\\Composer, Composer\\\\Composer\\|null given\\.$#" + message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#" count: 1 path: ../src/Composer/Command/RunScriptCommand.php - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" + message: "#^Only booleans are allowed in a negated boolean, int\\<0, max\\> given\\.$#" count: 1 - path: ../src/Composer/Command/ScriptAliasCommand.php + path: ../src/Composer/Command/RunScriptCommand.php - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" @@ -1742,7 +1127,7 @@ parameters: - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 4 + count: 2 path: ../src/Composer/Command/ShowCommand.php - @@ -1750,21 +1135,11 @@ parameters: count: 2 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 3 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Cannot call method getPrettyVersion\\(\\) on Composer\\\\Package\\\\BasePackage\\|int\\.$#" count: 1 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Cannot call method getVersion\\(\\) on Composer\\\\Package\\\\BasePackage\\|int\\.$#" count: 1 @@ -1775,11 +1150,6 @@ parameters: count: 2 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 3 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Foreach overwrites \\$packages with its value variable\\.$#" count: 1 @@ -1840,16 +1210,6 @@ parameters: count: 1 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Only booleans are allowed in &&, mixed given on the left side\\.$#" - count: 14 - path: ../src/Composer/Command/ShowCommand.php - - - - message: "#^Only booleans are allowed in &&, mixed given on the right side\\.$#" - count: 5 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Only booleans are allowed in &&, string given on the left side\\.$#" count: 1 @@ -1890,26 +1250,11 @@ parameters: count: 1 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 10 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Only booleans are allowed in a negated boolean, string\\|null given\\.$#" count: 1 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/ShowCommand.php - - - - message: "#^Only booleans are allowed in an elseif condition, mixed given\\.$#" - count: 5 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Only booleans are allowed in an if condition, Composer\\\\Composer\\|null given\\.$#" count: 3 @@ -1950,14 +1295,9 @@ parameters: count: 3 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 14 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Only booleans are allowed in an if condition, string\\|null given\\.$#" - count: 2 + count: 1 path: ../src/Composer/Command/ShowCommand.php - @@ -1970,16 +1310,6 @@ parameters: count: 1 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 3 - path: ../src/Composer/Command/ShowCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 4 - path: ../src/Composer/Command/ShowCommand.php - - message: "#^Parameter \\#1 \\$arrayTree of method Composer\\\\Command\\\\ShowCommand\\:\\:displayPackageTree\\(\\) expects array\\\\>, array\\\\>\\|string\\|null\\>\\> given\\.$#" count: 2 @@ -2047,7 +1377,7 @@ parameters: - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 12 + count: 10 path: ../src/Composer/Command/ShowCommand.php - @@ -2055,31 +1385,6 @@ parameters: count: 2 path: ../src/Composer/Command/ShowCommand.php - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/StatusCommand.php - - - - message: "#^Cannot call method getDownloadManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/StatusCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 3 - path: ../src/Composer/Command/StatusCommand.php - - - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/StatusCommand.php - - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/StatusCommand.php - - message: "#^Only booleans are allowed in &&, array\\\\|null given on the right side\\.$#" count: 1 @@ -2100,11 +1405,6 @@ parameters: count: 2 path: ../src/Composer/Command/StatusCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/StatusCommand.php - - message: "#^Only booleans are allowed in a ternary operator condition, array\\\\>\\> given\\.$#" count: 1 @@ -2125,11 +1425,6 @@ parameters: count: 2 path: ../src/Composer/Command/StatusCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 3 - path: ../src/Composer/Command/StatusCommand.php - - message: "#^Only booleans are allowed in an if condition, string\\|null given\\.$#" count: 3 @@ -2160,36 +1455,16 @@ parameters: count: 1 path: ../src/Composer/Command/SuggestsCommand.php - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/SuggestsCommand.php - - message: "#^Cannot call method getLockedRepository\\(\\) on Composer\\\\Package\\\\Locker\\|null\\.$#" count: 1 path: ../src/Composer/Command/SuggestsCommand.php - - - message: "#^Cannot call method getLocker\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/SuggestsCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 3 - path: ../src/Composer/Command/SuggestsCommand.php - - message: "#^Cannot call method getPlatformOverrides\\(\\) on Composer\\\\Package\\\\Locker\\|null\\.$#" count: 1 path: ../src/Composer/Command/SuggestsCommand.php - - - message: "#^Cannot call method getRepositoryManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/SuggestsCommand.php - - message: "#^Cannot call method isLocked\\(\\) on Composer\\\\Package\\\\Locker\\|null\\.$#" count: 1 @@ -2200,16 +1475,6 @@ parameters: count: 2 path: ../src/Composer/Command/SuggestsCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/SuggestsCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 3 - path: ../src/Composer/Command/SuggestsCommand.php - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" count: 1 @@ -2225,26 +1490,6 @@ parameters: count: 1 path: ../src/Composer/Command/UpdateCommand.php - - - message: "#^Cannot call method getConfig\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Cannot call method getEventDispatcher\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Cannot call method getInstallationManager\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Cannot call method getPackage\\(\\) on Composer\\\\Composer\\|null\\.$#" - count: 1 - path: ../src/Composer/Command/UpdateCommand.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 3 @@ -2255,44 +1500,9 @@ parameters: count: 1 path: ../src/Composer/Command/UpdateCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 5 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Only booleans are allowed in an elseif condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 7 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the left side\\.$#" - count: 3 - path: ../src/Composer/Command/UpdateCommand.php - - message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#" - count: 4 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Parameter \\#2 \\$composer of static method Composer\\\\Installer\\:\\:create\\(\\) expects Composer\\\\Composer, Composer\\\\Composer\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Parameter \\#4 \\$composer of method Composer\\\\Command\\\\UpdateCommand\\:\\:getPackagesInteractively\\(\\) expects Composer\\\\Composer, Composer\\\\Composer\\|null given\\.$#" - count: 1 - path: ../src/Composer/Command/UpdateCommand.php - - - - message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#" - count: 2 + count: 3 path: ../src/Composer/Command/UpdateCommand.php - @@ -2310,26 +1520,11 @@ parameters: count: 2 path: ../src/Composer/Command/ValidateCommand.php - - - message: "#^Only booleans are allowed in &&, mixed given on the left side\\.$#" - count: 2 - path: ../src/Composer/Command/ValidateCommand.php - - message: "#^Only booleans are allowed in a negated boolean, array\\ given\\.$#" count: 1 path: ../src/Composer/Command/ValidateCommand.php - - - message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/ValidateCommand.php - - - - message: "#^Only booleans are allowed in a ternary operator condition, mixed given\\.$#" - count: 2 - path: ../src/Composer/Command/ValidateCommand.php - - message: "#^Only booleans are allowed in an elseif condition, array\\ given\\.$#" count: 3 @@ -2345,11 +1540,6 @@ parameters: count: 5 path: ../src/Composer/Command/ValidateCommand.php - - - message: "#^Only booleans are allowed in an if condition, mixed given\\.$#" - count: 1 - path: ../src/Composer/Command/ValidateCommand.php - - message: "#^Parameter \\#1 \\$function of function call_user_func expects callable\\(\\)\\: mixed, array\\{Composer\\\\Package\\\\RootPackageInterface, 'getDevRequires'\\|'getRequires'\\} given\\.$#" count: 1 @@ -2470,16 +1660,6 @@ parameters: count: 1 path: ../src/Composer/Config/JsonConfigSource.php - - - message: "#^Parameter \\#3 \\$fallback of method Composer\\\\Config\\\\JsonConfigSource\\:\\:manipulateJson\\(\\) expects callable\\(\\)\\: mixed, array\\|false given\\.$#" - count: 1 - path: ../src/Composer/Config/JsonConfigSource.php - - - - message: "#^Parameter \\#3 \\$fallback of method Composer\\\\Config\\\\JsonConfigSource\\:\\:manipulateJson\\(\\) expects callable\\(\\)\\: mixed, string given\\.$#" - count: 3 - path: ../src/Composer/Config/JsonConfigSource.php - - message: "#^Call to function is_array\\(\\) with array\\ will always evaluate to true\\.$#" count: 1 @@ -6160,11 +5340,6 @@ parameters: count: 1 path: ../src/Composer/Util/AuthHelper.php - - - message: "#^Parameter \\#2 \\$username of method Composer\\\\IO\\\\IOInterface\\:\\:setAuthentication\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: ../src/Composer/Util/AuthHelper.php - - message: "#^Parameter \\#3 \\$consumerSecret of method Composer\\\\Util\\\\Bitbucket\\:\\:requestToken\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -6345,11 +5520,6 @@ parameters: count: 1 path: ../src/Composer/Util/Git.php - - - message: "#^Only booleans are allowed in an if condition, array\\\\|null given\\.$#" - count: 1 - path: ../src/Composer/Util/Git.php - - message: "#^Only booleans are allowed in an if condition, int\\<0, max\\>\\|false given\\.$#" count: 1 @@ -6362,7 +5532,7 @@ parameters: - message: "#^Parameter \\#1 \\$str of function rawurlencode expects string, string\\|null given\\.$#" - count: 17 + count: 15 path: ../src/Composer/Util/Git.php - @@ -6370,11 +5540,6 @@ parameters: count: 1 path: ../src/Composer/Util/Git.php - - - message: "#^Parameter \\#2 \\$username of method Composer\\\\IO\\\\IOInterface\\:\\:setAuthentication\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: ../src/Composer/Util/Git.php - - message: "#^Parameter \\#3 \\$consumerSecret of method Composer\\\\Util\\\\Bitbucket\\:\\:requestToken\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -6495,11 +5660,6 @@ parameters: count: 1 path: ../src/Composer/Util/Http/CurlDownloader.php - - - message: "#^Only booleans are allowed in a negated boolean, array\\\\> given\\.$#" - count: 1 - path: ../src/Composer/Util/Http/CurlDownloader.php - - message: "#^Only booleans are allowed in a negated boolean, resource\\|false given\\.$#" count: 1 @@ -6527,7 +5687,7 @@ parameters: - message: "#^Only booleans are allowed in an if condition, string\\|false given\\.$#" - count: 7 + count: 1 path: ../src/Composer/Util/Http/CurlDownloader.php - @@ -6625,18 +5785,13 @@ parameters: count: 2 path: ../src/Composer/Util/Http/CurlDownloader.php - - - message: "#^Parameter \\#6 \\$copyTo of method Composer\\\\Util\\\\Http\\\\CurlDownloader\\:\\:initDownload\\(\\) expects string\\|null, string\\|false given\\.$#" - count: 1 - path: ../src/Composer/Util/Http/CurlDownloader.php - - message: "#^Parameter \\#7 \\$attributes of method Composer\\\\Util\\\\Http\\\\CurlDownloader\\:\\:initDownload\\(\\) expects array\\{retryAuthFailure\\?\\: bool, redirects\\?\\: int, retries\\?\\: int, storeAuth\\?\\: bool\\}, non\\-empty\\-array\\ given\\.$#" 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, \\.\\.\\.\\}\\>\\.$#" + message: "#^Property Composer\\\\Util\\\\Http\\\\CurlDownloader\\:\\:\\$jobs \\(array\\\\) does not accept non\\-empty\\-array\\\\.$#" count: 1 path: ../src/Composer/Util/Http/CurlDownloader.php @@ -7969,12 +7124,7 @@ parameters: path: ../tests/Composer/Test/Json/JsonValidationExceptionTest.php - - message: "#^Property Composer\\\\Test\\\\Mock\\\\HttpDownloaderMock\\:\\:\\$defaultHandler \\(array\\{status\\: int, body\\: string, headers\\: array\\\\}\\) does not accept non\\-empty\\-array\\\\|int\\|string\\>\\.$#" - count: 1 - path: ../tests/Composer/Test/Mock/HttpDownloaderMock.php - - - - message: "#^Property Composer\\\\Test\\\\Mock\\\\HttpDownloaderMock\\:\\:\\$expectations \\(array\\\\}\\>\\|null\\) does not accept array\\\\>\\.$#" + message: "#^Offset 'url' on array\\{url\\: string, options\\?\\: array, status\\?\\: int, body\\?\\: string, headers\\?\\: array\\\\} on left side of \\?\\? always exists and is not nullable\\.$#" count: 1 path: ../tests/Composer/Test/Mock/HttpDownloaderMock.php @@ -7989,25 +7139,45 @@ parameters: path: ../tests/Composer/Test/Mock/InstallationManagerMock.php - - message: "#^Only booleans are allowed in an if condition, string given\\.$#" - count: 2 + message: "#^Offset 'callback' does not exist on array\\{cmd\\: array\\\\|string, return\\?\\: int, stdout\\?\\: string, stderr\\?\\: string, callback\\?\\: callable\\(\\)\\: mixed\\}\\.$#" + count: 1 path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php - - message: "#^Parameter \\#2 \\$cwd of method Composer\\\\Test\\\\Mock\\\\ProcessExecutorMock\\:\\:doExecute\\(\\) expects string, string\\|null given\\.$#" - count: 4 + message: "#^Offset 'cmd' on array\\{cmd\\: array\\\\|string, return\\?\\: int, stdout\\?\\: string, stderr\\?\\: string, callback\\?\\: callable\\(\\)\\: mixed\\} on left side of \\?\\? always exists and is not nullable\\.$#" + count: 1 + path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php + + - + message: "#^Offset 'return' does not exist on array\\{cmd\\: array\\\\|string, return\\?\\: int, stdout\\?\\: string, stderr\\?\\: string, callback\\?\\: callable\\(\\)\\: mixed\\}\\.$#" + count: 1 + path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php + + - + message: "#^Offset 'return' on array\\{return\\: int, stdout\\?\\: string, stderr\\?\\: string\\} on left side of \\?\\? always exists and is not nullable\\.$#" + count: 1 path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php - - message: "#^Property Composer\\\\Test\\\\Mock\\\\ProcessExecutorMock\\:\\:\\$defaultHandler \\(array\\{return\\: int, stdout\\: string, stderr\\: string\\}\\) does not accept non\\-empty\\-array\\\\.$#" + message: "#^Offset 'stderr' does not exist on array\\{cmd\\: array\\\\|string, return\\?\\: int, stdout\\?\\: string, stderr\\?\\: string, callback\\?\\: callable\\(\\)\\: mixed\\}\\.$#" count: 1 path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php - - message: "#^Property Composer\\\\Test\\\\Mock\\\\ProcessExecutorMock\\:\\:\\$expectations \\(array\\\\|string, return\\: int, stdout\\: string, stderr\\: string, callback\\: \\(callable\\(\\)\\: mixed\\)\\|null\\}\\>\\|null\\) does not accept array\\\\|\\(callable\\(\\)\\: mixed\\)\\|int\\|string\\>\\>\\.$#" + message: "#^Offset 'stdout' does not exist on array\\{cmd\\: array\\\\|string, return\\?\\: int, stdout\\?\\: string, stderr\\?\\: string, callback\\?\\: callable\\(\\)\\: mixed\\}\\.$#" count: 1 path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php + - + message: "#^Only booleans are allowed in an if condition, string given\\.$#" + count: 2 + path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php + + - + message: "#^Parameter \\#2 \\$cwd of method Composer\\\\Test\\\\Mock\\\\ProcessExecutorMock\\:\\:doExecute\\(\\) expects string, string\\|null given\\.$#" + count: 4 + path: ../tests/Composer/Test/Mock/ProcessExecutorMock.php + - message: "#^Composer\\\\Test\\\\Mock\\\\VersionGuesserMock\\:\\:__construct\\(\\) does not call parent constructor from Composer\\\\Package\\\\Version\\\\VersionGuesser\\.$#" count: 1 diff --git a/phpstan/config.neon b/phpstan/config.neon index 990a6b0b6..210f3f75d 100644 --- a/phpstan/config.neon +++ b/phpstan/config.neon @@ -2,6 +2,8 @@ includes: - ../vendor/phpstan/phpstan-phpunit/extension.neon - ../vendor/phpstan/phpstan-deprecation-rules/rules.neon - ../vendor/phpstan/phpstan-strict-rules/rules.neon + - ../vendor/phpstan/phpstan-symfony/extension.neon + - ../vendor/phpstan/phpstan-symfony/rules.neon - ./baseline.neon - ./ignore-by-php-version.neon.php @@ -51,6 +53,9 @@ parameters: - ../src - ../tests + symfony: + consoleApplicationLoader: ../tests/console-application.php + dynamicConstantNames: - Composer\Composer::BRANCH_ALIAS_VERSION - Composer\Composer::VERSION diff --git a/src/Composer/Command/AboutCommand.php b/src/Composer/Command/AboutCommand.php index 38ac0f226..18b0db279 100644 --- a/src/Composer/Command/AboutCommand.php +++ b/src/Composer/Command/AboutCommand.php @@ -37,7 +37,7 @@ EOT ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $composerVersion = Composer::getVersion(); diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index acf72c548..beaa65a77 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -69,9 +69,9 @@ EOT ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $composer = $this->getComposer(false); + $composer = $this->tryComposer(); $config = null; if ($composer) { @@ -86,20 +86,16 @@ EOT $config = Factory::createConfig(); } - if (null === $input->getOption('format')) { - $input->setOption('format', $config->get('archive-format')); - } - if (null === $input->getOption('dir')) { - $input->setOption('dir', $config->get('archive-dir')); - } + $format = $input->getOption('format') ?? $config->get('archive-format'); + $dir = $input->getOption('dir') ?? $config->get('archive-dir'); $returnCode = $this->archive( $this->getIO(), $config, $input->getArgument('package'), $input->getArgument('version'), - $input->getOption('format'), - $input->getOption('dir'), + $format, + $dir, $input->getOption('file'), $input->getOption('ignore-filters'), $composer @@ -113,17 +109,9 @@ EOT } /** - * @param string|null $packageName - * @param string|null $version - * @param string $format - * @param string $dest - * @param string|null $fileName - * @param bool $ignoreFilters - * - * @return int * @throws \Exception */ - protected function archive(IOInterface $io, Config $config, $packageName = null, $version = null, $format = 'tar', $dest = '.', $fileName = null, $ignoreFilters = false, Composer $composer = null) + protected function archive(IOInterface $io, Config $config, ?string $packageName, ?string $version, string $format, string $dest, ?string $fileName, bool $ignoreFilters, ?Composer $composer): int { if ($composer) { $archiveManager = $composer->getArchiveManager(); @@ -142,7 +130,7 @@ EOT return 1; } } else { - $package = $this->getComposer()->getPackage(); + $package = $this->requireComposer()->getPackage(); } $io->writeError('Creating the archive into "'.$dest.'".'); @@ -166,7 +154,7 @@ EOT { $io->writeError('Searching for the specified package.'); - if ($composer = $this->getComposer(false)) { + if ($composer = $this->tryComposer()) { $localRepo = $composer->getRepositoryManager()->getLocalRepository(); $repo = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories())); } else { diff --git a/src/Composer/Command/BaseCommand.php b/src/Composer/Command/BaseCommand.php index 38ddc570f..baea928ce 100644 --- a/src/Composer/Command/BaseCommand.php +++ b/src/Composer/Command/BaseCommand.php @@ -16,11 +16,15 @@ use Composer\Composer; use Composer\Config; use Composer\Console\Application; use Composer\Factory; +use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory; +use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface; use Composer\IO\IOInterface; use Composer\IO\NullIO; +use Composer\Pcre\Preg; use Composer\Plugin\PreCommandRunEvent; use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginEvents; +use Composer\Repository\PlatformRepository; use Composer\Util\Platform; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableSeparator; @@ -32,8 +36,6 @@ use Symfony\Component\Console\Terminal; /** * Base class for Composer commands * - * @method Application getApplication() - * * @author Ryan Weaver * @author Konstantin Kudryashov */ @@ -50,21 +52,52 @@ abstract class BaseCommand extends Command private $io; /** - * @param bool $required - * @param bool|null $disablePlugins - * @param bool|null $disableScripts + * Gets the application instance for this command. + */ + public function getApplication(): Application + { + $application = parent::getApplication(); + if (!$application instanceof Application) { + throw new \RuntimeException('Composer commands can only work with an '.Application::class.' instance set'); + } + + return $application; + } + + /** + * @param bool $required Should be set to false, or use `requireComposer` instead + * @param bool|null $disablePlugins If null, reads --no-plugins as default + * @param bool|null $disableScripts If null, reads --no-scripts as default * @throws \RuntimeException * @return Composer|null + * @deprecated since Composer 2.3.0 use requireComposer or tryComposer depending on whether you have $required set to true or false */ public function getComposer($required = true, $disablePlugins = null, $disableScripts = null) + { + if ($required) { + return $this->requireComposer($disablePlugins, $disableScripts); + } + + return $this->tryComposer($disablePlugins, $disableScripts); + } + + /** + * Retrieves the default Composer\Composer instance or throws + * + * Use this instead of getComposer if you absolutely need an instance + * + * @param bool|null $disablePlugins If null, reads --no-plugins as default + * @param bool|null $disableScripts If null, reads --no-scripts as default + * @throws \RuntimeException + */ + public function requireComposer(bool $disablePlugins = null, bool $disableScripts = null): Composer { if (null === $this->composer) { - $application = $this->getApplication(); + $application = parent::getApplication(); if ($application instanceof Application) { - /* @var $application Application */ - $this->composer = $application->getComposer($required, $disablePlugins, $disableScripts); - /** @phpstan-ignore-next-line */ - } elseif ($required) { + $this->composer = $application->getComposer(true, $disablePlugins, $disableScripts); + assert($this->composer instanceof Composer); + } else { throw new \RuntimeException( 'Could not create a Composer\Composer instance, you must inject '. 'one if this command is not used with a Composer\Console\Application instance' @@ -75,6 +108,26 @@ abstract class BaseCommand extends Command return $this->composer; } + /** + * Retrieves the default Composer\Composer instance or null + * + * Use this instead of getComposer(false) + * + * @param bool|null $disablePlugins If null, reads --no-plugins as default + * @param bool|null $disableScripts If null, reads --no-scripts as default + */ + public function tryComposer(bool $disablePlugins = null, bool $disableScripts = null): ?Composer + { + if (null === $this->composer) { + $application = parent::getApplication(); + if ($application instanceof Application) { + $this->composer = $application->getComposer(false, $disablePlugins, $disableScripts); + } + } + + return $this->composer; + } + /** * @return void */ @@ -112,10 +165,9 @@ abstract class BaseCommand extends Command public function getIO() { if (null === $this->io) { - $application = $this->getApplication(); + $application = parent::getApplication(); if ($application instanceof Application) { $this->io = $application->getIO(); - /** @phpstan-ignore-next-line */ } else { $this->io = new NullIO(); } @@ -147,7 +199,7 @@ abstract class BaseCommand extends Command $disableScripts = true; } - $composer = $this->getComposer(false, $disablePlugins, $disableScripts); + $composer = $this->tryComposer($disablePlugins, $disableScripts); if (null === $composer) { $composer = Factory::createGlobal($this->getIO(), $disablePlugins, $disableScripts); } @@ -194,7 +246,11 @@ abstract class BaseCommand extends Command break; } - if ($input->hasOption('prefer-install') && $input->getOption('prefer-install')) { + if (!$input->hasOption('prefer-dist') || !$input->hasOption('prefer-source')) { + return [$preferSource, $preferDist]; + } + + if ($input->hasOption('prefer-install') && is_string($input->getOption('prefer-install'))) { if ($input->getOption('prefer-source')) { throw new \InvalidArgumentException('--prefer-source can not be used together with --prefer-install'); } @@ -219,14 +275,32 @@ abstract class BaseCommand extends Command if ($input->getOption('prefer-source') || $input->getOption('prefer-dist') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs'))) { $preferSource = $input->getOption('prefer-source') || ($keepVcsRequiresPreferSource && $input->hasOption('keep-vcs') && $input->getOption('keep-vcs')); - $preferDist = (bool) $input->getOption('prefer-dist'); + $preferDist = $input->getOption('prefer-dist'); } return array($preferSource, $preferDist); } + protected function getPlatformRequirementFilter(InputInterface $input): PlatformRequirementFilterInterface + { + if (!$input->hasOption('ignore-platform-reqs') || !$input->hasOption('ignore-platform-req')) { + throw new \LogicException('Calling getPlatformRequirementFilter from a command which does not define the --ignore-platform-req[s] flags is not permitted.'); + } + + if (true === $input->getOption('ignore-platform-reqs')) { + return PlatformRequirementFilterFactory::ignoreAll(); + } + + $ignores = $input->getOption('ignore-platform-req'); + if (count($ignores) > 0) { + return PlatformRequirementFilterFactory::fromBoolOrList($ignores); + } + + return PlatformRequirementFilterFactory::ignoreNothing(); + } + /** - * @param array $requirements + * @param array $requirements * * @return array */ diff --git a/src/Composer/Command/BaseDependencyCommand.php b/src/Composer/Command/BaseDependencyCommand.php index ba48173da..4b24202ed 100644 --- a/src/Composer/Command/BaseDependencyCommand.php +++ b/src/Composer/Command/BaseDependencyCommand.php @@ -53,7 +53,7 @@ class BaseDependencyCommand extends BaseCommand protected function doExecute(InputInterface $input, OutputInterface $output, $inverted = false) { // Emit command event on startup - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); diff --git a/src/Composer/Command/CheckPlatformReqsCommand.php b/src/Composer/Command/CheckPlatformReqsCommand.php index 73aee9551..d86dec5da 100644 --- a/src/Composer/Command/CheckPlatformReqsCommand.php +++ b/src/Composer/Command/CheckPlatformReqsCommand.php @@ -46,12 +46,9 @@ EOT ); } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $requires = array(); $removePackages = array(); diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php index 51e02b789..015a49b6f 100644 --- a/src/Composer/Command/ClearCacheCommand.php +++ b/src/Composer/Command/ClearCacheCommand.php @@ -42,10 +42,7 @@ EOT ; } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $config = Factory::createConfig(); $io = $this->getIO(); diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index b843a9c70..bff2b67a9 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -208,10 +208,9 @@ EOT } /** - * @return int * @throws \Seld\JsonLint\ParsingException */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { // Open file in editor if (true === $input->getOption('editor')) { @@ -242,7 +241,7 @@ EOT // List the configuration of the file settings if (true === $input->getOption('list')) { - $this->listConfiguration($this->config->all(), $this->config->raw(), $output, null, (bool) $input->getOption('source')); + $this->listConfiguration($this->config->all(), $this->config->raw(), $output, null, $input->getOption('source')); return 0; } diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index a1eba060f..5a270a81c 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -122,10 +122,7 @@ EOT ; } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $config = Factory::createConfig(); $io = $this->getIO(); @@ -141,12 +138,14 @@ EOT } if ($input->isInteractive() && $input->getOption('ask')) { - $parts = explode("/", strtolower($input->getArgument('package')), 2); + $package = $input->getArgument('package'); + if (null === $package) { + throw new \RuntimeException('Not enough arguments (missing: "package").'); + } + $parts = explode("/", strtolower($package), 2); $input->setArgument('directory', $io->ask('New project directory ['.array_pop($parts).']: ')); } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); - return $this->installProject( $io, $config, @@ -163,7 +162,7 @@ EOT $input->getOption('no-scripts'), $input->getOption('no-progress'), $input->getOption('no-install'), - PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs), + $this->getPlatformRequirementFilter($input), !$input->getOption('no-secure-http'), $input->getOption('add-repository') ); @@ -173,7 +172,7 @@ EOT * @param string|null $packageName * @param string|null $directory * @param string|null $packageVersion - * @param string $stability + * @param string|null $stability * @param bool $preferSource * @param bool $preferDist * @param bool $installDevPackages diff --git a/src/Composer/Command/DependsCommand.php b/src/Composer/Command/DependsCommand.php index 9ea939946..dfd8d716b 100644 --- a/src/Composer/Command/DependsCommand.php +++ b/src/Composer/Command/DependsCommand.php @@ -50,12 +50,7 @@ EOT ; } - /** - * Execute the function. - * - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { return parent::doExecute($input, $output); } diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 242b34592..e08a71984 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -69,12 +69,9 @@ EOT ; } - /** - * @return int - */ protected function execute(InputInterface $input, OutputInterface $output) { - $composer = $this->getComposer(false); + $composer = $this->tryComposer(); $io = $this->getIO(); if ($composer) { diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index 2b105e81c..33013d975 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -53,12 +53,9 @@ EOT ; } - /** - * @return int - */ protected function execute(InputInterface $input, OutputInterface $output) { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'dump-autoload', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); @@ -81,8 +78,6 @@ EOT $this->getIO()->write('Generating autoload files'); } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); - $generator = $composer->getAutoloadGenerator(); if ($input->getOption('no-dev')) { $generator->setDevMode(false); @@ -96,7 +91,7 @@ EOT $generator->setClassMapAuthoritative($authoritative); $generator->setRunScripts(true); $generator->setApcu($apcu, $apcuPrefix); - $generator->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs)); + $generator->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input)); $numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); if ($authoritative) { diff --git a/src/Composer/Command/ExecCommand.php b/src/Composer/Command/ExecCommand.php index 7cc2c3fae..8ff06afd4 100644 --- a/src/Composer/Command/ExecCommand.php +++ b/src/Composer/Command/ExecCommand.php @@ -49,14 +49,11 @@ EOT ; } - /** - * @return int - */ protected function execute(InputInterface $input, OutputInterface $output) { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $binDir = $composer->getConfig()->get('bin-dir'); - if ($input->getOption('list') || !$input->getArgument('binary')) { + if ($input->getOption('list') || null === $input->getArgument('binary')) { $bins = glob($binDir . '/*'); $bins = array_merge($bins, array_map(function ($e) { return "$e (local)"; diff --git a/src/Composer/Command/FundCommand.php b/src/Composer/Command/FundCommand.php index d8bccb620..6e551a66a 100644 --- a/src/Composer/Command/FundCommand.php +++ b/src/Composer/Command/FundCommand.php @@ -43,12 +43,9 @@ class FundCommand extends BaseCommand ; } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $repo = $composer->getRepositoryManager()->getLocalRepository(); $remoteRepos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); diff --git a/src/Composer/Command/HomeCommand.php b/src/Composer/Command/HomeCommand.php index ad4a42713..533bed826 100644 --- a/src/Composer/Command/HomeCommand.php +++ b/src/Composer/Command/HomeCommand.php @@ -57,19 +57,16 @@ EOT ); } - /** - * @inheritDoc - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $repos = $this->initializeRepos(); $io = $this->getIO(); $return = 0; $packages = $input->getArgument('packages'); - if (!$packages) { + if (count($packages) === 0) { $io->writeError('No package specified, opening homepage for the root package'); - $packages = array($this->getComposer()->getPackage()->getName()); + $packages = array($this->requireComposer()->getPackage()->getName()); } foreach ($packages as $packageName) { @@ -163,7 +160,7 @@ EOT */ private function initializeRepos() { - $composer = $this->getComposer(false); + $composer = $this->tryComposer(); if ($composer) { return array_merge( diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 29a413760..f1d8e6f91 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -46,15 +46,17 @@ use Symfony\Component\Console\Helper\FormatterHelper; */ class InitCommand extends BaseCommand { + use PackageDiscoveryTrait; + + // Properties for PackageDiscoveryTrait /** @var ?CompositeRepository */ protected $repos; + /** @var RepositorySet[] */ + private $repositorySets; /** @var array */ private $gitConfig; - /** @var RepositorySet[] */ - private $repositorySets; - /** * @inheritDoc * @@ -93,9 +95,6 @@ EOT } /** - * @inheritDoc - * - * @return int * @throws \Seld\JsonLint\ParsingException */ protected function execute(InputInterface $input, OutputInterface $output) @@ -117,7 +116,7 @@ EOT } $repositories = $input->getOption('repository'); - if ($repositories) { + if (count($repositories) > 0) { $config = Factory::createConfig($io); foreach ($repositories as $repo) { $options['repositories'][] = RepositoryFactory::configFromString($io, $config, $repo, true); @@ -145,7 +144,7 @@ EOT $autoloadPath = null; if (isset($options['autoload'])) { $autoloadPath = $options['autoload']; - $namespace = $this->namespaceFromPackageName($input->getOption('name')); + $namespace = $this->namespaceFromPackageName((string) $input->getOption('name')); $options['autoload'] = (object) array( 'psr-4' => array( $namespace . '\\' => $autoloadPath, @@ -217,7 +216,7 @@ EOT // --autoload - Show post-install configuration info if ($autoloadPath) { - $namespace = $this->namespaceFromPackageName($input->getOption('name')); + $namespace = $this->namespaceFromPackageName((string) $input->getOption('name')); $io->writeError('PSR-4 autoloading configured. Use "namespace '.$namespace.';" in '.$autoloadPath); $io->writeError('Include the Composer autoloader with: require \'vendor/autoload.php\';'); @@ -240,7 +239,7 @@ EOT // initialize repos if configured $repositories = $input->getOption('repository'); - if ($repositories) { + if (count($repositories) > 0) { $config = Factory::createConfig($io); $repos = array(new PlatformRepository); $createDefaultPackagistRepo = true; @@ -282,7 +281,8 @@ EOT $cwd = realpath("."); - if (!$name = $input->getOption('name')) { + $name = $input->getOption('name'); + if (null === $name) { $name = basename($cwd); $name = Preg::replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $name); $name = strtolower($name); @@ -323,7 +323,7 @@ EOT ); $input->setOption('name', $name); - $description = $input->getOption('description') ?: false; + $description = $input->getOption('description') ?: null; $description = $io->ask( 'Description ['.$description.']: ', $description @@ -423,7 +423,7 @@ EOT $question = 'Would you like to define your dependencies (require) interactively [yes]? '; $require = $input->getOption('require'); $requirements = array(); - if ($require || $io->askConfirmation($question)) { + if (count($require) > 0 || $io->askConfirmation($question)) { $requirements = $this->determineRequirements($input, $output, $require, $platformRepo, $preferredStability); } $input->setOption('require', $requirements); @@ -431,14 +431,14 @@ EOT $question = 'Would you like to define your dev dependencies (require-dev) interactively [yes]? '; $requireDev = $input->getOption('require-dev'); $devRequirements = array(); - if ($requireDev || $io->askConfirmation($question)) { + if (count($requireDev) > 0 || $io->askConfirmation($question)) { $devRequirements = $this->determineRequirements($input, $output, $requireDev, $platformRepo, $preferredStability); } $input->setOption('require-dev', $devRequirements); // --autoload - input and validation $autoload = $input->getOption('autoload') ?: 'src/'; - $namespace = $this->namespaceFromPackageName($input->getOption('name')); + $namespace = $this->namespaceFromPackageName((string) $input->getOption('name')); $autoload = $io->askAndValidate( 'Add PSR-4 autoload mapping? Maps namespace "'.$namespace.'" to the entered relative path. ['.$autoload.', n to skip]: ', function ($value) use ($autoload) { @@ -489,197 +489,6 @@ EOT ); } - /** - * @return CompositeRepository - */ - protected function getRepos() - { - if (!$this->repos) { - $this->repos = new CompositeRepository(array_merge( - array(new PlatformRepository), - RepositoryFactory::defaultRepos($this->getIO()) - )); - } - - return $this->repos; - } - - /** - * @param array $requires - * @param PlatformRepository|null $platformRepo - * @param string $preferredStability - * @param bool $checkProvidedVersions - * @param bool $fixed - * - * @return array - * @throws \Exception - */ - final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), PlatformRepository $platformRepo = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false) - { - if ($requires) { - $requires = $this->normalizeRequirements($requires); - $result = array(); - $io = $this->getIO(); - - foreach ($requires as $requirement) { - if (!isset($requirement['version'])) { - // determine the best version automatically - list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, null, null, $fixed); - $requirement['version'] = $version; - - // replace package name from packagist.org - $requirement['name'] = $name; - - $io->writeError(sprintf( - 'Using version %s for %s', - $requirement['version'], - $requirement['name'] - )); - } else { - // check that the specified version/constraint exists before we proceed - list($name) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed); - - // replace package name from packagist.org - $requirement['name'] = $name; - } - - $result[] = $requirement['name'] . ' ' . $requirement['version']; - } - - return $result; - } - - $versionParser = new VersionParser(); - - // Collect existing packages - $composer = $this->getComposer(false); - $installedRepo = $composer ? $composer->getRepositoryManager()->getLocalRepository() : null; - $existingPackages = array(); - if ($installedRepo) { - foreach ($installedRepo->getPackages() as $package) { - $existingPackages[] = $package->getName(); - } - } - unset($composer, $installedRepo); - - $io = $this->getIO(); - while (null !== $package = $io->ask('Search for a package: ')) { - $matches = $this->getRepos()->search($package); - - if (count($matches)) { - // Remove existing packages from search results. - foreach ($matches as $position => $foundPackage) { - if (in_array($foundPackage['name'], $existingPackages, true)) { - unset($matches[$position]); - } - } - $matches = array_values($matches); - - $exactMatch = null; - $choices = array(); - foreach ($matches as $position => $foundPackage) { - $abandoned = ''; - if (isset($foundPackage['abandoned'])) { - if (is_string($foundPackage['abandoned'])) { - $replacement = sprintf('Use %s instead', $foundPackage['abandoned']); - } else { - $replacement = 'No replacement was suggested'; - } - $abandoned = sprintf('Abandoned. %s.', $replacement); - } - - $choices[] = sprintf(' %5s %s %s', "[$position]", $foundPackage['name'], $abandoned); - if ($foundPackage['name'] === $package) { - $exactMatch = true; - break; - } - } - - // no match, prompt which to pick - if (!$exactMatch) { - $io->writeError(array( - '', - sprintf('Found %s packages matching %s', count($matches), $package), - '', - )); - - $io->writeError($choices); - $io->writeError(''); - - $validator = function ($selection) use ($matches, $versionParser) { - if ('' === $selection) { - return false; - } - - if (is_numeric($selection) && isset($matches[(int) $selection])) { - $package = $matches[(int) $selection]; - - return $package['name']; - } - - if (Preg::isMatch('{^\s*(?P[\S/]+)(?:\s+(?P\S+))?\s*$}', $selection, $packageMatches)) { - if (isset($packageMatches['version'])) { - // parsing `acme/example ~2.3` - - // validate version constraint - $versionParser->parseConstraints($packageMatches['version']); - - return $packageMatches['name'].' '.$packageMatches['version']; - } - - // parsing `acme/example` - return $packageMatches['name']; - } - - throw new \Exception('Not a valid selection'); - }; - - $package = $io->askAndValidate( - 'Enter package # to add, or the complete package name if it is not listed: ', - $validator, - 3, - false - ); - } - - // no constraint yet, determine the best version automatically - if (false !== $package && false === strpos($package, ' ')) { - $validator = function ($input) { - $input = trim($input); - - return $input ?: false; - }; - - $constraint = $io->askAndValidate( - 'Enter the version constraint to require (or leave blank to use the latest version): ', - $validator, - 3, - false - ); - - if (false === $constraint) { - list(, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $platformRepo, $preferredStability); - - $io->writeError(sprintf( - 'Using version %s for %s', - $constraint, - $package - )); - } - - $package .= ' '.$constraint; - } - - if (false !== $package) { - $requires[] = $package; - $existingPackages[] = substr($package, 0, strpos($package, ' ')); - } - } - } - - return $requires; - } - /** * @param string $author * @@ -815,229 +624,6 @@ EOT return false !== filter_var($email, FILTER_VALIDATE_EMAIL); } - /** - * @param string|null $minimumStability - * - * @return RepositorySet - */ - private function getRepositorySet(InputInterface $input, $minimumStability = null) - { - $key = $minimumStability ?: 'default'; - - if (!isset($this->repositorySets[$key])) { - $this->repositorySets[$key] = $repositorySet = new RepositorySet($minimumStability ?: $this->getMinimumStability($input)); - $repositorySet->addRepository($this->getRepos()); - } - - return $this->repositorySets[$key]; - } - - /** - * @return string - */ - private function getMinimumStability(InputInterface $input) - { - if ($input->hasOption('stability')) { - return VersionParser::normalizeStability($input->getOption('stability') ?: 'stable'); - } - - $file = Factory::getComposerFile(); - if (is_file($file) && Filesystem::isReadable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { - if (!empty($composer['minimum-stability'])) { - return VersionParser::normalizeStability($composer['minimum-stability']); - } - } - - return 'stable'; - } - - /** - * Given a package name, this determines the best version to use in the require key. - * - * This returns a version with the ~ operator prefixed when possible. - * - * @param string $name - * @param PlatformRepository|null $platformRepo - * @param string $preferredStability - * @param string|null $requiredVersion - * @param string $minimumStability - * @param bool $fixed - * @throws \InvalidArgumentException - * @return array{string, string} name version - */ - private function findBestVersionAndNameForPackage(InputInterface $input, $name, PlatformRepository $platformRepo = null, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) - { - // handle ignore-platform-reqs flag if present - $ignorePlatformReqs = false; - if ($input->hasOption('ignore-platform-reqs') && $input->hasOption('ignore-platform-req')) { - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); - } - $platformRequirementFilter = PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs); - - // find the latest version allowed in this repo set - $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo); - $effectiveMinimumStability = $minimumStability ?: $this->getMinimumStability($input); - - $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter); - - if (!$package) { - // platform packages can not be found in the pool in versions other than the local platform's has - // so if platform reqs are ignored we just take the user's word for it - if ($platformRequirementFilter->isIgnored($name)) { - return array($name, $requiredVersion ?: '*'); - } - - // Check whether the package requirements were the problem - if (!($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter) && ($candidate = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, PlatformRequirementFilterFactory::ignoreAll()))) { - throw new \InvalidArgumentException(sprintf( - 'Package %s%s has requirements incompatible with your PHP version, PHP extensions and Composer version' . $this->getPlatformExceptionDetails($candidate, $platformRepo), - $name, - $requiredVersion ? ' at version '.$requiredVersion : '' - )); - } - // Check whether the minimum stability was the problem but the package exists - if ($package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) { - // we must first verify if a valid package would be found in a lower priority repository - if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter, RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) { - throw new \InvalidArgumentException( - 'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages from the higher priority repository do not match your minimum-stability and are therefore not installable. That repository is canonical so the lower priority repo\'s packages are not installable. See https://getcomposer.org/repoprio for details and assistance.' - ); - } - - throw new \InvalidArgumentException(sprintf( - 'Could not find a version of package %s matching your minimum-stability (%s). Require it with an explicit version constraint allowing its desired stability.', - $name, - $effectiveMinimumStability - )); - } - // Check whether the required version was the problem - if ($requiredVersion && $package = $versionSelector->findBestCandidate($name, null, $preferredStability, $platformRequirementFilter)) { - // we must first verify if a valid package would be found in a lower priority repository - if ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, PlatformRequirementFilterFactory::ignoreNothing(), RepositorySet::ALLOW_SHADOWED_REPOSITORIES)) { - throw new \InvalidArgumentException( - 'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages from the higher priority repository do not match your constraint and are therefore not installable. That repository is canonical so the lower priority repo\'s packages are not installable. See https://getcomposer.org/repoprio for details and assistance.' - ); - } - - throw new \InvalidArgumentException(sprintf( - 'Could not find package %s in a version matching "%s" and a stability matching "'.$effectiveMinimumStability.'".', - $name, - $requiredVersion - )); - } - // Check whether the PHP version was the problem for all versions - if (!($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter) && ($candidate = $versionSelector->findBestCandidate($name, null, $preferredStability, PlatformRequirementFilterFactory::ignoreAll(), RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES))) { - $additional = ''; - if (false === $versionSelector->findBestCandidate($name, null, $preferredStability, PlatformRequirementFilterFactory::ignoreAll())) { - $additional = PHP_EOL.PHP_EOL.'Additionally, the package was only found with a stability of "'.$candidate->getStability().'" while your minimum stability is "'.$effectiveMinimumStability.'".'; - } - - throw new \InvalidArgumentException(sprintf( - 'Could not find package %s in any version matching your PHP version, PHP extensions and Composer version' . $this->getPlatformExceptionDetails($candidate, $platformRepo) . '%s', - $name, - $additional - )); - } - - // Check for similar names/typos - $similar = $this->findSimilar($name); - if ($similar) { - if (in_array($name, $similar, true)) { - throw new \InvalidArgumentException(sprintf( - "Could not find package %s. It was however found via repository search, which indicates a consistency issue with the repository.", - $name - )); - } - - throw new \InvalidArgumentException(sprintf( - "Could not find package %s.\n\nDid you mean " . (count($similar) > 1 ? 'one of these' : 'this') . "?\n %s", - $name, - implode("\n ", $similar) - )); - } - - throw new \InvalidArgumentException(sprintf( - 'Could not find a matching version of package %s. Check the package spelling, your version constraint and that the package is available in a stability which matches your minimum-stability (%s).', - $name, - $effectiveMinimumStability - )); - } - - return array( - $package->getPrettyName(), - $fixed ? $package->getPrettyVersion() : $versionSelector->findRecommendedRequireVersion($package), - ); - } - - /** - * @return string - */ - private function getPlatformExceptionDetails(PackageInterface $candidate, PlatformRepository $platformRepo = null) - { - $details = array(); - if (!$platformRepo) { - return ''; - } - - foreach ($candidate->getRequires() as $link) { - if (!PlatformRepository::isPlatformPackage($link->getTarget())) { - continue; - } - $platformPkg = $platformRepo->findPackage($link->getTarget(), '*'); - if (!$platformPkg) { - 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()))) { - $platformPkgVersion = $platformPkg->getPrettyVersion(); - $platformExtra = $platformPkg->getExtra(); - if (isset($platformExtra['config.platform']) && $platformPkg instanceof CompletePackageInterface) { - $platformPkgVersion .= ' ('.$platformPkg->getDescription().')'; - } - $details[] = $candidate->getPrettyName().' '.$candidate->getPrettyVersion().' requires '.$link->getTarget().' '.$link->getPrettyConstraint().' which does not match your installed version '.$platformPkgVersion.'.'; - } - } - - if (!$details) { - return ''; - } - - return ':'.PHP_EOL.' - ' . implode(PHP_EOL.' - ', $details); - } - - /** - * @param string $package - * - * @return array - */ - private function findSimilar($package) - { - try { - $results = $this->repos->search($package); - } catch (\Exception $e) { - // ignore search errors - return array(); - } - $similarPackages = array(); - - $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository(); - - foreach ($results as $result) { - if ($installedRepo->findPackage($result['name'], '*')) { - // Ignore installed package - continue; - } - $similarPackages[$result['name']] = levenshtein($package, $result['name']); - } - asort($similarPackages); - - return array_keys(array_slice($similarPackages, 0, 5)); - } - /** * @return void */ diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 5d1d01836..0dcbd8f92 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -84,7 +84,8 @@ EOT $io->writeError('You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3.'); } - if ($args = $input->getArgument('packages')) { + $args = $input->getArgument('packages'); + if (count($args) > 0) { $io->writeError('Invalid argument '.implode(' ', $args).'. Use "composer require '.implode(' ', $args).'" instead to add packages to your composer.json.'); return 1; @@ -96,7 +97,7 @@ EOT return 1; } - $composer = $this->getComposer(true, $input->getOption('no-plugins'), $input->getOption('no-scripts')); + $composer = $this->requireComposer(); if ((!$composer->getLocker() || !$composer->getLocker()->isLocked()) && !HttpDownloader::isCurlEnabled()) { $io->writeError('Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.'); @@ -115,8 +116,6 @@ EOT $apcuPrefix = $input->getOption('apcu-autoloader-prefix'); $apcu = $apcuPrefix !== null || $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); - $composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress')); $install @@ -129,7 +128,7 @@ EOT ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu, $apcuPrefix) - ->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs)) + ->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input)) ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index a50a6bae8..5c02e9d72 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -54,12 +54,9 @@ EOT ; } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'licenses', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); diff --git a/src/Composer/Command/OutdatedCommand.php b/src/Composer/Command/OutdatedCommand.php index 503541584..bdf60f664 100644 --- a/src/Composer/Command/OutdatedCommand.php +++ b/src/Composer/Command/OutdatedCommand.php @@ -21,7 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** * @author Jordi Boggiano */ -class OutdatedCommand extends ShowCommand +class OutdatedCommand extends BaseCommand { /** * @return void @@ -63,7 +63,7 @@ EOT ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $args = array( 'command' => 'show', @@ -75,7 +75,7 @@ EOT if ($input->getOption('direct')) { $args['--direct'] = true; } - if ($input->getArgument('package')) { + if (null !== $input->getArgument('package')) { $args['package'] = $input->getArgument('package'); } if ($input->getOption('strict')) { @@ -90,9 +90,7 @@ EOT if ($input->getOption('no-dev')) { $args['--no-dev'] = true; } - if ($input->getOption('ignore-platform-req')) { - $args['--ignore-platform-req'] = $input->getOption('ignore-platform-req'); - } + $args['--ignore-platform-req'] = $input->getOption('ignore-platform-req'); if ($input->getOption('ignore-platform-reqs')) { $args['--ignore-platform-reqs'] = true; } diff --git a/src/Composer/Command/PackageDiscoveryTrait.php b/src/Composer/Command/PackageDiscoveryTrait.php new file mode 100644 index 000000000..157701e76 --- /dev/null +++ b/src/Composer/Command/PackageDiscoveryTrait.php @@ -0,0 +1,424 @@ +repos) { + $this->repos = new CompositeRepository(array_merge( + array(new PlatformRepository), + RepositoryFactory::defaultRepos($this->getIO()) + )); + } + + return $this->repos; + } + + private function getRepositorySet(InputInterface $input, ?string $minimumStability = null): RepositorySet + { + $key = $minimumStability ?? 'default'; + + if (!isset($this->repositorySets[$key])) { + $this->repositorySets[$key] = $repositorySet = new RepositorySet($minimumStability ?? $this->getMinimumStability($input)); + $repositorySet->addRepository($this->getRepos()); + } + + return $this->repositorySets[$key]; + } + + private function getMinimumStability(InputInterface $input): string + { + if ($input->hasOption('stability')) { // @phpstan-ignore-line as InitCommand does have this option but not all classes using this trait do + return VersionParser::normalizeStability($input->getOption('stability') ?? 'stable'); + } + + // @phpstan-ignore-next-line as RequireCommand does not have the option above so this code is reachable there + $file = Factory::getComposerFile(); + if (is_file($file) && Filesystem::isReadable($file) && is_array($composer = json_decode((string) file_get_contents($file), true))) { + if (!empty($composer['minimum-stability'])) { + return VersionParser::normalizeStability($composer['minimum-stability']); + } + } + + return 'stable'; + } + + /** + * @param array $requires + * + * @return array + * @throws \Exception + */ + final protected function determineRequirements(InputInterface $input, OutputInterface $output, array $requires = array(), ?PlatformRepository $platformRepo = null, string $preferredStability = 'stable', bool $checkProvidedVersions = true, bool $fixed = false): array + { + if (count($requires) > 0) { + $requires = $this->normalizeRequirements($requires); + $result = array(); + $io = $this->getIO(); + + foreach ($requires as $requirement) { + if (!isset($requirement['version'])) { + // determine the best version automatically + list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, null, null, $fixed); + $requirement['version'] = $version; + + // replace package name from packagist.org + $requirement['name'] = $name; + + $io->writeError(sprintf( + 'Using version %s for %s', + $requirement['version'], + $requirement['name'] + )); + } else { + // check that the specified version/constraint exists before we proceed + list($name) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed); + + // replace package name from packagist.org + $requirement['name'] = $name; + } + + $result[] = $requirement['name'] . ' ' . $requirement['version']; + } + + return $result; + } + + $versionParser = new VersionParser(); + + // Collect existing packages + $composer = $this->tryComposer(); + $installedRepo = null; + if (null !== $composer) { + $installedRepo = $composer->getRepositoryManager()->getLocalRepository(); + } + $existingPackages = array(); + if (null !== $installedRepo) { + foreach ($installedRepo->getPackages() as $package) { + $existingPackages[] = $package->getName(); + } + } + unset($composer, $installedRepo); + + $io = $this->getIO(); + while (null !== $package = $io->ask('Search for a package: ')) { + $matches = $this->getRepos()->search($package); + + if (count($matches) > 0) { + // Remove existing packages from search results. + foreach ($matches as $position => $foundPackage) { + if (in_array($foundPackage['name'], $existingPackages, true)) { + unset($matches[$position]); + } + } + $matches = array_values($matches); + + $exactMatch = false; + $choices = array(); + foreach ($matches as $position => $foundPackage) { + $abandoned = ''; + if (isset($foundPackage['abandoned'])) { + if (is_string($foundPackage['abandoned'])) { + $replacement = sprintf('Use %s instead', $foundPackage['abandoned']); + } else { + $replacement = 'No replacement was suggested'; + } + $abandoned = sprintf('Abandoned. %s.', $replacement); + } + + $choices[] = sprintf(' %5s %s %s', "[$position]", $foundPackage['name'], $abandoned); + if ($foundPackage['name'] === $package) { + $exactMatch = true; + break; + } + } + + // no match, prompt which to pick + if (!$exactMatch) { + $io->writeError(array( + '', + sprintf('Found %s packages matching %s', count($matches), $package), + '', + )); + + $io->writeError($choices); + $io->writeError(''); + + $validator = function ($selection) use ($matches, $versionParser) { + if ('' === $selection) { + return false; + } + + if (is_numeric($selection) && isset($matches[(int) $selection])) { + $package = $matches[(int) $selection]; + + return $package['name']; + } + + if (Preg::isMatch('{^\s*(?P[\S/]+)(?:\s+(?P\S+))?\s*$}', $selection, $packageMatches)) { + if (isset($packageMatches['version'])) { + // parsing `acme/example ~2.3` + + // validate version constraint + $versionParser->parseConstraints($packageMatches['version']); + + return $packageMatches['name'].' '.$packageMatches['version']; + } + + // parsing `acme/example` + return $packageMatches['name']; + } + + throw new \Exception('Not a valid selection'); + }; + + $package = $io->askAndValidate( + 'Enter package # to add, or the complete package name if it is not listed: ', + $validator, + 3, + false + ); + } + + // no constraint yet, determine the best version automatically + if (false !== $package && false === strpos($package, ' ')) { + $validator = function ($input) { + $input = trim($input); + + return strlen($input) > 0 ? $input : false; + }; + + $constraint = $io->askAndValidate( + 'Enter the version constraint to require (or leave blank to use the latest version): ', + $validator, + 3, + false + ); + + if (false === $constraint) { + list(, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $platformRepo, $preferredStability); + + $io->writeError(sprintf( + 'Using version %s for %s', + $constraint, + $package + )); + } + + $package .= ' '.$constraint; + } + + if (false !== $package) { + $requires[] = $package; + $existingPackages[] = explode(' ', $package)[0]; + } + } + } + + return $requires; + } + + /** + * Given a package name, this determines the best version to use in the require key. + * + * This returns a version with the ~ operator prefixed when possible. + * + * @throws \InvalidArgumentException + * @return array{string, string} name version + */ + private function findBestVersionAndNameForPackage(InputInterface $input, string $name, ?PlatformRepository $platformRepo = null, string $preferredStability = 'stable', ?string $requiredVersion = null, ?string $minimumStability = null, bool $fixed = false): array + { + // handle ignore-platform-reqs flag if present + $platformRequirementFilter = $this->getPlatformRequirementFilter($input); + + // find the latest version allowed in this repo set + $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo); + $effectiveMinimumStability = $minimumStability ?? $this->getMinimumStability($input); + + $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter); + + if (false === $package) { + // platform packages can not be found in the pool in versions other than the local platform's has + // so if platform reqs are ignored we just take the user's word for it + if ($platformRequirementFilter->isIgnored($name)) { + return array($name, $requiredVersion ?: '*'); + } + + // Check whether the package requirements were the problem + if (!($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter) && false !== ($candidate = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, PlatformRequirementFilterFactory::ignoreAll()))) { + throw new \InvalidArgumentException(sprintf( + 'Package %s%s has requirements incompatible with your PHP version, PHP extensions and Composer version' . $this->getPlatformExceptionDetails($candidate, $platformRepo), + $name, + is_string($requiredVersion) ? ' at version '.$requiredVersion : '' + )); + } + // Check whether the minimum stability was the problem but the package exists + if (false !== ($package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES))) { + // we must first verify if a valid package would be found in a lower priority repository + if (false !== ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $platformRequirementFilter, RepositorySet::ALLOW_SHADOWED_REPOSITORIES))) { + throw new \InvalidArgumentException( + 'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages from the higher priority repository do not match your minimum-stability and are therefore not installable. That repository is canonical so the lower priority repo\'s packages are not installable. See https://getcomposer.org/repoprio for details and assistance.' + ); + } + + throw new \InvalidArgumentException(sprintf( + 'Could not find a version of package %s matching your minimum-stability (%s). Require it with an explicit version constraint allowing its desired stability.', + $name, + $effectiveMinimumStability + )); + } + // Check whether the required version was the problem + if (is_string($requiredVersion) && false !== ($package = $versionSelector->findBestCandidate($name, null, $preferredStability, $platformRequirementFilter))) { + // we must first verify if a valid package would be found in a lower priority repository + if (false !== ($allReposPackage = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, PlatformRequirementFilterFactory::ignoreNothing(), RepositorySet::ALLOW_SHADOWED_REPOSITORIES))) { + throw new \InvalidArgumentException( + 'Package '.$name.' exists in '.$allReposPackage->getRepository()->getRepoName().' and '.$package->getRepository()->getRepoName().' which has a higher repository priority. The packages from the higher priority repository do not match your constraint and are therefore not installable. That repository is canonical so the lower priority repo\'s packages are not installable. See https://getcomposer.org/repoprio for details and assistance.' + ); + } + + throw new \InvalidArgumentException(sprintf( + 'Could not find package %s in a version matching "%s" and a stability matching "'.$effectiveMinimumStability.'".', + $name, + $requiredVersion + )); + } + // Check whether the PHP version was the problem for all versions + if (!$platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter && false !== ($candidate = $versionSelector->findBestCandidate($name, null, $preferredStability, PlatformRequirementFilterFactory::ignoreAll(), RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES))) { + $additional = ''; + if (false === $versionSelector->findBestCandidate($name, null, $preferredStability, PlatformRequirementFilterFactory::ignoreAll())) { + $additional = PHP_EOL.PHP_EOL.'Additionally, the package was only found with a stability of "'.$candidate->getStability().'" while your minimum stability is "'.$effectiveMinimumStability.'".'; + } + + throw new \InvalidArgumentException(sprintf( + 'Could not find package %s in any version matching your PHP version, PHP extensions and Composer version' . $this->getPlatformExceptionDetails($candidate, $platformRepo) . '%s', + $name, + $additional + )); + } + + // Check for similar names/typos + $similar = $this->findSimilar($name); + if (count($similar) > 0) { + if (in_array($name, $similar, true)) { + throw new \InvalidArgumentException(sprintf( + "Could not find package %s. It was however found via repository search, which indicates a consistency issue with the repository.", + $name + )); + } + + throw new \InvalidArgumentException(sprintf( + "Could not find package %s.\n\nDid you mean " . (count($similar) > 1 ? 'one of these' : 'this') . "?\n %s", + $name, + implode("\n ", $similar) + )); + } + + throw new \InvalidArgumentException(sprintf( + 'Could not find a matching version of package %s. Check the package spelling, your version constraint and that the package is available in a stability which matches your minimum-stability (%s).', + $name, + $effectiveMinimumStability + )); + } + + return array( + $package->getPrettyName(), + $fixed ? $package->getPrettyVersion() : $versionSelector->findRecommendedRequireVersion($package), + ); + } + + /** + * @return array + */ + private function findSimilar(string $package): array + { + try { + if (null === $this->repos) { + throw new \LogicException('findSimilar was called before $this->repos was initialized'); + } + $results = $this->repos->search($package); + } catch (\Throwable $e) { + if ($e instanceof \LogicException) { + throw $e; + } + + // ignore search errors + return array(); + } + $similarPackages = array(); + + $installedRepo = $this->requireComposer()->getRepositoryManager()->getLocalRepository(); + + foreach ($results as $result) { + if (null !== $installedRepo->findPackage($result['name'], '*')) { + // Ignore installed package + continue; + } + $similarPackages[$result['name']] = levenshtein($package, $result['name']); + } + asort($similarPackages); + + return array_keys(array_slice($similarPackages, 0, 5)); + } + + private function getPlatformExceptionDetails(PackageInterface $candidate, ?PlatformRepository $platformRepo = null): string + { + $details = array(); + if (null === $platformRepo) { + return ''; + } + + foreach ($candidate->getRequires() as $link) { + if (!PlatformRepository::isPlatformPackage($link->getTarget())) { + continue; + } + $platformPkg = $platformRepo->findPackage($link->getTarget(), '*'); + if (null === $platformPkg) { + 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()))) { + $platformPkgVersion = $platformPkg->getPrettyVersion(); + $platformExtra = $platformPkg->getExtra(); + if (isset($platformExtra['config.platform']) && $platformPkg instanceof CompletePackageInterface) { + $platformPkgVersion .= ' ('.$platformPkg->getDescription().')'; + } + $details[] = $candidate->getPrettyName().' '.$candidate->getPrettyVersion().' requires '.$link->getTarget().' '.$link->getPrettyConstraint().' which does not match your installed version '.$platformPkgVersion.'.'; + } + } + + if (count($details) === 0) { + return ''; + } + + return ':'.PHP_EOL.' - ' . implode(PHP_EOL.' - ', $details); + } +} diff --git a/src/Composer/Command/ProhibitsCommand.php b/src/Composer/Command/ProhibitsCommand.php index c51d320f5..304b116e2 100644 --- a/src/Composer/Command/ProhibitsCommand.php +++ b/src/Composer/Command/ProhibitsCommand.php @@ -51,12 +51,7 @@ EOT ; } - /** - * Execute the function. - * - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { return parent::doExecute($input, $output, true); } diff --git a/src/Composer/Command/ReinstallCommand.php b/src/Composer/Command/ReinstallCommand.php index 7a1160743..9daec7643 100644 --- a/src/Composer/Command/ReinstallCommand.php +++ b/src/Composer/Command/ReinstallCommand.php @@ -70,11 +70,11 @@ EOT ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $io = $this->getIO(); - $composer = $this->getComposer(true, $input->getOption('no-plugins'), $input->getOption('no-scripts')); + $composer = $this->requireComposer(); $localRepo = $composer->getRepositoryManager()->getLocalRepository(); $packagesToReinstall = array(); @@ -139,8 +139,6 @@ EOT $downloadManager = $composer->getDownloadManager(); $package = $composer->getPackage(); - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); - $installationManager->setOutputProgress(!$input->getOption('no-progress')); if ($input->getOption('no-plugins')) { $installationManager->disablePlugins(); @@ -166,7 +164,7 @@ EOT $generator = $composer->getAutoloadGenerator(); $generator->setClassMapAuthoritative($authoritative); $generator->setApcu($apcu, $apcuPrefix); - $generator->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs)); + $generator->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input)); $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); } diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 7705e3f0a..0f7e37806 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -80,7 +80,7 @@ EOT protected function interact(InputInterface $input, OutputInterface $output) { if ($input->getOption('unused')) { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $locker = $composer->getLocker(); if (!$locker->isLocked()) { throw new \UnexpectedValueException('A valid composer.lock file is required to run this command with --unused'); @@ -115,7 +115,7 @@ EOT } $input->setArgument('packages', array_merge($input->getArgument('packages'), $unused)); - if (!$input->getArgument('packages')) { + if (count($input->getArgument('packages')) === 0) { $this->getIO()->writeError('No unused packages to remove'); $this->setCode(function () { return 0; @@ -125,7 +125,6 @@ EOT } /** - * @return int * @throws \Seld\JsonLint\ParsingException */ protected function execute(InputInterface $input, OutputInterface $output) @@ -210,13 +209,13 @@ EOT return 0; } - if ($composer = $this->getComposer(false)) { + if ($composer = $this->tryComposer()) { $composer->getPluginManager()->deactivateInstalledPlugins(); } // Update packages $this->resetComposer(); - $composer = $this->getComposer(true, $input->getOption('no-plugins'), $input->getOption('no-scripts')); + $composer = $this->requireComposer(); if ($dryRun) { $rootPackage = $composer->getPackage(); @@ -258,8 +257,6 @@ EOT $io->writeError('Running composer update '.implode(' ', $packages).$flags.''); - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); - $install ->setVerbose($input->getOption('verbose')) ->setDevMode($updateDevMode) @@ -269,7 +266,7 @@ EOT ->setUpdate(true) ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs)) + ->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input)) ->setDryRun($dryRun) ; diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 92fc2114a..086649af9 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -14,6 +14,7 @@ namespace Composer\Command; use Composer\DependencyResolver\Request; use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory; +use Composer\Repository\RepositorySet; use Composer\Util\Filesystem; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; @@ -38,8 +39,16 @@ use Composer\Util\Silencer; * @author Jérémy Romey * @author Jordi Boggiano */ -class RequireCommand extends InitCommand +class RequireCommand extends BaseCommand { + use PackageDiscoveryTrait; + + // Properties for PackageDiscoveryTrait + /** @var ?CompositeRepository */ + protected $repos; + /** @var RepositorySet[] */ + private $repositorySets; + /** @var bool */ private $newlyCreated; /** @var bool */ @@ -110,7 +119,6 @@ EOT } /** - * @return int * @throws \Seld\JsonLint\ParsingException */ protected function execute(InputInterface $input, OutputInterface $output) @@ -177,11 +185,11 @@ EOT } } - $composer = $this->getComposer(true, $input->getOption('no-plugins')); + $composer = $this->requireComposer(); $repos = $composer->getRepositoryManager()->getRepositories(); - $platformOverrides = $composer->getConfig()->get('platform') ?: array(); - // initialize $this->repos as it is used by the parent InitCommand + $platformOverrides = $composer->getConfig()->get('platform'); + // initialize $this->repos as it is used by the PackageDiscoveryTrait $this->repos = new CompositeRepository(array_merge( array($platformRepo = new PlatformRepository(array(), $platformOverrides)), $repos @@ -256,7 +264,7 @@ EOT $this->firstRequire = $this->newlyCreated; if (!$this->firstRequire) { $composerDefinition = $this->json->read(); - if (empty($composerDefinition['require']) && empty($composerDefinition['require-dev'])) { + if (count($composerDefinition['require'] ?? []) === 0 && count($composerDefinition['require-dev'] ?? []) === 0) { $this->firstRequire = true; } } @@ -355,7 +363,7 @@ EOT { // Update packages $this->resetComposer(); - $composer = $this->getComposer(true, $input->getOption('no-plugins'), $input->getOption('no-scripts')); + $composer = $this->requireComposer(); $this->dependencyResolutionCompleted = false; $composer->getEventDispatcher()->addListener(InstallerEvents::PRE_OPERATIONS_EXEC, array($this, 'markSolverComplete'), 10000); @@ -401,7 +409,6 @@ EOT $install = Installer::create($io, $composer); - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); list($preferSource, $preferDist) = $this->getPreferredInstallOptions($composer->getConfig(), $input); $install @@ -416,7 +423,7 @@ EOT ->setUpdate(true) ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs)) + ->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input)) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; @@ -472,7 +479,7 @@ EOT return true; } - protected function interact(InputInterface $input, OutputInterface $output) + protected function interact(InputInterface $input, OutputInterface $output): void { return; } diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 6bb0f0a64..f35688c15 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -54,7 +54,7 @@ class RunScriptCommand extends BaseCommand ->setAliases(array('run')) ->setDescription('Runs the scripts defined in composer.json.') ->setDefinition(array( - new InputArgument('script', InputArgument::OPTIONAL, 'Script name to run.'), + new InputArgument('script', InputArgument::REQUIRED, 'Script name to run.'), new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), new InputOption('timeout', null, InputOption::VALUE_REQUIRED, 'Sets script timeout in seconds, or 0 for never.'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Sets the dev mode.'), @@ -73,14 +73,11 @@ EOT ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { if ($input->getOption('list')) { return $this->listScripts($output); } - if (!$input->getArgument('script')) { - throw new \RuntimeException('Missing required argument "script"'); - } $script = $input->getArgument('script'); if (!in_array($script, $this->scriptEvents)) { @@ -89,7 +86,7 @@ EOT } } - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $devMode = $input->getOption('dev') || !$input->getOption('no-dev'); $event = new ScriptEvent($script, $composer, $this->getIO(), $devMode); $hasListeners = $composer->getEventDispatcher()->hasEventListeners($event); @@ -117,7 +114,7 @@ EOT */ protected function listScripts(OutputInterface $output) { - $scripts = $this->getComposer()->getPackage()->getScripts(); + $scripts = $this->requireComposer()->getPackage()->getScripts(); if (!count($scripts)) { return 0; diff --git a/src/Composer/Command/ScriptAliasCommand.php b/src/Composer/Command/ScriptAliasCommand.php index 1cf3e2ba9..801068c9a 100644 --- a/src/Composer/Command/ScriptAliasCommand.php +++ b/src/Composer/Command/ScriptAliasCommand.php @@ -64,12 +64,9 @@ EOT ; } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $args = $input->getArguments(); diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index 04d5e549d..0299767bc 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -56,7 +56,7 @@ EOT ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { // init repos $platformRepo = new PlatformRepository; @@ -69,7 +69,7 @@ EOT return 1; } - if (!($composer = $this->getComposer(false))) { + if (!($composer = $this->tryComposer())) { $composer = Factory::create($this->getIO(), array(), $input->hasParameterOption('--no-plugins')); } $localRepo = $composer->getRepositoryManager()->getLocalRepository(); diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 59f8ef416..d3a1a3343 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -75,10 +75,9 @@ EOT } /** - * @return int * @throws FilesystemException */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { // trigger autoloading of a few classes which may be needed when verifying/swapping the phar file // to ensure we do not try to load them from the new phar, see https://github.com/composer/composer/issues/10252 diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 0603c12e9..920ad1116 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -15,6 +15,7 @@ namespace Composer\Command; use Composer\Composer; use Composer\DependencyResolver\DefaultPolicy; use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory; +use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface; use Composer\Json\JsonFile; use Composer\Package\BasePackage; use Composer\Package\CompletePackageInterface; @@ -114,7 +115,7 @@ EOT $this->initStyles($output); } - $composer = $this->getComposer(false); + $composer = $this->tryComposer(); $io = $this->getIO(); if ($input->getOption('installed')) { @@ -123,7 +124,7 @@ EOT if ($input->getOption('outdated')) { $input->setOption('latest', true); - } elseif ($input->getOption('ignore')) { + } elseif (count($input->getOption('ignore')) > 0) { $io->writeError('You are using the option "ignore" for action other than "outdated", it will be ignored.'); } @@ -158,7 +159,7 @@ EOT return 1; } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); + $platformReqFilter = $this->getPlatformRequirementFilter($input); // init repos $platformOverrides = array(); @@ -169,12 +170,15 @@ EOT $lockedRepo = null; if ($input->getOption('self')) { - $package = $this->getComposer()->getPackage(); + $package = $this->requireComposer()->getPackage(); if ($input->getOption('name-only')) { $io->write($package->getName()); return 0; } + if ($input->getArgument('package')) { + throw new \InvalidArgumentException('You cannot use --self together with a package name'); + } $repos = $installedRepo = new InstalledRepository(array(new RootPackageRepository($package))); } elseif ($input->getOption('platform')) { $repos = $installedRepo = new InstalledRepository(array($platformRepo)); @@ -213,7 +217,7 @@ EOT } else { // --installed / default case if (!$composer) { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); } $rootPkg = $composer->getPackage(); $repos = $installedRepo = new InstalledRepository(array($composer->getRepositoryManager()->getLocalRepository())); @@ -243,31 +247,33 @@ EOT $packageFilter = $input->getArgument('package'); // show single package or single version - if (($packageFilter && false === strpos($packageFilter, '*')) || !empty($package)) { - if (empty($package)) { - list($package, $versions) = $this->getPackage($installedRepo, $repos, $input->getArgument('package'), $input->getArgument('version')); - - if (empty($package)) { - $options = $input->getOptions(); - $hint = ''; - if ($input->getOption('locked')) { - $hint .= ' in lock file'; - } - if (isset($options['working-dir'])) { - $hint .= ' in ' . $options['working-dir'] . '/composer.json'; - } - if (PlatformRepository::isPlatformPackage($input->getArgument('package')) && !$input->getOption('platform')) { - $hint .= ', try using --platform (-p) to show platform packages'; - } - if (!$input->getOption('all')) { - $hint .= ', try using --all (-a) to show all available packages'; - } - - throw new \InvalidArgumentException('Package "' . $packageFilter . '" not found'.$hint.'.'); + if (isset($package)) { + $versions = array($package->getPrettyVersion() => $package->getVersion()); + } elseif (null !== $packageFilter && str_contains($packageFilter, '*')) { + list($package, $versions) = $this->getPackage($installedRepo, $repos, $packageFilter, $input->getArgument('version')); + + if (!isset($package)) { + $options = $input->getOptions(); + $hint = ''; + if ($input->getOption('locked')) { + $hint .= ' in lock file'; } - } else { - $versions = array($package->getPrettyVersion() => $package->getVersion()); + if (isset($options['working-dir'])) { + $hint .= ' in ' . $options['working-dir'] . '/composer.json'; + } + if (PlatformRepository::isPlatformPackage($packageFilter) && !$input->getOption('platform')) { + $hint .= ', try using --platform (-p) to show platform packages'; + } + if (!$input->getOption('all')) { + $hint .= ', try using --all (-a) to show all available packages'; + } + + throw new \InvalidArgumentException('Package "' . $packageFilter . '" not found'.$hint.'.'); } + } + + if (isset($package)) { + assert(isset($versions)); $exitCode = 0; if ($input->getOption('tree')) { @@ -278,32 +284,34 @@ EOT } else { $this->displayPackageTree(array($arrayTree)); } - } else { - $latestPackage = null; - if ($input->getOption('latest')) { - $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $input->getOption('minor-only'), $ignorePlatformReqs); - } - if ( - $input->getOption('outdated') - && $input->getOption('strict') - && $latestPackage - && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() - && (!$latestPackage instanceof CompletePackageInterface || !$latestPackage->isAbandoned()) - ) { - $exitCode = 1; - } - if ($input->getOption('path')) { - $io->write($package->getName(), false); - $io->write(' ' . strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n")); - return $exitCode; - } + return $exitCode; + } - if ('json' === $format) { - $this->printPackageInfoAsJson($package, $versions, $installedRepo, $latestPackage ?: null); - } else { - $this->printPackageInfo($package, $versions, $installedRepo, $latestPackage ?: null); - } + $latestPackage = null; + if ($input->getOption('latest')) { + $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $input->getOption('minor-only'), $platformReqFilter); + } + if ( + $input->getOption('outdated') + && $input->getOption('strict') + && $latestPackage + && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() + && (!$latestPackage instanceof CompletePackageInterface || !$latestPackage->isAbandoned()) + ) { + $exitCode = 1; + } + if ($input->getOption('path')) { + $io->write($package->getName(), false); + $io->write(' ' . strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n")); + + return $exitCode; + } + + if ('json' === $format) { + $this->printPackageInfoAsJson($package, $versions, $installedRepo, $latestPackage ?: null); + } else { + $this->printPackageInfo($package, $versions, $installedRepo, $latestPackage ?: null); } return $exitCode; @@ -407,7 +415,7 @@ EOT if ($showLatest && $showVersion) { foreach ($packages[$type] as $package) { if (is_object($package)) { - $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $showMinorOnly, $ignorePlatformReqs); + $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $showMinorOnly, $platformReqFilter); if ($latestPackage === false) { continue; } @@ -597,7 +605,7 @@ EOT */ protected function getRootRequires() { - $rootPackage = $this->getComposer()->getPackage(); + $rootPackage = $this->requireComposer()->getPackage(); return array_map( 'strtolower', @@ -717,7 +725,7 @@ EOT $io->write('source : ' . sprintf('[%s] %s %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); $io->write('dist : ' . sprintf('[%s] %s %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); if ($installedRepo->hasPackage($package)) { - $io->write('path : ' . sprintf('%s', realpath($this->getComposer()->getInstallationManager()->getInstallPath($package)))); + $io->write('path : ' . sprintf('%s', realpath($this->requireComposer()->getInstallationManager()->getInstallPath($package)))); } $io->write('names : ' . implode(', ', $package->getNames())); @@ -865,7 +873,7 @@ EOT $latestPackage = $package; } - if ($package->getSourceType()) { + if (null !== $package->getSourceType()) { $json['source'] = array( 'type' => $package->getSourceType(), 'url' => $package->getSourceUrl(), @@ -873,7 +881,7 @@ EOT ); } - if ($package->getDistType()) { + if (null !== $package->getDistType()) { $json['dist'] = array( 'type' => $package->getDistType(), 'url' => $package->getDistUrl(), @@ -882,7 +890,7 @@ EOT } if ($installedRepo->hasPackage($package)) { - $json['path'] = realpath($this->getComposer()->getInstallationManager()->getInstallPath($package)); + $json['path'] = realpath($this->requireComposer()->getInstallationManager()->getInstallPath($package)); if ($json['path'] === false) { unset($json['path']); } @@ -1281,12 +1289,9 @@ EOT /** * Given a package, this finds the latest package matching it * - * @param bool $minorOnly - * @param bool|string $ignorePlatformReqs - * * @return PackageInterface|false */ - private function findLatestPackage(PackageInterface $package, Composer $composer, PlatformRepository $platformRepo, $minorOnly = false, $ignorePlatformReqs = false) + private function findLatestPackage(PackageInterface $package, Composer $composer, PlatformRepository $platformRepo, bool $minorOnly, PlatformRequirementFilterInterface $platformReqFilter) { // find the latest version allowed in this repo set $name = $package->getName(); @@ -1311,7 +1316,7 @@ EOT $targetVersion = '^' . $package->getVersion(); } - $candidate = $versionSelector->findBestCandidate($name, $targetVersion, $bestStability, PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs)); + $candidate = $versionSelector->findBestCandidate($name, $targetVersion, $bestStability, $platformReqFilter); while ($candidate instanceof AliasPackage) { $candidate = $candidate->getAliasOf(); } diff --git a/src/Composer/Command/StatusCommand.php b/src/Composer/Command/StatusCommand.php index cc4876fd0..eed32b623 100644 --- a/src/Composer/Command/StatusCommand.php +++ b/src/Composer/Command/StatusCommand.php @@ -59,12 +59,9 @@ EOT ; } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'status', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); @@ -86,7 +83,7 @@ EOT private function doExecute(InputInterface $input) { // init repos - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $installedRepo = $composer->getRepositoryManager()->getLocalRepository(); diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index 345890e61..8c72ba045 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -50,12 +50,9 @@ EOT ; } - /** - * @inheritDoc - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $composer = $this->getComposer(); + $composer = $this->requireComposer(); $installedRepos = array( new RootPackageRepository(clone $composer->getPackage()), diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 9b752a218..4abeaf171 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -109,10 +109,6 @@ EOT ; } - /** - * @return int - * @throws \Exception - */ protected function execute(InputInterface $input, OutputInterface $output) { $io = $this->getIO(); @@ -123,7 +119,7 @@ EOT $io->writeError('You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3.'); } - $composer = $this->getComposer(true, $input->getOption('no-plugins'), $input->getOption('no-scripts')); + $composer = $this->requireComposer(); if (!HttpDownloader::isCurlEnabled()) { $io->writeError('Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.'); @@ -133,7 +129,7 @@ EOT $reqs = $this->formatRequirements($input->getOption('with')); // extract --with shorthands from the allowlist - if ($packages) { + if (count($packages) > 0) { $allowlistPackagesWithRequirements = array_filter($packages, function ($pkg) { return Preg::isMatch('{\S+[ =:]\S+}', $pkg); }); @@ -219,8 +215,6 @@ EOT $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); - $install ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) @@ -236,7 +230,7 @@ EOT ->setUpdateMirrors($updateMirrors) ->setUpdateAllowList($packages) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs)) + ->setPlatformRequirementFilter($this->getPlatformRequirementFilter($input)) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index e66cbf23b..700461bcf 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -66,10 +66,7 @@ EOT ); } - /** - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $file = $input->getArgument('file') ?: Factory::getComposerFile(); $io = $this->getIO(); diff --git a/src/Composer/Config/ConfigSourceInterface.php b/src/Composer/Config/ConfigSourceInterface.php index 111d5d53c..a50975442 100644 --- a/src/Composer/Config/ConfigSourceInterface.php +++ b/src/Composer/Config/ConfigSourceInterface.php @@ -63,7 +63,7 @@ interface ConfigSourceInterface * Add a property * * @param string $name Name - * @param string $value Value + * @param string|string[] $value Value * * @return void */ diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index b650309e4..604c41f9a 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -62,7 +62,7 @@ class JsonConfigSource implements ConfigSourceInterface */ public function addRepository($name, $config, $append = true) { - $this->manipulateJson('addRepository', $name, $config, $append, function (&$config, $repo, $repoConfig) use ($append) { + $this->manipulateJson('addRepository', function (&$config, $repo, $repoConfig) use ($append) { // if converting from an array format to hashmap format, and there is a {"packagist.org":false} repo, we have // to convert it to "packagist.org": false key on the hashmap otherwise it fails schema validation if (isset($config['repositories'])) { @@ -83,7 +83,7 @@ class JsonConfigSource implements ConfigSourceInterface } else { $config['repositories'] = array($repo => $repoConfig) + $config['repositories']; } - }); + }, $name, $config, $append); } /** @@ -91,9 +91,9 @@ class JsonConfigSource implements ConfigSourceInterface */ public function removeRepository($name) { - $this->manipulateJson('removeRepository', $name, function (&$config, $repo) { + $this->manipulateJson('removeRepository', function (&$config, $repo) { unset($config['repositories'][$repo]); - }); + }, $name); } /** @@ -102,7 +102,7 @@ class JsonConfigSource implements ConfigSourceInterface public function addConfigSetting($name, $value) { $authConfig = $this->authConfig; - $this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) use ($authConfig) { + $this->manipulateJson('addConfigSetting', function (&$config, $key, $val) use ($authConfig) { if (Preg::isMatch('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\.}', $key)) { list($key, $host) = explode('.', $key, 2); if ($authConfig) { @@ -113,7 +113,7 @@ class JsonConfigSource implements ConfigSourceInterface } else { $config['config'][$key] = $val; } - }); + }, $name, $value); } /** @@ -122,7 +122,7 @@ class JsonConfigSource implements ConfigSourceInterface public function removeConfigSetting($name) { $authConfig = $this->authConfig; - $this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) use ($authConfig) { + $this->manipulateJson('removeConfigSetting', function (&$config, $key) use ($authConfig) { if (Preg::isMatch('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\.}', $key)) { list($key, $host) = explode('.', $key, 2); if ($authConfig) { @@ -133,7 +133,7 @@ class JsonConfigSource implements ConfigSourceInterface } else { unset($config['config'][$key]); } - }); + }, $name); } /** @@ -141,7 +141,7 @@ class JsonConfigSource implements ConfigSourceInterface */ public function addProperty($name, $value) { - $this->manipulateJson('addProperty', $name, $value, function (&$config, $key, $val) { + $this->manipulateJson('addProperty', function (&$config, $key, $val) { if (strpos($key, 'extra.') === 0 || strpos($key, 'scripts.') === 0) { $bits = explode('.', $key); $last = array_pop($bits); @@ -156,7 +156,7 @@ class JsonConfigSource implements ConfigSourceInterface } else { $config[$key] = $val; } - }); + }, $name, $value); } /** @@ -164,7 +164,7 @@ class JsonConfigSource implements ConfigSourceInterface */ public function removeProperty($name) { - $this->manipulateJson('removeProperty', $name, function (&$config, $key) { + $this->manipulateJson('removeProperty', function (&$config, $key) { if (strpos($key, 'extra.') === 0 || strpos($key, 'scripts.') === 0) { $bits = explode('.', $key); $last = array_pop($bits); @@ -179,7 +179,7 @@ class JsonConfigSource implements ConfigSourceInterface } else { unset($config[$key]); } - }); + }, $name); } /** @@ -187,9 +187,9 @@ class JsonConfigSource implements ConfigSourceInterface */ public function addLink($type, $name, $value) { - $this->manipulateJson('addLink', $type, $name, $value, function (&$config, $type, $name, $value) { + $this->manipulateJson('addLink', function (&$config, $type, $name, $value) { $config[$type][$name] = $value; - }); + }, $type, $name, $value); } /** @@ -197,30 +197,25 @@ class JsonConfigSource implements ConfigSourceInterface */ public function removeLink($type, $name) { - $this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) { + $this->manipulateJson('removeSubNode', function (&$config, $type, $name) { unset($config[$type][$name]); - }); - $this->manipulateJson('removeMainKeyIfEmpty', $type, function (&$config, $type) { + }, $type, $name); + $this->manipulateJson('removeMainKeyIfEmpty', function (&$config, $type) { if (0 === count($config[$type])) { unset($config[$type]); } - }); + }, $type); } /** * @param string $method - * @param mixed ...$args * @param callable $fallback + * @param mixed ...$args * * @return void */ - protected function manipulateJson($method, $args, $fallback) + private function manipulateJson($method, $fallback, ...$args) { - $args = func_get_args(); - // remove method & fallback - array_shift($args); - $fallback = array_pop($args); - if ($this->file->exists()) { if (!is_writable($this->file->getPath())) { throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file->getPath())); diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index bee090c36..da4b7708c 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -33,7 +33,7 @@ use Symfony\Component\Process\ExecutableFinder; * The Event Dispatcher. * * Example in command: - * $dispatcher = new EventDispatcher($this->getComposer(), $this->getApplication()->getIO()); + * $dispatcher = new EventDispatcher($this->requireComposer(), $this->getApplication()->getIO()); * // ... * $dispatcher->dispatch(ScriptEvents::POST_INSTALL_CMD); * // ... diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php index 4650e1ed9..0837309d5 100644 --- a/src/Composer/IO/IOInterface.php +++ b/src/Composer/IO/IOInterface.php @@ -135,10 +135,10 @@ interface IOInterface extends LoggerInterface * Asks a question to the user. * * @param string $question The question to ask - * @param string $default The default answer if none is given by the user + * @param string|bool|int|float|null $default The default answer if none is given by the user * * @throws \RuntimeException If there is no data to read in the input stream - * @return string|null The user answer + * @return mixed The user answer */ public function ask($question, $default = null); diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 5f8e8aeda..f2ea4dc24 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -237,7 +237,7 @@ class Git $storeAuth = $this->config->get('store-auths'); } - if ($auth) { + if (null !== $auth) { $authUrl = $match[1] . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[2] . $match[3]; $command = call_user_func($commandCallable, $authUrl); diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 96f0dfe8e..a53fd7fb7 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -28,7 +28,7 @@ use React\Promise\Promise; * @author Jordi Boggiano * @author Nicolas Grekas * @phpstan-type Attributes array{retryAuthFailure: bool, redirects: int, retries: int, storeAuth: bool} - * @phpstan-type Job array{url: string, origin: string, attributes: Attributes, options: mixed[], progress: mixed[], curlHandle: resource, filename: string|false, headerHandle: resource, bodyHandle: resource, resolve: callable, reject: callable} + * @phpstan-type Job array{url: string, origin: string, attributes: Attributes, options: mixed[], progress: mixed[], curlHandle: resource, filename: string|null, headerHandle: resource, bodyHandle: resource, resolve: callable, reject: callable} */ class CurlDownloader { @@ -158,12 +158,11 @@ class CurlDownloader */ private function initDownload($resolve, $reject, $origin, $url, $options, $copyTo = null, array $attributes = array()) { - $attributes = array_merge(array( - 'retryAuthFailure' => true, - 'redirects' => 0, - 'retries' => 0, - 'storeAuth' => false, - ), $attributes); + // set defaults in a PHPStan-happy way (array_merge is not well supported) + $attributes['retryAuthFailure'] = $attributes['retryAuthFailure'] ?? true; + $attributes['redirects'] = $attributes['redirects'] ?? 0; + $attributes['retries'] = $attributes['retries'] ?? 0; + $attributes['storeAuth'] = $attributes['storeAuth'] ?? false; $originalOptions = $options; @@ -300,7 +299,7 @@ class CurlDownloader if (is_resource($job['bodyHandle'])) { fclose($job['bodyHandle']); } - if ($job['filename']) { + if (null !== $job['filename']) { @unlink($job['filename'].'~'); } unset($this->jobs[$id]); @@ -314,7 +313,7 @@ class CurlDownloader { static $timeoutWarning = false; - if (!$this->jobs) { + if (count($this->jobs) === 0) { return; } @@ -382,7 +381,7 @@ class CurlDownloader } // prepare response object - if ($job['filename']) { + if (null !== $job['filename']) { $contents = $job['filename'].'~'; if ($statusCode >= 300) { rewind($job['bodyHandle']); @@ -437,7 +436,7 @@ class CurlDownloader } // resolve promise - if ($job['filename']) { + if (null !== $job['filename']) { rename($job['filename'].'~', $job['filename']); call_user_func($job['resolve'], $response); } else { @@ -582,7 +581,7 @@ class CurlDownloader */ private function restartJob(array $job, $url, array $attributes = array()) { - if ($job['filename']) { + if (null !== $job['filename']) { @unlink($job['filename'].'~'); } @@ -599,7 +598,7 @@ class CurlDownloader */ private function failResponse(array $job, Response $response, $errorMessage) { - if ($job['filename']) { + if (null !== $job['filename']) { @unlink($job['filename'].'~'); } @@ -623,7 +622,7 @@ class CurlDownloader if (is_resource($job['bodyHandle'])) { fclose($job['bodyHandle']); } - if ($job['filename']) { + if (null !== $job['filename']) { @unlink($job['filename'].'~'); } call_user_func($job['reject'], $e); diff --git a/tests/Composer/Test/ApplicationTest.php b/tests/Composer/Test/ApplicationTest.php index b2877de50..caeadf9a4 100644 --- a/tests/Composer/Test/ApplicationTest.php +++ b/tests/Composer/Test/ApplicationTest.php @@ -61,7 +61,7 @@ class ApplicationTest extends TestCase $inputMock->expects($this->any()) ->method('getFirstArgument') - ->will($this->returnValue('show')); + ->will($this->returnValue('about')); $output = new BufferedOutput(); $expectedOutput = ''; @@ -122,7 +122,7 @@ class ApplicationTest extends TestCase $inputMock->expects($this->any()) ->method('getFirstArgument') - ->will($this->returnValue('show')); + ->will($this->returnValue('about')); $outputMock->expects($this->never()) ->method("writeln"); diff --git a/tests/Composer/Test/Command/ArchiveCommandTest.php b/tests/Composer/Test/Command/ArchiveCommandTest.php index d454552e2..fda64ba7a 100644 --- a/tests/Composer/Test/Command/ArchiveCommandTest.php +++ b/tests/Composer/Test/Command/ArchiveCommandTest.php @@ -53,9 +53,12 @@ class ArchiveCommandTest extends TestCase 'mergeApplicationDefinition', 'getSynopsis', 'initialize', - 'getComposer', + 'tryComposer', + 'requireComposer', ))->getMock(); - $command->expects($this->atLeastOnce())->method('getComposer') + $command->expects($this->atLeastOnce())->method('tryComposer') + ->willReturn($composer); + $command->expects($this->atLeastOnce())->method('requireComposer') ->willReturn($composer); $command->run($input, $output); @@ -74,10 +77,10 @@ class ArchiveCommandTest extends TestCase 'mergeApplicationDefinition', 'getSynopsis', 'initialize', - 'getComposer', + 'tryComposer', 'archive', ))->getMock(); - $command->expects($this->once())->method('getComposer') + $command->expects($this->once())->method('tryComposer') ->willReturn(null); $command->expects($this->once())->method('archive') ->with( diff --git a/tests/Composer/Test/Command/RunScriptCommandTest.php b/tests/Composer/Test/Command/RunScriptCommandTest.php index 066f156b2..eaffdbd92 100644 --- a/tests/Composer/Test/Command/RunScriptCommandTest.php +++ b/tests/Composer/Test/Command/RunScriptCommandTest.php @@ -80,10 +80,10 @@ class RunScriptCommandTest extends TestCase 'mergeApplicationDefinition', 'getSynopsis', 'initialize', - 'getComposer', + 'requireComposer', )) ->getMock(); - $command->expects($this->any())->method('getComposer')->willReturn($composer); + $command->expects($this->any())->method('requireComposer')->willReturn($composer); $command->run($input, $output); } diff --git a/tests/Composer/Test/Mock/HttpDownloaderMock.php b/tests/Composer/Test/Mock/HttpDownloaderMock.php index fe482ee67..a317d8b45 100644 --- a/tests/Composer/Test/Mock/HttpDownloaderMock.php +++ b/tests/Composer/Test/Mock/HttpDownloaderMock.php @@ -64,10 +64,23 @@ class HttpDownloaderMock extends HttpDownloader throw new \UnexpectedValueException('Unexpected keys in process execution step: '.implode(', ', array_keys($diff))); } - return array_merge($default, $expect); + // set defaults in a PHPStan-happy way (array_merge is not well supported) + $expect['url'] = $expect['url'] ?? $default['url']; + $expect['options'] = $expect['options'] ?? $default['options']; + $expect['status'] = $expect['status'] ?? $default['status']; + $expect['body'] = $expect['body'] ?? $default['body']; + $expect['headers'] = $expect['headers'] ?? $default['headers']; + + return $expect; }, $expectations); $this->strict = $strict; - $this->defaultHandler = array_merge($this->defaultHandler, $defaultHandler); + + // set defaults in a PHPStan-happy way (array_merge is not well supported) + $defaultHandler['status'] = $defaultHandler['status'] ?? $this->defaultHandler['status']; + $defaultHandler['body'] = $defaultHandler['body'] ?? $this->defaultHandler['body']; + $defaultHandler['headers'] = $defaultHandler['headers'] ?? $this->defaultHandler['headers']; + + $this->defaultHandler = $defaultHandler; } public function assertComplete(): void diff --git a/tests/Composer/Test/Mock/ProcessExecutorMock.php b/tests/Composer/Test/Mock/ProcessExecutorMock.php index c8f04aa7e..778799744 100644 --- a/tests/Composer/Test/Mock/ProcessExecutorMock.php +++ b/tests/Composer/Test/Mock/ProcessExecutorMock.php @@ -55,15 +55,30 @@ class ProcessExecutorMock extends ProcessExecutor $default = array('cmd' => '', 'return' => 0, 'stdout' => '', 'stderr' => '', 'callback' => null); $this->expectations = array_map(function ($expect) use ($default) { if (is_string($expect)) { - $expect = array('cmd' => $expect); + $command = $expect; + $expect = $default; + $expect['cmd'] = $command; } elseif (count($diff = array_diff_key(array_merge($default, $expect), $default)) > 0) { throw new \UnexpectedValueException('Unexpected keys in process execution step: '.implode(', ', array_keys($diff))); } - return array_merge($default, $expect); + // set defaults in a PHPStan-happy way (array_merge is not well supported) + $expect['cmd'] = $expect['cmd'] ?? $default['cmd']; + $expect['return'] = $expect['return'] ?? $default['return']; + $expect['stdout'] = $expect['stdout'] ?? $default['stdout']; + $expect['stderr'] = $expect['stderr'] ?? $default['stderr']; + $expect['callback'] = $expect['callback'] ?? $default['callback']; + + return $expect; }, $expectations); $this->strict = $strict; - $this->defaultHandler = array_merge($this->defaultHandler, $defaultHandler); + + // set defaults in a PHPStan-happy way (array_merge is not well supported) + $defaultHandler['return'] = $defaultHandler['return'] ?? $this->defaultHandler['return']; + $defaultHandler['stdout'] = $defaultHandler['stdout'] ?? $this->defaultHandler['stdout']; + $defaultHandler['stderr'] = $defaultHandler['stderr'] ?? $this->defaultHandler['stderr']; + + $this->defaultHandler = $defaultHandler; } public function assertComplete(): void diff --git a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/CommandProvider.php b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/CommandProvider.php index c71665299..2c22ebf84 100644 --- a/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/CommandProvider.php +++ b/tests/Composer/Test/Plugin/Fixtures/plugin-v8/Installer/CommandProvider.php @@ -35,7 +35,7 @@ class Command extends BaseCommand $this->setName('custom-plugin-command'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Executing'); diff --git a/tests/console-application.php b/tests/console-application.php new file mode 100644 index 000000000..f795a549e --- /dev/null +++ b/tests/console-application.php @@ -0,0 +1,5 @@ +