diff --git a/composer.json b/composer.json index 0b0a2a7c..ffeae7d2 100644 --- a/composer.json +++ b/composer.json @@ -20,14 +20,15 @@ "wp-cli/extension-command": "^1.2 || ^2", "wp-cli/media-command": "^1.1 || ^2", "wp-cli/super-admin-command": "^1 || ^2", - "wp-cli/wp-cli-tests": "^4" + "wp-cli/wp-cli-tests": "dev-main" }, "config": { "process-timeout": 7200, "sort-packages": true, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, - "johnpbloch/wordpress-core-installer": true + "johnpbloch/wordpress-core-installer": true, + "phpstan/extension-installer": true }, "lock": false }, @@ -230,12 +231,14 @@ "behat-rerun": "rerun-behat-tests", "lint": "run-linter-tests", "phpcs": "run-phpcs-tests", + "phpstan": "run-phpstan-tests", "phpunit": "run-php-unit-tests", "phpcbf": "run-phpcbf-cleanup", "prepare-tests": "install-package-tests", "test": [ "@lint", "@phpcs", + "@phpstan", "@phpunit", "@behat" ] diff --git a/entity-command.php b/entity-command.php index 2146f886..6cb54ff3 100644 --- a/entity-command.php +++ b/entity-command.php @@ -60,17 +60,7 @@ ); WP_CLI::add_command( 'taxonomy', 'Taxonomy_Command' ); WP_CLI::add_command( 'term', 'Term_Command' ); -WP_CLI::add_command( - 'term meta', - 'Term_Meta_Command', - array( - 'before_invoke' => function () { - if ( Utils\wp_version_compare( '4.4', '<' ) ) { - WP_CLI::error( 'Requires WordPress 4.4 or greater.' ); - } - }, - ) -); +WP_CLI::add_command( 'term meta', 'Term_Meta_Command' ); WP_CLI::add_command( 'user', 'User_Command' ); WP_CLI::add_command( 'user application-password', @@ -84,17 +74,7 @@ ) ); WP_CLI::add_command( 'user meta', 'User_Meta_Command' ); -WP_CLI::add_command( - 'user session', - 'User_Session_Command', - array( - 'before_invoke' => function () { - if ( Utils\wp_version_compare( '4.0', '<' ) ) { - WP_CLI::error( 'Requires WordPress 4.0 or greater.' ); - } - }, - ) -); +WP_CLI::add_command( 'user session', 'User_Session_Command' ); WP_CLI::add_command( 'user term', 'User_Term_Command' ); if ( class_exists( 'WP_CLI\Dispatcher\CommandNamespace' ) ) { diff --git a/features/post.feature b/features/post.feature index c3f72a52..858668bf 100644 --- a/features/post.feature +++ b/features/post.feature @@ -451,40 +451,6 @@ Feature: Manage WordPress posts Test Post """ - @less-than-wp-4.4 - Scenario: Creating/updating posts with meta keys for WP < 4.4 has no effect so should give warning - When I try `wp post create --post_title='Test Post' --post_content='Test post content' --meta_input='{"key1":"value1","key2":"value2"}' --porcelain` - Then the return code should be 0 - And STDOUT should be a number - And save STDOUT as {POST_ID} - And STDERR should be: - """ - Warning: The 'meta_input' field was only introduced in WordPress 4.4 so will have no effect. - """ - - When I run `wp post meta list {POST_ID} --format=count` - Then STDOUT should be: - """ - 0 - """ - - When I try `wp post update {POST_ID} --meta_input='{"key2":"value2b","key3":"value3"}'` - Then the return code should be 0 - And STDERR should be: - """ - Warning: The 'meta_input' field was only introduced in WordPress 4.4 so will have no effect. - """ - And STDOUT should be: - """ - Success: Updated post {POST_ID}. - """ - - When I run `wp post meta list {POST_ID} --format=count` - Then STDOUT should be: - """ - 0 - """ - Scenario: Publishing a post and setting a date fails if the edit_date flag is not passed. Given a WP install diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..5cf50e82 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,20 @@ +parameters: + level: 6 + paths: + - src + - entity-command.php + scanDirectories: + - vendor/wp-cli/wp-cli/php + - vendor/wp-cli/wp-cli-tests + scanFiles: + - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php + treatPhpDocTypesAsCertain: false + dynamicConstantNames: + - WP_DEBUG + - WP_DEBUG_LOG + - WP_DEBUG_DISPLAY + ignoreErrors: + - identifier: missingType.iterableValue + - identifier: missingType.property + - identifier: missingType.parameter + - identifier: missingType.return diff --git a/src/Comment_Command.php b/src/Comment_Command.php index 0661ba91..c11dc1d0 100644 --- a/src/Comment_Command.php +++ b/src/Comment_Command.php @@ -243,6 +243,7 @@ public function get( $args, $assoc_args ) { } if ( ! isset( $comment->url ) ) { + // @phpstan-ignore property.notFound $comment->url = get_comment_link( $comment ); } @@ -366,6 +367,8 @@ public function get( $args, $assoc_args ) { public function list_( $args, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); + // To be fixed in wp-cli/wp-cli. + // @phpstan-ignore property.notFound if ( 'ids' === $formatter->format ) { $assoc_args['fields'] = 'comment_ID'; } @@ -376,29 +379,22 @@ public function list_( $args, $assoc_args ) { $assoc_args['count'] = true; } - if ( ! empty( $assoc_args['comment__in'] ) - && ! empty( $assoc_args['orderby'] ) - && 'comment__in' === $assoc_args['orderby'] - && Utils\wp_version_compare( '4.4', '<' ) ) { - $comments = []; - foreach ( $assoc_args['comment__in'] as $comment_id ) { - $comment = get_comment( $comment_id ); - if ( $comment ) { - $comments[] = $comment; - } else { - WP_CLI::warning( "Invalid comment {$comment_id}." ); - } - } - } else { - $query = new WP_Comment_Query(); - $comments = $query->query( $assoc_args ); - } + $query = new WP_Comment_Query(); + $comments = $query->query( $assoc_args ); if ( 'count' === $formatter->format ) { + /** + * @var int $comments + */ echo $comments; } else { if ( 'ids' === $formatter->format ) { - $comments = wp_list_pluck( $comments, 'comment_ID' ); + /** + * @var \WP_Comment[] $comments + */ + $items = wp_list_pluck( $comments, 'comment_ID' ); + + $comments = $items; } elseif ( is_array( $comments ) ) { $comments = array_map( function ( $comment ) { @@ -439,7 +435,7 @@ public function delete( $args, $assoc_args ) { $args, $assoc_args, function ( $comment_id, $assoc_args ) { - $force = Utils\get_flag_value( $assoc_args, 'force' ); + $force = (bool) Utils\get_flag_value( $assoc_args, 'force' ); $status = wp_get_comment_status( $comment_id ); $result = wp_delete_comment( $comment_id, $force ); @@ -457,6 +453,9 @@ function ( $comment_id, $assoc_args ) { private function call( $args, $status, $success, $failure ) { $comment_id = absint( $args ); + /** + * @var callable $func + */ $func = "wp_{$status}_comment"; if ( ! $func( $comment_id ) ) { @@ -642,16 +641,17 @@ public function unapprove( $args, $assoc_args ) { * total_comments: 19 */ public function count( $args, $assoc_args ) { - $post_id = Utils\get_flag_value( $args, 0, 0 ); + $post_id = $args[0] ?? null; $count = wp_count_comments( $post_id ); // Move total_comments to the end of the object $total = $count->total_comments; unset( $count->total_comments ); + // @phpstan-ignore assign.propertyReadOnly $count->total_comments = $total; - foreach ( $count as $status => $count ) { + foreach ( (array) $count as $status => $count ) { WP_CLI::line( str_pad( "$status:", 17 ) . $count ); } } @@ -673,6 +673,9 @@ public function count( $args, $assoc_args ) { public function recount( $args ) { foreach ( $args as $id ) { if ( wp_update_comment_count( $id ) ) { + /** + * @var \WP_Post $post + */ $post = get_post( $id ); WP_CLI::log( "Updated post {$post->ID} comment count to {$post->comment_count}." ); } else { diff --git a/src/Comment_Meta_Command.php b/src/Comment_Meta_Command.php index 7cb01821..38b6c398 100644 --- a/src/Comment_Meta_Command.php +++ b/src/Comment_Meta_Command.php @@ -104,11 +104,11 @@ protected function delete_metadata( $object_id, $meta_key, $meta_value = '' ) { /** * Check that the comment ID exists * - * @param int + * @param int $object_id */ protected function check_object_id( $object_id ) { $fetcher = new CommentFetcher(); - $comment = $fetcher->get_check( $object_id ); + $comment = $fetcher->get_check( (string) $object_id ); return $comment->comment_ID; } } diff --git a/src/Menu_Command.php b/src/Menu_Command.php index e6806530..b23df5c7 100644 --- a/src/Menu_Command.php +++ b/src/Menu_Command.php @@ -70,7 +70,7 @@ public function create( $args, $assoc_args ) { } elseif ( Utils\get_flag_value( $assoc_args, 'porcelain' ) ) { - WP_CLI::line( $menu_id ); + WP_CLI::line( (string) $menu_id ); } else { WP_CLI::success( "Created menu {$menu_id}." ); } @@ -166,6 +166,7 @@ public function list_( $args, $assoc_args ) { $menu_locations = get_nav_menu_locations(); foreach ( $menus as &$menu ) { + // @phpstan-ignore property.notFound $menu->locations = []; foreach ( $menu_locations as $location => $term_id ) { diff --git a/src/Menu_Item_Command.php b/src/Menu_Item_Command.php index c61d9f7d..418d3fa4 100644 --- a/src/Menu_Item_Command.php +++ b/src/Menu_Item_Command.php @@ -92,7 +92,7 @@ class Menu_Item_Command extends WP_CLI_Command { public function list_( $args, $assoc_args ) { $items = wp_get_nav_menu_items( $args[0] ); - if ( false === $items || is_wp_error( $items ) ) { + if ( false === $items ) { WP_CLI::error( 'Invalid menu.' ); } @@ -408,10 +408,10 @@ public function delete( $args, $assoc_args ) { private function add_or_update_item( $method, $type, $args, $assoc_args ) { $menu = $args[0]; - $menu_item_db_id = Utils\get_flag_value( $args, 1, 0 ); + $menu_item_db_id = $args[1] ?? 0; $menu = wp_get_nav_menu_object( $menu ); - if ( ! $menu || is_wp_error( $menu ) ) { + if ( false === $menu ) { WP_CLI::error( 'Invalid menu.' ); } @@ -422,6 +422,14 @@ private function add_or_update_item( $method, $type, $args, $assoc_args ) { if ( 'update' === $method ) { $menu_item_obj = get_post( $menu_item_db_id ); + + if ( ! $menu_item_obj ) { + WP_CLI::error( 'Invalid menu.' ); + } + + /** + * @var object{title: string, url: string, description: string, object: string, object_id: int, menu_item_parent: int, attr_title: string, target: string, classes: string[], xfn: string, post_status: string, menu_order: int} $menu_item_obj + */ $menu_item_obj = wp_setup_nav_menu_item( $menu_item_obj ); // Correct the menu position if this was the first item. See https://core.trac.wordpress.org/ticket/28140 @@ -502,7 +510,7 @@ private function add_or_update_item( $method, $type, $args, $assoc_args ) { } if ( 'add' === $method && ! empty( $assoc_args['porcelain'] ) ) { - WP_CLI::line( $result ); + WP_CLI::line( (string) $result ); } elseif ( 'add' === $method ) { WP_CLI::success( 'Menu item added.' ); } elseif ( 'update' === $method ) { diff --git a/src/Menu_Location_Command.php b/src/Menu_Location_Command.php index 540c87e4..a5bc555d 100644 --- a/src/Menu_Location_Command.php +++ b/src/Menu_Location_Command.php @@ -77,6 +77,8 @@ public function list_( $args, $assoc_args ) { $formatter = new Formatter( $assoc_args, [ 'location', 'description' ] ); + // To be fixed in wp-cli/wp-cli. + // @phpstan-ignore property.notFound if ( 'ids' === $formatter->format ) { $ids = array_map( function ( $o ) { @@ -107,6 +109,8 @@ function ( $o ) { * Success: Assigned location primary to menu primary-menu. * * @subcommand assign + * + * @param array{string, string} $args */ public function assign( $args, $assoc_args ) { @@ -147,18 +151,20 @@ public function assign( $args, $assoc_args ) { * Success: Removed location from menu. * * @subcommand remove + * + * @param array{string, string} $args */ public function remove( $args, $assoc_args ) { list( $menu, $location ) = $args; $menu = wp_get_nav_menu_object( $menu ); - if ( ! $menu || is_wp_error( $menu ) ) { + if ( false === $menu ) { WP_CLI::error( 'Invalid menu.' ); } $locations = get_nav_menu_locations(); - if ( Utils\get_flag_value( $locations, $location ) !== $menu->term_id ) { + if ( ( $locations[ $location ] ?? null ) !== $menu->term_id ) { WP_CLI::error( "Menu isn't assigned to location." ); } diff --git a/src/Option_Command.php b/src/Option_Command.php index 73bc32b2..d9f3a60e 100644 --- a/src/Option_Command.php +++ b/src/Option_Command.php @@ -133,6 +133,7 @@ public function add( $args, $assoc_args ) { $autoload = 'yes'; } + // @phpstan-ignore argument.type if ( ! add_option( $key, $value, '', $autoload ) ) { WP_CLI::error( "Could not add option '{$key}'. Does it already exist?" ); } else { @@ -321,8 +322,8 @@ public function list_( $args, $assoc_args ) { function ( $a, $b ) use ( $orderby, $order ) { // Sort array. return 'asc' === $order - ? $a->$orderby > $b->$orderby - : $a->$orderby < $b->$orderby; + ? $a->$orderby <=> $b->$orderby + : $b->$orderby <=> $a->$orderby; } ); } elseif ( 'option_id' === $orderby && 'desc' === $order ) { // Sort by default descending. @@ -422,7 +423,11 @@ public function update( $args, $assoc_args ) { $autoload = null; } + /** + * @var string $value + */ $value = sanitize_option( $key, $value ); + // Sanitization WordPress normally performs when getting an option if ( in_array( $key, [ 'siteurl', 'home', 'category_base', 'tag_base' ], true ) ) { $value = untrailingslashit( $value ); @@ -431,6 +436,7 @@ public function update( $args, $assoc_args ) { if ( $value === $old_value && null === $autoload ) { WP_CLI::success( "Value passed for '{$key}' option is unchanged." ); + // @phpstan-ignore argument.type } elseif ( update_option( $key, $value, $autoload ) ) { WP_CLI::success( "Updated '{$key}' option." ); } else { @@ -744,17 +750,11 @@ function ( $key ) { } private static function esc_like( $old ) { + /** + * @var \wpdb $wpdb + */ global $wpdb; - // Remove notices in 4.0 and support backwards compatibility - if ( method_exists( $wpdb, 'esc_like' ) ) { - // 4.0 - $old = $wpdb->esc_like( $old ); - } else { - // phpcs:ignore WordPress.WP.DeprecatedFunctions.like_escapeFound -- called in WordPress 3.9 or less. - $old = like_escape( esc_sql( $old ) ); - } - - return $old; + return $wpdb->esc_like( $old ); } } diff --git a/src/Post_Command.php b/src/Post_Command.php index a4a5ba42..a9c1a60e 100644 --- a/src/Post_Command.php +++ b/src/Post_Command.php @@ -182,10 +182,6 @@ public function create( $args, $assoc_args ) { $assoc_args['post_category'] = $this->get_category_ids( $assoc_args['post_category'] ); } - if ( isset( $assoc_args['meta_input'] ) && Utils\wp_version_compare( '4.4', '<' ) ) { - WP_CLI::warning( "The 'meta_input' field was only introduced in WordPress 4.4 so will have no effect." ); - } - $array_arguments = [ 'meta_input' ]; $assoc_args = Utils\parse_shell_arrays( $assoc_args, $array_arguments ); @@ -351,10 +347,6 @@ public function update( $args, $assoc_args ) { $assoc_args['post_category'] = $this->get_category_ids( $assoc_args['post_category'] ); } - if ( isset( $assoc_args['meta_input'] ) && Utils\wp_version_compare( '4.4', '<' ) ) { - WP_CLI::warning( "The 'meta_input' field was only introduced in WordPress 4.4 so will have no effect." ); - } - $array_arguments = [ 'meta_input' ]; $assoc_args = Utils\parse_shell_arrays( $assoc_args, $array_arguments ); @@ -387,7 +379,7 @@ public function edit( $args, $assoc_args ) { $result = $this->_edit( $post->post_content, "WP-CLI post {$post->ID}" ); if ( false === $result ) { - WP_CLI::warning( 'No change made to post content.', 'Aborted' ); + WP_CLI::warning( 'No change made to post content.' ); } else { $this->update( $args, [ 'post_content' => $result ] ); } @@ -644,9 +636,12 @@ public function list_( $args, $assoc_args ) { $query_args['post_type'] = explode( ',', $query_args['post_type'] ); } + // To be fixed in wp-cli/wp-cli. + // @phpstan-ignore property.notFound if ( 'ids' === $formatter->format ) { $query_args['fields'] = 'ids'; $query = new WP_Query( $query_args ); + // @phpstan-ignore argument.type echo implode( ' ', $query->posts ); } elseif ( 'count' === $formatter->format ) { $query_args['fields'] = 'ids'; @@ -656,8 +651,13 @@ public function list_( $args, $assoc_args ) { $query = new WP_Query( $query_args ); $posts = array_map( function ( $post ) { - $post->url = get_permalink( $post->ID ); - return $post; + /** + * @var \WP_Post $post + */ + + // @phpstan-ignore property.notFound + $post->url = get_permalink( $post->ID ); + return $post; }, $query->posts ); @@ -806,11 +806,14 @@ public function generate( $args, $assoc_args ) { // Get the total number of posts. $total = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = %s", $post_data['post_type'] ) ); + /** + * @var \WP_Post_Type $post_type + */ + $post_type = get_post_type_object( $post_data['post_type'] ); + $label = ! empty( $post_data['post_title'] ) ? $post_data['post_title'] - : get_post_type_object( $post_data['post_type'] )->labels->singular_name; - - $hierarchical = get_post_type_object( $post_data['post_type'] )->hierarchical; + : $post_type->labels->singular_name; $limit = $post_data['count'] + $total; @@ -827,7 +830,7 @@ public function generate( $args, $assoc_args ) { for ( $index = $total; $index < $limit; $index++ ) { - if ( $hierarchical ) { + if ( $post_type->hierarchical ) { if ( $this->maybe_make_child() && $current_depth < $post_data['max_depth'] ) { @@ -933,7 +936,7 @@ private function read_from_file_or_stdin( $arg ) { } else { $readfile = 'php://stdin'; } - return file_get_contents( $readfile ); + return (string) file_get_contents( $readfile ); } /** @@ -1010,7 +1013,7 @@ private function get_category( $post_id ) { private function get_tags( $post_id ) { $tag_data = get_the_tags( $post_id ); $tag_arr = []; - if ( $tag_data ) { + if ( $tag_data && ! is_wp_error( $tag_data ) ) { foreach ( $tag_data as $tag ) { array_push( $tag_arr, $tag->slug ); } diff --git a/src/Post_Meta_Command.php b/src/Post_Meta_Command.php index 96aa2341..f225851d 100644 --- a/src/Post_Meta_Command.php +++ b/src/Post_Meta_Command.php @@ -30,11 +30,11 @@ class Post_Meta_Command extends CommandWithMeta { /** * Check that the post ID exists * - * @param int + * @param int $object_id */ protected function check_object_id( $object_id ) { $fetcher = new PostFetcher(); - $post = $fetcher->get_check( $object_id ); + $post = $fetcher->get_check( (string) $object_id ); return $post->ID; } diff --git a/src/Post_Type_Command.php b/src/Post_Type_Command.php index 88c01029..335477bc 100644 --- a/src/Post_Type_Command.php +++ b/src/Post_Type_Command.php @@ -147,8 +147,9 @@ public function list_( $args, $assoc_args ) { $types = array_map( function ( $type ) use ( $counts ) { - $type->count = isset( $counts[ $type->name ] ) ? $counts[ $type->name ] : 0; - return $type; + // @phpstan-ignore property.notFound + $type->count = isset( $counts[ $type->name ] ) ? $counts[ $type->name ] : 0; + return $type; }, $types ); diff --git a/src/Signup_Command.php b/src/Signup_Command.php index 4144257e..4125ceea 100644 --- a/src/Signup_Command.php +++ b/src/Signup_Command.php @@ -130,9 +130,12 @@ public function list_( $args, $assoc_args ) { $signups = array(); - $per_page = (int) Utils\get_flag_value( $assoc_args, 'per_page' ); + /** + * @var string|null $per_page + */ + $per_page = Utils\get_flag_value( $assoc_args, 'per_page' ); - $limit = $per_page ? $wpdb->prepare( 'LIMIT %d', $per_page ) : ''; + $limit = $per_page ? $wpdb->prepare( 'LIMIT %d', (int) $per_page ) : ''; $query = "SELECT * FROM $wpdb->signups {$limit}"; @@ -308,7 +311,7 @@ public function delete( $args, $assoc_args ) { /** * Deletes signup. * - * @param stdClasss $signup + * @param object{signup_id: int|string} $signup * @return bool True if success; otherwise false. */ private function delete_signup( $signup ) { diff --git a/src/Site_Command.php b/src/Site_Command.php index de5451a5..132e080c 100644 --- a/src/Site_Command.php +++ b/src/Site_Command.php @@ -29,6 +29,8 @@ * Success: The site at 'http://www.example.com/example' was deleted. * * @package wp-cli + * + * @phpstan-type UserSite object{userblog_id: int, blogname: string, domain: string, path: string, site_id: int, siteurl: string, archived: int, spam: int, deleted: int} */ class Site_Command extends CommandWithDBObject { @@ -89,6 +91,9 @@ private function empty_posts() { * Delete terms, taxonomies, and tax relationships. */ private function empty_taxonomies() { + /** + * @var \wpdb $wpdb + */ global $wpdb; // Empty taxonomies and terms @@ -278,6 +283,10 @@ public function empty_( $args, $assoc_args ) { $files_to_unlink = []; $directories_to_delete = []; $is_main_site = is_main_site(); + + /** + * @var \SplFileInfo $fileinfo + */ foreach ( $files as $fileinfo ) { $realpath = $fileinfo->getRealPath(); // Don't clobber subsites when operating on the main site @@ -360,7 +369,7 @@ public function delete( $args, $assoc_args ) { WP_CLI::confirm( "Are you sure you want to delete the '{$site_url}' site?", $assoc_args ); - wpmu_delete_blog( $blog->blog_id, ! Utils\get_flag_value( $assoc_args, 'keep-tables' ) ); + wpmu_delete_blog( (int) $blog->blog_id, ! Utils\get_flag_value( $assoc_args, 'keep-tables' ) ); WP_CLI::success( "The site at '{$site_url}' was deleted." ); } @@ -400,7 +409,11 @@ public function create( $args, $assoc_args ) { global $wpdb, $current_site; - $base = $assoc_args['slug']; + $base = $assoc_args['slug']; + + /** + * @var string $title + */ $title = Utils\get_flag_value( $assoc_args, 'title', ucfirst( $base ) ); $email = empty( $assoc_args['email'] ) ? '' : $assoc_args['email']; @@ -479,7 +492,7 @@ public function create( $args, $assoc_args ) { } if ( Utils\get_flag_value( $assoc_args, 'porcelain' ) ) { - WP_CLI::line( $id ); + WP_CLI::line( (string) $id ); } else { $site_url = trailingslashit( get_site_url( $id ) ); WP_CLI::success( "Site {$id} created: {$site_url}" ); @@ -806,6 +819,9 @@ public function list_( $args, $assoc_args ) { $user = ( new UserFetcher() )->get_check( $assoc_args['site_user'] ); if ( $user ) { + /** + * @phpstan-var UserSite[] $blogs + */ $blogs = get_blogs_of_user( $user->ID ); foreach ( $blogs as $blog ) { @@ -830,6 +846,9 @@ public function list_( $args, $assoc_args ) { $iterator = new TableIterator( $iterator_args ); + /** + * @var iterable $iterator + */ $iterator = Utils\iterator_map( $iterator, function ( $blog ) { @@ -1142,6 +1161,8 @@ public function set_private( $args, $assoc_args ) { private function update_site_status( $ids, $pref, $value ) { $value = (int) $value; + $action = 'updated'; + switch ( $pref ) { case 'archived': $action = $value ? 'archived' : 'unarchived'; @@ -1175,7 +1196,7 @@ private function update_site_status( $ids, $pref, $value ) { continue; } - update_blog_status( $site->blog_id, $pref, $value ); + update_blog_status( $site->blog_id, $pref, (string) $value ); WP_CLI::success( "Site {$site->blog_id} {$action}." ); } } @@ -1190,6 +1211,9 @@ private function update_site_status( $ids, $pref, $value ) { * @throws ExitException */ private function get_sites_ids( $args, $assoc_args ) { + /** + * @var string|false $slug + */ $slug = Utils\get_flag_value( $assoc_args, 'slug', false ); if ( $slug ) { diff --git a/src/Site_Meta_Command.php b/src/Site_Meta_Command.php index acc58741..e9bcfadb 100644 --- a/src/Site_Meta_Command.php +++ b/src/Site_Meta_Command.php @@ -30,11 +30,11 @@ class Site_Meta_Command extends CommandWithMeta { /** * Check that the site ID exists * - * @param int + * @param int $object_id */ protected function check_object_id( $object_id ) { $fetcher = new SiteFetcher(); - $site = $fetcher->get_check( $object_id ); + $site = $fetcher->get_check( (string) $object_id ); return $site->blog_id; } diff --git a/src/Site_Option_Command.php b/src/Site_Option_Command.php index b2a96883..c4e9e4b4 100644 --- a/src/Site_Option_Command.php +++ b/src/Site_Option_Command.php @@ -406,17 +406,11 @@ function ( $key ) { } private static function esc_like( $old ) { + /** + * @var \wpdb $wpdb + */ global $wpdb; - // Remove notices in 4.0 and support backwards compatibility - if ( method_exists( $wpdb, 'esc_like' ) ) { - // 4.0 - $old = $wpdb->esc_like( $old ); - } else { - // phpcs:ignore WordPress.WP.DeprecatedFunctions.like_escapeFound -- called in WordPress 3.9 or less. - $old = like_escape( esc_sql( $old ) ); - } - - return $old; + return $wpdb->esc_like( $old ); } } diff --git a/src/Taxonomy_Command.php b/src/Taxonomy_Command.php index 1f1d05f9..8bae2961 100644 --- a/src/Taxonomy_Command.php +++ b/src/Taxonomy_Command.php @@ -38,16 +38,6 @@ class Taxonomy_Command extends WP_CLI_Command { 'public', ); - public function __construct() { - - if ( Utils\wp_version_compare( 3.7, '<' ) ) { - // remove description for wp <= 3.7 - $this->fields = array_values( array_diff( $this->fields, array( 'description' ) ) ); - } - - parent::__construct(); - } - /** * Gets the term counts for each supplied taxonomy. * @@ -173,8 +163,11 @@ public function list_( $args, $assoc_args ) { $taxonomies = array_map( function ( $taxonomy ) use ( $counts ) { + // @phpstan-ignore assign.propertyType $taxonomy->object_type = implode( ', ', $taxonomy->object_type ); - $taxonomy->count = isset( $counts[ $taxonomy->name ] ) ? $counts[ $taxonomy->name ] : 0; + + // @phpstan-ignore property.notFound + $taxonomy->count = isset( $counts[ $taxonomy->name ] ) ? $counts[ $taxonomy->name ] : 0; return $taxonomy; }, $taxonomies diff --git a/src/Term_Command.php b/src/Term_Command.php index e0218909..489ddf3e 100644 --- a/src/Term_Command.php +++ b/src/Term_Command.php @@ -133,23 +133,15 @@ public function list_( $args, $assoc_args ) { $assoc_args = array_merge( $defaults, $assoc_args ); if ( ! empty( $assoc_args['term_id'] ) ) { + /** + * @var \WP_Term $term + */ $term = get_term_by( 'id', $assoc_args['term_id'], $args[0] ); $terms = [ $term ]; - } elseif ( ! empty( $assoc_args['include'] ) - && ! empty( $assoc_args['orderby'] ) - && 'include' === $assoc_args['orderby'] - && Utils\wp_version_compare( '4.7', '<' ) ) { - $terms = []; - $term_ids = explode( ',', $assoc_args['include'] ); - foreach ( $term_ids as $term_id ) { - $term = get_term_by( 'id', $term_id, $args[0] ); - if ( $term && ! is_wp_error( $term ) ) { - $terms[] = $term; - } else { - WP_CLI::warning( "Invalid term {$term_id}." ); - } - } } else { + /** + * @var \WP_Term[] $terms + */ // phpcs:ignore WordPress.WP.DeprecatedParameters.Get_termsParam2Found -- Required for backward compatibility. $terms = get_terms( $args, $assoc_args ); } @@ -158,7 +150,9 @@ public function list_( $args, $assoc_args ) { function ( $term ) { $term->count = (int) $term->count; $term->parent = (int) $term->parent; - $term->url = get_term_link( $term ); + + // @phpstan-ignore property.notFound + $term->url = get_term_link( $term ); return $term; }, $terms @@ -215,11 +209,6 @@ public function create( $args, $assoc_args ) { $porcelain = Utils\get_flag_value( $assoc_args, 'porcelain' ); unset( $assoc_args['porcelain'] ); - // Compatibility for < WP 4.0 - if ( $assoc_args['parent'] > 0 && ! term_exists( (int) $assoc_args['parent'] ) ) { - WP_CLI::error( 'Parent term does not exist.' ); - } - $assoc_args = wp_slash( $assoc_args ); $term = wp_slash( $term ); $result = wp_insert_term( $term, $taxonomy, $assoc_args ); @@ -227,7 +216,7 @@ public function create( $args, $assoc_args ) { if ( is_wp_error( $result ) ) { WP_CLI::error( $result->get_error_message() ); } elseif ( $porcelain ) { - WP_CLI::line( $result['term_id'] ); + WP_CLI::line( (string) $result['term_id'] ); } else { WP_CLI::success( "Created {$taxonomy} {$result['term_id']}." ); } @@ -284,13 +273,19 @@ public function get( $args, $assoc_args ) { list( $taxonomy, $term ) = $args; - $term = get_term_by( Utils\get_flag_value( $assoc_args, 'by' ), $term, $taxonomy ); + /** + * @var string $field + */ + $field = Utils\get_flag_value( $assoc_args, 'by' ); + + $term = get_term_by( $field, $term, $taxonomy ); if ( ! $term ) { WP_CLI::error( "Term doesn't exist." ); } if ( ! isset( $term->url ) ) { + // @phpstan-ignore property.notFound $term->url = get_term_link( $term ); } @@ -368,7 +363,12 @@ public function update( $args, $assoc_args ) { $assoc_args = wp_slash( $assoc_args ); - $term = get_term_by( Utils\get_flag_value( $assoc_args, 'by' ), $term, $taxonomy ); + /** + * @var string $field + */ + $field = Utils\get_flag_value( $assoc_args, 'by' ); + + $term = get_term_by( $field, $term, $taxonomy ); if ( ! $term ) { WP_CLI::error( "Term doesn't exist." ); @@ -528,10 +528,13 @@ public function generate( $args, $assoc_args ) { WP_CLI::error( "'{$taxonomy}' is not a registered taxonomy." ); } - $label = get_taxonomy( $taxonomy )->labels->singular_name; - $slug = sanitize_title_with_dashes( $label ); + /** + * @var \WP_Taxonomy $tax + */ + $tax = get_taxonomy( $taxonomy ); - $hierarchical = get_taxonomy( $taxonomy )->hierarchical; + $label = $tax->labels->singular_name; + $slug = sanitize_title_with_dashes( $label ); $format = Utils\get_flag_value( $assoc_args, 'format', 'progress' ); @@ -551,7 +554,7 @@ public function generate( $args, $assoc_args ) { for ( $index = $max_id + 1; $index <= $max_id + $count; $index++ ) { - if ( $hierarchical ) { + if ( $tax->hierarchical ) { if ( $previous_term_id && $this->maybe_make_child() && $current_depth < $max_depth ) { @@ -636,6 +639,10 @@ public function recount( $args ) { WP_CLI::warning( "Taxonomy {$taxonomy} does not exist." ); } else { + /** + * @var \WP_Term[] $terms + */ + // phpcs:ignore WordPress.WP.DeprecatedParameters.Get_termsParam2Found -- Required for backward compatibility. $terms = get_terms( $taxonomy, [ 'hide_empty' => false ] ); $term_taxonomy_ids = wp_list_pluck( $terms, 'term_taxonomy_id' ); @@ -680,17 +687,33 @@ public function recount( $args ) { * Success: Migrated the term 'video' from taxonomy 'category' to taxonomy 'post_tag' for 1 post. */ public function migrate( $args, $assoc_args ) { - $term_reference = $args[0]; - $original_taxonomy = Utils\get_flag_value( $assoc_args, 'from' ); + $term_reference = $args[0]; + + /** + * @var string $original_taxonomy + */ + $original_taxonomy = Utils\get_flag_value( $assoc_args, 'from' ); + /** + * @var string $destination_taxonomy + */ $destination_taxonomy = Utils\get_flag_value( $assoc_args, 'to' ); - $term = get_term_by( Utils\get_flag_value( $assoc_args, 'by' ), $term_reference, $original_taxonomy ); + /** + * @var string $field + */ + $field = Utils\get_flag_value( $assoc_args, 'by' ); + + $term = get_term_by( $field, $term_reference, $original_taxonomy ); if ( ! $term ) { WP_CLI::error( "Taxonomy term '{$term_reference}' for taxonomy '{$original_taxonomy}' doesn't exist." ); } - $original_taxonomy = get_taxonomy( $original_taxonomy ); + $tax = get_taxonomy( $original_taxonomy ); + + if ( ! $tax ) { + WP_CLI::error( "Taxonomy '{$original_taxonomy}' doesn't exist." ); + } $id = wp_insert_term( $term->name, @@ -706,12 +729,15 @@ public function migrate( $args, $assoc_args ) { WP_CLI::error( $id->get_error_message() ); } - $post_ids = get_objects_in_term( $term->term_id, $original_taxonomy->name ); + /** + * @var string[] $post_ids + */ + $post_ids = get_objects_in_term( $term->term_id, $tax->name ); foreach ( $post_ids as $post_id ) { - $type = get_post_type( $post_id ); - if ( in_array( $type, $original_taxonomy->object_type, true ) ) { - $term_taxonomy_id = wp_set_object_terms( $post_id, $id['term_id'], $destination_taxonomy, true ); + $type = get_post_type( (int) $post_id ); + if ( in_array( $type, $tax->object_type, true ) ) { + $term_taxonomy_id = wp_set_object_terms( (int) $post_id, $id['term_id'], $destination_taxonomy, true ); if ( is_wp_error( $term_taxonomy_id ) ) { WP_CLI::error( "Failed to assign the term '{$term->slug}' to the post {$post_id}. Reason: " . $term_taxonomy_id->get_error_message() ); @@ -720,14 +746,14 @@ public function migrate( $args, $assoc_args ) { WP_CLI::log( "Term '{$term->slug}' assigned to post {$post_id}." ); } - clean_post_cache( $post_id ); + clean_post_cache( (int) $post_id ); } clean_term_cache( $term->term_id ); WP_CLI::log( "Term '{$term->slug}' migrated." ); - $del = wp_delete_term( $term->term_id, $original_taxonomy->name ); + $del = wp_delete_term( $term->term_id, $tax->name ); if ( is_wp_error( $del ) ) { WP_CLI::error( "Failed to delete the term '{$term->slug}'. Reason: " . $del->get_error_message() ); @@ -736,7 +762,7 @@ public function migrate( $args, $assoc_args ) { WP_CLI::log( "Old instance of term '{$term->slug}' removed from its original taxonomy." ); $post_count = count( $post_ids ); $post_plural = Utils\pluralize( 'post', $post_count ); - WP_CLI::success( "Migrated the term '{$term->slug}' from taxonomy '{$original_taxonomy->name}' to taxonomy '{$destination_taxonomy}' for {$post_count} {$post_plural}." ); + WP_CLI::success( "Migrated the term '{$term->slug}' from taxonomy '{$tax->name}' to taxonomy '{$destination_taxonomy}' for {$post_count} {$post_plural}." ); } private function maybe_make_child() { diff --git a/src/Term_Meta_Command.php b/src/Term_Meta_Command.php index f751b6eb..29d2ed24 100644 --- a/src/Term_Meta_Command.php +++ b/src/Term_Meta_Command.php @@ -29,11 +29,11 @@ class Term_Meta_Command extends CommandWithMeta { /** * Check that the term ID exists * - * @param int + * @param int $object_id */ protected function check_object_id( $object_id ) { $term = get_term( $object_id ); - if ( ! $term ) { + if ( ! $term || is_wp_error( $term ) ) { WP_CLI::error( "Could not find the term with ID {$object_id}." ); } return $term->term_id; @@ -52,7 +52,7 @@ protected function check_object_id( $object_id ) { * value for the specified metadata key, no change * will be made. * - * @return int|false The meta ID on success, false on failure. + * @return int|false|WP_Error The meta ID on success, false on failure, WP_Error when term_id is ambiguous between taxonomies. */ protected function add_metadata( $object_id, $meta_key, $meta_value, $unique = false ) { return add_term_meta( $object_id, $meta_key, $meta_value, $unique ); @@ -69,8 +69,8 @@ protected function add_metadata( $object_id, $meta_key, $meta_value, $unique = f * metadata entries with the specified value. * Otherwise, update all entries. * - * @return int|bool Meta ID if the key didn't exist, true on successful - * update, false on failure. + * @return int|bool|WP_Error Meta ID if the key didn't exist, true on successful + * update, false on failure, WP_Error when term_id is ambiguous between taxonomies. */ protected function update_metadata( $object_id, $meta_key, $meta_value, $prev_value = '' ) { return update_term_meta( $object_id, $meta_key, $meta_value, $prev_value ); diff --git a/src/User_Application_Password_Command.php b/src/User_Application_Password_Command.php index 89cb4ea9..0dffef71 100644 --- a/src/User_Application_Password_Command.php +++ b/src/User_Application_Password_Command.php @@ -163,8 +163,8 @@ public function list_( $args, $assoc_args ) { static function ( $a, $b ) use ( $orderby, $order ) { // Sort array. return 'asc' === $order - ? $a[ $orderby ] > $b[ $orderby ] - : $a[ $orderby ] < $b[ $orderby ]; + ? $a[ $orderby ] <=> $b[ $orderby ] + : $b[ $orderby ] <=> $a[ $orderby ]; } ); @@ -315,8 +315,15 @@ public function get( $args, $assoc_args ) { public function create( $args, $assoc_args ) { $args = $this->replace_login_with_user_id( $args ); + /** + * @var string $user_id + * @var string $app_name + */ list( $user_id, $app_name ) = $args; + /** + * @var string $app_id + */ $app_id = Utils\get_flag_value( $assoc_args, 'app-id', '' ); $arguments = [ @@ -324,7 +331,7 @@ public function create( $args, $assoc_args ) { 'app_id' => $app_id, ]; - $result = WP_Application_Passwords::create_new_application_password( $user_id, $arguments ); + $result = WP_Application_Passwords::create_new_application_password( (int) $user_id, $arguments ); if ( $result instanceof WP_Error ) { WP_CLI::error( $result ); diff --git a/src/User_Command.php b/src/User_Command.php index 5bad384c..ae6a9e80 100644 --- a/src/User_Command.php +++ b/src/User_Command.php @@ -31,6 +31,8 @@ * Success: Removed user 123 from http://example.com. * * @package wp-cli + * + * @phpstan-import-type UserSite from Site_Command */ class User_Command extends CommandWithDBObject { @@ -152,6 +154,8 @@ public function list_( $args, $assoc_args ) { $formatter = $this->get_formatter( $assoc_args ); + // To be fixed in wp-cli/wp-cli. + // @phpstan-ignore property.notFound if ( in_array( $formatter->format, [ 'ids', 'count' ], true ) ) { $assoc_args['fields'] = 'ids'; } else { @@ -184,8 +188,14 @@ function ( $user ) { return $user; } + /** + * @var \WP_User $user + */ + + // @phpstan-ignore assign.propertyType $user->roles = implode( ',', $user->roles ); - $user->url = get_author_posts_url( $user->ID, $user->user_nicename ); + // @phpstan-ignore property.notFound + $user->url = get_author_posts_url( $user->ID, $user->user_nicename ); return $user; } ); @@ -273,8 +283,13 @@ public function get( $args, $assoc_args ) { * $ wp user delete $(wp user list --role=contributor --field=ID | head -n 100) */ public function delete( $args, $assoc_args ) { - $network = Utils\get_flag_value( $assoc_args, 'network' ) && is_multisite(); + $network = Utils\get_flag_value( $assoc_args, 'network' ) && is_multisite(); + + /** + * @var string|null $reassign + */ $reassign = Utils\get_flag_value( $assoc_args, 'reassign' ); + $reassign = (int) $reassign; if ( $network && $reassign ) { WP_CLI::error( 'Reassigning content to a different user is not supported on multisite.' ); @@ -434,7 +449,7 @@ public function create( $args, $assoc_args ) { if ( is_multisite() ) { $result = wpmu_validate_user_signup( $user->user_login, $user->user_email ); - if ( is_wp_error( $result['errors'] ) && ! empty( $result['errors']->errors ) ) { + if ( ! empty( $result['errors']->errors ) ) { WP_CLI::error( $result['errors'] ); } $user_id = wpmu_create_user( $user->user_login, $user->user_pass, $user->user_email ); @@ -465,7 +480,7 @@ public function create( $args, $assoc_args ) { } if ( Utils\get_flag_value( $assoc_args, 'porcelain' ) ) { - WP_CLI::line( $user_id ); + WP_CLI::line( (string) $user_id ); } else { WP_CLI::success( "Created user {$user_id}." ); if ( isset( $generated_pass ) ) { @@ -637,7 +652,7 @@ public function generate( $args, $assoc_args ) { ] ); - if ( false === $role ) { + if ( false === $role && ! is_wp_error( $user_id ) ) { delete_user_option( $user_id, 'capabilities' ); delete_user_option( $user_id, 'user_level' ); } @@ -645,7 +660,10 @@ public function generate( $args, $assoc_args ) { if ( 'progress' === $format ) { $notify->tick(); } elseif ( 'ids' === $format ) { - echo $user_id; + if ( ! is_wp_error( $user_id ) ) { + echo $user_id; + } + if ( $index < $limit - 1 ) { echo ' '; } @@ -710,7 +728,7 @@ public function exists( $args ) { public function set_role( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); - $role = Utils\get_flag_value( $args, 1, get_option( 'default_role' ) ); + $role = $args[1] ?? get_option( 'default_role' ); self::validate_role( $role ); @@ -881,6 +899,9 @@ public function add_cap( $args, $assoc_args ) { * @subcommand remove-cap */ public function remove_cap( $args, $assoc_args ) { + /** + * @var \WP_User $user + */ $user = $this->fetcher->get_check( $args[0] ); if ( $user ) { $cap = $args[1]; @@ -966,7 +987,9 @@ public function list_caps( $args, $assoc_args ) { } break; case 'user': - // Get the user's capabilities + /** + * @var array $user_capabilities + */ $user_capabilities = get_user_meta( $user->ID, 'wp_capabilities', true ); // Loop through each capability and only return the non-inherited ones @@ -977,7 +1000,9 @@ public function list_caps( $args, $assoc_args ) { } break; case 'role': - // Get the user's capabilities + /** + * @var array $user_capabilities + */ $user_capabilities = get_user_meta( $user->ID, 'wp_capabilities', true ); // Loop through each capability and only return the inherited ones (including the role name) @@ -1076,6 +1101,10 @@ public function import_csv( $args, $assoc_args ) { $file_object->setFlags( SplFileObject::READ_CSV ); $csv_data = []; $indexes = []; + + /** + * @var string[] $line + */ foreach ( $file_object as $line ) { if ( empty( $line[0] ) ) { continue; @@ -1086,6 +1115,11 @@ public function import_csv( $args, $assoc_args ) { continue; } + /** + * @var array $data + */ + $data = []; + foreach ( $indexes as $n => $key ) { $data[ $key ] = $line[ $n ]; } @@ -1096,6 +1130,9 @@ public function import_csv( $args, $assoc_args ) { $csv_data = new CsvIterator( $filename ); } + /** + * @var array{ID: string, role: string, roles: string, user_pass: string, user_registered: string, display_name: string|false, user_login: string, user_email: string} $new_user + */ foreach ( $csv_data as $new_user ) { $defaults = [ 'role' => get_option( 'default_role' ), @@ -1151,13 +1188,18 @@ public function import_csv( $args, $assoc_args ) { WP_CLI::log( "{$existing_user->user_login} added as {$new_user['role']}." ); } + if ( is_wp_error( $user_id ) ) { + WP_CLI::warning( $user_id ); + continue; + } + // Create the user } else { unset( $new_user['ID'] ); // Unset else it will just return the ID if ( is_multisite() ) { $result = wpmu_validate_user_signup( $new_user['user_login'], $new_user['user_email'] ); - if ( is_wp_error( $result['errors'] ) && ! empty( $result['errors']->errors ) ) { + if ( ! empty( $result['errors']->errors ) ) { WP_CLI::warning( $result['errors'] ); continue; } @@ -1176,22 +1218,24 @@ public function import_csv( $args, $assoc_args ) { $user_id = wp_insert_user( $new_user ); } + if ( is_wp_error( $user_id ) ) { + WP_CLI::warning( $user_id ); + continue; + } + if ( Utils\get_flag_value( $assoc_args, 'send-email' ) ) { self::wp_new_user_notification( $user_id, $new_user['user_pass'] ); } } - if ( is_wp_error( $user_id ) ) { - WP_CLI::warning( $user_id ); - continue; - - } - if ( false === $new_user['role'] ) { delete_user_option( $user_id, 'capabilities' ); delete_user_option( $user_id, 'user_level' ); } + /** + * @var \WP_User $user + */ $user = get_user_by( 'id', $user_id ); foreach ( $secondary_roles as $secondary_role ) { $user->add_role( $secondary_role ); @@ -1256,7 +1300,7 @@ public function import_csv( $args, $assoc_args ) { */ public function reset_password( $args, $assoc_args ) { $porcelain = Utils\get_flag_value( $assoc_args, 'porcelain' ); - $skip_email = Utils\get_flag_value( $assoc_args, 'skip-email' ); + $skip_email = (bool) Utils\get_flag_value( $assoc_args, 'skip-email' ); $show_new_pass = Utils\get_flag_value( $assoc_args, 'show-password' ); if ( $skip_email ) { @@ -1313,24 +1357,13 @@ private static function validate_role( $role, $warn_user_only = false ) { } /** - * Accommodates three different behaviors for wp_new_user_notification() - * - 4.3.1 and above: expect second argument to be deprecated - * - 4.3: Second argument was repurposed as $notify - * - Below 4.3: Send the password in the notification + * Wrapper around wp_new_user_notification(). * - * @param string $user_id - * @param string $password + * @param string|int $user_id + * @param mixed $password */ public static function wp_new_user_notification( $user_id, $password ) { - if ( Utils\wp_version_compare( '4.3.1', '>=' ) ) { - wp_new_user_notification( $user_id, null, 'both' ); - } elseif ( Utils\wp_version_compare( '4.3', '>=' ) ) { - // phpcs:ignore WordPress.WP.DeprecatedParameters.Wp_new_user_notificationParam2Found -- Only called in valid conditions. - wp_new_user_notification( $user_id, 'both' ); - } else { - // phpcs:ignore WordPress.WP.DeprecatedParameters.Wp_new_user_notificationParam2Found -- Only called in valid conditions. - wp_new_user_notification( $user_id, $password ); - } + wp_new_user_notification( (int) $user_id, null, 'both' ); } /** @@ -1381,6 +1414,9 @@ private function update_msuser_status( $user_ids, $pref, $value ) { WP_CLI::error( 'This is not a multisite installation.' ); } + $action = 'updated'; + $verb = 'update'; + if ( 'spam' === $pref ) { $action = (int) $value ? 'marked as spam' : 'removed from spam'; $verb = (int) $value ? 'spam' : 'unspam'; @@ -1409,6 +1445,10 @@ private function update_msuser_status( $user_ids, $pref, $value ) { } // Make that user's blog as spam too. + + /** + * @phpstan-var UserSite[] $blogs + */ $blogs = (array) get_blogs_of_user( $user_id, true ); foreach ( $blogs as $details ) { // Only mark site as spam if not main site. diff --git a/src/User_Meta_Command.php b/src/User_Meta_Command.php index c5ee8033..ccee72cb 100644 --- a/src/User_Meta_Command.php +++ b/src/User_Meta_Command.php @@ -316,7 +316,7 @@ protected function delete_metadata( $object_id, $meta_key, $meta_value = '' ) { * Replaces user_login value with user ID * user meta is a special case that also supports user_login * - * @param array + * @param array $args * @return array */ private function replace_login_with_user_id( $args ) { diff --git a/src/User_Session_Command.php b/src/User_Session_Command.php index d5b9e195..3949d6f9 100644 --- a/src/User_Session_Command.php +++ b/src/User_Session_Command.php @@ -71,8 +71,8 @@ public function __construct() { */ public function destroy( $args, $assoc_args ) { $user = $this->fetcher->get_check( $args[0] ); - $token = Utils\get_flag_value( $args, 1, null ); - $all = Utils\get_flag_value( $assoc_args, 'all', false ); + $token = $args[1] ?? null; + $all = (bool) Utils\get_flag_value( $assoc_args, 'all', false ); $manager = WP_Session_Tokens::get_instance( $user->ID ); if ( $token && $all ) { @@ -174,6 +174,10 @@ protected function get_all_sessions( WP_Session_Tokens $manager ) { // Make the private session data accessible to WP-CLI $get_sessions = new ReflectionMethod( $manager, 'get_sessions' ); $get_sessions->setAccessible( true ); + + /** + * @var array $sessions + */ $sessions = $get_sessions->invoke( $manager ); array_walk( diff --git a/src/WP_CLI/CommandWithDBObject.php b/src/WP_CLI/CommandWithDBObject.php index cb3215bb..767a0bb2 100644 --- a/src/WP_CLI/CommandWithDBObject.php +++ b/src/WP_CLI/CommandWithDBObject.php @@ -33,9 +33,9 @@ abstract class CommandWithDBObject extends WP_CLI_Command { * Create a given database object. * Exits with status. * - * @param array $args Arguments passed to command. Generally unused. - * @param array $assoc_args Parameters passed to command to be passed to callback. - * @param string $callback Function used to create object. + * @param array $args Arguments passed to command. Generally unused. + * @param array $assoc_args Parameters passed to command to be passed to callback. + * @param callable $callback Function used to create object. */ protected function _create( $args, $assoc_args, $callback ) { unset( $assoc_args[ $this->obj_id_key ] ); @@ -57,9 +57,9 @@ protected function _create( $args, $assoc_args, $callback ) { * Update a given database object. * Exits with status. * - * @param array $args Collection of one or more object ids to update. - * @param array $assoc_args Fields => values to update on each object. - * @param string $callback Function used to update object. + * @param array $args Collection of one or more object ids to update. + * @param array $assoc_args Fields => values to update on each object. + * @param callable $callback Function used to update object. */ protected function _update( $args, $assoc_args, $callback ) { $status = 0; @@ -184,17 +184,4 @@ protected function get_formatter( &$assoc_args ) { } return new Formatter( $assoc_args, $fields, $this->obj_type ); } - - /** - * Given a callback, display the URL for one or more objects. - * - * @param array $args One or more object references. - * @param string $callback Function to get URL for the object. - */ - protected function _url( $args, $callback ) { - foreach ( $args as $obj_id ) { - $object = $this->fetcher->get_check( $obj_id ); - WP_CLI::line( $callback( $object->{$this->obj_id_key} ) ); - } - } } diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index 984f66c1..23cc5baf 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -91,7 +91,7 @@ public function list_( $args, $assoc_args ) { foreach ( $values as $item_value ) { if ( Utils\get_flag_value( $assoc_args, 'unserialize' ) ) { - $item_value = maybe_unserialize( $item_value ); + $item_value = maybe_unserialize( (string) $item_value ); } $items[] = (object) [ @@ -112,8 +112,8 @@ public function list_( $args, $assoc_args ) { function ( $a, $b ) use ( $orderby, $order ) { // Sort array. return 'asc' === $order - ? $a->$orderby > $b->$orderby - : $a->$orderby < $b->$orderby; + ? $a->$orderby <=> $b->$orderby + : $b->$orderby <=> $a->$orderby; } ); @@ -159,7 +159,7 @@ public function get( $args, $assoc_args ) { list( $object_id, $meta_key ) = $args; $object_id = $this->check_object_id( $object_id ); - $single = Utils\get_flag_value( $assoc_args, 'single', true ); + $single = (bool) Utils\get_flag_value( $assoc_args, 'single', true ); $value = $this->get_metadata( $object_id, $meta_key, $single ); @@ -504,6 +504,8 @@ protected function update_metadata( $object_id, $meta_key, $meta_value, $prev_va * specified. * * @return mixed Single metadata value, or array of values. + * + * @phpstan-return ($single is true ? string : array) */ protected function get_metadata( $object_id, $meta_key = '', $single = false ) { return get_metadata( $this->meta_type, $object_id, $meta_key, $single ); @@ -545,7 +547,7 @@ private function get_fields() { /** * Check that the object ID exists * - * @param int + * @param int $object_id */ protected function check_object_id( $object_id ) { // Needs to be set in subclass diff --git a/src/WP_CLI/CommandWithTerms.php b/src/WP_CLI/CommandWithTerms.php index 1fa4562f..0e4f9c4d 100644 --- a/src/WP_CLI/CommandWithTerms.php +++ b/src/WP_CLI/CommandWithTerms.php @@ -103,6 +103,9 @@ public function list_( $args, $assoc_args ) { $taxonomy_args['fields'] = 'ids'; } + /** + * @var \WP_Term[] $items + */ $items = wp_get_object_terms( $object_id, $taxonomy_names, $taxonomy_args ); $formatter = $this->get_formatter( $assoc_args ); @@ -145,12 +148,15 @@ public function remove( $args, $assoc_args ) { $this->taxonomy_exists( $taxonomy ); + /** + * @var string|null $field + */ $field = Utils\get_flag_value( $assoc_args, 'by' ); if ( $field ) { $terms = $this->prepare_terms( $field, $terms, $taxonomy ); } - if ( Utils\get_flag_value( $assoc_args, 'all' ) ) { + if ( (bool) Utils\get_flag_value( $assoc_args, 'all' ) ) { // No need to specify terms while removing all terms. if ( $terms ) { @@ -164,7 +170,12 @@ public function remove( $args, $assoc_args ) { if ( 'category' === $taxonomy ) { // Set default category to post. - $default_category = (int) get_option( 'default_category' ); + + /** + * @var string $default_category + */ + $default_category = get_option( 'default_category' ); + $default_category = (int) $default_category; $default_category = ( ! empty( $default_category ) ) ? $default_category : 1; $default_category = wp_set_object_terms( $object_id, [ $default_category ], $taxonomy, true ); @@ -235,6 +246,9 @@ public function add( $args, $assoc_args ) { $this->taxonomy_exists( $taxonomy ); + /** + * @var string|null $field + */ $field = Utils\get_flag_value( $assoc_args, 'by' ); if ( $field ) { $terms = $this->prepare_terms( $field, $terms, $taxonomy ); @@ -283,6 +297,9 @@ public function set( $args, $assoc_args ) { $this->taxonomy_exists( $taxonomy ); + /** + * @var string|null $field + */ $field = Utils\get_flag_value( $assoc_args, 'by' ); if ( $field ) { $terms = $this->prepare_terms( $field, $terms, $taxonomy );