WordPress plugin development often involves repetitive tasks like database cleanup, bulk content updates, or cache management. While the WordPress admin dashboard is user-friendly, it’s inefficient for handling large-scale operations. WP-CLI (WordPress Command Line Interface) empowers developers to automate these tasks, streamline workflows, and enhance scalability through custom commands.
This guide explores how to leverage WP-CLI for plugin development, focusing on creating custom commands to perform maintenance tasks. You’ll learn to build, test, and deploy commands that save time, reduce errors, and integrate seamlessly into your development workflow.
WP-CLI is a command-line tool that enables developers to interact with WordPress programmatically. Here’s why it’s indispensable:
Example: Manually deleting 10,000 spam comments via the dashboard is tedious. A custom WP-CLI command can accomplish this in seconds.

Let’s build a plugin that adds a command to delete expired transients (temporary cached data).
Create a directory wp-content/plugins/my-cli-tool with:
In my-cli-tool.php:
// PHP Code
<?php
/**
* Plugin Name: My CLI Tool
* Description: Custom WP-CLI commands for maintenance tasks.
*/
if (defined('WP_CLI') && WP_CLI) {
require_once __DIR__ . '/includes/class-transient-cleaner.php';
WP_CLI::add_command('my-cli-tool', 'Transient_Cleaner_Command');
}
Explanation:
In includes/class-transient-cleaner.php:
<?php
class Transient_Cleaner_Command {
/**
* Deletes expired transients.
*
* ## OPTIONS
* [--dry-run]
* : Preview the transients to delete without executing.
*
* ## EXAMPLES
* wp my-cli-tool delete_expired_transients --dry-run
* wp my-cli-tool delete_expired_transients
*/
public function delete_expired_transients($args, $assoc_args) {
global $wpdb;
$dry_run = isset($assoc_args['dry-run']) ? true : false;
// Fetch expired transients
$time = time();
$expired = $wpdb->get_col(
"SELECT option_name FROM $wpdb->options
WHERE option_name LIKE '_transient_timeout_%'
AND option_value < $time"
);
if (empty($expired)) {
WP_CLI::success('No expired transients found.');
return;
}
$count = 0;
foreach ($expired as $transient) {
$transient = str_replace('_transient_timeout_', '', $transient);
if (!$dry_run) {
delete_transient($transient);
}
WP_CLI::log("Deleted transient: $transient");
$count++;
}
WP_CLI::success(
sprintf('Deleted %d expired transients%s.', $count, $dry_run ? ' (dry run)' : '')
);
}
}
Key Components:
Usage:
// bash code
# Dry run to preview transients
wp my-cli-tool delete_expired_transients --dry-run
# Execute deletion
wp my-cli-tool delete_expired_transients
Accept user input via positional and associative arguments:
// PHP code
public function update_posts($args, $assoc_args) {
$post_type = isset($assoc_args['post-type']) ? $assoc_args['post-type'] : 'post';
$batch_size = isset($assoc_args['batch']) ? (int)$assoc_args['batch'] : 100;
WP_CLI::log("Updating $post_type posts in batches of $batch_size...");
}
Run with:
// bash code
wp my-cli-tool update_posts --post-type=product --batch=50
Use WP_CLI::confirm() for user confirmation:
// PHP code
public function reset_data() {
WP_CLI::confirm('Are you sure you want to reset all plugin data?');
// Proceed with reset logic
}
Track long-running tasks:
// PHP Code
public function process_posts() {
$posts = get_posts(['posts_per_page' => -1]);
$progress = WP_CLI\Utils\make_progress_bar('Processing posts', count($posts));
foreach ($posts as $post) {
// Process each post
$progress->tick();
}
$progress->finish();
}
Enhance readability with colored text:
// PHP Code
WP_CLI::success(WP_CLI::colorize('%GSuccess:%n Data imported.'));
Problem: A WooCommerce store with 50,000 outdated orders needed cleanup. Manual deletion via the dashboard was impractical.
Solution: A custom WP-CLI command to delete orders older than 30 days.
Command Code:
// PHP Code
class Woo_Cleanup_Command {
/**
* Delete old WooCommerce orders.
*
* ## OPTIONS
* --days=<days>
* : Delete orders older than X days.
*
* ## EXAMPLES
* wp woo-cleanup delete_old_orders --days=30
*/
public function delete_old_orders($args, $assoc_args) {
$days = isset($assoc_args['days']) ? (int)$assoc_args['days'] : 30;
$date = date('Y-m-d', strtotime("-$days days"));
$orders = wc_get_orders([
'date_created' => '<' . $date,
'limit' => -1,
'status' => 'completed',
]);
if (empty($orders)) {
WP_CLI::success('No orders found.');
return;
}
$progress = WP_CLI\Utils\make_progress_bar('Deleting orders', count($orders));
foreach ($orders as $order) {
$order->delete(true); // Force delete
$progress->tick();
}
$progress->finish();
WP_CLI::success(sprintf('Deleted %d orders older than %d days.', count($orders), $days));
}
}
Impact:
Use Composer to integrate libraries like wp-cli/php-cli-tools for advanced utilities:
// bash code
composer require wp-cli/php-cli-tools
Example: Format data as tables:
use WP_CLI\Utils\format_items;
public function list_users() {
$users = get_users();
$data = [];
foreach ($users as $user) {
$data[] = [
'ID' => $user->ID,
'Username' => $user->user_login,
'Email' => $user->user_email,
];
}
format_items('table', $data, ['ID', 'Username', 'Email']);
}
Security Considerations
// PHP Code
public function reset_settings() {
if (!current_user_can('manage_options')) {
WP_CLI::error('Permission denied.');
}
// Proceed with reset
}
// PHP Code
$post_id = isset($assoc_args['post_id']) ? absint($assoc_args['post_id']) : 0;
public function import_data($file) {
try {
if (!file_exists($file)) {
throw new Exception('File not found.');
// Import logic } catch (Exception $e) { WP_CLI::error($e->getMessage());
} }
---
### **Best Practices for WP-CLI Commands**
1. **Idempotency**: Ensure commands can be run multiple times without adverse effects.
2. **Documentation**: Provide clear usage instructions in the command's DocBlock.
3. **Error Handling**: Implement robust error handling to prevent crashes.
4. **Performance**: Optimize queries and avoid loading unnecessary data.
---
### **Integrating WP-CLI into Workflows**
WP-CLI commands can be integrated into deployment scripts or scheduled tasks. For example, you can automate database backups or cache clearing during deployment:
```bash
# Backup database before deployment
wp db export backup.sql
# Clear cache after deployment
wp cache flush
WP-CLI is a powerful tool for WordPress developers. It enables efficient site management through custom commands. By mastering WP-CLI, you can automate maintenance tasks, streamline workflows, and enhance your plugin development process. This guide provides an overview of creating, testing, and deploying WP-CLI commands, along with best practices and real-world applications.
With the knowledge gained, you can now harness the full potential of WP-CLI to improve your development efficiency and tackle complex tasks with ease. Happy coding!

Hassan Tahir wrote this article, drawing on his experience to clarify WordPress concepts and enhance developer understanding. Through his work, he aims to help both beginners and professionals refine their skills and tackle WordPress projects with greater confidence.