Skip to content

Add ID range notation to commands that accept one or more IDs#593

Open
Copilot wants to merge 7 commits intomainfrom
copilot/add-ranges-to-commands
Open

Add ID range notation to commands that accept one or more IDs#593
Copilot wants to merge 7 commits intomainfrom
copilot/add-ranges-to-commands

Conversation

Copy link
Contributor

Copilot AI commented Mar 14, 2026

Commands accepting multiple IDs now support range notation, allowing bulk operations without shell expansion limits or spawning multiple WP-CLI processes.

Supported syntax

Pattern Meaning
15-35 All existing IDs from 15 to 35 inclusive
34- All existing IDs from 34 onwards
-35 All existing IDs from 1 through 35

Ranges can be mixed with individual IDs freely: wp post delete 3 7 12-24 19 34- --force

Only existing IDs within a range are processed — no warnings for gaps.

Implementation

  • src/WP_CLI/ExpandsIdRanges.php — New trait with expand_id_ranges(array $args, callable $get_ids_in_range): array. Parses range notation and delegates DB lookup to a command-specific callback. Results are deduplicated.

  • CommandWithDBObject — Uses the trait, making it available to Post, Comment, User, and Site commands.

  • Term_Command — Uses the trait directly (extends WP_CLI_Command, not CommandWithDBObject). Range expansion is skipped when --by=slug is passed.

  • Per-command DB helpers — Each command gets a private get_*_ids_in_range(int $start, ?int $end): array method querying its table via $wpdb->prepare() with BETWEEN/>= clauses.

Commands updated

  • post delete, post update
  • comment delete, trash, untrash, spam, unspam, approve, unapprove, update
  • user delete, update, reset-password, spam, unspam
  • term delete
  • site activate, archive, deactivate, mature, unmature, private, public, spam, unspam, unarchive
Original prompt

This section details on the original issue you should resolve

<issue_title>Add ranges to commands that accept one or more IDs</issue_title>
<issue_description>For commands that accept one or more IDs as positional parameters, I would like to investigate adding ranges and partial ranges as valid IDs.

A range would take the following form: <range start>-<range end>. This would mean that all existing IDs that fall into the range that starts at <range start> and ends at <range end> would be included.

Example: 15-35 would mean all IDs starting from 15 and going up to and including 35 would be processed. Only valid existing IDs within that range would be processed, so that no warnings are being thrown for missing IDs in-between.

A partial range would take one of these forms: <range start>- or -<range-end>. This would mean that either all existing IDs starting from <range start> without any upper bound or all existing IDs from 1 onwards up to and including <range end> would be included.

Ranges and individual IDs can be combined in any way, so you can use something like the following: 3 7 12-24 19 34-.

Commands that could profit from this range notation:

  • comment approve
  • comment delete
  • comment recount
  • comment spam
  • comment trash
  • comment unapprove
  • comment unspam
  • comment untrash
  • comment update
  • embed cache clear (*)
  • embed cache trigger (*)
  • media regenerate
  • post delete
  • post meta delete (*)
  • post meta update (*)
  • post update
  • site activate
  • site archive
  • site deactivate
  • site delete (*)
  • site mature
  • site private
  • site public
  • site spam
  • site unarchive
  • site unmature
  • site unspam
  • term delete
  • term meta delete (*)
  • term meta update (*)
  • term update (*)
  • user add-cap (*)
  • user add-role (*)
  • user delete
  • user meta delete (*)
  • user meta update (*)
  • user remove-cap (*)
  • user remove-role (*)
  • user reset-password
  • user set-role (*)
  • user spam
  • user unspam
  • user update
  • widget delete
  • widget move (*)
  • widget update (*)

(*) => not variadic yet</issue_description>

Comments on the Issue (you are @copilot in this section)

@schlessera @danielbachhuber, @johnbillion Would love your thoughts on the above.

I know that we can already use xargs for doing similar things, but I do think this makes it much easier for people to execute for complex operations, and lets you more easily fine-tune the IDs you're targeting.</comment_new>
<comment_new>@danielbachhuber
I'm 👎 on this for a few reasons:

  1. It violates our philosophy of composability.
  2. It would be new syntax for the user to learn.
  3. It's valuable for a WP-CLI user to learn to use xargs and other shell tools in conjunction with WP-CLI.</comment_new>
    <comment_new>@schlessera
    Responding to @danielbachhuber's cons:
  1. It violates our philosophy of composability.

Composability is still available, but extended so that you can also compose ranges. So, you can do something like this:

wp post delete $(command to figure out start)-$(command to figure out end)

I don't see this as breaking that principle, it is a mere extension to make the syntax more expressive.

  1. It would be new syntax for the user to learn.

Yes, I agree. I don't think this would be a big issue, because a) the syntax is actually far from complex and b) it is purely optional, you can still just use one regular ID without ever noticing something was changed.

  1. It's valuable for a WP-CLI user to learn to use xargs and other shell tools in conjunction with WP-CLI.

I agree with that, but I don't think skipping a valuable performance and expressiveness improvement for that sake is a good move or a service to users. For a lot of users, this syntax change might actually mean they will never even have a need for xargs and don't need to deal with its much more complex syntax.

Responding to @johnbillion's questions:

Does this syntax provide real value? What use cases does this improve? Some example use cases would be good.

The original use case that triggered this idea is wp-cli/media-command#82

Can it be made performant internally? More so than piping in IDs with xargs?

It will actually be more performant than the xargs right out of the box, because the IDs can be retrieved with 1 query to add them to the loop. So, a range like 1-300 will entail 1 query and 1 loading of WordPress only.</comment_new>
<comment_new>@danielbachhuber

Composability is still available, but extended so that you can also compose ranges. So, you can do something like this:

I ...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@github-actions github-actions bot added command:comment-approve Related to 'comment approve' command command:comment-delete Related to 'comment delete' command command:comment-recount Related to 'comment recount' command command:comment-spam Related to 'comment spam' command command:comment-trash Related to 'comment trash' command command:comment-unapprove Related to 'comment unapprove' command command:comment-unspam Related to 'comment unspam' command command:comment-untrash Related to 'comment untrash' command command:comment-update Related to 'comment update' command command:post-delete Related to 'post delete' command labels Mar 14, 2026
Copilot AI and others added 2 commits March 14, 2026 21:53
… commands

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
… in reset_password

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Copilot AI changed the title [WIP] Add ranges to commands that accept one or more IDs Add ID range notation to commands that accept one or more IDs Mar 14, 2026
Copilot AI requested a review from swissspidy March 14, 2026 21:57
@swissspidy

This comment was marked as resolved.

@codecov
Copy link

codecov bot commented Mar 14, 2026

Codecov Report

❌ Patch coverage is 69.44444% with 33 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Site_Command.php 10.00% 9 Missing ⚠️
src/Term_Command.php 67.85% 9 Missing ⚠️
src/WP_CLI/ExpandsIdRanges.php 70.58% 5 Missing ⚠️
src/User_Command.php 84.00% 4 Missing ⚠️
src/Comment_Command.php 82.35% 3 Missing ⚠️
src/Post_Command.php 72.72% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

… to protected

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>

This comment was marked as resolved.

@swissspidy

This comment was marked as resolved.

… and term feature files

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>

This comment was marked as resolved.

@swissspidy swissspidy marked this pull request as ready for review March 15, 2026 09:21
@swissspidy swissspidy requested a review from a team as a code owner March 15, 2026 09:21
Copilot AI review requested due to automatic review settings March 15, 2026 09:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for ID range notation (e.g. 15-35, 34-, -35) to multiple WP-CLI entity commands so users can target large sets of objects without shell expansion limits or running multiple CLI processes.

Changes:

  • Introduces a reusable WP_CLI\ExpandsIdRanges trait to parse range arguments and delegate ID lookups to command-specific callbacks.
  • Wires range expansion into Post, Comment, User, Site (via CommandWithDBObject) and Term (directly) commands, with per-command SQL helpers to fetch IDs in a range.
  • Adds Behat feature coverage for range usage in post, comment, term, and user workflows (partial coverage).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/WP_CLI/ExpandsIdRanges.php Adds generic range parsing + deduplication for positional args.
src/WP_CLI/CommandWithDBObject.php Mixes the trait into the base DB-object command class.
src/Post_Command.php Expands ranges for post update / post delete and adds a DB helper.
src/Comment_Command.php Expands ranges across several comment subcommands and adds a DB helper.
src/User_Command.php Expands ranges for multiple user subcommands and adds a DB helper.
src/Term_Command.php Uses trait directly for term delete (skips when --by=slug).
src/Site_Command.php Expands ranges for site status subcommands via get_sites_ids() and adds a DB helper.
phpcs.xml.dist Excludes the new namespaced trait file from the global-prefix namespace sniff.
features/*.feature Adds new scenarios validating range expansion for a subset of updated commands.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +35 to +47
} elseif ( preg_match( '/^(\d+)-$/', $arg, $matches ) ) {
// Open-ended range: "34-"
$ids = array_merge( $ids, $get_ids_in_range( (int) $matches[1], null ) );
} elseif ( preg_match( '/^-(\d+)$/', $arg, $matches ) ) {
// Lower-bounded range: "-35"
$ids = array_merge( $ids, $get_ids_in_range( 1, (int) $matches[1] ) );
} else {
$ids[] = $arg;
}
}

return array_values( array_unique( $ids ) );
}
Comment on lines 1463 to 1467
return [ $blog_id ];
}

return $args;
return self::expand_id_ranges( $args, [ $this, 'get_site_ids_in_range' ] );
}
Comment on lines 560 to 605
@@ -577,6 +580,7 @@ public function trash( $args, $assoc_args ) {
*/
public function untrash( $args, $assoc_args ) {
$this->check_server_name();
$args = self::expand_id_ranges( $args, [ $this, 'get_comment_ids_in_range' ] );
foreach ( $args as $id ) {
$this->call( $id, __FUNCTION__, 'Untrashed %s.', 'Failed untrashing %s.' );
}
@@ -597,6 +601,7 @@ public function untrash( $args, $assoc_args ) {
* Success: Marked as spam comment 1337.
*/
public function spam( $args, $assoc_args ) {
$args = self::expand_id_ranges( $args, [ $this, 'get_comment_ids_in_range' ] );
foreach ( $args as $id ) {
Comment on lines 562 to +564
public function update( $args, $assoc_args ) {
$args = self::expand_id_ranges( $args, [ $this, 'get_user_ids_in_range' ] );

swissspidy and others added 2 commits March 15, 2026 14:17
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

command:comment-approve Related to 'comment approve' command command:comment-delete Related to 'comment delete' command command:comment-recount Related to 'comment recount' command command:comment-spam Related to 'comment spam' command command:comment-trash Related to 'comment trash' command command:comment-unapprove Related to 'comment unapprove' command command:comment-unspam Related to 'comment unspam' command command:comment-untrash Related to 'comment untrash' command command:comment-update Related to 'comment update' command command:post-delete Related to 'post delete' command

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ranges to commands that accept one or more IDs

3 participants