From 94597ee83b32b8e2545bde68ed91af97f7821ae2 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 13 Feb 2026 10:10:28 +0100 Subject: [PATCH 1/4] Fix some newly reported PHPStan issues --- phpcs.xml.dist | 3 +- phpstan.neon.dist | 1 + phpunit.xml.dist | 2 +- src/Package_Command.php | 8 +- tests/phpstan/scan-files.php | 101 ++++++++++++++++++++ tests/{ => phpunit}/ComposerJsonTest.php | 0 tests/{ => phpunit}/JsonManipulatorTest.php | 0 7 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/phpstan/scan-files.php rename tests/{ => phpunit}/ComposerJsonTest.php (100%) rename tests/{ => phpunit}/JsonManipulatorTest.php (100%) diff --git a/phpcs.xml.dist b/phpcs.xml.dist index d9496398..3d5db779 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -18,7 +18,7 @@ */src/WP_CLI/JsonManipulator\.php$ */src/WP_CLI/Package/Compat/Min_Composer_1_10/NullIOMethodsTrait\.php$ */src/WP_CLI/Package/Compat/Min_Composer_2_3/NullIOMethodsTrait\.php$ - */tests/JsonManipulatorTest\.php$ + */tests/phpunit/JsonManipulatorTest\.php$ @@ -63,4 +63,5 @@ */src/Package_Command\.php$ + /tests/phpstan/scan-files diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 533df9d1..a2cd8ec7 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,6 +7,7 @@ parameters: - vendor/wp-cli/wp-cli/php scanFiles: - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php + - tests/phpstan/scan-files.php treatPhpDocTypesAsCertain: false ignoreErrors: # WP_CLI_VERSION is defined in wp-cli core at runtime diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b29e2a0e..13e9e2bc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,7 +13,7 @@ colors="true" verbose="true"> - tests + tests/phpunit diff --git a/src/Package_Command.php b/src/Package_Command.php index b5a4af8f..b8b220bf 100644 --- a/src/Package_Command.php +++ b/src/Package_Command.php @@ -822,8 +822,8 @@ private function package_index() { * Displays a set of packages * * @param string $context - * @param array - * @param array + * @param array $packages + * @param array $assoc_args */ private function show_packages( $context, $packages, $assoc_args ) { $default_fields = []; @@ -1130,8 +1130,8 @@ private function create_default_composer_json( $composer_path ) { * * @param PackageInterface $package * @param Composer $composer - * @param string $phpVersion - * @param bool $minorOnly + * @param string $php_version + * @param bool $minor_only * * @return PackageInterface|null */ diff --git a/tests/phpstan/scan-files.php b/tests/phpstan/scan-files.php new file mode 100644 index 00000000..fcb0d818 --- /dev/null +++ b/tests/phpstan/scan-files.php @@ -0,0 +1,101 @@ + Date: Fri, 13 Feb 2026 10:15:58 +0100 Subject: [PATCH 2/4] Remove < 2.3 compat code, bump PHPStan level --- composer.json | 4 +- phpstan.neon.dist | 2 +- src/Package_Command.php | 70 ++++++------------- .../Min_Composer_1_10/NullIOMethodsTrait.php | 36 ---------- .../Min_Composer_2_3/NullIOMethodsTrait.php | 36 ---------- .../Package/Compat/NullIOMethodsTrait.php | 29 -------- src/WP_CLI/Package/ComposerIO.php | 31 +++++++- 7 files changed, 53 insertions(+), 155 deletions(-) delete mode 100644 src/WP_CLI/Package/Compat/Min_Composer_1_10/NullIOMethodsTrait.php delete mode 100644 src/WP_CLI/Package/Compat/Min_Composer_2_3/NullIOMethodsTrait.php delete mode 100644 src/WP_CLI/Package/Compat/NullIOMethodsTrait.php diff --git a/composer.json b/composer.json index 64caff7a..63cde76d 100644 --- a/composer.json +++ b/composer.json @@ -13,8 +13,8 @@ ], "require": { "ext-json": "*", - "composer/composer": "^2.2.25", - "wp-cli/wp-cli": "^2.12" + "composer/composer": "^2.9.5", + "wp-cli/wp-cli": "^2.13" }, "require-dev": { "wp-cli/scaffold-command": "^1 || ^2", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a2cd8ec7..ff3cec96 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - level: 1 + level: 5 paths: - src - package-command.php diff --git a/src/Package_Command.php b/src/Package_Command.php index b8b220bf..4aa1f0ed 100644 --- a/src/Package_Command.php +++ b/src/Package_Command.php @@ -3,7 +3,6 @@ use Composer\Composer; use Composer\Config; use Composer\Config\JsonConfigSource; -use Composer\DependencyResolver\Pool; use Composer\Factory; use Composer\IO\NullIO; use Composer\Installer; @@ -223,9 +222,7 @@ public function install( $args, $assoc_args ) { $dir_package = false; $version = ''; if ( $this->is_git_repository( $package_name ) ) { - if ( '' === $version ) { - $version = "dev-{$this->get_github_default_branch( $package_name, $insecure )}"; - } + $version = "dev-{$this->get_github_default_branch( $package_name, $insecure )}"; $git_package = $package_name; $matches = []; if ( preg_match( '#([^:\/]+\/[^\/]+)\.git#', $package_name, $matches ) ) { @@ -247,7 +244,7 @@ public function install( $args, $assoc_args ) { $gitlab_token = getenv( 'GITLAB_TOKEN' ); // Use GITLAB_TOKEN if available to avoid authorization failures or rate-limiting. $headers = $gitlab_token && strpos( $package_name, '://gitlab.com/' ) !== false ? [ 'PRIVATE-TOKEN' => $gitlab_token ] : []; $response = Utils\http_request( 'GET', $package_name, null, $headers, $options ); - if ( 20 !== (int) substr( $response->status_code, 0, 2 ) ) { + if ( 20 !== (int) substr( (string) $response->status_code, 0, 2 ) ) { @unlink( $temp ); // @codingStandardsIgnoreLine WP_CLI::error( sprintf( "Couldn't download package from '%s' (HTTP code %d).", $package_name, $response->status_code ) ); } @@ -316,11 +313,9 @@ public function install( $args, $assoc_args ) { } } - if ( $this->is_composer_v2() ) { - $package_name = function_exists( 'mb_strtolower' ) - ? mb_strtolower( $package_name ) - : strtolower( $package_name ); - } + $package_name = function_exists( 'mb_strtolower' ) + ? mb_strtolower( $package_name ) + : strtolower( $package_name ); if ( '' === $version ) { $version = self::DEFAULT_DEV_BRANCH_CONSTRAINTS; @@ -866,7 +861,7 @@ private function show_packages( $context, $packages, $assoc_args ) { $update_version = ''; if ( 'list' === $context && ! $skip_update_check ) { try { - $latest = $this->find_latest_package( $package, $composer, null ); + $latest = $this->find_latest_package( $package, $composer ); if ( $latest && $latest->getFullPrettyVersion() !== $package->getFullPrettyVersion() ) { $update = 'available'; $update_version = $latest->getPrettyVersion(); @@ -929,7 +924,7 @@ private function get_package_by_shortened_identifier( $package_name, $insecure = // Check if the package exists on Packagist. $url = "https://repo.packagist.org/p2/{$package_name}.json"; $response = Utils\http_request( 'GET', $url, null, [], $options ); - if ( 20 === (int) substr( $response->status_code, 0, 2 ) ) { + if ( 20 === (int) substr( (string) $response->status_code, 0, 2 ) ) { return $package_name; } @@ -938,7 +933,7 @@ private function get_package_by_shortened_identifier( $package_name, $insecure = $github_token = getenv( 'GITHUB_TOKEN' ); // Use GITHUB_TOKEN if available to avoid authorization failures or rate-limiting. $headers = $github_token ? [ 'Authorization' => 'token ' . $github_token ] : []; $response = Utils\http_request( 'GET', $url, null /*data*/, $headers, $options ); - if ( 20 === (int) substr( $response->status_code, 0, 2 ) ) { + if ( 20 === (int) substr( (string) $response->status_code, 0, 2 ) ) { return $url; } @@ -948,7 +943,7 @@ private function get_package_by_shortened_identifier( $package_name, $insecure = $headers = $github_token ? [ 'Authorization' => 'token ' . $github_token ] : []; $headers = $gitlab_token && strpos( $package_name, '://gitlab.com/' ) !== false ? [ 'PRIVATE-TOKEN' => $gitlab_token ] : []; $response = Utils\http_request( 'GET', $url, null /*data*/, $headers, $options ); - if ( 20 === (int) substr( $response->status_code, 0, 2 ) ) { + if ( 20 === (int) substr( (string) $response->status_code, 0, 2 ) ) { return $url; } @@ -976,9 +971,6 @@ private function get_installed_packages() { if ( in_array( $package->getPrettyName(), $installed_package_keys, true ) ) { $installed_packages[] = $package; } elseif ( false !== $idx ) { // Legacy incorrect name check. - if ( ! $this->is_composer_v2() ) { - WP_CLI::warning( sprintf( "Found package '%s' misnamed '%s' in '%s'.", $package->getPrettyName(), $installed_package_keys[ $idx ], $this->get_composer_json_path() ) ); - } $installed_packages[] = $package; } } @@ -1130,12 +1122,11 @@ private function create_default_composer_json( $composer_path ) { * * @param PackageInterface $package * @param Composer $composer - * @param string $php_version * @param bool $minor_only * - * @return PackageInterface|null + * @return PackageInterface|false */ - private function find_latest_package( PackageInterface $package, Composer $composer, $php_version, $minor_only = false ) { + private function find_latest_package( PackageInterface $package, Composer $composer, $minor_only = false ) { // Find the latest version allowed in this pool/repository set. $name = $package->getPrettyName(); $version_selector = $this->get_version_selector( $composer ); @@ -1156,11 +1147,7 @@ private function find_latest_package( PackageInterface $package, Composer $compo $target_version = '^' . $package->getVersion(); } - if ( $this->is_composer_v2() ) { - return $version_selector->findBestCandidate( $name, $target_version, $best_stability ); - } - - return $version_selector->findBestCandidate( $name, $target_version, $php_version, $best_stability ); + return $version_selector->findBestCandidate( $name, $target_version, $best_stability ); } /** @@ -1168,18 +1155,12 @@ private function find_latest_package( PackageInterface $package, Composer $compo */ private function get_version_selector( Composer $composer ) { if ( ! $this->version_selector ) { - if ( $this->is_composer_v2() ) { - $repository_set = new Repository\RepositorySet( - $composer->getPackage()->getMinimumStability(), - $composer->getPackage()->getStabilityFlags() - ); - $repository_set->addRepository( new CompositeRepository( $composer->getRepositoryManager()->getRepositories() ) ); - $this->version_selector = new VersionSelector( $repository_set ); - } else { - $pool = new Pool( $composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags() ); - $pool->addRepository( new CompositeRepository( $composer->getRepositoryManager()->getRepositories() ) ); - $this->version_selector = new VersionSelector( $pool ); - } + $repository_set = new Repository\RepositorySet( + $composer->getPackage()->getMinimumStability(), + $composer->getPackage()->getStabilityFlags() + ); + $repository_set->addRepository( new CompositeRepository( $composer->getRepositoryManager()->getRepositories() ) ); + $this->version_selector = new VersionSelector( $repository_set ); } return $this->version_selector; @@ -1213,7 +1194,7 @@ private function check_github_package_name( $package_name, $version = '', $insec $raw_content_url = "https://raw.githubusercontent.com/{$package_name}/{$this->get_raw_git_version( $version )}/composer.json"; $response = Utils\http_request( 'GET', $raw_content_url, null /*data*/, $headers, $options ); - if ( 20 !== (int) substr( $response->status_code, 0, 2 ) ) { + if ( 20 !== (int) substr( (string) $response->status_code, 0, 2 ) ) { // Could not get composer.json. Possibly private so warn and return best guess from input (always xxx/xxx). WP_CLI::warning( sprintf( @@ -1360,7 +1341,7 @@ private function get_github_latest_release_tag( $package_name, $insecure ) { $url = "https://api.github.com/repos/{$package_name}/releases/latest"; $options = [ 'insecure' => $insecure ]; $response = Utils\http_request( 'GET', $url, null, [], $options ); - if ( 20 !== (int) substr( $response->status_code, 0, 2 ) ) { + if ( 20 !== (int) substr( (string) $response->status_code, 0, 2 ) ) { WP_CLI::warning( 'Could not guess stable version from GitHub repository, falling back to master branch' ); return 'master'; } @@ -1500,15 +1481,6 @@ static function () use ( ); } - /** - * Check whether we are dealing with Composer version 2.0.0+. - * - * @return bool - */ - private function is_composer_v2() { - return version_compare( Composer::getVersion(), '2.0.0', '>=' ); - } - /** * Try to retrieve default branch via GitHub API. * @@ -1529,7 +1501,7 @@ private function get_github_default_branch( $package_name, $insecure = false ) { $github_api_repo_url = "https://api.github.com/repos/{$package_name}"; $response = Utils\http_request( 'GET', $github_api_repo_url, null /*data*/, $headers, $options ); - if ( 20 !== (int) substr( $response->status_code, 0, 2 ) ) { + if ( 20 !== (int) substr( (string) $response->status_code, 0, 2 ) ) { WP_CLI::warning( sprintf( "Couldn't fetch default branch for package '%s' (HTTP code %d). Presuming default branch is 'master'.", diff --git a/src/WP_CLI/Package/Compat/Min_Composer_1_10/NullIOMethodsTrait.php b/src/WP_CLI/Package/Compat/Min_Composer_1_10/NullIOMethodsTrait.php deleted file mode 100644 index 2b59b4c7..00000000 --- a/src/WP_CLI/Package/Compat/Min_Composer_1_10/NullIOMethodsTrait.php +++ /dev/null @@ -1,36 +0,0 @@ -]+)>#', '$1$2', $messages ); - foreach ( $messages as $message ) { - // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags - WP_CLI::log( strip_tags( trim( $message ) ) ); - } - } -} diff --git a/src/WP_CLI/Package/Compat/Min_Composer_2_3/NullIOMethodsTrait.php b/src/WP_CLI/Package/Compat/Min_Composer_2_3/NullIOMethodsTrait.php deleted file mode 100644 index 27d21871..00000000 --- a/src/WP_CLI/Package/Compat/Min_Composer_2_3/NullIOMethodsTrait.php +++ /dev/null @@ -1,36 +0,0 @@ -]+)>#', '$1$2', $messages ); - foreach ( $messages as $message ) { - // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags - WP_CLI::log( strip_tags( trim( $message ) ) ); - } - } -} diff --git a/src/WP_CLI/Package/Compat/NullIOMethodsTrait.php b/src/WP_CLI/Package/Compat/NullIOMethodsTrait.php deleted file mode 100644 index cfe1a71c..00000000 --- a/src/WP_CLI/Package/Compat/NullIOMethodsTrait.php +++ /dev/null @@ -1,29 +0,0 @@ -]+)>#', '$1$2', $messages ); + foreach ( $messages as $message ) { + // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags + WP_CLI::log( strip_tags( trim( $message ) ) ); + } + } } From 58a135efec7459dca118aad0840ffe6547e53bba Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 13 Feb 2026 10:26:16 +0100 Subject: [PATCH 3/4] Update phpcs.xml.dist Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- phpcs.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 3d5db779..7e5f9cb2 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -63,5 +63,5 @@ */src/Package_Command\.php$ - /tests/phpstan/scan-files + */tests/phpstan/scan-files\.php$ From f2022c6407cf800f0faf88dd9a1b029b1f289277 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 13 Feb 2026 10:23:41 +0100 Subject: [PATCH 4/4] Update spellcheck config --- .typos.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.typos.toml b/.typos.toml index ef831af7..bea9937e 100644 --- a/.typos.toml +++ b/.typos.toml @@ -7,5 +7,5 @@ extend-ignore-re = [ [files] extend-exclude = [ - "tests/JsonManipulatorTest.php" + "tests/phpunit/JsonManipulatorTest.php" ]