Add ID range notation to commands that accept one or more IDs#593
Open
Add ID range notation to commands that accept one or more IDs#593
Conversation
… 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
This comment was marked as resolved.
This comment was marked as resolved.
Codecov Report❌ Patch coverage is
📢 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.
This comment was marked as resolved.
This comment was marked as resolved.
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.
This comment was marked as resolved.
Contributor
There was a problem hiding this comment.
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\ExpandsIdRangestrait 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, anduserworkflows (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' ] ); | ||
|
|
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Commands accepting multiple IDs now support range notation, allowing bulk operations without shell expansion limits or spawning multiple WP-CLI processes.
Supported syntax
15-3534--35Ranges can be mixed with individual IDs freely:
wp post delete 3 7 12-24 19 34- --forceOnly existing IDs within a range are processed — no warnings for gaps.
Implementation
src/WP_CLI/ExpandsIdRanges.php— New trait withexpand_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 (extendsWP_CLI_Command, notCommandWithDBObject). Range expansion is skipped when--by=slugis passed.Per-command DB helpers — Each command gets a private
get_*_ids_in_range(int $start, ?int $end): arraymethod querying its table via$wpdb->prepare()withBETWEEN/>=clauses.Commands updated
post delete,post updatecomment delete,trash,untrash,spam,unspam,approve,unapprove,updateuser delete,update,reset-password,spam,unspamterm deletesite activate,archive,deactivate,mature,unmature,private,public,spam,unspam,unarchiveOriginal 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-35would 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 from1onwards 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 approvecomment deletecomment recountcomment spamcomment trashcomment unapprovecomment unspamcomment untrashcomment updateembed cache clear(*)embed cache trigger(*)media regeneratepost deletepost meta delete(*)post meta update(*)post updatesite activatesite archivesite deactivatesite delete(*)site maturesite privatesite publicsite spamsite unarchivesite unmaturesite unspamterm deleteterm meta delete(*)term meta update(*)term update(*)user add-cap(*)user add-role(*)user deleteuser meta delete(*)user meta update(*)user remove-cap(*)user remove-role(*)user reset-passworduser set-role(*)user spamuser unspamuser updatewidget deletewidget 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
xargsfor 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:
xargsand other shell tools in conjunction with WP-CLI.</comment_new><comment_new>@schlessera
Responding to @danielbachhuber's cons:
Composability is still available, but extended so that you can also compose ranges. So, you can do something like this:
I don't see this as breaking that principle, it is a mere extension to make the syntax more expressive.
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.
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
xargsand don't need to deal with its much more complex syntax.Responding to @johnbillion's questions:
The original use case that triggered this idea is wp-cli/media-command#82
It will actually be more performant than the
xargsright out of the box, because the IDs can be retrieved with 1 query to add them to the loop. So, a range like1-300will entail 1 query and 1 loading of WordPress only.</comment_new><comment_new>@danielbachhuber
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.