From bb0e04b44c8dcfff98e21e41cd5ad3fe9bb17801 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 11:12:20 +0000 Subject: [PATCH 1/3] Document all accepted fields for the catch-all write commands `user update`, `post create`, `post update`, `comment create` and `comment update` accept `--=`, and their documented field lists have drifted from what core actually accepts. `comment update` documented no fields at all. Fill the lists in from the wp_insert_user(), wp_insert_post() and wp_update_comment() docblocks in core: - user update: syntax_highlighting, comment_shortcuts, admin_color, use_ssl, user_activation_key, spam, show_admin_bar_front, locale, meta_input - post create: page_template, import_id - post update: page_template - comment create / comment update: the full column set, including the mixed-case comment_author_IP and comment_post_ID Two related changes: - `user application-password update` advertised `--=` while documenting that only `name` is supported. Core's WP_Application_Passwords::update_application_password() only ever reads `$update['name']`, so declare that parameter and drop the catch-all. The command is then validated without depending on anything else. - `--meta_input` and `--comment_meta` take arrays, so run them through Utils\parse_shell_arrays() the way Post_Command::update() already does. Without it a JSON value reaches core as a string. The new parameters have no effect on validation on their own, since the catch-all still suppresses it. They are what gives typo detection something accurate to match against. Refs https://github.com/wp-cli/wp-cli/issues/5286 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014SSZzqMJRDTiLiDxQEPYcL --- src/Comment_Command.php | 95 +++++++++++++++++++++++ src/Post_Command.php | 10 +++ src/User_Application_Password_Command.php | 4 +- src/User_Command.php | 32 ++++++++ 4 files changed, 139 insertions(+), 2 deletions(-) diff --git a/src/Comment_Command.php b/src/Comment_Command.php index 3b1578017..384af1fe9 100644 --- a/src/Comment_Command.php +++ b/src/Comment_Command.php @@ -80,6 +80,51 @@ public function __construct() { * * ## OPTIONS * + * [--comment_agent=] + * : The HTTP user agent of the comment author. + * + * [--comment_approved=] + * : Whether the comment has been approved. Default 1. + * + * [--comment_author=] + * : The name of the author of the comment. + * + * [--comment_author_email=] + * : The email address of the comment author. + * + * [--comment_author_IP=] + * : The IP address of the comment author. + * + * [--comment_author_url=] + * : The URL address of the comment author. + * + * [--comment_content=] + * : The content of the comment. + * + * [--comment_date=] + * : The date the comment was submitted. + * + * [--comment_date_gmt=] + * : The date the comment was submitted in the GMT timezone. + * + * [--comment_karma=] + * : The karma of the comment. Default 0. + * + * [--comment_parent=] + * : ID of this comment's parent, if any. Default 0. + * + * [--comment_post_ID=] + * : ID of the post that relates to the comment, if any. + * + * [--comment_type=] + * : Comment type. Default 'comment'. + * + * [--comment_meta=] + * : Array in JSON format of comment meta values keyed by their comment meta key. + * + * [--user_id=] + * : ID of the user who submitted the comment. Default 0. + * * [--=] * : Associative args for the new comment. See wp_insert_comment(). * @@ -100,6 +145,8 @@ public function __construct() { * @param array $assoc_args Associative arguments. */ public function create( $args, $assoc_args ) { + $assoc_args = Utils\parse_shell_arrays( $assoc_args, [ 'comment_meta' ] ); + $assoc_args = wp_slash( $assoc_args ); parent::_create( $args, @@ -137,6 +184,52 @@ function ( $params ) { * ... * : One or more IDs of comments to update. * + * [--comment_agent=] + * : The HTTP user agent of the comment author. + * + * [--comment_approved=] + * : Whether the comment has been approved. + * + * [--comment_author=] + * : The name of the author of the comment. + * + * [--comment_author_email=] + * : The email address of the comment author. + * + * [--comment_author_IP=] + * : The IP address of the comment author. + * + * [--comment_author_url=] + * : The URL address of the comment author. + * + * [--comment_content=] + * : The content of the comment. + * + * [--comment_date=] + * : The date the comment was submitted. + * + * [--comment_date_gmt=] + * : The date the comment was submitted in the GMT timezone. Note that + * wp_update_comment() always recalculates this from 'comment_date'. + * + * [--comment_karma=] + * : The karma of the comment. + * + * [--comment_parent=] + * : ID of this comment's parent, if any. + * + * [--comment_post_ID=] + * : ID of the post that relates to the comment, if any. + * + * [--comment_type=] + * : Comment type. + * + * [--comment_meta=] + * : Array in JSON format of comment meta values keyed by their comment meta key. + * + * [--user_id=] + * : ID of the user who submitted the comment. + * * --= * : One or more fields to update. See wp_update_comment(). * @@ -150,6 +243,8 @@ function ( $params ) { * @param array $assoc_args Associative arguments. */ public function update( $args, $assoc_args ) { + $assoc_args = Utils\parse_shell_arrays( $assoc_args, [ 'comment_meta' ] ); + $assoc_args = wp_slash( $assoc_args ); parent::_update( $args, diff --git a/src/Post_Command.php b/src/Post_Command.php index 5e10c546b..fd472fbea 100644 --- a/src/Post_Command.php +++ b/src/Post_Command.php @@ -127,6 +127,13 @@ public function __construct() { * [--meta_input=] * : Array in JSON format of post meta values keyed by their post meta key. Default empty. * + * [--page_template=] + * : Page template to use. + * + * [--import_id=] + * : The post ID to be used when inserting a new post. If specified, must not + * match any existing post ID. + * * [] * : Read post content from . If this value is present, the * `--post_content` argument will be ignored. @@ -338,6 +345,9 @@ function ( $params ) { * [--meta_input=] * : Array in JSON format of post meta values keyed by their post meta key. Default empty. * + * [--page_template=] + * : Page template to use. + * * [] * : Read post content from . If this value is present, the * `--post_content` argument will be ignored. diff --git a/src/User_Application_Password_Command.php b/src/User_Application_Password_Command.php index 0dffef714..b28e002ba 100644 --- a/src/User_Application_Password_Command.php +++ b/src/User_Application_Password_Command.php @@ -357,8 +357,8 @@ public function create( $args, $assoc_args ) { * * : The universally unique ID of the application password. * - * [--=] - * : Update the with a new . Currently supported fields: name. + * [--name=] + * : The new name of the application password. * * ## EXAMPLES * diff --git a/src/User_Command.php b/src/User_Command.php index 59d6f3d34..a508059ad 100644 --- a/src/User_Command.php +++ b/src/User_Command.php @@ -538,12 +538,42 @@ public function create( $args, $assoc_args ) { * [--rich_editing=] * : A string for whether to enable the rich editor or not. False if not empty. * + * [--syntax_highlighting=] + * : Whether to enable the rich code editor for the user. Accepts 'true' or + * 'false' as a string literal, not boolean. + * + * [--comment_shortcuts=] + * : Whether to enable comment moderation keyboard shortcuts for the user. + * Accepts 'true' or 'false' as a string literal, not boolean. + * + * [--admin_color=] + * : Admin color scheme for the user. + * + * [--use_ssl=] + * : Whether the user should always access the admin over https. + * * [--user_registered=] * : The date the user registered. * + * [--user_activation_key=] + * : Password reset key. + * + * [--spam=] + * : Multisite only. Whether the user is marked as spam. + * + * [--show_admin_bar_front=] + * : Whether to display the admin bar for the user on the site's front end. + * Accepts 'true' or 'false' as a string literal, not boolean. + * * [--role=] * : A string used to set the user's role. * + * [--locale=] + * : The user's locale. + * + * [--meta_input=] + * : Array in JSON format of user meta values keyed by their user meta key. + * * --= * : One or more fields to update. For accepted fields, see wp_update_user(). * @@ -584,6 +614,8 @@ public function update( $args, $assoc_args ) { add_filter( 'send_password_change_email', '__return_false' ); } + $assoc_args = Utils\parse_shell_arrays( $assoc_args, [ 'meta_input' ] ); + $assoc_args = wp_slash( $assoc_args ); parent::_update( $user_ids, $assoc_args, 'wp_update_user' ); From 72daaa2ac96b9502036c12da5e4f9a4055552cfb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 13:38:10 +0000 Subject: [PATCH 2/3] Narrow the comment_meta argument before parsing it PHPStan reported two errors on the calls added in the previous commit: Parameter #1 $assoc_args of function WP_CLI\Utils\parse_shell_arrays expects array, array given. Utils\parse_shell_arrays() is annotated for string values throughout, while Comment_Command's methods correctly declare their arguments as array - a flag arrives as true, not as a string. Rather than weaken that annotation, hand the function only the one key it needs to look at, and only when it holds a string. That is not a workaround for the type checker: is_json() rejects anything that is not a string, so a non-string value was never going to be parsed either way. Post_Command makes the same call without complaint only because its $assoc_args parameters carry no type at all, and missingType.parameter is in the ignore list. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014SSZzqMJRDTiLiDxQEPYcL --- src/Comment_Command.php | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Comment_Command.php b/src/Comment_Command.php index 384af1fe9..833fb5466 100644 --- a/src/Comment_Command.php +++ b/src/Comment_Command.php @@ -75,6 +75,32 @@ public function __construct() { $this->fetcher = new CommentFetcher(); } + /** + * Decodes the `--comment_meta` argument from its JSON representation. + * + * Utils\parse_shell_arrays() is typed for string values throughout, while + * comment arguments are not, so narrow to the single key it needs to see. + * It only ever acts on strings anyway, since is_json() rejects everything + * else. + * + * @param array $assoc_args Associative arguments. + * @return array Associative arguments, with comment_meta decoded. + */ + private static function parse_comment_meta( $assoc_args ) { + if ( ! isset( $assoc_args['comment_meta'] ) || ! is_string( $assoc_args['comment_meta'] ) ) { + return $assoc_args; + } + + $parsed = Utils\parse_shell_arrays( + [ 'comment_meta' => $assoc_args['comment_meta'] ], + [ 'comment_meta' ] + ); + + $assoc_args['comment_meta'] = $parsed['comment_meta']; + + return $assoc_args; + } + /** * Creates a new comment. * @@ -145,7 +171,7 @@ public function __construct() { * @param array $assoc_args Associative arguments. */ public function create( $args, $assoc_args ) { - $assoc_args = Utils\parse_shell_arrays( $assoc_args, [ 'comment_meta' ] ); + $assoc_args = self::parse_comment_meta( $assoc_args ); $assoc_args = wp_slash( $assoc_args ); parent::_create( @@ -243,7 +269,7 @@ function ( $params ) { * @param array $assoc_args Associative arguments. */ public function update( $args, $assoc_args ) { - $assoc_args = Utils\parse_shell_arrays( $assoc_args, [ 'comment_meta' ] ); + $assoc_args = self::parse_comment_meta( $assoc_args ); $assoc_args = wp_slash( $assoc_args ); parent::_update( From 54c2ae5980f9ce73013dca796e3b0c4bd78aa40c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 15:23:02 +0000 Subject: [PATCH 3/3] Drop the comment_meta workaround now that upstream is fixed wp-cli/wp-cli#6393 corrected parse_shell_arrays()'s $assoc_args parameter to array, which is what it always accepted. The local wrapper existed only to work around the previous annotation, so revert to the plain call and let all four call sites in this repo look the same again. This does not need to wait for a wp-cli release: composer.json sets minimum-stability to dev and requires ^3.0, which resolves to dev-main. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014SSZzqMJRDTiLiDxQEPYcL --- src/Comment_Command.php | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/Comment_Command.php b/src/Comment_Command.php index 833fb5466..384af1fe9 100644 --- a/src/Comment_Command.php +++ b/src/Comment_Command.php @@ -75,32 +75,6 @@ public function __construct() { $this->fetcher = new CommentFetcher(); } - /** - * Decodes the `--comment_meta` argument from its JSON representation. - * - * Utils\parse_shell_arrays() is typed for string values throughout, while - * comment arguments are not, so narrow to the single key it needs to see. - * It only ever acts on strings anyway, since is_json() rejects everything - * else. - * - * @param array $assoc_args Associative arguments. - * @return array Associative arguments, with comment_meta decoded. - */ - private static function parse_comment_meta( $assoc_args ) { - if ( ! isset( $assoc_args['comment_meta'] ) || ! is_string( $assoc_args['comment_meta'] ) ) { - return $assoc_args; - } - - $parsed = Utils\parse_shell_arrays( - [ 'comment_meta' => $assoc_args['comment_meta'] ], - [ 'comment_meta' ] - ); - - $assoc_args['comment_meta'] = $parsed['comment_meta']; - - return $assoc_args; - } - /** * Creates a new comment. * @@ -171,7 +145,7 @@ private static function parse_comment_meta( $assoc_args ) { * @param array $assoc_args Associative arguments. */ public function create( $args, $assoc_args ) { - $assoc_args = self::parse_comment_meta( $assoc_args ); + $assoc_args = Utils\parse_shell_arrays( $assoc_args, [ 'comment_meta' ] ); $assoc_args = wp_slash( $assoc_args ); parent::_create( @@ -269,7 +243,7 @@ function ( $params ) { * @param array $assoc_args Associative arguments. */ public function update( $args, $assoc_args ) { - $assoc_args = self::parse_comment_meta( $assoc_args ); + $assoc_args = Utils\parse_shell_arrays( $assoc_args, [ 'comment_meta' ] ); $assoc_args = wp_slash( $assoc_args ); parent::_update(