From b9ab9b2450a89735d1d3a109aa45204899cabeb1 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 12 Feb 2026 09:51:57 +0100 Subject: [PATCH 1/2] PHPStan improvements --- composer.json | 2 +- phpcs.xml.dist | 3 +++ phpstan.neon.dist | 4 +--- src/Core_Language_Command.php | 3 +++ src/Plugin_Language_Command.php | 13 ++++++++----- src/Site_Switch_Language_Command.php | 3 +++ src/Theme_Language_Command.php | 11 +++++++---- src/WP_CLI/CommandWithTranslation.php | 23 ++++++++++++++++++++--- src/WP_CLI/LanguagePackUpgrader.php | 2 ++ tests/phpstan/scan-files.php | 3 +++ 10 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 tests/phpstan/scan-files.php diff --git a/composer.json b/composer.json index f90ab423d..29aeb5d9d 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ } ], "require": { - "wp-cli/wp-cli": "^2.12" + "wp-cli/wp-cli": "^2.13" }, "require-dev": { "wp-cli/db-command": "^1.3 || ^2", diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 43d0296b0..e9e5fa44a 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -83,4 +83,7 @@ */src/Language_Namespace\.php$ + + */tests/phpstan + diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4f134e997..ecb0735b9 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 strictRules: uselessCast: true @@ -16,7 +17,4 @@ parameters: numericOperandsInArithmeticOperators: true switchConditionsMatchingType: true ignoreErrors: - - identifier: missingType.iterableValue - - identifier: missingType.property - - identifier: missingType.parameter - identifier: missingType.return diff --git a/src/Core_Language_Command.php b/src/Core_Language_Command.php index c1b01af1e..07ffd7174 100644 --- a/src/Core_Language_Command.php +++ b/src/Core_Language_Command.php @@ -386,6 +386,9 @@ public function activate( $args ) { $this->activate_language( $language_code ); } + /** + * @param string $language_code + */ private function activate_language( $language_code ) { $available = $this->get_installed_languages(); diff --git a/src/Plugin_Language_Command.php b/src/Plugin_Language_Command.php index 532e51100..a9bc5039f 100644 --- a/src/Plugin_Language_Command.php +++ b/src/Plugin_Language_Command.php @@ -263,6 +263,9 @@ public function install( $args, $assoc_args ) { if ( $all ) { $this->install_many( $args, $assoc_args ); } else { + /** + * @var non-empty-list $args + */ $this->install_one( $args, $assoc_args ); } } @@ -270,8 +273,8 @@ public function install( $args, $assoc_args ) { /** * Installs translations for a plugin. * - * @param array $args Runtime arguments. - * @param array $assoc_args Runtime arguments. + * @param non-empty-list $args Positional arguments. + * @param array{all?: bool, format: string} $assoc_args Associative arguments. */ private function install_one( $args, $assoc_args ) { $plugin = array_shift( $args ); @@ -314,8 +317,8 @@ private function install_one( $args, $assoc_args ) { /** * Installs translations for all installed plugins. * - * @param array $args Runtime arguments. - * @param array $assoc_args Runtime arguments. + * @param string[] $args Positional arguments. + * @param array{all?: bool, format: string} $assoc_args Associative arguments. */ private function install_many( $args, $assoc_args ) { $language_codes = (array) $args; @@ -627,7 +630,7 @@ public function update( $args, $assoc_args ) { * Uses the same filter core uses in plugins.php to determine which plugins * should be available to manage through the WP_Plugins_List_Table class. * - * @return array + * @return array */ private function get_all_plugins() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using WP native hook. diff --git a/src/Site_Switch_Language_Command.php b/src/Site_Switch_Language_Command.php index 3cfa277c4..9c172c315 100644 --- a/src/Site_Switch_Language_Command.php +++ b/src/Site_Switch_Language_Command.php @@ -17,6 +17,9 @@ class Site_Switch_Language_Command extends WP_CLI\CommandWithTranslation { * Success: Language activated. * * @throws WP_CLI\ExitException + * + * @param array{0: string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function __invoke( $args, $assoc_args ) { list( $language_code ) = $args; diff --git a/src/Theme_Language_Command.php b/src/Theme_Language_Command.php index a95896272..fd0548ac5 100644 --- a/src/Theme_Language_Command.php +++ b/src/Theme_Language_Command.php @@ -268,6 +268,9 @@ public function install( $args, $assoc_args ) { if ( $all ) { $this->install_many( $args, $assoc_args ); } else { + /** + * @var non-empty-list $args + */ $this->install_one( $args, $assoc_args ); } } @@ -275,8 +278,8 @@ public function install( $args, $assoc_args ) { /** * Installs translations for a theme. * - * @param array $args Runtime arguments. - * @param array $assoc_args Runtime arguments. + * @param non-empty-list $args Positional arguments. + * @param array{all?: bool, format: string} $assoc_args Associative arguments. */ private function install_one( $args, $assoc_args ) { $theme = array_shift( $args ); @@ -318,8 +321,8 @@ private function install_one( $args, $assoc_args ) { /** * Installs translations for all installed themes. * - * @param array $args Runtime arguments. - * @param array $assoc_args Runtime arguments. + * @param string[] $args Positional arguments. + * @param array{all?: bool, format: string} $assoc_args Associative arguments. */ private function install_many( $args, $assoc_args ) { $language_codes = (array) $args; diff --git a/src/WP_CLI/CommandWithTranslation.php b/src/WP_CLI/CommandWithTranslation.php index 8d9a763de..0984b6ae2 100644 --- a/src/WP_CLI/CommandWithTranslation.php +++ b/src/WP_CLI/CommandWithTranslation.php @@ -11,12 +11,21 @@ * @package wp-cli */ abstract class CommandWithTranslation extends WP_CLI_Command { + /** + * @var string + */ protected $obj_type; + /** + * @var string[] + */ protected $obj_fields; /** * Callback to sort array by a 'language' key. + * + * @param array{language: string, english_name: string, native_name: string, updated: string} $a + * @param array{language: string, english_name: string, native_name: string, updated: string} $b */ protected function sort_translations_callback( $a, $b ) { return strnatcasecmp( $a['language'], $b['language'] ); @@ -66,7 +75,7 @@ public function update( $args, $assoc_args ) { $plugins = get_plugins( '/' . $update->slug ); /** - * @var array{Name: string}> $plugin_data + * @var array{Name: string} $plugin_data */ $plugin_data = array_shift( $plugins ); $name = $plugin_data['Name']; @@ -115,6 +124,8 @@ public function update( $args, $assoc_args ) { */ $upgrader_instance = Utils\get_upgrader( $upgrader ); + // Wrong docblock in core. + // @phpstan-ignore argument.type $result = $upgrader_instance->upgrade( $update ); $results[] = $result; @@ -157,7 +168,7 @@ public function update( $args, $assoc_args ) { * * @see wp_get_translation_updates() * - * @return array + * @return array */ protected function get_translation_updates() { $available = $this->get_installed_languages(); @@ -202,7 +213,7 @@ protected function get_translation_updates() { $updates = array(); /** - * @var object{translations: array} $transient + * @var object{translations: array{type: string, slug: string, language: string}} $transient */ $transient = get_site_transient( $transient ); @@ -214,6 +225,10 @@ protected function get_translation_updates() { $updates[] = (object) $translation; } + /** + * @var array $updates + */ + return $updates; } @@ -376,6 +391,8 @@ protected function get_all_languages( $slug = null ) { * * @param array $assoc_args Parameters passed to command. Determines formatting. * @return Formatter + * + * @phpstan-ignore missingType.iterableValue */ protected function get_formatter( &$assoc_args ) { return new Formatter( $assoc_args, $this->obj_fields, $this->obj_type ); diff --git a/src/WP_CLI/LanguagePackUpgrader.php b/src/WP_CLI/LanguagePackUpgrader.php index 0b7acb7b6..335696672 100644 --- a/src/WP_CLI/LanguagePackUpgrader.php +++ b/src/WP_CLI/LanguagePackUpgrader.php @@ -57,6 +57,8 @@ public function generic_strings() { * @param bool $check_signatures Whether to validate file signatures. Default false. * @param array $hook_extra Extra arguments to pass to the filter hooks. Default empty array. * @return string|\WP_Error The full path to the downloaded package file, or a WP_Error object. + * + * @phpstan-ignore missingType.iterableValue */ public function download_package( $package, $check_signatures = false, $hook_extra = [] ) { diff --git a/tests/phpstan/scan-files.php b/tests/phpstan/scan-files.php new file mode 100644 index 000000000..04eaf4c2a --- /dev/null +++ b/tests/phpstan/scan-files.php @@ -0,0 +1,3 @@ + Date: Thu, 12 Feb 2026 11:25:09 +0100 Subject: [PATCH 2/2] Update src/WP_CLI/CommandWithTranslation.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/WP_CLI/CommandWithTranslation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WP_CLI/CommandWithTranslation.php b/src/WP_CLI/CommandWithTranslation.php index 0984b6ae2..453059016 100644 --- a/src/WP_CLI/CommandWithTranslation.php +++ b/src/WP_CLI/CommandWithTranslation.php @@ -213,7 +213,7 @@ protected function get_translation_updates() { $updates = array(); /** - * @var object{translations: array{type: string, slug: string, language: string}} $transient + * @var object{translations: list} $transient */ $transient = get_site_transient( $transient );