Technote

Development workflow Advanced

Series WP-CLI automation recipes Part 6 of 8

Adding your own WP-CLI command to a plugin

Registration is three lines and your docblock becomes the help text. What deserves thought is choosing between log, warning and error — because error stops the run.

When an eval-file script starts wanting more than three arguments, the job has outgrown a script. Promote it to a command and WP-CLI takes over argument parsing, validation, help text and output formatting for you.

Registration is three lines

Put it in a plugin, behind a guard.

if ( defined( 'WP_CLI' ) && WP_CLI ) {
    WP_CLI::add_command( 'acme checklist', 'Acme_CLI_Checklist' );
}

Pass a class and its public methods become subcommands: run() becomes wp acme checklist run, list() becomes wp acme checklist list. If you want no subcommands at all, define __invoke() and nothing else.

Putting it in a plugin rather than the theme matters. A command is an operations tool, and your operations tools should not vanish on the day you swap themes.

Your docblock is the help text

WP-CLI reads each method’s PHPDoc to build both the wp help screen and the argument validation rules. Writing the comment is defining the interface.

/**
 * Run the site diagnostic.
 *
 * ## OPTIONS
 *
 * [--format=<format>]
 * : Output format.
 * ---
 * default: table
 * options:
 *   - table
 *   - json
 * ---
 *
 * ## EXAMPLES
 *
 *     wp acme checklist run --format=json
 *
 * @when after_wp_load
 */
public function run( $args, $assoc_args ) { /* … */ }

$args holds positional arguments and $assoc_args the flags. This is where the restriction from part four disappears — a real command takes -- flags freely, and WP-CLI rejects values outside the declared options before your code ever runs.

Use log, warning and error deliberately

This is the heart of the part. The four differ in where they write and what they do next.

Four functions — only the top one halts the run

The deciding question is always “is there any point continuing from here?”

One item failing inside a loop over 800 records is a warning. Calling error there stops at the first failure and leaves you knowing nothing about the other 799. Conversely, a missing API credential that guarantees every item will fail is an error — leave it as a warning and the same message floods the log 800 times with the real cause buried inside it.

The distinction is a contract rather than a preference. As part eight shows, CI reads exit codes, not prose. A command that ends in error fails the build; a warning only lands in the log. Choosing between them here is really choosing what counts as a reason to block a deploy.

Output formats and confirmation

When the result is tabular, hand it to the utility rather than printing it yourself.

WP_CLIUtilsformat_items( $assoc_args['format'], $rows, [ 'item', 'status', 'note' ] );

That single line gives you table, CSV, JSON and YAML together. The moment --format=json works, your command becomes input for another script — a part in a pipeline rather than a destination.

Anything irreversible deserves a confirmation step.

WP_CLI::confirm( 'Really delete these?', $assoc_args );

Passing $assoc_args through is the trick: it lets --yes skip the prompt, so the guard protects people without standing in the way of automation.

The diagnostic commands we built this way are published with their source on the free tools page, and the reasoning behind them is collected in the development workflow archive.

Next part

Having built a command, you need it to run on a schedule. The next part explains why WordPress’s own cron depends on traffic, and how to move it to system cron.

More on this topic

All technotes

Development workflow Advanced

The design QA checklist to run before release

Most design problems found after release are catchable before it, in order. Here is the pass, in three stages: rules, states and real devices.

Designers 6 min read

Development workflow Advanced

Never deploy without a way back

A rollback is not one button. It is two tracks — code and database — with an order to reverse them in, written down before you deploy.

Designers 9 min read

Development workflow Advanced

Adding your own hooks: extension points instead of edits

Code with no extension points gets forked or edited in place. Where you put do_action and apply_filters — and how many — decides how long that code survives.

Developers 7 min read

₩270,000 · Join the program