Adding custom commands to WP-CLI

WP-CLI allows you to add your own command. To achieve that, just write your command handler and use add_command() to register it to WP-CLI. In the following content, you’ll get to know how to add a single custom command or a group of custom commands to WP-CLI.

Example 1: Add a single custom WP-CLI command

If you are intending to add just a single simple command, you may just write a function handler. If you need to add a group of commands, see the second example.

Step 1: Write your custom command handler

The command handler accepts two parameters. Both of them are the parameters passed to the command.

  • $args, an array of positional arguments that does not start with --.
  • $assoc_args, an array of associative arguments that starts with --.

For example the command wp post update.

$ wp post update 26 --post_name=wp-cli-command-examples --post_category=25,33

In the above example, the values of $args and $assoc_args will be:

$args = array( "id" );
$argv = array(
    "post_name" => "wp-cli-command-exmples",
    "post_category" => "25,33"
);

Note:

In the above example, the value of post_category parameter is a string instead of an array. It is up to the handler to parse the value to an array.

If you want to update a post’s author, you need to pass an user ID to wp post update command instead of the user name. Therefore we implement a custom command that allows using user name.

In the example we name the custom command wp update-post-author. You may write it in a single file or just write it in your theme’s function.php file. Here we create a new file named wpc-cli-update-post-author.php in the current theme directory.

wp-cli-update-post-author.php:

/**
 * WP-CLI custom command: Update the author of a post
 * Syntax: wp update-post-author <id> <author name|author id>
 * Examples:
 * 1. Update author by name for post 123: wp update-post-author 123 john
 * 2. Update author by id for post 123:   wp udpate-post-author 123 2
 */
function wp_cli_update_post_author( $args, $assoc_args ) {

    if ( empty( $args ) || count( $args ) < 2 ) {
         WP_CLI::error( 'ID or post author is not specified.' );
    }

    // Get post_author ID if it is a name
    $post_author = $args[1];
    $post_author_id;
    if ( ! is_numeric( $post_author ) ) {
        $post_author_id = username_exists( $post_author );
        if ( $post_author_id === false ) {
            WP_CLI::error( "Post author '$post_author' not exist." );
        }
    } else {
        $post_author_id = intval( $post_author );
    }

    $params = array (
        'ID' => intval( $args[0] ),
        'post_author' => $post_author_id
    );

    $response = wp_update_post( $params, true );
    if ( is_wp_error( $response ) ) {
        WP_CLI::error( $response->get_error_message() );
    } else {
        WP_CLI::success( 'Updated!' );
    }
}

WP-CLI provides some functions for outputting to the command interface from the handler.

Note:

Make sure your command name in unique.

Step 2: Register your custom command to WP-CLI

Registering a command is simple, just add below code to your theme’s function.php .

if ( defined( 'WP_CLI' ) && WP_CLI ) {
    wp_require_once ( "wp-cli-update-post-author.php" );
    WP_CLI::add_command( 'update-post-author', 'wp_cli_update_post_author' );
}

Step 3: Test your custom command

Make sure your WordPress is started. Below is the output when running wp get-post-cats.

# Get the original author
$ wp post get 123 --field=post_author
1

$ wp update-post-author 123 john
Success: Updated!

# Check the result
$ wp post get 123 --field=post_author
2

Note:

If you gets some notice information besides the normal output, see how to supress WP-CLI notices.

Example 2: Add a group of custom WP-CLI commands

The process of adding a custom command with multiple subcommands is similar to that adding one. The difference is you need to extending WP_CLI_Command class and implement subcommands in it. WP_CLI::add_command() will automatically infer those subcommands inside it.

In the below example, we implement a command named wp post-category,it has two sub commands wp post-category add and wp post-category remove which adds or removes categories of a post.

<?php
/**
 * WP-CLI custom command: post-category
 * Subcommand:
 * - add
 * - remove
 */
class Post_Category_Command extends WP_CLI_Command {

    private function add_or_remove_category( $action, $args, $assoc_args ) {
        if ( empty( $args ) || count( $args ) < 2 ) {
            WP_CLI::error( 'ID or author is not specified.' );
        }

        $id = intval( $args[0] );
        $original_cats = get_the_category( $id );
        $original_cat_ids = array();
        foreach( $original_cats as $c ) {
            $original_cat_ids[$c->term_id] = $c->name;
        }

        array_shift( $args );
        $cats = $args;
        $updated_cats = array();
        foreach( $cats as $cat ) {
            $cat_id = null;
            if ( ! is_numeric( $cat ) ) {
                $cat_id = category_exists( $cat );
                if ( $cat_id === false ) {
                    WP_CLI::warning( "No such post category '$cat'." );
                    continue;
                }
            } else {
                $cat_id = intval( $cat );
            }

            if ( $action === 'add' ) {
                if ( ! array_key_exists( $cat_id, $original_cat_ids ) ) {
                    $original_cat_ids[$cat_id] = $cat_id;
                    $updated_cats[] = $cat;
                } else {
                    WP_CLI::warning( "Post category '$cat' already exists for the post." );
                }
            } else {
                if ( array_key_exists( $cat_id, $original_cat_ids ) ) {
                    unset( $original_cat_ids[ $cat_id ] );
                    $updated_cats[] = $cat;
                } else {
                    WP_CLI::warning( "Post category '$cat' does not exist for the post." );
                }
            }
        }

        $params = array (
            'ID' => $id,
            'post_category' => array_keys( $original_cat_ids )
        );

        $response = wp_update_post( $params, true );
        if ( is_wp_error( $response ) ) {
            WP_CLI::error( $response->get_error_message() );
        } else {
            $msg = ( $action === 'add' ) ? 'Added categories: ' : 'Removed categories: ';
            WP_CLI::success( $msg . implode( ',', $updated_cats ) );
        }
    }

    /**
     * Subcommand: Add one or multiple categories to a post.
     * Syntax: post-category add <id> <category_name|category_id> [category_name|category_id...]
     * Examples:
     * 1. Add categories of php and wordpress to post 123:
     *    "wp post-category add 123 php wordpress"
     * 2. Add category 2 to post 123:
     *    "wp post-category add 123 2"
     */
    public function add( $args, $assoc_args ) {
        $this->add_or_remove_category( 'add', $args, $assoc_args );
    }

    /**
     * Subcommand: Remove one or multiple categories from a post.
     * Syntax: post-category add <id> <category_name|category_id> [category_name|category_id...]
     * Examples:
     * 1. Remove category wordpress from post 123: "wp post-category remove 123 wordpress"
     * 2. Remove category 2 from post 123: "wp post-category remove 123 2"
     */
    public function remove( $args, $assoc_args ) {
        $this->add_or_remove_category( 'remove', $args, $assoc_args );
    }
}

Add below code to your theme’s function.php.

if ( defined( 'WP_CLI' ) && WP_CLI ) {
    WP_CLI::add_command( 'post-category', 'Post_Category_Command' );
}

Now you are ready to run these custom commands like below:

# Add category 'php' and 'wordpress' for post with ID '123'.
$ wp post-category add 123 php wordpress
Success: Removed categories: php

# Remove category 'php' for post with ID '123'.
$ wp post-category remove 123 php
Success: Removed categories: php

# Remove category 'php' for post with ID '123' again.
# Because the post has not belong to 'php', it gives warning for that information.
$ wp post-category remove 123 php
Warning: Post category 'php' does not exist for the post.
Success: Removed categories: