Merge pull request #453 from wp-cli/add/phpstan

This commit is contained in:
Pascal Birchler 2025-08-15 17:59:55 +02:00 committed by GitHub
commit fa25c5525a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 275 additions and 89 deletions

View file

@ -18,14 +18,14 @@
], ],
"require": { "require": {
"composer/semver": "^1.4 || ^2 || ^3", "composer/semver": "^1.4 || ^2 || ^3",
"wp-cli/wp-cli": "^2.12" "wp-cli/wp-cli": "^2.13"
}, },
"require-dev": { "require-dev": {
"wp-cli/cache-command": "^2.0", "wp-cli/cache-command": "^2.0",
"wp-cli/entity-command": "^1.3 || ^2", "wp-cli/entity-command": "^1.3 || ^2",
"wp-cli/language-command": "^2.0", "wp-cli/language-command": "^2.0",
"wp-cli/scaffold-command": "^1.2 || ^2", "wp-cli/scaffold-command": "^1.2 || ^2",
"wp-cli/wp-cli-tests": "^5.0.1" "wp-cli/wp-cli-tests": "^5"
}, },
"config": { "config": {
"process-timeout": 7200, "process-timeout": 7200,

15
phpstan.neon.dist Normal file
View file

@ -0,0 +1,15 @@
parameters:
level: 9
paths:
- src
- extension-command.php
scanDirectories:
- vendor/wp-cli/wp-cli/php
scanFiles:
- vendor/php-stubs/wordpress-stubs/wordpress-stubs.php
treatPhpDocTypesAsCertain: false
ignoreErrors:
- identifier: missingType.iterableValue
- identifier: missingType.property
- identifier: missingType.parameter
- identifier: missingType.return

View file

@ -72,6 +72,9 @@ class Plugin_AutoUpdates_Command {
* $ wp plugin auto-updates enable hello * $ wp plugin auto-updates enable hello
* Plugin auto-updates for 'hello' enabled. * Plugin auto-updates for 'hello' enabled.
* Success: Enabled 1 of 1 plugin auto-updates. * Success: Enabled 1 of 1 plugin auto-updates.
*
* @param string[] $args Positional arguments.
* @param array{all?: bool, 'disabled-only'?: bool} $assoc_args Associative arguments.
*/ */
public function enable( $args, $assoc_args ) { public function enable( $args, $assoc_args ) {
$all = Utils\get_flag_value( $assoc_args, 'all', false ); $all = Utils\get_flag_value( $assoc_args, 'all', false );

View file

@ -1,5 +1,6 @@
<?php <?php


use WP_CLI\CommandWithUpgrade;
use WP_CLI\ParsePluginNameInput; use WP_CLI\ParsePluginNameInput;
use WP_CLI\Utils; use WP_CLI\Utils;
use WP_CLI\WpOrgApi; use WP_CLI\WpOrgApi;
@ -41,9 +42,11 @@ use function WP_CLI\Utils\normalize_path;
* Success: Installed 1 of 1 plugins. * Success: Installed 1 of 1 plugins.
* *
* @package wp-cli * @package wp-cli
*
* @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
* @extends CommandWithUpgrade<string,>
*/ */
class Plugin_Command extends \WP_CLI\CommandWithUpgrade { class Plugin_Command extends CommandWithUpgrade {

use ParsePluginNameInput; use ParsePluginNameInput;


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


/**
* Plugin fetcher instance.
*
* @var \WP_CLI\Fetchers\Plugin
*/
protected $fetcher;

public function __construct() { public function __construct() {
require_once ABSPATH . 'wp-admin/includes/plugin.php'; require_once ABSPATH . 'wp-admin/includes/plugin.php';
require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
@ -209,6 +205,9 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
} }


protected function status_single( $args ) { protected function status_single( $args ) {
/**
* @var object{name: string, file: string} $plugin
*/
$plugin = $this->fetcher->get_check( $args[0] ); $plugin = $this->fetcher->get_check( $args[0] );
$file = $plugin->file; $file = $plugin->file;


@ -345,11 +344,18 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* Plugin 'bbpress' network activated. * Plugin 'bbpress' network activated.
* Plugin 'buddypress' network activated. * Plugin 'buddypress' network activated.
* Success: Activated 2 of 2 plugins. * Success: Activated 2 of 2 plugins.
*
* @param array $args
* @param array $assoc_args
*/ */
public function activate( $args, $assoc_args = array() ) { public function activate( $args, $assoc_args = [] ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network', false ); $network_wide = Utils\get_flag_value( $assoc_args, 'network', false );
$all = Utils\get_flag_value( $assoc_args, 'all', false ); $all = Utils\get_flag_value( $assoc_args, 'all', false );
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude' ); $all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', '' );

/**
* @var string $all_exclude
*/


$args = $this->check_optional_args_and_all( $args, $all, 'activate', $all_exclude ); $args = $this->check_optional_args_and_all( $args, $all, 'activate', $all_exclude );
if ( ! $args ) { if ( ! $args ) {
@ -358,7 +364,11 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {


$successes = 0; $successes = 0;
$errors = 0; $errors = 0;
$plugins = $this->fetcher->get_many( $args );
/**
* @var array<object{name: string, file: string}> $plugins
*/
$plugins = $this->fetcher->get_many( $args );
if ( count( $plugins ) < count( $args ) ) { if ( count( $plugins ) < count( $args ) ) {
$errors = count( $args ) - count( $plugins ); $errors = count( $args ) - count( $plugins );
} }
@ -387,7 +397,7 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {


if ( is_wp_error( $result ) ) { if ( is_wp_error( $result ) ) {
$message = $result->get_error_message(); $message = $result->get_error_message();
$message = preg_replace( '/<a\s[^>]+>.*<\/a>/im', '', $message ); $message = (string) preg_replace( '/<a\s[^>]+>.*<\/a>/im', '', $message );
$message = wp_strip_all_tags( $message ); $message = wp_strip_all_tags( $message );
$message = str_replace( 'Error: ', '', $message ); $message = str_replace( 'Error: ', '', $message );
WP_CLI::warning( "Failed to activate plugin. {$message}" ); WP_CLI::warning( "Failed to activate plugin. {$message}" );
@ -437,10 +447,14 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* Plugin 'ninja-forms' deactivated. * Plugin 'ninja-forms' deactivated.
* Success: Deactivated 2 of 2 plugins. * Success: Deactivated 2 of 2 plugins.
*/ */
public function deactivate( $args, $assoc_args = array() ) { public function deactivate( $args, $assoc_args = [] ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network' ); $network_wide = Utils\get_flag_value( $assoc_args, 'network' );
$disable_all = Utils\get_flag_value( $assoc_args, 'all' ); $disable_all = Utils\get_flag_value( $assoc_args, 'all' );
$disable_all_exclude = Utils\get_flag_value( $assoc_args, 'exclude' ); $disable_all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', '' );

/**
* @var string $disable_all_exclude
*/


$args = $this->check_optional_args_and_all( $args, $disable_all, 'deactivate', $disable_all_exclude ); $args = $this->check_optional_args_and_all( $args, $disable_all, 'deactivate', $disable_all_exclude );
if ( ! $args ) { if ( ! $args ) {
@ -530,7 +544,7 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* Plugin 'akismet' activated. * Plugin 'akismet' activated.
* Success: Toggled 1 of 1 plugins. * Success: Toggled 1 of 1 plugins.
*/ */
public function toggle( $args, $assoc_args = array() ) { public function toggle( $args, $assoc_args ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network' ); $network_wide = Utils\get_flag_value( $assoc_args, 'network' );


$successes = 0; $successes = 0;
@ -574,6 +588,9 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
$path = untrailingslashit( WP_PLUGIN_DIR ); $path = untrailingslashit( WP_PLUGIN_DIR );


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


@ -591,6 +608,9 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
list($wp_core_version) = explode( '-', $wp_version ); list($wp_core_version) = explode( '-', $wp_version );
$wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) ); $wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) );


/**
* @var \WP_Error|PluginInformation $api
*/
$api = plugins_api( 'plugin_information', array( 'slug' => $slug ) ); $api = plugins_api( 'plugin_information', array( 'slug' => $slug ) );


if ( is_wp_error( $api ) ) { if ( is_wp_error( $api ) ) {
@ -755,6 +775,9 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
$auto_updates = []; $auto_updates = [];
} }


/**
* @var string[] $recently_active
*/
$recently_active = is_network_admin() ? get_site_option( 'recently_activated' ) : get_option( 'recently_activated' ); $recently_active = is_network_admin() ? get_site_option( 'recently_activated' ) : get_option( 'recently_activated' );


if ( false === $recently_active ) { if ( false === $recently_active ) {
@ -763,10 +786,11 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {


foreach ( $this->get_all_plugins() as $file => $details ) { foreach ( $this->get_all_plugins() as $file => $details ) {
$all_update_info = $this->get_update_info(); $all_update_info = $this->get_update_info();
$update_info = ( isset( $all_update_info->response[ $file ] ) && null !== $all_update_info->response[ $file ] ) ? (array) $all_update_info->response[ $file ] : null; // @phpstan-ignore notIdentical.alwaysTrue
$name = Utils\get_plugin_name( $file ); $update_info = ( isset( $all_update_info->response[ $file ] ) && null !== $all_update_info->response[ $file ] ) ? (array) $all_update_info->response[ $file ] : null;
$wporg_info = $this->get_wporg_data( $name ); $name = Utils\get_plugin_name( $file );
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file, false, false ); $wporg_info = $this->get_wporg_data( $name );
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file, false, false );


if ( ! isset( $duplicate_names[ $name ] ) ) { if ( ! isset( $duplicate_names[ $name ] ) ) {
$duplicate_names[ $name ] = array(); $duplicate_names[ $name ] = array();
@ -875,7 +899,7 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {


if ( isset( $plugin_update_info->requires ) && version_compare( $wp_version, $requires, '>=' ) ) { if ( isset( $plugin_update_info->requires ) && version_compare( $wp_version, $requires, '>=' ) ) {
$reason = "This update requires WordPress version $plugin_update_info->requires, but the version installed is $wp_version."; $reason = "This update requires WordPress version $plugin_update_info->requires, but the version installed is $wp_version.";
} elseif ( ! isset( $update_info['package'] ) ) { } elseif ( ! isset( $plugin_update_info->package ) ) {
$reason = 'Update file not provided. Contact author for more details'; $reason = 'Update file not provided. Contact author for more details';
} else { } else {
$reason = 'Update not available'; $reason = 'Update not available';
@ -904,7 +928,7 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* *
* @param string $plugin_name The plugin slug. * @param string $plugin_name The plugin slug.
* *
* @return string The status of the plugin, includes the last update date. * @return array{status: string, last_updated: string|false, status?: string, last_updated?: string} The status of the plugin, includes the last update date.
*/ */
protected function get_wporg_data( $plugin_name ) { protected function get_wporg_data( $plugin_name ) {
$data = [ $data = [
@ -947,10 +971,12 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
$r_body = wp_remote_retrieve_body( $request ); $r_body = wp_remote_retrieve_body( $request );
if ( strpos( $r_body, 'pubDate' ) !== false ) { if ( strpos( $r_body, 'pubDate' ) !== false ) {
// Very raw check, not validating the format or anything else. // Very raw check, not validating the format or anything else.
$xml = simplexml_load_string( $r_body ); $xml = simplexml_load_string( $r_body );
$xml_pub_date = $xml->xpath( '//pubDate' ); if ( false !== $xml ) {
if ( $xml_pub_date ) { $xml_pub_date = $xml->xpath( '//pubDate' );
$data['last_updated'] = wp_date( 'Y-m-d', (string) strtotime( $xml_pub_date[0] ) ); if ( $xml_pub_date ) {
$data['last_updated'] = wp_date( 'Y-m-d', strtotime( $xml_pub_date[0] ) ?: null );
}
} }
} }


@ -1115,6 +1141,9 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
'status', 'status',
); );


/**
* @var object{name: string, file: string} $plugin
*/
$plugin = $this->fetcher->get_check( $args[0] ); $plugin = $this->fetcher->get_check( $args[0] );
$file = $plugin->file; $file = $plugin->file;


@ -1173,11 +1202,14 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* Uninstalled and deleted 'tinymce-templates' plugin. * Uninstalled and deleted 'tinymce-templates' plugin.
* Success: Uninstalled 2 of 2 plugins. * Success: Uninstalled 2 of 2 plugins.
*/ */
public function uninstall( $args, $assoc_args = array() ) { public function uninstall( $args, $assoc_args = [] ) {

$all = Utils\get_flag_value( $assoc_args, 'all', false ); $all = Utils\get_flag_value( $assoc_args, 'all', false );
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false ); $all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false );


/**
* @var string $all_exclude
*/

// Check if plugin names or --all is passed. // Check if plugin names or --all is passed.
$args = $this->check_optional_args_and_all( $args, $all, 'uninstall', $all_exclude ); $args = $this->check_optional_args_and_all( $args, $all, 'uninstall', $all_exclude );
if ( ! $args ) { if ( ! $args ) {
@ -1222,6 +1254,9 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) { if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) {
$translations = $plugin_translations[ $plugin_slug ]; $translations = $plugin_translations[ $plugin_slug ];


/**
* @var \WP_Filesystem_Base $wp_filesystem
*/
global $wp_filesystem; global $wp_filesystem;
require_once ABSPATH . '/wp-admin/includes/file.php'; require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem(); WP_Filesystem();
@ -1233,7 +1268,11 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {


$json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' ); $json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' );
if ( $json_translation_files ) { if ( $json_translation_files ) {
array_map( array( $wp_filesystem, 'delete' ), $json_translation_files ); /**
* @var callable $callback
*/
$callback = [ $wp_filesystem, 'delete' ];
array_map( $callback, $json_translation_files );
} }
} }
} }
@ -1257,6 +1296,10 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
// Remove deleted plugins from the plugin updates list. // Remove deleted plugins from the plugin updates list.
$current = get_site_transient( $this->upgrade_transient ); $current = get_site_transient( $this->upgrade_transient );
if ( $current ) { if ( $current ) {
/**
* @var object{response: array<string, mixed>, checked: array<string, mixed>}&\stdClass $current
*/

// Don't remove the plugins that weren't deleted. // Don't remove the plugins that weren't deleted.
$deleted = array_diff( $deleted_plugin_files, $delete_errors ); $deleted = array_diff( $deleted_plugin_files, $delete_errors );


@ -1292,7 +1335,7 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* *
* @subcommand is-installed * @subcommand is-installed
*/ */
public function is_installed( $args, $assoc_args = array() ) { public function is_installed( $args, $assoc_args ) {
if ( $this->fetcher->get( $args[0] ) ) { if ( $this->fetcher->get( $args[0] ) ) {
WP_CLI::halt( 0 ); WP_CLI::halt( 0 );
} else { } else {
@ -1322,7 +1365,7 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* *
* @subcommand is-active * @subcommand is-active
*/ */
public function is_active( $args, $assoc_args = array() ) { public function is_active( $args, $assoc_args ) {
$network_wide = Utils\get_flag_value( $assoc_args, 'network' ); $network_wide = Utils\get_flag_value( $assoc_args, 'network' );


$plugin = $this->fetcher->get( $args[0] ); $plugin = $this->fetcher->get( $args[0] );
@ -1366,10 +1409,14 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* Deleted 'tinymce-templates' plugin. * Deleted 'tinymce-templates' plugin.
* Success: Deleted 2 of 2 plugins. * Success: Deleted 2 of 2 plugins.
*/ */
public function delete( $args, $assoc_args = array() ) { public function delete( $args, $assoc_args ) {
$all = Utils\get_flag_value( $assoc_args, 'all', false ); $all = Utils\get_flag_value( $assoc_args, 'all', false );
$all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false ); $all_exclude = Utils\get_flag_value( $assoc_args, 'exclude', false );


/**
* @var string $all_exclude
*/

// Check if plugin names or --all is passed. // Check if plugin names or --all is passed.
$args = $this->check_optional_args_and_all( $args, $all, 'delete', $all_exclude ); $args = $this->check_optional_args_and_all( $args, $all, 'delete', $all_exclude );
if ( ! $args ) { if ( ! $args ) {
@ -1505,7 +1552,11 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
* @subcommand list * @subcommand list
*/ */
public function list_( $_, $assoc_args ) { public function list_( $_, $assoc_args ) {
/**
* @var string $fields
*/
$fields = Utils\get_flag_value( $assoc_args, 'fields' ); $fields = Utils\get_flag_value( $assoc_args, 'fields' );

if ( ! empty( $fields ) ) { if ( ! empty( $fields ) ) {
$fields = explode( ',', $fields ); $fields = explode( ',', $fields );
$this->check_wporg['status'] = in_array( 'wporg_status', $fields, true ); $this->check_wporg['status'] = in_array( 'wporg_status', $fields, true );
@ -1578,7 +1629,7 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
/** /**
* Gets the details of a plugin. * Gets the details of a plugin.
* *
* @param object * @param string $file Plugin file name.
* @return array * @return array
*/ */
private function get_details( $file ) { private function get_details( $file ) {
@ -1591,8 +1642,8 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
/** /**
* Performs deletion of plugin files * Performs deletion of plugin files
* *
* @param $plugin - Plugin fetcher object (name, file) * @param $plugin Plugin fetcher object (name, file)
* @return bool - If plugin was deleted * @return bool Whether plugin was deleted
*/ */
private function delete_plugin( $plugin ) { private function delete_plugin( $plugin ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound

View file

@ -28,6 +28,9 @@ use WP_CLI\Utils;
*/ */
class Theme_AutoUpdates_Command { class Theme_AutoUpdates_Command {


/**
* @use ParseThemeNameInput<\WP_Theme>
*/
use ParseThemeNameInput; use ParseThemeNameInput;


/** /**

View file

@ -41,9 +41,15 @@ use WP_CLI\Utils;
* Author: the WordPress team * Author: the WordPress team
* *
* @package wp-cli * @package wp-cli
*
* @phpstan-type ThemeInformation object{name: string, slug: non-empty-string, version: string, new_version: string, download_link: string, requires_php?: string, requires?: string}&\stdClass
* @extends CommandWithUpgrade<\WP_Theme>
*/ */
class Theme_Command extends CommandWithUpgrade { class Theme_Command extends CommandWithUpgrade {


/**
* @use ParseThemeNameInput<\WP_Theme>
*/
use ParseThemeNameInput; use ParseThemeNameInput;


protected $item_type = 'theme'; protected $item_type = 'theme';
@ -211,8 +217,11 @@ class Theme_Command extends CommandWithUpgrade {
* *
* $ wp theme activate twentysixteen * $ wp theme activate twentysixteen
* Success: Switched to 'Twenty Sixteen' theme. * Success: Switched to 'Twenty Sixteen' theme.
*
* @param string[] $args Positional arguments.
* @param array $assoc_args Associative arguments. Unused.
*/ */
public function activate( $args = array() ) { public function activate( $args, $assoc_args = [] ) {
$theme = $this->fetcher->get_check( $args[0] ); $theme = $this->fetcher->get_check( $args[0] );


$errors = $theme->errors(); $errors = $theme->errors();
@ -232,7 +241,7 @@ class Theme_Command extends CommandWithUpgrade {
WP_CLI::error( "The '{$theme->get_stylesheet()}' theme cannot be activated without its parent, '{$theme->get_template()}'." ); WP_CLI::error( "The '{$theme->get_stylesheet()}' theme cannot be activated without its parent, '{$theme->get_template()}'." );
} }


switch_theme( $theme->get_template(), $theme->get_stylesheet() ); switch_theme( $theme->get_stylesheet() );


if ( $this->is_active_theme( $theme ) ) { if ( $this->is_active_theme( $theme ) ) {
WP_CLI::success( "Switched to '$name' theme." ); WP_CLI::success( "Switched to '$name' theme." );
@ -279,6 +288,9 @@ class Theme_Command extends CommandWithUpgrade {
WP_CLI::error( 'This is not a multisite installation.' ); WP_CLI::error( 'This is not a multisite installation.' );
} }


/**
* @var \WP_Theme $theme
*/
$theme = $this->fetcher->get_check( $args[0] ); $theme = $this->fetcher->get_check( $args[0] );
$name = $theme->get( 'Name' ); $name = $theme->get( 'Name' );


@ -290,6 +302,11 @@ class Theme_Command extends CommandWithUpgrade {
if ( empty( $allowed_themes ) ) { if ( empty( $allowed_themes ) ) {
$allowed_themes = array(); $allowed_themes = array();
} }

/**
* @var array<string, bool> $allowed_themes
*/

$allowed_themes[ $theme->get_stylesheet() ] = true; $allowed_themes[ $theme->get_stylesheet() ] = true;
call_user_func( "update{$_site}_option", 'allowedthemes', $allowed_themes ); call_user_func( "update{$_site}_option", 'allowedthemes', $allowed_themes );


@ -344,6 +361,11 @@ class Theme_Command extends CommandWithUpgrade {


# Add the current theme to the allowed themes option or site option # Add the current theme to the allowed themes option or site option
$allowed_themes = call_user_func( "get{$_site}_option", 'allowedthemes' ); $allowed_themes = call_user_func( "get{$_site}_option", 'allowedthemes' );

/**
* @var array<string, bool> $allowed_themes
*/

if ( ! empty( $allowed_themes[ $theme->get_stylesheet() ] ) ) { if ( ! empty( $allowed_themes[ $theme->get_stylesheet() ] ) ) {
unset( $allowed_themes[ $theme->get_stylesheet() ] ); unset( $allowed_themes[ $theme->get_stylesheet() ] );
} }
@ -400,6 +422,9 @@ class Theme_Command extends CommandWithUpgrade {
list($wp_core_version) = explode( '-', $wp_version ); list($wp_core_version) = explode( '-', $wp_version );
$wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) ); $wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) );


/**
* @var \WP_Error|ThemeInformation $api
*/
$api = themes_api( 'theme_information', array( 'slug' => $slug ) ); $api = themes_api( 'theme_information', array( 'slug' => $slug ) );


if ( is_wp_error( $api ) ) { if ( is_wp_error( $api ) ) {
@ -732,7 +757,7 @@ class Theme_Command extends CommandWithUpgrade {
* *
* @subcommand is-installed * @subcommand is-installed
*/ */
public function is_installed( $args, $assoc_args = array() ) { public function is_installed( $args, $assoc_args ) {
$theme = wp_get_theme( $args[0] ); $theme = wp_get_theme( $args[0] );


if ( $theme->exists() ) { if ( $theme->exists() ) {
@ -761,7 +786,7 @@ class Theme_Command extends CommandWithUpgrade {
* *
* @subcommand is-active * @subcommand is-active
*/ */
public function is_active( $args, $assoc_args = array() ) { public function is_active( $args, $assoc_args ) {
$theme = wp_get_theme( $args[0] ); $theme = wp_get_theme( $args[0] );


if ( ! $theme->exists() ) { if ( ! $theme->exists() ) {

View file

@ -74,8 +74,11 @@ class Theme_Mod_Command extends WP_CLI_Command {
* | background_color | dd3333 | * | background_color | dd3333 |
* | header_textcolor | | * | header_textcolor | |
* +------------------+--------+ * +------------------+--------+
*
* @param string[] $args Positional arguments.
* @param array{field?: string, all?: bool, format: string} $assoc_args Associative arguments.
*/ */
public function get( $args = array(), $assoc_args = array() ) { public function get( $args, $assoc_args ) {


if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) { if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) {
WP_CLI::error( 'You must specify at least one mod or use --all.' ); WP_CLI::error( 'You must specify at least one mod or use --all.' );
@ -162,10 +165,13 @@ class Theme_Mod_Command extends WP_CLI_Command {
* +------------------+---------+ * +------------------+---------+
* *
* @subcommand list * @subcommand list
*
* @param string[] $args Positional arguments. Unused.
* @param array{field?: string, format: string} $assoc_args Associative arguments.
*/ */
public function list_( $args = array(), $assoc_args = array() ) { public function list_( $args, $assoc_args ) {


$assoc_args['all'] = 1; $assoc_args['all'] = true;


$this->get( $args, $assoc_args ); $this->get( $args, $assoc_args );
} }
@ -194,8 +200,11 @@ class Theme_Mod_Command extends WP_CLI_Command {
* # Remove multiple theme mods. * # Remove multiple theme mods.
* $ wp theme mod remove background_color header_textcolor * $ wp theme mod remove background_color header_textcolor
* Success: 2 mods removed. * Success: 2 mods removed.
*
* @param string[] $args Positional arguments.
* @param array{all?: bool} $assoc_args Associative arguments.
*/ */
public function remove( $args = array(), $assoc_args = array() ) { public function remove( $args, $assoc_args ) {


if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) { if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'all' ) && empty( $args ) ) {
WP_CLI::error( 'You must specify at least one mod or use --all.' ); WP_CLI::error( 'You must specify at least one mod or use --all.' );
@ -232,8 +241,11 @@ class Theme_Mod_Command extends WP_CLI_Command {
* # Set theme mod * # Set theme mod
* $ wp theme mod set background_color 000000 * $ wp theme mod set background_color 000000
* Success: Theme mod background_color set to 000000. * Success: Theme mod background_color set to 000000.
*
* @param array{0: string, 1: string} $args Positional arguments.
* @param array $assoc_args Associative arguments. Unused.
*/ */
public function set( $args = array(), $assoc_args = array() ) { public function set( $args, $assoc_args ) {
list( $mod, $value ) = $args; list( $mod, $value ) = $args;


set_theme_mod( $mod, $value ); set_theme_mod( $mod, $value );

View file

@ -11,6 +11,12 @@ use WP_CLI\Loggers;
use WP_CLI\Utils; use WP_CLI\Utils;
use WP_Error; use WP_Error;


/**
* @phpstan-import-type ThemeInformation from \Theme_Command
* @phpstan-import-type PluginInformation from \Plugin_Command
*
* @template T
*/
abstract class CommandWithUpgrade extends \WP_CLI_Command { abstract class CommandWithUpgrade extends \WP_CLI_Command {


protected $fetcher; protected $fetcher;
@ -61,25 +67,43 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
$this->fetcher = new Fetchers\Plugin(); $this->fetcher = new Fetchers\Plugin();
} }


/**
* @return class-string<\WP_Upgrader>
*/
abstract protected function get_upgrader_class( $force ); abstract protected function get_upgrader_class( $force );


abstract protected function get_item_list(); abstract protected function get_item_list();


/** /**
* @param array List of update candidates * @param array $items List of update candidates
* @param array List of item names * @param array $args List of item names
* @return array List of update candidates * @return array List of update candidates
*/ */
abstract protected function filter_item_list( $items, $args ); abstract protected function filter_item_list( $items, $args );


abstract protected function get_all_items(); abstract protected function get_all_items();


/**
* Get the status for a given extension.
*
* @param T $file Extension to get the status for.
*
* @return string Status of the extension.
*/
abstract protected function get_status( $file ); abstract protected function get_status( $file );


abstract protected function status_single( $args ); abstract protected function status_single( $args );


abstract protected function install_from_repo( $slug, $assoc_args ); abstract protected function install_from_repo( $slug, $assoc_args );


/**
* Activates an extension.
*
* @param string[] $args Positional arguments.
* @param array $assoc_args Associative arguments.
*/
abstract public function activate( $args, $assoc_args = [] );

public function status( $args ) { public function status( $args ) {
// Force WordPress to check for updates. // Force WordPress to check for updates.
call_user_func( $this->upgrade_refresh ); call_user_func( $this->upgrade_refresh );
@ -197,13 +221,16 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {


$filter = false; $filter = false;
// If a GitHub URL, do some guessing as to the correct plugin/theme directory. // If a GitHub URL, do some guessing as to the correct plugin/theme directory.
if ( $is_remote && 'github.com' === $this->parse_url_host_component( $slug, PHP_URL_HOST ) if ( $is_remote && 'github.com' === Utils\parse_url( $slug, PHP_URL_HOST )
// Don't attempt to rename ZIPs uploaded to the releases page or coming from a raw source. // Don't attempt to rename ZIPs uploaded to the releases page or coming from a raw source.
&& ! preg_match( '#github\.com/[^/]+/[^/]+/(?:releases/download|raw)/#', $slug ) ) { && ! preg_match( '#github\.com/[^/]+/[^/]+/(?:releases/download|raw)/#', $slug ) ) {


$filter = function ( $source ) use ( $slug ) { $filter = function ( $source ) use ( $slug ) {

/**
$slug_dir = Utils\basename( $this->parse_url_host_component( $slug, PHP_URL_PATH ), '.zip' ); * @var string $path
*/
$path = Utils\parse_url( $slug, PHP_URL_PATH );
$slug_dir = Utils\basename( $path, '.zip' );


// Don't use the zip name if archive attached to release, as name likely to contain version tag/branch. // Don't use the zip name if archive attached to release, as name likely to contain version tag/branch.
if ( preg_match( '#github\.com/[^/]+/([^/]+)/archive/#', $slug, $matches ) ) { if ( preg_match( '#github\.com/[^/]+/([^/]+)/archive/#', $slug, $matches ) ) {
@ -215,7 +242,7 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
if ( $source_dir === $slug_dir ) { if ( $source_dir === $slug_dir ) {
return $source; return $source;
} }
$new_path = substr_replace( $source, $slug_dir, strrpos( $source, $source_dir ), strlen( $source_dir ) ); $new_path = substr_replace( $source, $slug_dir, (int) strrpos( $source, $source_dir ), strlen( $source_dir ) );


if ( $GLOBALS['wp_filesystem']->move( $source, $new_path ) ) { if ( $GLOBALS['wp_filesystem']->move( $source, $new_path ) ) {
WP_CLI::log( sprintf( "Renamed Github-based project from '%s' to '%s'.", $source_dir, $slug_dir ) ); WP_CLI::log( sprintf( "Renamed Github-based project from '%s' to '%s'.", $source_dir, $slug_dir ) );
@ -294,8 +321,10 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
/** /**
* Prepare an API response for downloading a particular version of an item. * Prepare an API response for downloading a particular version of an item.
* *
* @param object $response wordpress.org API response * @param object $response Wordpress.org API response.
* @param string $version The desired version of the package * @param string $version The desired version of the package.
*
* @phpstan-param PluginInformation|ThemeInformation $response
*/ */
protected static function alter_api_response( $response, $version ) { protected static function alter_api_response( $response, $version ) {
if ( $response->version === $version ) { if ( $response->version === $version ) {
@ -346,8 +375,8 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
} }


protected function get_upgrader( $assoc_args ) { protected function get_upgrader( $assoc_args ) {
$force = (bool) Utils\get_flag_value( $assoc_args, 'force', false ); $force = Utils\get_flag_value( $assoc_args, 'force', false );
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false ); $insecure = Utils\get_flag_value( $assoc_args, 'insecure', false );
$upgrader_class = $this->get_upgrader_class( $force ); $upgrader_class = $this->get_upgrader_class( $force );
return Utils\get_upgrader( $upgrader_class, $insecure ); return Utils\get_upgrader( $upgrader_class, $insecure );
} }
@ -384,19 +413,22 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
} }
); );


$minor = (bool) Utils\get_flag_value( $assoc_args, 'minor', false ); $minor = Utils\get_flag_value( $assoc_args, 'minor', false );
$patch = (bool) Utils\get_flag_value( $assoc_args, 'patch', false ); $patch = Utils\get_flag_value( $assoc_args, 'patch', false );


if ( if (
in_array( $this->item_type, [ 'plugin', 'theme' ], true ) && in_array( $this->item_type, [ 'plugin', 'theme' ], true ) &&
( $minor || $patch ) ( $minor || $patch )
) { ) {
$type = $minor ? 'minor' : 'patch'; $type = $minor ? 'minor' : 'patch';
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false ); $insecure = Utils\get_flag_value( $assoc_args, 'insecure', false );


$items_to_update = self::get_minor_or_patch_updates( $items_to_update, $type, $insecure, true, $this->item_type ); $items_to_update = self::get_minor_or_patch_updates( $items_to_update, $type, $insecure, true, $this->item_type );
} }


/**
* @var string|null $exclude
*/
$exclude = Utils\get_flag_value( $assoc_args, 'exclude' ); $exclude = Utils\get_flag_value( $assoc_args, 'exclude' );
if ( isset( $exclude ) ) { if ( isset( $exclude ) ) {
$exclude_items = explode( ',', trim( $assoc_args['exclude'], ',' ) ); $exclude_items = explode( ',', trim( $assoc_args['exclude'], ',' ) );
@ -475,6 +507,9 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
foreach ( $items_to_update as $name => $item_data ) { foreach ( $items_to_update as $name => $item_data ) {
if ( isset( $transient->response[ $name ] ) ) { if ( isset( $transient->response[ $name ] ) ) {
if ( is_object( $transient->response[ $name ] ) ) { if ( is_object( $transient->response[ $name ] ) ) {
/**
* @var object{response: array<string, ThemeInformation|PluginInformation>} $transient
*/
$transient->response[ $name ]->new_version = $item_data['update_version']; $transient->response[ $name ]->new_version = $item_data['update_version'];
$transient->response[ $name ]->package = $item_data['update_package']; $transient->response[ $name ]->package = $item_data['update_package'];
} else { } else {
@ -490,6 +525,10 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
remove_filter( 'site_transient_' . $this->upgrade_transient, $transient_filter, 999 ); remove_filter( 'site_transient_' . $this->upgrade_transient, $transient_filter, 999 );
} }


/**
* @var array $items_to_update
*/

// Let the user know the results. // Let the user know the results.
$num_to_update = count( $items_to_update ); $num_to_update = count( $items_to_update );
$num_updated = count( $num_updated = count(
@ -545,14 +584,14 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
protected function _list( $_, $assoc_args ) { protected function _list( $_, $assoc_args ) {


// Force WordPress to check for updates if `--skip-update-check` is not passed. // Force WordPress to check for updates if `--skip-update-check` is not passed.
if ( false === (bool) Utils\get_flag_value( $assoc_args, 'skip-update-check', false ) ) { if ( false === Utils\get_flag_value( $assoc_args, 'skip-update-check', false ) ) {
delete_site_transient( $this->upgrade_transient ); delete_site_transient( $this->upgrade_transient );
call_user_func( $this->upgrade_refresh ); call_user_func( $this->upgrade_refresh );
} }


$all_items = $this->get_all_items(); $all_items = $this->get_all_items();


if ( false !== (bool) Utils\get_flag_value( $assoc_args, 'recently-active', false ) ) { if ( false !== Utils\get_flag_value( $assoc_args, 'recently-active', false ) ) {
$all_items = array_filter( $all_items = array_filter(
$all_items, $all_items,
function ( $value ) { function ( $value ) {
@ -624,6 +663,9 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
* @return bool * @return bool
*/ */
protected function has_update( $slug ) { protected function has_update( $slug ) {
/**
* @var object{checked: array<string, string>, response: array<string, string>, no_update: array<string, object{new_version: string, package: string, requires: string}&\stdClass>} $update_list
*/
$update_list = get_site_transient( $this->upgrade_transient ); $update_list = get_site_transient( $this->upgrade_transient );


return isset( $update_list->response[ $slug ] ); return isset( $update_list->response[ $slug ] );
@ -632,10 +674,15 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
/** /**
* Get the available update info * Get the available update info
* *
* @return mixed * @return object{checked: array<string, string>, response: array<string, array<string, string|null>>, no_update: array<string, object{new_version: string, package: string, requires: string}&\stdClass>} $update_list
*/ */
protected function get_update_info() { protected function get_update_info() {
return get_site_transient( $this->upgrade_transient ); /**
* @var object{checked: array<string, string>, response: array<string, array<string, string|null>>, no_update: array<string, object{new_version: string, package: string, requires: string}&\stdClass>} $update_list
*/
$update_list = get_site_transient( $this->upgrade_transient );

return $update_list;
} }


private $map = [ private $map = [
@ -688,8 +735,12 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
$wp_org_api = new WpOrgApi( [ 'insecure' => $insecure ] ); $wp_org_api = new WpOrgApi( [ 'insecure' => $insecure ] );
foreach ( $items as $i => $item ) { foreach ( $items as $i => $item ) {
try { try {
$data = call_user_func( /**
[ $wp_org_api, "get_{$item_type}_info" ], * @var callable $callback
*/
$callback = [ $wp_org_api, "get_{$item_type}_info" ];
$data = call_user_func(
$callback,
$item['name'], $item['name'],
// The default. // The default.
'en_US', 'en_US',
@ -780,9 +831,15 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
if ( 'plugin' === $this->item_type ) { if ( 'plugin' === $this->item_type ) {
$api = plugins_api( 'query_plugins', $api_args ); $api = plugins_api( 'query_plugins', $api_args );
} else { } else {
// fields[screenshot_count] could be an int, not a bool.
// @phpstan-ignore argument.type
$api = themes_api( 'query_themes', $api_args ); $api = themes_api( 'query_themes', $api_args );
} }


/**
* @var \WP_Error|object{info: object{page: int, pages: int, results: int}} $api
*/

if ( is_wp_error( $api ) ) { if ( is_wp_error( $api ) ) {
WP_CLI::error( $api->get_error_message() . __( ' Try again' ) ); WP_CLI::error( $api->get_error_message() . __( ' Try again' ) );
} }
@ -803,7 +860,10 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
} }


if ( 'table' === $format ) { if ( 'table' === $format ) {
$count = Utils\get_flag_value( $api->info, 'results', 'unknown' ); /**
* @var string $count
*/
$count = Utils\get_flag_value( (array) $api->info, 'results', 'unknown' );
WP_CLI::success( sprintf( 'Showing %s of %s %s.', count( $items ), $count, $plural ) ); WP_CLI::success( sprintf( 'Showing %s of %s %s.', count( $items ), $count, $plural ) );
} }


@ -832,18 +892,6 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
return true; return true;
} }


/**
* Retrieves PHP_URL_HOST component from URL.
*
* @param int $component The component to retrieve.
*
* @return string
*/
private function parse_url_host_component( $url, $component ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url -- parse_url will only be used in absence of wp_parse_url.
return function_exists( 'wp_parse_url' ) ? wp_parse_url( $url, $component ) : parse_url( $url, $component );
}

/** /**
* Add versioned GitHub URLs to cache allowlist. * Add versioned GitHub URLs to cache allowlist.
* *
@ -895,6 +943,9 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
} }


if ( 404 === wp_remote_retrieve_response_code( $response ) ) { if ( 404 === wp_remote_retrieve_response_code( $response ) ) {
/**
* @var object{status: string, message: string} $decoded_body
*/
return new \WP_Error( return new \WP_Error(
$decoded_body->status, $decoded_body->status,
$decoded_body->message $decoded_body->message
@ -905,6 +956,10 @@ abstract class CommandWithUpgrade extends \WP_CLI_Command {
return new \WP_Error( 500, 'Empty response received from GitHub.com API' ); return new \WP_Error( 500, 'Empty response received from GitHub.com API' );
} }


/**
* @var array<int, object{name: string}> $decoded_body
*/

if ( ! isset( $decoded_body[0] ) ) { if ( ! isset( $decoded_body[0] ) ) {
return new \WP_Error( '400', 'The given Github repository does not have any releases' ); return new \WP_Error( '400', 'The given Github repository does not have any releases' );
} }

View file

@ -4,6 +4,8 @@ namespace WP_CLI\Fetchers;


/** /**
* Fetch a WordPress plugin based on one of its attributes. * Fetch a WordPress plugin based on one of its attributes.
*
* @extends Base<object{name: string, file: string}>
*/ */
class Plugin extends Base { class Plugin extends Base {


@ -15,10 +17,12 @@ class Plugin extends Base {
/** /**
* Get a plugin object by name * Get a plugin object by name
* *
* @param string $name * @param string|int $name Plugin name.
* @return object|false * @return object{name: string, file: string}|false
*/ */
public function get( $name ) { public function get( $name ) {
$name = (string) $name;

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Calling native WordPress hook. // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Calling native WordPress hook.
foreach ( apply_filters( 'all_plugins', get_plugins() ) as $file => $_ ) { foreach ( apply_filters( 'all_plugins', get_plugins() ) as $file => $_ ) {
if ( "$name.php" === $file || if ( "$name.php" === $file ||

View file

@ -6,6 +6,8 @@ use WP_CLI\Utils;


/** /**
* Fetch a WordPress theme based on one of its attributes. * Fetch a WordPress theme based on one of its attributes.
*
* @extends Base<\WP_Theme>
*/ */
class Theme extends Base { class Theme extends Base {


@ -17,8 +19,8 @@ class Theme extends Base {
/** /**
* Get a theme object by name * Get a theme object by name
* *
* @param string $name * @param string|int $name
* @return object|false * @return \WP_Theme|false
*/ */
public function get( $name ) { public function get( $name ) {
// Workaround to equalize folder naming conventions across Win/Mac/Linux. // Workaround to equalize folder naming conventions across Win/Mac/Linux.
@ -26,7 +28,7 @@ class Theme extends Base {
$existing_themes = wp_get_themes( array( 'errors' => null ) ); $existing_themes = wp_get_themes( array( 'errors' => null ) );
$existing_stylesheets = array_keys( $existing_themes ); $existing_stylesheets = array_keys( $existing_themes );
if ( ! in_array( $name, $existing_stylesheets, true ) ) { if ( ! in_array( $name, $existing_stylesheets, true ) ) {
$inexact_match = $this->find_inexact_match( $name, $existing_themes ); $inexact_match = $this->find_inexact_match( (string) $name, $existing_themes );
if ( false !== $inexact_match ) { if ( false !== $inexact_match ) {
$this->msg .= sprintf( " Did you mean '%s'?", $inexact_match ); $this->msg .= sprintf( " Did you mean '%s'?", $inexact_match );
} }

View file

@ -5,6 +5,9 @@ namespace WP_CLI;
use WP_CLI; use WP_CLI;
use Theme_AutoUpdates_Command; use Theme_AutoUpdates_Command;


/**
* @template T of \WP_Theme
*/
trait ParseThemeNameInput { trait ParseThemeNameInput {


/** /**
@ -54,11 +57,17 @@ trait ParseThemeNameInput {
$theme_version_info = array(); $theme_version_info = array();


if ( is_multisite() ) { if ( is_multisite() ) {
/**
* @var array<string, array{enabled: string}>} $site_enabled
*/
$site_enabled = get_option( 'allowedthemes' ); $site_enabled = get_option( 'allowedthemes' );
if ( empty( $site_enabled ) ) { if ( empty( $site_enabled ) ) {
$site_enabled = array(); $site_enabled = array();
} }


/**
* @var array<string, array{enabled: string}>} $network_enabled
*/
$network_enabled = get_site_option( 'allowedthemes' ); $network_enabled = get_site_option( 'allowedthemes' );
if ( empty( $network_enabled ) ) { if ( empty( $network_enabled ) ) {
$network_enabled = array(); $network_enabled = array();
@ -169,7 +178,9 @@ trait ParseThemeNameInput {
* @return bool|string * @return bool|string
*/ */
protected function is_theme_version_valid( $slug, $version ) { protected function is_theme_version_valid( $slug, $version ) {
// Get Theme Info. /**
* @var \WP_Error|object{name: string, slug: string, version: string, download_link: string} $theme_info
*/
$theme_info = themes_api( 'theme_information', array( 'slug' => $slug ) ); $theme_info = themes_api( 'theme_information', array( 'slug' => $slug ) );


// Return empty string for themes not on WP.org. // Return empty string for themes not on WP.org.
@ -184,7 +195,7 @@ trait ParseThemeNameInput {
/** /**
* Get the status for a given theme. * Get the status for a given theme.
* *
* @param WP_Theme $theme Theme to get the status for. * @param T $theme Theme to get the status for.
* *
* @return string Status of the theme. * @return string Status of the theme.
*/ */
@ -203,7 +214,7 @@ trait ParseThemeNameInput {
/** /**
* Check whether a given theme is the active theme. * Check whether a given theme is the active theme.
* *
* @param WP_Theme $theme Theme to check. * @param \WP_Theme $theme Theme to check.
* *
* @return bool Whether the provided theme is the active theme. * @return bool Whether the provided theme is the active theme.
*/ */
@ -214,7 +225,7 @@ trait ParseThemeNameInput {
/** /**
* Check whether a given theme is the active theme parent. * Check whether a given theme is the active theme parent.
* *
* @param WP_Theme $theme Theme to check. * @param \WP_Theme $theme Theme to check.
* *
* @return bool Whether the provided theme is the active theme. * @return bool Whether the provided theme is the active theme.
*/ */
@ -225,9 +236,14 @@ trait ParseThemeNameInput {
/** /**
* Get the available update info. * Get the available update info.
* *
* @return mixed Available update info. * @return object{checked: array<string, string>, response: array<string, array<string, string|null>>, no_update: array<string, object{new_version: string, package: string, requires: string}&\stdClass>} Available update info.
*/ */
protected function get_update_info() { protected function get_update_info() {
return get_site_transient( 'update_themes' ); /**
* @var object{checked: array<string, string>, response: array<string, array<string, string|null>>, no_update: array<string, object{new_version: string, package: string, requires: string}&\stdClass>} $result
*/
$result = get_site_transient( 'update_themes' );

return $result;
} }
} }