How to Create a Custom WP-CLI Command in 5 Minutes
Register a small, namespaced WP-CLI command that reports and optionally deletes old draft posts.
WP-CLI commands are a practical way to turn repeated WordPress maintenance tasks into safe, repeatable terminal workflows. This example creates wp site cleanup-drafts, which lists draft posts older than a date and supports a --dry-run mode before deletion.
Put the file in an active plugin or an automatically loaded MU plugin. WP-CLI loads the command only when the WP_CLI constant is available.
The command
<?php
if ( defined( 'WP_CLI' ) && WP_CLI ) { class My_Site_Cleanup_Drafts_Command { /** * Remove or report old drafts. * * ## OPTIONS * * --before=<date> * : Delete drafts published before this date, for example 2024-01-01. * * [--dry-run] * : Report matching drafts without deleting them. * * ## EXAMPLES * * wp site cleanup-drafts --before=2024-01-01 --dry-run * wp site cleanup-drafts --before=2024-01-01 */ public function __invoke( $args, $assoc_args ) { $before = isset( $assoc_args['before'] ) ? $assoc_args['before'] : '';
if ( ! $before || ! strtotime( $before ) ) { WP_CLI::error( 'Pass a valid --before date, such as 2024-01-01.' ); }
$query = new WP_Query( array( 'post_type' => 'post', 'post_status' => 'draft', 'posts_per_page' => 100, 'fields' => 'ids', 'date_query' => array( array( 'before' => gmdate( 'Y-m-d', strtotime( $before ) ) ), ), ) );
$dry_run = isset( $assoc_args['dry-run'] ); $count = 0;
foreach ( $query->posts as $post_id ) { $title = get_the_title( $post_id );
if ( $dry_run ) { WP_CLI::log( sprintf( '%d: %s', $post_id, $title ) ); } else { wp_delete_post( $post_id, true ); }
$count++; }
$message = $dry_run ? 'drafts would be removed' : 'drafts removed'; WP_CLI::success( sprintf( '%d %s.', $count, $message ) ); } }
WP_CLI::add_command( 'site cleanup-drafts', 'My_Site_Cleanup_Drafts_Command' );}Run it safely
Start with the dry run:
wp site cleanup-drafts --before=2024-01-01 --dry-runReview the IDs and titles. Only then run the command without --dry-run. The example caps the query at 100 records per invocation so a large cleanup does not create one unbounded request; change the query to use pagination if the site has more than 100 matching drafts.
The WP-CLI command cookbook and WP_CLI::add_command() reference cover command registration, arguments, and documentation blocks. For scheduled maintenance that runs without a terminal, see the WP-Cron cleanup snippet.