Skip to content

Commit fa25c55

Browse files
authored
Merge pull request #453 from wp-cli/add/phpstan
2 parents d48f31f + ec9d94e commit fa25c55

11 files changed

+275
-89
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
],
1919
"require": {
2020
"composer/semver": "^1.4 || ^2 || ^3",
21-
"wp-cli/wp-cli": "^2.12"
21+
"wp-cli/wp-cli": "^2.13"
2222
},
2323
"require-dev": {
2424
"wp-cli/cache-command": "^2.0",
2525
"wp-cli/entity-command": "^1.3 || ^2",
2626
"wp-cli/language-command": "^2.0",
2727
"wp-cli/scaffold-command": "^1.2 || ^2",
28-
"wp-cli/wp-cli-tests": "^5.0.1"
28+
"wp-cli/wp-cli-tests": "^5"
2929
},
3030
"config": {
3131
"process-timeout": 7200,

phpstan.neon.dist

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
parameters:
2+
level: 9
3+
paths:
4+
- src
5+
- extension-command.php
6+
scanDirectories:
7+
- vendor/wp-cli/wp-cli/php
8+
scanFiles:
9+
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
10+
treatPhpDocTypesAsCertain: false
11+
ignoreErrors:
12+
- identifier: missingType.iterableValue
13+
- identifier: missingType.property
14+
- identifier: missingType.parameter
15+
- identifier: missingType.return

src/Plugin_AutoUpdates_Command.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ public function __construct() {
7272
* $ wp plugin auto-updates enable hello
7373
* Plugin auto-updates for 'hello' enabled.
7474
* Success: Enabled 1 of 1 plugin auto-updates.
75+
*
76+
* @param string[] $args Positional arguments.
77+
* @param array{all?: bool, 'disabled-only'?: bool} $assoc_args Associative arguments.
7578
*/
7679
public function enable( $args, $assoc_args ) {
7780
$all = Utils\get_flag_value( $assoc_args, 'all', false );

src/Plugin_Command.php

Lines changed: 86 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use WP_CLI\CommandWithUpgrade;
34
use WP_CLI\ParsePluginNameInput;
45
use WP_CLI\Utils;
56
use WP_CLI\WpOrgApi;
@@ -41,9 +42,11 @@
4142
* Success: Installed 1 of 1 plugins.
4243
*
4344
* @package wp-cli
45+
*
46+
* @phpstan-type PluginInformation object{name: string, slug: non-empty-string, version: string, new_version: string, download_link: string, requires_php?: string, requires?: string, package: string}&\stdClass
47+
* @extends CommandWithUpgrade<string,>
4448
*/
45-
class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
46-
49+
class Plugin_Command extends CommandWithUpgrade {
4750
use ParsePluginNameInput;
4851

4952
protected $item_type = 'plugin';
@@ -66,13 +69,6 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
6669
'auto_update',
6770
);
6871

69-
/**
70-
* Plugin fetcher instance.
71-
*
72-
* @var \WP_CLI\Fetchers\Plugin
73-
*/
74-
protected $fetcher;
75-
7672
public function __construct() {
7773
require_once ABSPATH . 'wp-admin/includes/plugin.php';
7874
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
@@ -209,6 +205,9 @@ public function search( $args, $assoc_args ) {
209205
}
210206

211207
protected function status_single( $args ) {
208+
/**
209+
* @var object{name: string, file: string} $plugin
210+
*/
212211
$plugin = $this->fetcher->get_check( $args[0] );
213212
$file = $plugin->file;
214213

@@ -345,11 +344,18 @@ protected function get_all_items() {
345344
* Plugin 'bbpress' network activated.
346345
* Plugin 'buddypress' network activated.
347346
* Success: Activated 2 of 2 plugins.
347+
*
348+
* @param array $args
349+
* @param array $assoc_args
348350
*/
349-
public function activate( $args, $assoc_args = array() ) {
351+
public function activate( $args, $assoc_args = [] ) {
350352
$network_wide = Utils\get_flag_value( $assoc_args, 'network', false );
351353
$all = Utils\get_flag_value( $assoc_args, 'all', false );
352-
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude' );
354+
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', '' );
355+
356+
/**
357+
* @var string $all_exclude
358+
*/
353359

354360
$args = $this->check_optional_args_and_all( $args, $all, 'activate', $all_exclude );
355361
if ( ! $args ) {
@@ -358,7 +364,11 @@ public function activate( $args, $assoc_args = array() ) {
358364

359365
$successes = 0;
360366
$errors = 0;
361-
$plugins = $this->fetcher->get_many( $args );
367+
368+
/**
369+
* @var array<object{name: string, file: string}> $plugins
370+
*/
371+
$plugins = $this->fetcher->get_many( $args );
362372
if ( count( $plugins ) < count( $args ) ) {
363373
$errors = count( $args ) - count( $plugins );
364374
}
@@ -387,7 +397,7 @@ public function activate( $args, $assoc_args = array() ) {
387397

388398
if ( is_wp_error( $result ) ) {
389399
$message = $result->get_error_message();
390-
$message = preg_replace( '/<a\s[^>]+>.*<\/a>/im', '', $message );
400+
$message = (string) preg_replace( '/<a\s[^>]+>.*<\/a>/im', '', $message );
391401
$message = wp_strip_all_tags( $message );
392402
$message = str_replace( 'Error: ', '', $message );
393403
WP_CLI::warning( "Failed to activate plugin. {$message}" );
@@ -437,10 +447,14 @@ public function activate( $args, $assoc_args = array() ) {
437447
* Plugin 'ninja-forms' deactivated.
438448
* Success: Deactivated 2 of 2 plugins.
439449
*/
440-
public function deactivate( $args, $assoc_args = array() ) {
450+
public function deactivate( $args, $assoc_args = [] ) {
441451
$network_wide = Utils\get_flag_value( $assoc_args, 'network' );
442452
$disable_all = Utils\get_flag_value( $assoc_args, 'all' );
443-
$disable_all_exclude = Utils\get_flag_value( $assoc_args, 'exclude' );
453+
$disable_all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', '' );
454+
455+
/**
456+
* @var string $disable_all_exclude
457+
*/
444458

445459
$args = $this->check_optional_args_and_all( $args, $disable_all, 'deactivate', $disable_all_exclude );
446460
if ( ! $args ) {
@@ -530,7 +544,7 @@ public function deactivate( $args, $assoc_args = array() ) {
530544
* Plugin 'akismet' activated.
531545
* Success: Toggled 1 of 1 plugins.
532546
*/
533-
public function toggle( $args, $assoc_args = array() ) {
547+
public function toggle( $args, $assoc_args ) {
534548
$network_wide = Utils\get_flag_value( $assoc_args, 'network' );
535549

536550
$successes = 0;
@@ -574,6 +588,9 @@ public function path( $args, $assoc_args ) {
574588
$path = untrailingslashit( WP_PLUGIN_DIR );
575589

576590
if ( ! empty( $args ) ) {
591+
/**
592+
* @var object{name: string, file: string} $plugin
593+
*/
577594
$plugin = $this->fetcher->get_check( $args[0] );
578595
$path .= '/' . $plugin->file;
579596

@@ -591,6 +608,9 @@ protected function install_from_repo( $slug, $assoc_args ) {
591608
list($wp_core_version) = explode( '-', $wp_version );
592609
$wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) );
593610

611+
/**
612+
* @var \WP_Error|PluginInformation $api
613+
*/
594614
$api = plugins_api( 'plugin_information', array( 'slug' => $slug ) );
595615

596616
if ( is_wp_error( $api ) ) {
@@ -755,6 +775,9 @@ protected function get_item_list() {
755775
$auto_updates = [];
756776
}
757777

778+
/**
779+
* @var string[] $recently_active
780+
*/
758781
$recently_active = is_network_admin() ? get_site_option( 'recently_activated' ) : get_option( 'recently_activated' );
759782

760783
if ( false === $recently_active ) {
@@ -763,10 +786,11 @@ protected function get_item_list() {
763786

764787
foreach ( $this->get_all_plugins() as $file => $details ) {
765788
$all_update_info = $this->get_update_info();
766-
$update_info = ( isset( $all_update_info->response[ $file ] ) && null !== $all_update_info->response[ $file ] ) ? (array) $all_update_info->response[ $file ] : null;
767-
$name = Utils\get_plugin_name( $file );
768-
$wporg_info = $this->get_wporg_data( $name );
769-
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file, false, false );
789+
// @phpstan-ignore notIdentical.alwaysTrue
790+
$update_info = ( isset( $all_update_info->response[ $file ] ) && null !== $all_update_info->response[ $file ] ) ? (array) $all_update_info->response[ $file ] : null;
791+
$name = Utils\get_plugin_name( $file );
792+
$wporg_info = $this->get_wporg_data( $name );
793+
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file, false, false );
770794

771795
if ( ! isset( $duplicate_names[ $name ] ) ) {
772796
$duplicate_names[ $name ] = array();
@@ -875,7 +899,7 @@ protected function get_item_list() {
875899

876900
if ( isset( $plugin_update_info->requires ) && version_compare( $wp_version, $requires, '>=' ) ) {
877901
$reason = "This update requires WordPress version $plugin_update_info->requires, but the version installed is $wp_version.";
878-
} elseif ( ! isset( $update_info['package'] ) ) {
902+
} elseif ( ! isset( $plugin_update_info->package ) ) {
879903
$reason = 'Update file not provided. Contact author for more details';
880904
} else {
881905
$reason = 'Update not available';
@@ -904,7 +928,7 @@ protected function get_item_list() {
904928
*
905929
* @param string $plugin_name The plugin slug.
906930
*
907-
* @return string The status of the plugin, includes the last update date.
931+
* @return array{status: string, last_updated: string|false, status?: string, last_updated?: string} The status of the plugin, includes the last update date.
908932
*/
909933
protected function get_wporg_data( $plugin_name ) {
910934
$data = [
@@ -947,10 +971,12 @@ protected function get_wporg_data( $plugin_name ) {
947971
$r_body = wp_remote_retrieve_body( $request );
948972
if ( strpos( $r_body, 'pubDate' ) !== false ) {
949973
// Very raw check, not validating the format or anything else.
950-
$xml = simplexml_load_string( $r_body );
951-
$xml_pub_date = $xml->xpath( '//pubDate' );
952-
if ( $xml_pub_date ) {
953-
$data['last_updated'] = wp_date( 'Y-m-d', (string) strtotime( $xml_pub_date[0] ) );
974+
$xml = simplexml_load_string( $r_body );
975+
if ( false !== $xml ) {
976+
$xml_pub_date = $xml->xpath( '//pubDate' );
977+
if ( $xml_pub_date ) {
978+
$data['last_updated'] = wp_date( 'Y-m-d', strtotime( $xml_pub_date[0] ) ?: null );
979+
}
954980
}
955981
}
956982

@@ -1115,6 +1141,9 @@ public function get( $args, $assoc_args ) {
11151141
'status',
11161142
);
11171143

1144+
/**
1145+
* @var object{name: string, file: string} $plugin
1146+
*/
11181147
$plugin = $this->fetcher->get_check( $args[0] );
11191148
$file = $plugin->file;
11201149

@@ -1173,11 +1202,14 @@ public function get( $args, $assoc_args ) {
11731202
* Uninstalled and deleted 'tinymce-templates' plugin.
11741203
* Success: Uninstalled 2 of 2 plugins.
11751204
*/
1176-
public function uninstall( $args, $assoc_args = array() ) {
1177-
1205+
public function uninstall( $args, $assoc_args = [] ) {
11781206
$all = Utils\get_flag_value( $assoc_args, 'all', false );
11791207
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false );
11801208

1209+
/**
1210+
* @var string $all_exclude
1211+
*/
1212+
11811213
// Check if plugin names or --all is passed.
11821214
$args = $this->check_optional_args_and_all( $args, $all, 'uninstall', $all_exclude );
11831215
if ( ! $args ) {
@@ -1222,6 +1254,9 @@ public function uninstall( $args, $assoc_args = array() ) {
12221254
if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) {
12231255
$translations = $plugin_translations[ $plugin_slug ];
12241256

1257+
/**
1258+
* @var \WP_Filesystem_Base $wp_filesystem
1259+
*/
12251260
global $wp_filesystem;
12261261
require_once ABSPATH . '/wp-admin/includes/file.php';
12271262
WP_Filesystem();
@@ -1233,7 +1268,11 @@ public function uninstall( $args, $assoc_args = array() ) {
12331268

12341269
$json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' );
12351270
if ( $json_translation_files ) {
1236-
array_map( array( $wp_filesystem, 'delete' ), $json_translation_files );
1271+
/**
1272+
* @var callable $callback
1273+
*/
1274+
$callback = [ $wp_filesystem, 'delete' ];
1275+
array_map( $callback, $json_translation_files );
12371276
}
12381277
}
12391278
}
@@ -1257,6 +1296,10 @@ public function uninstall( $args, $assoc_args = array() ) {
12571296
// Remove deleted plugins from the plugin updates list.
12581297
$current = get_site_transient( $this->upgrade_transient );
12591298
if ( $current ) {
1299+
/**
1300+
* @var object{response: array<string, mixed>, checked: array<string, mixed>}&\stdClass $current
1301+
*/
1302+
12601303
// Don't remove the plugins that weren't deleted.
12611304
$deleted = array_diff( $deleted_plugin_files, $delete_errors );
12621305

@@ -1292,7 +1335,7 @@ public function uninstall( $args, $assoc_args = array() ) {
12921335
*
12931336
* @subcommand is-installed
12941337
*/
1295-
public function is_installed( $args, $assoc_args = array() ) {
1338+
public function is_installed( $args, $assoc_args ) {
12961339
if ( $this->fetcher->get( $args[0] ) ) {
12971340
WP_CLI::halt( 0 );
12981341
} else {
@@ -1322,7 +1365,7 @@ public function is_installed( $args, $assoc_args = array() ) {
13221365
*
13231366
* @subcommand is-active
13241367
*/
1325-
public function is_active( $args, $assoc_args = array() ) {
1368+
public function is_active( $args, $assoc_args ) {
13261369
$network_wide = Utils\get_flag_value( $assoc_args, 'network' );
13271370

13281371
$plugin = $this->fetcher->get( $args[0] );
@@ -1366,10 +1409,14 @@ public function is_active( $args, $assoc_args = array() ) {
13661409
* Deleted 'tinymce-templates' plugin.
13671410
* Success: Deleted 2 of 2 plugins.
13681411
*/
1369-
public function delete( $args, $assoc_args = array() ) {
1412+
public function delete( $args, $assoc_args ) {
13701413
$all = Utils\get_flag_value( $assoc_args, 'all', false );
13711414
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false );
13721415

1416+
/**
1417+
* @var string $all_exclude
1418+
*/
1419+
13731420
// Check if plugin names or --all is passed.
13741421
$args = $this->check_optional_args_and_all( $args, $all, 'delete', $all_exclude );
13751422
if ( ! $args ) {
@@ -1505,7 +1552,11 @@ public function delete( $args, $assoc_args = array() ) {
15051552
* @subcommand list
15061553
*/
15071554
public function list_( $_, $assoc_args ) {
1555+
/**
1556+
* @var string $fields
1557+
*/
15081558
$fields = Utils\get_flag_value( $assoc_args, 'fields' );
1559+
15091560
if ( ! empty( $fields ) ) {
15101561
$fields = explode( ',', $fields );
15111562
$this->check_wporg['status'] = in_array( 'wporg_status', $fields, true );
@@ -1578,7 +1629,7 @@ private static function get_template_path( $template ) {
15781629
/**
15791630
* Gets the details of a plugin.
15801631
*
1581-
* @param object
1632+
* @param string $file Plugin file name.
15821633
* @return array
15831634
*/
15841635
private function get_details( $file ) {
@@ -1591,8 +1642,8 @@ private function get_details( $file ) {
15911642
/**
15921643
* Performs deletion of plugin files
15931644
*
1594-
* @param $plugin - Plugin fetcher object (name, file)
1595-
* @return bool - If plugin was deleted
1645+
* @param $plugin Plugin fetcher object (name, file)
1646+
* @return bool Whether plugin was deleted
15961647
*/
15971648
private function delete_plugin( $plugin ) {
15981649
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound

src/Theme_AutoUpdates_Command.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
*/
2929
class Theme_AutoUpdates_Command {
3030

31+
/**
32+
* @use ParseThemeNameInput<\WP_Theme>
33+
*/
3134
use ParseThemeNameInput;
3235

3336
/**

0 commit comments

Comments
 (0)