WP-CLI 手册(英文原版)

WP-CLI 是 WordPress 的命令行工具。本书(英文)介绍它的安装配置、常用命令、扩展包的编写与发布,以及参与开发的方法,适合需要批量管理 WordPress 站点的开发者和运维人员。

Handbook

Here are some helpful guides and resources for using WP-CLI.

Can’t find what you’re looking for? Open an issue to request improvements.

Support

Guides

For users

For developers

References

Contributing

Misc

Guides

Guides

Guides

Guides

Beginner's Guide

So you've heard about WP-CLI and want to try it out, but you're not sure where to start? This guide is for you. No programming experience required!

This guide is designed to be read from start to finish. Each section builds on the previous one, taking you from opening a terminal for the first time all the way to running your first WP-CLI commands and getting help when you need it.

What Is the Command Line?

When you use your computer, you normally interact with it by clicking on things — icons, buttons, menus. This is called a graphical user interface (GUI).

The command line (also called the terminal, shell, or console) is a different way to interact with your computer. Instead of clicking, you type text commands and press Enter to run them. It might look intimidating at first, but it's really just another way to tell your computer what to do.

Opening the Terminal

On macOS:

  1. Press Command (⌘) + Space to open Spotlight Search.
  2. Type Terminal and press Enter.
  3. A window with a black or white background and a blinking cursor will appear. That's the terminal!

On Windows:

  1. Press Windows key + R to open the Run dialog.
  2. Type cmd and press Enter.
  3. Alternatively, search for "Command Prompt" or "PowerShell" in the Start menu.

Tip for Windows users: WP-CLI works best with Git Bash or Windows Subsystem for Linux (WSL). You can download Git Bash from git-scm.com.

On Linux:

  1. Look for "Terminal" in your applications menu, or press Ctrl + Alt + T on many distributions.

Basic Terminal Navigation

Once the terminal is open, here are a few essential commands to know:

Command What it does
pwd Shows the folder you are currently in
ls (macOS/Linux) or dir (Windows) Lists files and folders in the current folder
cd folder-name Moves into a folder named folder-name
cd .. Goes up one folder level
clear Clears the terminal screen

Don't worry — you don't need to memorize all of these right now. You can come back to this table whenever you need a reminder.

What Is WP-CLI?

WP-CLI is a command line interface for WordPress. It lets you manage your WordPress website by typing commands in the terminal, instead of clicking through the WordPress admin dashboard.

Think of it this way: anything you can do in the WordPress admin (installing plugins, creating users, updating settings), you can also do with WP-CLI — and usually much faster.

Why Should I Use WP-CLI?

Here are a few reasons why even non-technical WordPress users find WP-CLI useful:

Installing WP-CLI

For step-by-step installation instructions on macOS, Linux, and Windows, see the Installing guide. It covers system requirements, the recommended Phar download method, and alternative installation options such as Composer and Homebrew.

Running WP-CLI: Inside vs. Outside the WordPress Folder

WP-CLI needs to know which WordPress installation to work with. By default, it looks for WordPress in your current directory (the folder your terminal is currently in).

This means you generally need to cd into your WordPress site's folder before running WP-CLI commands. For example:

cd /var/www/mysite
wp plugin list

If you run wp plugin list from a folder that doesn't contain a WordPress installation, you'll get an error like:

Error: This does not seem to be a WordPress installation.

Where is my WordPress folder?

Running Your First WP-CLI Command

Navigate in your terminal to your WordPress folder (see above). For example:

cd /var/www/mysite

Then try this command:

wp --info

This displays information about your WP-CLI installation. If it works, you're ready to go!

To see the version of WordPress you have installed:

wp core version

To list the plugins on your site:

wp plugin list

Reading WP-CLI Output

When you run a WP-CLI command, it prints a response in the terminal. Here's what the common messages mean:

Getting Help

Built-in help

WP-CLI has built-in documentation for every command. If you get stuck, run wp help to see a list of all available commands:

wp help

To get help on a specific command, add the command name after help. For example:

wp help plugin
wp help plugin install

Community support

If the built-in help isn't enough, these resources can help:

Using WP-CLI on a Remote Server (SSH)

If your WordPress site is hosted on a remote server (most live websites are), you can still use WP-CLI. There are two common approaches:

  1. SSH into the server and run WP-CLI there. Most hosts provide SSH access. Connect with a command like:

    ssh username@yoursite.com
    

    Then navigate to your WordPress folder and use WP-CLI normally.

  2. Use WP-CLI's built-in SSH support. WP-CLI can run commands on a remote server directly from your local machine. See the Running Commands Remotely guide for details.

Learning More

Now that you've got the basics, here are some great next steps:

You don't need to learn everything at once. Start with a task you want to speed up, find the right command, and go from there!

Guides

Check status of a given plugin

One of the check types included in wp doctor is Plugin_Status, or the ability to assert that a given plugin should be active, installed, or uninstalled. Although the check type isn't used by any of the default diagnostic checks, you can use the Plugin_Status check type in your custom doctor.yml configuration file.

As an example, here are two checks using Plugin_Status, one which ensures Akismet is active on the system and another which ensures Hello Dolly is uninstalled:

plugin-akismet-active:
  check: Plugin_Status
  options:
    name: akismet
    status: active
plugin-hello-uninstalled:
  check: Plugin_Status
  options:
    name: hello
    status: uninstalled

Run together, you might see:

$ wp doctor check --config=plugin-status.yml --all
+--------------------------+---------+----------------------------------------------------------------+
| name                     | status  | message                                                        |
+--------------------------+---------+----------------------------------------------------------------+
| plugin-akismet-active    | success | Plugin 'akismet' is 'active' as expected.                      |
| plugin-hello-uninstalled | error   | Plugin 'hello' is 'inactive' but expected to be 'uninstalled'. |
+--------------------------+---------+----------------------------------------------------------------+

The Plugin_Status check type accepts the following options:

Guides

Commands Cookbook

Creating your own custom WP-CLI command can be easier than it looks — and you can use wp scaffold package (repo) to dynamically generate everything but the command itself.

Overview

WP-CLI's goal is to offer a complete alternative to the WordPress admin; for any action you might want to perform in the WordPress admin, there should be an equivalent WP-CLI command. A command is an atomic unit of WP-CLI functionality. wp plugin install (doc) is one such command, as is wp plugin activate (doc). Commands are useful to WordPress users because they can offer simple, precise interfaces for performing complex tasks.

But, the WordPress admin is a Swiss Army knife of infinite complexity. There's no way just this project could handle every use case. This is why WP-CLI includes a set of common internal commands, while also offering a rich internal API for third-parties to write and register their own commands.

WP-CLI commands can be distributed as standalone packages, or bundled with WordPress plugins or themes. Standalone packages can be published as Composer packages and discovered through Packagist. You can use wp scaffold package (repo) to dynamically generate everything but the command itself.

Packages are to WP-CLI as plugins are to WordPress. There are distinct differences in the approach you should take to creating a WP-CLI package. While WP-CLI is an ever-growing alternative to /wp-admin it is important to note that you must first write your package to work with the WP-CLI internal API before considering how you work with WordPress APIs.

Command types

Bundled commands:

Third-party commands:

All commands:

Anatomy of a command

WP-CLI supports registering any callable class, function, or closure as a command. WP_CLI::add_command() (doc) is used for both internal and third-party command registration.

The synopsis of a command defines which positional and associative arguments a command accepts. Let's take a look at the synopsis for wp plugin install:

$ wp plugin install
usage: wp plugin install <plugin|zip|url>... [--version=<version>] [--force] [--activate] [--activate-network]

In this example, <plugin|zip|url>... is the accepted positional argument. In fact, wp plugin install accepts the same positional argument (the slug, ZIP, or URL of a plugin to install) multiple times. [--version=<version>] is one of the accepted associative arguments. It's used to denote the version of the plugin to install. Notice, too, the square brackets around the argument definition; square brackets mean the argument is optional.

WP-CLI also has a series of global arguments which work with all commands. For instance, including --debug means your command execution will display all PHP errors, and add extra verbosity to the WP-CLI bootstrap process.

Required registration arguments

When registering a command, WP_CLI::add_command() requires two arguments:

  1. $name is the command's name within WP-CLI's namespace (e.g. plugin install or post list).
  2. $callable is the implementation of the command, as a callable class, function, or closure.

In the following example, each instance of wp foo is functionally equivalent:

// 1. Command is a function
function foo_command( $args ) {
    WP_CLI::success( $args[0] );
}
WP_CLI::add_command( 'foo', 'foo_command' );

// 2. Command is a closure
$foo_command = function( $args ) {
    WP_CLI::success( $args[0] );
};
WP_CLI::add_command( 'foo', $foo_command );

// 3. Command is a method on a class
class Foo_Command {
    public function __invoke( $args ) {
        WP_CLI::success( $args[0] );
    }
}
WP_CLI::add_command( 'foo', 'Foo_Command' );

// 4. Command is a method on a class with constructor arguments
class Foo_Command {
    protected $bar;
    public function __construct( $bar ) {
        $this->bar = $bar;
    }
    public function __invoke( $args ) {
        WP_CLI::success( $this->bar . ':' . $args[0] );
    }
}
$instance = new Foo_Command( 'Some text' );
WP_CLI::add_command( 'foo', $instance );

Importantly, classes behave a bit differently than functions and closures in that:

Note: Historically, WP-CLI provided a base WP_CLI_Command class to extend, however extending this class is not required and will not change how your command behaves.

All commands can be registered to their own top-level namespace (e.g. wp foo), or as subcommands to an existing namespace (e.g. wp core foo). For the latter, simply include the existing namespace as a part of the command definition.

class Foo_Command {
    public function __invoke( $args ) {
        WP_CLI::success( $args[0] );
    }
}
WP_CLI::add_command( 'core foo', 'Foo_Command' );

Quick and dirty execution

Writing a short script for a one-off task, and don't need to register it formally with WP_CLI::add_command()? wp eval-file is your ticket (doc).

Given a simple-command.php file:

<?php
WP_CLI::success( "The script has run!" );

Your "command" can be run with wp eval-file simple-command.php. If the command doesn't have a dependency on WordPress, or WordPress isn't available, you can use the --skip-wordpress flag to avoid loading WordPress.

Optional registration arguments

WP-CLI supports two ways of registering optional arguments for your command: through the callable's PHPDoc, or passed as a third $args parameter to WP_CLI::add_command().

Annotating with PHPDoc

A typical WP-CLI class looks like this:

<?php
/**
 * Implements example command.
 */
class Example_Command {

	/**
	 * Prints a greeting.
	 *
	 * ## OPTIONS
	 *
	 * <name>
	 * : The name of the person to greet.
	 *
	 * [--type=<type>]
	 * : Whether or not to greet the person with success or error.
	 * ---
	 * default: success
	 * options:
	 *   - success
	 *   - error
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     wp example hello Newman
	 *
	 * @when after_wp_load
	 */
	function hello( $args, $assoc_args ) {
		list( $name ) = $args;

		// Print the message with type
		$type = $assoc_args['type'];
		WP_CLI::$type( "Hello, $name!" );
	}
}

WP_CLI::add_command( 'example', 'Example_Command' );

This command's PHPDoc is interpreted in three ways:

Shortdesc

The shortdesc is the first line in the PHPDoc:

	/**
	 * Prints a greeting.

Longdesc

The longdesc is middle part of the PHPDoc:

	 * ## OPTIONS
	 *
	 * <name>
	 * : The name of the person to greet.
	 *
	 * [--type=<type>]
	 * : Whether or not to greet the person with success or error.
	 * ---
	 * default: success
	 * options:
	 *   - success
	 *   - error
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     wp example hello Newman

Options defined in the longdesc are interpreted as the command's synopsis:

Note: To accept arbitrary/unlimited number of optional associative arguments you would use the annotation [--<field>=<value>]. So for example:

	 * [--<field>=<value>]
	 * : Allow unlimited number of associative parameters.

A command's synopsis is used for validating the arguments, before passing them to the implementation.

Argument aliases

You can define one or more aliases for a parameter by appending them after a | separator inside the synopsis token. This lets users pass a short form (or any alternative name) and have WP-CLI automatically map it to the canonical argument name in $assoc_args.

 * [--with-dependencies|w]
 * : Include dependencies in the operation.

With this definition, both --with-dependencies and -w are accepted, and $assoc_args['with-dependencies'] is populated in either case.

Multiple aliases are separated by additional | characters:

 * [--verbose|v|wordy]
 * : Enable verbose output.

Aliases work for value-carrying associative arguments too:

 * [--format=<format>|f]
 * : Output format.

Running wp example hello -f=json is then equivalent to wp example hello --format=json. Note that WP-CLI accepts both -<alias>=value (single-dash) and --<name>=value (double-dash) forms for short-form aliases.

YAML parameter options

The --- block after a parameter description allows you to set extra metadata for that parameter using YAML.

default sets the value used when the argument is omitted:

 * [--type=<type>]
 * : Whether or not to greet the person with success or error.
 * ---
 * default: success
 * options:
 *   - success
 *   - error
 * ---

options restricts the accepted values. WP-CLI validates user input against this list and returns an error for any value not in it.

sensitive marks an argument as containing sensitive data (e.g. passwords, API keys):

 * [--password=<password>]
 * : Database password.
 * ---
 * sensitive: true
 * ---

The longdesc is also displayed when calling the help command, for example, wp help example hello. Its syntax is Markdown Extra and here are a few more notes on how it's handled by WP-CLI:

For more details on how you should format your command docs, please see WP-CLI's documentation standards.

Docblock tags

This is the last section and it starts immediately after the longdesc:

	 * @when after_wp_load
	 */

Here's the list of defined tags:

@subcommand

There are cases where you can't make the method name have the name of the subcommand. For example, you can't have a method named list, because list is a reserved keyword in PHP.

That's when the @subcommand tag comes to the rescue:

	/**
	 * @subcommand list
	 */
	function _list( $args, $assoc_args ) {
		...
	}

	/**
	 * @subcommand do-chores
	 */
	function do_chores( $args, $assoc_args ) {
		...
	}

@alias

With the @alias tag, you can add another way of calling a subcommand. Example:

	/**
	 * @alias hi
	 */
	function hello( $args, $assoc_args ) {
		...
	}
$ wp example hi Joe
Success: Hello, Joe!

@when

This is a special tag that tells WP-CLI when to execute the command. It supports all registered WP-CLI hooks.

Most WP-CLI commands execute after WordPress has loaded. The default behavior for a command is:

@when after_wp_load

To have a WP-CLI command run before WordPress loads, use:

@when before_wp_load

Do keep in mind most WP-CLI hooks fire before WordPress is loaded. If your command is loaded from a plugin or theme, @when will be essentially ignored.

It has no effect if the command using it is loaded from a plugin or a theme, because by that time WordPress itself will have already been loaded.

@skipglobalargcheck

WP-CLI emits a warning at command-registration time if a command defines an argument whose name collides with an existing global argument (e.g. --debug, --user, --quiet). This helps command authors avoid confusing behaviour where the global argument takes precedence over the command-specific one.

If you intentionally want to reuse a global argument name (for example, when wrapping another tool that uses the same flag), you can silence the warning with @skipglobalargcheck:

	/**
	 * @skipglobalargcheck
	 * @when before_wp_load
	 */
	function my_command( $args, $assoc_args ) {
		...
	}

WP_CLI::add_command()'s third $args parameter

Each of the configuration options supported by PHPDoc can instead be passed as the third argument in command registration:

$hello_command = function( $args, $assoc_args ) {
	list( $name ) = $args;
	$type = $assoc_args['type'];
	WP_CLI::$type( "Hello, $name!" );
	if ( isset( $assoc_args['honk'] ) ) {
		WP_CLI::log( 'Honk!' );
	}
};
WP_CLI::add_command( 'example hello', $hello_command, array(
	'shortdesc' => 'Prints a greeting.',
	'synopsis' => array(
		array(
			'type'        => 'positional',
			'name'        => 'name',
			'description' => 'The name of the person to greet.',
			'optional'    => false,
			'repeating'   => false,
		),
		array(
			'type'        => 'assoc',
			'name'        => 'type',
			'description' => 'Whether or not to greet the person with success or error.',
			'optional'    => true,
			'default'     => 'success',
			'options'     => array( 'success', 'error' ),
		),
		array(
			'type'     => 'flag',
			'name'     => 'honk',
			'optional' => true,
		),
	),
	'when' => 'after_wp_load',
	'longdesc' =>   '## EXAMPLES' . "\n\n" . 'wp example hello Newman',
) );

Note that the longdesc attribute will be appended to the description of the options generated from the synopsis, so this argument is great for adding examples of usage. If there is no synopsis, the longdesc attribute will be used as is to provide a description.

Command internals

Now that you know how to register a command, the world is your oyster. Inside your callback, your command can do whatever it wants.

Accepting arguments

In order to handle runtime arguments, you have to add two parameters to your callable: $args and $assoc_args.

function hello( $args, $assoc_args ) {
	/* Code goes here*/
}

The $args variable will store all the positional arguments:

$ wp example hello Joe Doe
WP_CLI::line( $args[0] ); // Joe
WP_CLI::line( $args[1] ); // Doe

The $assoc_args variable will store all the arguments defined like --key=value or --flag or --no-flag:

$ wp example hello --name='Joe Doe' --verbose --no-option
WP_CLI::line( $assoc_args['name'] ); // Joe Doe
WP_CLI::line( $assoc_args['verbose'] ); // true
WP_CLI::line( $assoc_args['option'] ); // false

Also, you can combine argument types:

$ wp example hello --name=Joe foo --verbose bar
WP_CLI::line( $assoc_args['name'] ); // Joe
WP_CLI::line( $assoc_args['verbose'] ); // true
WP_CLI::line( $args[0] ); // foo
WP_CLI::line( $args[1] ); // bar

Effectively reusing WP-CLI internal APIs

As an example, say you were tasked with finding all unused themes on a multisite network (#2523). If you had to perform this task manually through the WordPress admin, it would probably take hours, if not days, of effort. However, if you're familiar with writing WP-CLI commands, you could complete the task in 15 minutes or less.

Here's what such a command looks like:

/**
 * Find unused themes on a multisite network.
 *
 * Iterates through all sites on a network to find themes which aren't enabled
 * on any site.
 */
$find_unused_themes_command = function() {
	$response = WP_CLI::launch_self( 'site list', array(), array( 'format' => 'json' ), false, true );
	$sites = json_decode( $response->stdout );
	$unused = array();
	$used = array();
	foreach( $sites as $site ) {
		WP_CLI::log( "Checking {$site->url} for unused themes..." );
		$response = WP_CLI::launch_self( 'theme list', array(), array( 'url' => $site->url, 'format' => 'json' ), false, true );
		$themes = json_decode( $response->stdout );
		foreach( $themes as $theme ) {
			if ( 'no' == $theme->enabled && 'inactive' == $theme->status && ! in_array( $theme->name, $used ) ) {
				$unused[ $theme->name ] = $theme;
			} else {
				if ( isset( $unused[ $theme->name ] ) ) {
					unset( $unused[ $theme->name ] );
				}
				$used[] = $theme->name;
			}
		}
	}
	WP_CLI\Utils\format_items( 'table', $unused, array( 'name', 'version' ) );
};
WP_CLI::add_command( 'find-unused-themes', $find_unused_themes_command, array(
	'before_invoke' => function(){
		if ( ! is_multisite() ) {
			WP_CLI::error( 'This is not a multisite installation.' );
		}
	},
) );

Let's run through the internal APIs this command uses to achieve its goal:

Help rendering

Your command's PHPDoc (or registered definition) is rendered using the help command. The output is ordered like this:

  1. Short description
  2. Synopsis
  3. Long description (OPTIONS, EXAMPLES etc.)
  4. Global parameters

Writing tests

WP-CLI makes use of a Behat-based testing framework, which you should use too. Behat is a great choice for your WP-CLI commands because:

Behat tests live in the features/ directory of your project. Here's an example from features/cli-info.feature:

Feature: Review CLI information

  Scenario: Get the path to the packages directory
    Given an empty directory

    When I run `wp cli info --format=json`
    Then STDOUT should be JSON containing:
      """
      {"wp_cli_packages_dir_path":"/tmp/wp-cli-home/.wp-cli/packages/"}
      """

    When I run `WP_CLI_PACKAGES_DIR=/tmp/packages wp cli info --format=json`
    Then STDOUT should be JSON containing:
      """
      {"wp_cli_packages_dir_path":"/tmp/packages/"}
      """

Functional tests typically follow this pattern:

Convinced? Head on over to wp-cli/scaffold-package-command to get started.

Adding commands globally

If you want to have custom commands available globally, without needing to create a full plugin or package, you can use WP-CLI's require configuration option.

Edit your global config file (usually found at ~/.wp-cli/config.yml) to require a PHP file:

require:
  - ~/.wp-cli/commands.php

Then create the file and add your custom commands:

<?php

WP_CLI::add_command( 'hello-world', function () {
    WP_CLI::success( "Hello World!" );
} );

Any commands registered in that file will be globally available whenever you run WP-CLI. This is great for personal helper commands that you want available across all your WordPress projects.

You can also pass the --require=<path> flag on the command line, or set the WP_CLI_REQUIRE environment variable, to load a custom PHP file for a single invocation.

Distribution

Now that you've produced a command you're proud of, it's time to share it with the world. There are two common ways of doing so.

Include in a plugin or theme

One way to share WP-CLI commands is by packaging them in your plugin or theme. Many people do so by conditionally loading (and registering) the command based on the presence of the WP_CLI constant.

if ( defined( 'WP_CLI' ) && WP_CLI ) {
	require_once dirname( __FILE__ ) . '/inc/class-plugin-cli-command.php';
}

Alternatively, you can use the cli_init hook to register your commands. This hook is fired during the WP-CLI Runner startup process, providing a dedicated event for command registration without needing to check for the WP_CLI constant.

/**
 * Register custom WP-CLI commands using the cli_init hook.
 */
function myplugin_register_cli_commands() {
	require_once dirname( __FILE__ ) . '/inc/class-plugin-cli-command.php';
	WP_CLI::add_command( 'myplugin', 'MyPlugin_CLI_Command' );
}
add_action( 'cli_init', 'myplugin_register_cli_commands' );

Both approaches are valid. Use the WP_CLI constant check when you need to conditionally load code based on whether WP-CLI is present. Use the cli_init hook when you want to hook into a specific point in the WP-CLI initialization process.

Distribute as a stand-alone command

Standalone WP-CLI commands can be installed from any git repository, ZIP file or folder. The only technical requirement is to include a valid composer.json file with an autoload declaration. We recommend including "type": "wp-cli-package" to distinguish your project explicitly as a WP-CLI package.

Here's a full composer.json example from the server command:


{
	"name": "wp-cli/server-command",
	"description": "Start a development server for WordPress",
	"type": "wp-cli-package",
	"homepage": "https://github.com/wp-cli/server-command",
	"license": "MIT",
	"authors": [
   	    {
      	        "name": "Package Maintainer",
                "email": "packagemaintainer@homepage.com",
                "homepage": "https://www.homepage.com"
            }
        ],
	"require": {
		"php": ">=5.3.29"
	},
	"autoload": {
		"files": [ "command.php" ]
	}
}

Note the autoload declaration, which loads command.php.

Once you've added a valid composer.json file to your project repository, WP-CLI users can pull it in via the package manager from the location you opted to store it in. Here are a few examples of storage locations and the corresponding syntax of installing it via the package manager:

Git repository

To install a package that is found in a git repository, you can provide either the HTTPS or the SSH link to the git repository to the package install command.

# Installing the package using an HTTPS link
$ wp package install https://github.com/wp-cli/server-command.git

# Installing the package using an SSH link
$ wp package install git@github.com:wp-cli/server-command.git

ZIP file

You can install a package from a ZIP file by providing the path to that file to the wp package install command.

# Installing the package using a ZIP file
$ wp package install ~/Downloads/server-command-main.zip
Guides

Common Issues

Error: Can't connect to the database

A few possibilities:

a) you're using MAMP, but WP-CLI is not using the MAMP PHP binary.

You can check which PHP WP-CLI is using by running wp --info.

If you need to specify an alternate PHP binary, see using a custom PHP binary.

b) it's a WordPress multisite install.

c) the database credentials in wp-config.php are actually incorrect.

Running wp --info produces HTML output

If you run wp --info on a server with Phar support disabled, you may see:

$ wp --info
Content-type: text/html; charset=UTF-8

When using the WP-CLI Phar, you'll need to whitelist Phar support in your php.ini:

suhosin.executor.include.whitelist = phar

PHP Fatal error: Cannot redeclare wp_unregister_GLOBALS()

If you get this fatal error running the wp command, you may have moved or edited wp-config.php beyond what wp-cli supports:

PHP Fatal error: Cannot redeclare wp_unregister_GLOBALS() (previously declared in /var/www/foo.com/wp-includes/load.php:18) in /var/www/foo.com/wp-includes/load.php on line 33

One of WP-CLI's requirements is that the line:

require_once(ABSPATH . 'wp-settings.php');

remains in the wp-config.php file, so if you've modified or moved it, put it back there. It gets matched by a regex when WP-CLI runs.

PHP Fatal error: Call to undefined function <WordPress function>

Before WP-CLI can load wp-settings.php, it needs to know all of the constants defined in wp-config.php (database connection details and so on). Because WP-CLI doesn't want WordPress to load yet when it's pulling the constants out of wp-config.php, it uses regex to strip the require_once(ABSPATH . 'wp-settings.php'); statement.

If you've modified your wp-config.php in a way that calls WordPress functions, PHP will fail out with a fatal error, as your wp-config.php is calling a WordPress function before WordPress has been loaded to define it.

Example:

$ wp core check-update
PHP Fatal error:  Call to undefined function add_filter() in phar:///usr/local/bin/wp/php/WP_CLI/Runner.php(952) : eval()'d code on line N

Modifying wp-config.php beyond constant definitions is not best practice. You should move any modifications to a WordPress mu-plugin, which will retain the functionality of your modifications while allowing wp-cli to parse your wp-config.php without throwing a PHP error, as well as preventing other errors.

See: #1631

PHP Fatal error: Call to undefined function cli\posix_isatty()

Please ensure you have the php-process extension installed. For example for Centos 6: yum install php-process

PHP Fatal error: Allowed memory size of 999999 bytes exhausted (tried to allocate 99 bytes)

If you run into a PHP fatal error relating to memory when running wp package install, you're likely running out of memory.

WP-CLI uses Composer under the hood to manage WP-CLI packages. However, Composer is a bit of a memory hog, so you'll need to increase your memory limit to accommodate it.

Edit your php.ini as a permanent fix:

# Find your php.ini for PHP-CLI
$ php -i | grep php.ini
Configuration File (php.ini) Path => /usr/local/etc/php/7.0
Loaded Configuration File => /usr/local/etc/php/7.0/php.ini
# Increase memory_limit to 512M or greater
$ vim /usr/local/etc/php/7.0/php.ini
memory_limit = 512M

Set memory_limit on the fly as a temporary fix:

$ php -d memory_limit=512M "$(which wp)" package install <package-name>

If your PHP process is still running out of memory, try these steps:

  1. Restart PHP.
  2. Check for additional php.ini files:
$ php -i | grep additional
Scan this dir for additional .ini files => /usr/local/etc/php/7.1/conf.d
# Edit the additional file(s) and increase the memory_limit to 512M or greater
$ vim /usr/local/etc/php/7.1/conf.d
memory_limit = 512M

Error: YIKES! It looks like you're running this as root.

Running WP-CLI as root is extremely dangerous. When you execute WP-CLI as root, any code within your WordPress instance (including third-party plugins and themes you've installed) will have full privileges to the entire server. This can enable malicious code within the WordPress instance to compromise the entire server.

The WP-CLI project strongly discourages running WP-CLI as root.

See also: #973

PHP notice: Undefined index on $_SERVER superglobal

The $_SERVER superglobal is an array typically populated by a web server with information such as headers, paths, and script locations. PHP CLI doesn't populate this variable, nor does WP-CLI, because many of the variable details are meaningless at the command line.

Before accessing a value on the $_SERVER superglobal, you should check if the key is set:

if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
  $_SERVER['HTTPS']='on';
}

When using $_SERVER['HTTP_HOST'] in your wp-config.php, you'll need to set a default value in WP-CLI context:

if ( defined( 'WP_CLI' ) && WP_CLI && ! isset( $_SERVER['HTTP_HOST'] ) ) {
    $_SERVER['HTTP_HOST'] = 'example.com';
}

See also: #730

PHP notice: Use of undefined constant STDOUT

The STDOUT constant is defined by the PHP CLI. If you receive an error notice that STDOUT is missing, it's likely because you're not running WP-CLI by PHP CLI. Please review your server configuration accordingly.

PHP Parse error: syntax error, unexpected '?' in ... /php/WP_CLI/Runner.php ... eval()'d code on line 1

If you get this error running the wp command, the most likely cause is a Unicode BOM at the start of your wp-config.php. This issue will be addressed in a future release of WP-CLI, but in the meantime you can solve the issue by running:

$ sed -i '1s/^\xEF\xBB\xBF//' $(wp config path)

or by manually removing the BOM using your favorite editor.

See also: wp-cli/search-replace-command#71

Can't find wp-content directory / use of $_SERVER['document_root']

$_SERVER['document_root'] is defined by the webserver based on the incoming web request. Because this type of context is unavailable to PHP CLI, $_SERVER['document_root'] is unavailable to WP-CLI. Furthermore, WP-CLI can't safely mock $_SERVER['document_root'] as it does with $_SERVER['http_host'] and a few other $_SERVER values.

If you're using $_SERVER['document_root'] in your wp-config.php file, you should instead use dirname( __FILE__ ) or similar.

See also: #785

Conflict between global parameters and command arguments

All of the global parameters (e.g. --url=<url>) may conflict with the arguments you'd like to accept for your command. For instance, adding a RSS widget to a sidebar will not populate the feed URL for that widget:

$ wp widget add rss sidebar-1 1 --url="http://www.smashingmagazine.com/feed/" --items=3
Success: Added widget to sidebar.

Use the WP_CLI_STRICT_ARGS_MODE environment variable to tell WP-CLI to treat any arguments before the command as global, and after the command as local:

WP_CLI_STRICT_ARGS_MODE=1 wp --url=wp.dev/site2 widget add rss sidebar-1 1 --url="http://wp-cli.org/feed/"

In this example, --url=wp.dev/site2 is the global argument, setting WP-CLI to run against 'site2' on a WP multisite install. --url="http://wp-cli.org/feed/" is the local argument, setting the RSS feed widget with the proper URL.

See also: #3128

Warning: Some code is trying to do a URL redirect

Most of the time, it's some plugin or theme code that disables wp-admin access to non-admins.

Quick fix, other than disabling the protection, is to pass the user parameter: --user=some_admin

See also: #477

Cannot create a post with Latin characters in the title on Windows

Considering the following example:

wp post create --post_title="Perícias Contábeis"

Using UTF-8 in PHP arguments doesn't work on Windows for PHP <= 7.0, however it will work for PHP >= 7.1, as it was fixed as part of Support for long and UTF-8 path. A workaround for PHP <= 7.0 is to use the --prompt option:

echo "Perícias Contábeis" | wp post create --post_type=page --post_status=publish --prompt=post_title

See also: #4714

Arguments containing newlines are truncated on Windows

On Windows, wp is a .bat wrapper around php wp-cli.phar. Batch files terminate a command at a newline, including a newline inside a quoted argument, because cmd.exe splits the line before quoting is evaluated.

Any flags positioned after the multi-line value never reach WP-CLI. The command still succeeds with the arguments it did receive, so there is no error — WordPress silently applies its defaults for the missing values. In the example below the post is created, but as a draft under the site's default category and attributed to the current user, because everything after --post_content (--post_category, --post_author and --post_status) was dropped:

wp post create --post_content="<multi-line content>" --post_category=3 --post_author=2 --post_status=publish

Pass the content over STDIN instead, using - as the positional argument. The newline then lives in the piped stream rather than in the command line, so nothing is truncated:

type content.html | wp post create - --post_status=publish --post_category=3 --post_author=2

The same applies in PowerShell:

$content | wp post create - --post_status=publish --post_category=3 --post_author=2

wp post create and wp post update also accept a file path in place of -.

See also: #2712

The installation hangs

If the installation seems to hang forever while trying to clone the resources from GitHub, please ensure that you are allowed to connect to Github using SSL (port 443) and Git (port 9418) for outbound connections.

W3 Total Cache Error: some files appear to be missing or out of place.

W3 Total Cache object caching can cause this problem. Disabling object caching and optionally removing wp-content/object-cache.php will allow WP-CLI to work again.

See also: #587

The automated updater doesn't work for versions before 3.4

The wp core update command is designed to work for WordPress 3.4 and above. To be able to update an older website to latest WordPress, you could try one of the following alternatives:

  1. Fully-automated: Run wp core download --force to download latest WordPress and replace it with your files (don't worry, wp-config.php will remain intact). Then, run wp core update-db to update the database. Since the procedure isn't ideal, run once again wp core download --force and the new version should be available.
  2. Semi-automated: Run wp core download --force to download all files and replace them in your current installation, then navigate to /wp-admin/ and run the database upgrade when prompted.

PHP Fatal error: Maximum function nesting level of '#' reached, aborting!

You're encountering a limitation associated with PHP Xdebug. Here are a few solutions to the problem:

  1. Temporarily turn off Xdebug while executing the CLI command: XDEBUG_MODE=off wp <command> ...
  2. Increase the value of xdebug.max_nesting_level in your php.ini file. You can learn more about it here - xdebug.max-nesting-level

If you're looking for a quick fix while still retaining debugging tools, you can change the max_nesting_level value to execute the command just once:

php -d xdebug.max_nesting_level=512 wp <command> ...

If you're not in the process of debugging code, it's advisable to completely disable Xdebug. For instructions on how to permanently switch off Xdebug, please refer to the documentation of your local environment or your hosting provider.

You might experience similar issues with the alternate error message: Error: Xdebug has detected a possible infinite loop, and aborted your script with a stack depth of '#' frames

Guides

Common Tasks with WP-CLI

Not sure what you can do with WP-CLI? This page showcases practical, everyday WordPress management tasks you can accomplish from the command line — with examples you can use right away.

New to the command line? Start with the Beginner's Guide first.

Plugins

Install a plugin

wp plugin install contact-form-7

Install and activate a plugin in one step

wp plugin install contact-form-7 --activate

Activate an already-installed plugin

wp plugin activate contact-form-7

Deactivate a plugin

wp plugin deactivate contact-form-7

Update a specific plugin

wp plugin update akismet

Update all plugins at once

wp plugin update --all

List all installed plugins and their status

wp plugin list

Delete a plugin

wp plugin delete hello

Themes

Install a theme

wp theme install twentytwentyfour

Activate a theme

wp theme activate twentytwentyfour

Update all themes

wp theme update --all

List all installed themes

wp theme list

WordPress Core

Check which version of WordPress is installed

wp core version

Check if a WordPress update is available

wp core check-update

Update WordPress to the latest version

wp core update

Update the WordPress database after an update

wp core update-db

Users

List all users

wp user list

Create a new user

wp user create jane jane@example.com --role=editor

Change a user's password

wp user update jane --user_pass=newpassword

Delete a user

wp user delete jane --reassign=1

Tip: The --reassign=1 option moves the deleted user's posts to the user with ID 1 (usually the site admin). Replace 1 with the ID of the user you want to reassign posts to.

Add an administrator role to an existing user

wp user set-role jane administrator

Posts and Pages

Create a new post

wp post create --post_title="My First Post" --post_status=publish --post_type=post

Create a new page

wp post create --post_title="About Us" --post_status=publish --post_type=page

List all published posts

wp post list --post_status=publish

Delete a post (sends it to trash)

wp post delete 42

Permanently delete a post

wp post delete 42 --force

Comments

List all comments awaiting moderation

wp comment list --status=hold

Approve a comment

wp comment approve 5

Delete spam comments

wp comment delete $(wp comment list --status=spam --format=ids)

Note: The $(...) syntax works in bash (macOS, Linux, Git Bash, or WSL on Windows). It does not work in Windows Command Prompt. Windows users should use Git Bash, WSL, or PowerShell to run this command.

Options and Settings

Get the site URL

wp option get siteurl

Get the site title

wp option get blogname

Change the site title

wp option update blogname "My New Site Title"

Discourage search engines from indexing the site

wp option update blog_public 0

Re-enable search engine indexing

wp option update blog_public 1

Search and Replace

Preview what would change when moving a site to a new domain

wp search-replace 'http://oldsite.com' 'http://newsite.com' --dry-run

Replace URLs when moving to a new domain

wp search-replace 'http://oldsite.com' 'http://newsite.com'

Media

Regenerate all thumbnail sizes

Useful after changing your theme or adding a new image size:

wp media regenerate --yes

Caching

Flush the object cache

wp cache flush

Flush rewrite rules (useful after adding a new plugin or changing permalink settings)

wp rewrite flush

Database

Export the database to a file

wp db export backup.sql

Import a database from a file

wp db import backup.sql

Open an interactive database console

wp db cli

WP-CLI Itself

Check the installed version of WP-CLI

wp cli version

Update WP-CLI to the latest version

wp cli update

Get system information useful for troubleshooting

wp --info

Going Further

These examples only scratch the surface. WP-CLI has commands for nearly every part of WordPress. To explore more:

Guides

Customize doctor diagnostic checks

Even though wp doctor comes with a number of default diagnostic checks, it's designed with extensibility at its core. Checks are defined at runtime, read from a doctor.yml configuration file naming each check with its options.

doctor.yml format

Let's take a look at the first two checks in the included doctor.yml:

autoload-options-size:
  check: Autoload_Options_Size
constant-savequeries-falsy:
  check: Constant_Definition
  options:
    constant: SAVEQUERIES
    falsy: true

In this example:

For the sake of completeness, it's also worth noting Autoload_Options_Size accepts 'threshold_kb' as an optional configuration option. The default value for 'threshold_kb' is 900, so it doesn't need to be included in the doctor.yml.

Custom doctor.yml configuration files

Run your own doctor checks by creating a doctor.yml and supplying it with wp doctor check --config=doctor.yml. Use different configurations for different environments by creating separate prod.yml and dev.yml files.

If you want your custom file to extend an existing doctor config, you can use the magical _ config file option to define which config file to inherit. 'default' is a magic reference to the bundled doctor.yml; you also specify an entire file path.

Take a look at this example:

_:
  inherit: default
  skipped_checks:
    - autoload-options-size
constant-disallow-file-mods-falsy:
  check: Constant_Definition
  options:
    constant: DISALLOW_FILE_MODS
    falsy: true
plugin-akismet-active:
  check: Plugin_Status
  options:
    plugin: akismet
    status: active
plugin-akismet-valid-api-key:
  class: Akismet_Valid_API_Key
  require: akismet-valid-api-key.php

This custom doctor.yml file:

Available check types

Some wp doctor check types are configurable, meaning the default setting can be changed, while other check types are abstracted in such a way that they can be reusable. For instance, the Autoload_Options_Size check accepts an option 'threshold_kb' while Plugin_Status accepts 'plugin' and 'status' as options.

The configurable check types include:

The abstracted check types include:

Of course, some check types don't need configuration options:

You can write your own custom check type by extending the runcommand\Doctor\Checks\Check class.

Guides

Default doctor diagnostic checks

Although its power comes from its ability to be customized, wp doctor includes a number of default diagnostic checks considered to be recommendations for production websites.

Use wp doctor list to view these default checks:

name description
autoload-options-size Warns when autoloaded options size exceeds threshold of 900 kb.
constant-savequeries-falsy Confirms expected state of the SAVEQUERIES constant.
constant-wp-debug-falsy Confirms expected state of the WP_DEBUG constant.
core-update Errors when new WordPress minor release is available; warns for major release.
core-verify-checksums Verifies WordPress files against published checksums; errors on failure.
cron-count Errors when there's an excess of 50 total cron jobs registered.
cron-duplicates Errors when there's an excess of 10 duplicate cron jobs registered.
file-eval Checks files on the filesystem for regex pattern `eval\(.*base64_decode\(.*`.
option-blog-public Confirms the expected value of the 'blog_public' option.
plugin-active-count Warns when there are greater than 80 plugins activated.
plugin-deactivated Warns when greater than 40% of plugins are deactivated.
plugin-update Warns when there are plugin updates available.
theme-update Warns when there are theme updates available.

To explain these further:

If you create a custom doctor.yml config file, you can use wp doctor list --config=<file> to view the diagnostic checks listed in the file.

Guides

Doctor Guides

Here are some helpful guides for using wp doctor:

Guides

External Resources

Blog posts & tutorials

Slides

Videos

Guides

Force output to a specific locale

WP-CLI always outputs English because it doesn't support localization. But, because WordPress supports localization, you may see non-English output when performing specific commands.

For instance:

$ wp theme update --all
מעבר למצב תחזוקה...
מוריד עדכונים מ-https://downloads.wordpress.org/theme/hueman.3.3.25.zip...
Using cached file '/home/xxx/.wp-cli/cache/theme/hueman-3.3.25.zip'...
פתיחת עדכון...
התקנת גרסה חדשה...
הסרת הגרסה הקודמת של התבנית...
התבנית עודכנה בהצלחה.
ביטול מצב תחזוקה...
+--------+-------------+-------------+---------+
| name   | old_version | new_version | status  |
+--------+-------------+-------------+---------+
| hueman | 3.3.24      | 3.3.25      | Updated |
+--------+-------------+-------------+---------+
Success: Updated 1 of 1 themes.

To force WordPress to always output English at the command line, you need to filter the active locale.

Given a force-locale.php file:

<?php
WP_CLI::add_wp_hook( 'pre_option_WPLANG', function() {
    return 'en_US';
});

You can force the locale to en_US with:

wp --require=force-locale.php

One nice thing about this approach is that you can easily apply it across multiple WP installs using a config file.

Guides

Installing

Requirements

WP-CLI requires PHP 7.2.24 or later. To check the version of PHP available on your system, run:

php --version

WP-CLI has no additional requirements beyond those of WordPress itself.

First, download wp-cli.phar using wget or curl. For example:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

Verifying the download (optional but recommended)

Before using the downloaded file, you can verify its authenticity and integrity. See our guide to verifying WP-CLI downloads for detailed instructions on using GPG signatures or checksums to ensure the file hasn't been tampered with.

Quick verification using GPG:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar.asc
curl -L https://raw.githubusercontent.com/wp-cli/builds/gh-pages/wp-cli.pgp | gpg --import
gpg --verify wp-cli.phar.asc wp-cli.phar

Then, check if it works:

php wp-cli.phar --info

To be able to type just wp, instead of php wp-cli.phar, you need to make the file executable and move it to somewhere in your PATH. For example:

chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

Now try running wp --info. If WP-CLI is installed successfully, you'll see output like this:

OS:  Linux 4.10.0-42-generic #46~16.04.1-Ubuntu SMP Mon Dec 4 15:57:59 UTC 2017 x86_64
Shell:   /usr/bin/zsh
PHP binary:    /usr/bin/php
PHP version:     7.1.12-1+ubuntu16.04.1+deb.sury.org+1
php.ini used:   /etc/php/7.1/cli/php.ini
MySQL binary:
MySQL version:
SQL modes:
WP-CLI root dir:        /home/wp-cli/.wp-cli
WP-CLI packages dir:    /home/wp-cli/.wp-cli/packages/
WP-CLI global config:   /home/wp-cli/.wp-cli/config.yml
WP-CLI project config:
WP-CLI version: 1.5.0

Voila! You're now an official WP-CLI user.

Wondering what to do next? Check out the quick start guide for a short introduction and some example usage.

Updating WP-CLI

If you have installed WP-CLI using the recommended Phar method, you can update it at any time by running wp cli update (although if WP-CLI is owned by root, that may be sudo wp cli update). If you installed WP-CLI using the Composer or Git-based installations, see the specific instructions for updating associated with each method below.

When you run wp cli update, you'll be prompted to confirm that you wish to update with a message similar to the following:

You have version 0.21.1. Would you like to update to 0.23.1? [y/n]

After you accept, you should see a success message:

Success: Updated WP-CLI to 0.23.1

If you're already running the latest version of WP-CLI, you'll see this message:

WP-CLI is at the latest version.

Want to live life on the edge? Run wp cli update --nightly to use the latest nightly build of WP-CLI. The nightly build is more or less stable enough for you to use in your local environment, and always includes the latest and greatest.

For more information about wp cli update, including flags and options that can be used, read the full docs page on the update command.

Tab completions

Bash & Z-Shell

WP-CLI also comes with a tab completion script for Bash and Z-Shell. Just download wp-completion.bash and source it from ~/.bash_profile:

source /FULL/PATH/TO/wp-completion.bash

To have this change take effect in your currently active shell, run source ~/.bash_profile afterwards.

Oh My Zsh

If you're using the Oh My Zsh framework, you can enable the built-in wp-cli plugin, by adding it to the plugins=(wp-cli git [...]) line in your ~/.zshrc file.

Note: the Oh My Zsh plugin comes with the bash completion script included, so it's unnecessary to have both.

To have this change take effect in your currently active shell, run source ~/.zshrc afterwards.

Fish

If you're using the Fish shell, you can download wp.fish and move it to ~/.config/fish/completions/wp.fish. Afterwards just type wp and press TAB, and fish will automatically source wp.fish.

Dash/Alfred workflow

If you're using Dash and Alfred, you can add a custom Alfred workflow to look up WP-CLI command information.

Setup

Open Dash and download the WP-CLI docset: Dash › Preferences › Downloads › User Contributions › Search for WP-CLI

Still in Dash, activate the Alfred integration: Dash › Preferences › Integration › Alfred

Usage

Open Alfred and try searching for a specific command. For example, this gives you an overview of the plugin command and its subcommands: wp-cli plugin

Alternative installation methods

Note: For all Phar-based installation methods below, you can verify the downloaded file's authenticity and integrity. See the guide to verifying WP-CLI downloads for instructions.

Installing via Git

If you intend to work on WP-CLI itself, see the Setting up section in Pull Requests.

Installing nightly via Phar

The "nightly" is the bleeding-edge version of WP-CLI, built straight from the main branch.

Just follow the normal installation instructions, except change the URL to the phar file:

https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar

Installing via Composer

As part of a project

Add the following line to your project's composer.json file:

"require" : {
	"wp-cli/wp-cli-bundle": "*"
}

To add any additional suggested packages seen in the wp-cli-bundle package, such as psy/psysh, run:

composer require --dev $(composer suggests --by-package | awk '/wp-cli\/wp-cli-bundle/' RS= | grep -o -P '(?<=- ).*(?=:)')

Optionally (if run on a server or for e.g. in a virtual machine locally) you can automate setting up the command and making it available in the users path. Let's assume Composer installed into /var/www/vendor (you can get the composer vendor-dir config variable specific to your machine via composer config --list | grep "\[vendor-dir\]"), we can add the following scripts/commands to the composer.json file. The second and third line set up bash completion so we don't have to remember every single command:

"scripts" : {
	"post-install-cmd" : [
		"[[ -f /usr/bin/wp ]] || sudo ln -s /var/www/vendor/wp-cli/wp-cli/bin/wp /usr/bin/wp",
		"source /var/www/vendor/wp-cli/wp-cli/utils/wp-completion.bash",
    "[[ -f ~/.bash_profile ]] || touch ~/.bash_profile",
		"source ~/.bash_profile"
	]
}

Above script assumes that your current shell is bash, which might not be the case for all users. Example for a vagrant box (added to the scripts-block):

cat /etc/passwd | grep $(whoami)
vagrant:x:1000:1000::/home/vagrant:/bin/bash

In case you got bash available and installed for your OS, you can switch dynamically:

"scripts" : {
	"post-update-cmd" : [
		"/bin/bash -c \"[[ -f /usr/local/bin/wp ]] || sudo ln -s /var/www/vendor/wp-cli/wp-cli/bin/wp /usr/bin/wp\"",
		"/bin/bash -c \"source /var/www/vendor/wp-cli/wp-cli/utils/wp-completion.bash\"",
    "/bin/bash -c \"[[ -f ~/.bash_profile ]] || touch ~/.bash_profile\"",
		"/bin/bash -c \"source ~/.bash_profile\""
	]
}

As a project

Needs php and composer (or php composer.phar) set up as console commands.

composer create-project wp-cli/wp-cli-bundle --prefer-source

Then run wp-cli-bundle/vendor/wp-cli/wp-cli/bin/wp or add wp-cli-bundle/vendor/wp-cli/wp-cli/bin folder to PATH for global wp command (on Windows, use wp-cli/bin/wp.bat instead).

To update, you'll need to:

cd wp-cli-bundle
git pull origin main
composer install

Global require

If you prefer to have PHP tools installed globally via Composer and have something like ~/.composer/vendor/bin in your PATH (or C:\Users\you\AppData\Roaming\Composer\vendor\bin on Windows), you can just run:

composer global require wp-cli/wp-cli-bundle

To update everything globally, run composer global update.

Installing a specific version

If you want to install a specific version of WP-CLI then append the version numbers behind the packages

composer create-project wp-cli/wp-cli-bundle:2.1.0 --no-dev

The version must be in a format that Composer can understand and can be found on packagist.org.

Installing bleeding-edge

If you want to install bleeding-edge then use dev-main:

composer create-project wp-cli/wp-cli-bundle:dev-main --no-dev

Installing globally as a project

You can specify a custom install path for WP-CLI, like so:

composer create-project wp-cli/wp-cli-bundle /usr/share/wp-cli --no-dev
sudo ln -s /usr/share/wp-cli-bundle/vendor/wp-cli/wp-cli/bin /usr/bin/wp

Installing via Homebrew

brew install wp-cli

Here's the formula.

Installing via Docker

The Docker community maintains WordPress and WP-CLI images.

To include the WP-CLI image in your own project:

image: wordpress:cli

Installing on Windows

Install via composer as described above or use the following method.

Make sure you have php installed and in your path so you can execute it globally.

Download wp-cli.phar manually and save it to a folder, for example c:\wp-cli

Create a file named wp.bat in c:\wp-cli with the following contents:

@ECHO OFF
php "c:/wp-cli/wp-cli.phar" %*

Add c:\wp-cli to your path:

setx path "%path%;c:\wp-cli"

You can now use WP-CLI from anywhere in Windows command line.

Using WP-CLI with Git Bash on Windows

If you use Git Bash as your shell on Windows, the wp.bat wrapper will not work. Instead, create a file named wp (no extension) in c:\wp-cli with the following contents:

#!/usr/bin/env bash

script_path=$(command -v "$0" 2>/dev/null || printf '%s\n' "$0")
d=${script_path%[/\\]*}
[ "$d" = "$script_path" ] && d=.
dir=$(cd "$d" && pwd)

# See if we are running in Cygwin by checking for cygpath program
if command -v 'cygpath' >/dev/null 2>&1; then
	# Cygwin paths start with /cygdrive/ which will break windows PHP,
	# so we need to translate the dir path to windows format. However
	# we could be using cygwin PHP which does not require this, so we
	# test if the path to PHP starts with /cygdrive/ rather than /usr/bin
	if [[ $(which php) == /cygdrive/* ]]; then
		dir=$(cygpath -m "$dir")
	fi
fi

php "${dir}/wp-cli.phar" "$@"

Then make the file executable:

chmod +x /c/wp-cli/wp

You can now use wp from Git Bash.

Installing via .deb package

On Debian or Ubuntu, just download and open one of the .deb packages: https://github.com/wp-cli/builds/tree/gh-pages/deb

Installing on Fedora 30+

su -c 'dnf install wp-cli'

Installing on CentOS

su -c 'yum install wp-cli'

Using a custom PHP binary

In some cases, like for MAMP installs, you might not want to use the default PHP binary.

To use the latest PHP version provided by MAMP, you'll need to modify your PATH environment variable with the following added to your ~/.bash_profile or ~/.zsh_profile:

PHP_VERSION=$(ls /Applications/MAMP/bin/php/ | sort -n | tail -1)
export PATH=/Applications/MAMP/bin/php/${PHP_VERSION}/bin:$PATH

To use a specific PHP version provided by MAMP, you'll need to determine the path to the PHP version's executable, and modify your PATH environment variable with the following added to your ~/.bash_profile or ~/.zsh_profile:

export PATH=/Applications/MAMP/bin/php/php5.5.26/bin:$PATH

Note there's no assignment of the PHP_VERSION variable in this case, because we aren't dynamically looking up the latest PHP version.

Once you have added that and saved the file, reload the file with:

source ~/.bash_profile

After you've done that, run wp --info to make sure the change has been applied correctly.

For Composer and Git-based WP-CLI installation, you can alternatively set the WP_CLI_PHP environment variable, if you don't want to modify PATH for some reason.

Guides

Installing WP-CLI Packages

WP-CLI packages are community-maintained projects built on WP-CLI. They can contain WP-CLI commands, but they can also just extend WP-CLI in some way.

To find existing packages to install, take a look at Packagist.

Installing a Package

Use the wp package install command to install a package. Packages can be specified as:

# Install a package by name
wp package install wp-cli/server-command

# Install from a Git URL
wp package install https://github.com/wp-cli/server-command.git

# Install from a local directory
wp package install /path/to/package

# Install from a .zip file
wp package install package.zip

Packages are installed to ~/.wp-cli/packages/ by default. Use the WP_CLI_PACKAGES_DIR environment variable to provide a custom path. See also: Sharing WP-CLI Packages.

Version Constraints

When installing WP-CLI packages, you can specify version constraints to control which version of a package is installed.

Version constraints are specified by appending a colon (:) followed by the constraint to the package name:

wp package install <package-name>:<version-constraint>

WP-CLI uses Composer's version constraint syntax. Here are some commonly used examples:

# Install a specific version
wp package install wp-cli/server-command:2.0.0

# Install with caret operator (allows non-breaking updates)
wp package install wp-cli/server-command:^1.0

# Install with tilde operator (allows patch-level updates)
wp package install wp-cli/server-command:~1.2

# Install the latest stable release
wp package install wp-cli/server-command:@stable

# Install from a development branch
wp package install wp-cli/server-command:dev-main

For complete documentation on version constraint syntax and operators, see the Composer documentation on versions and constraints.

Guides

Quick Start

Congratulations! You've installed WP-CLI for the first time, and are ready to level-up your use of WordPress. This page contains a brief introduction to WP-CLI with some example usage.

Introduction

WP-CLI is a command line interface for WordPress. The project's goal is to offer a complete alternative to the WordPress admin; for any action you might want to perform in the WordPress admin, there should be an equivalent WP-CLI command.

For instance, because you can install a plugin from the WordPress admin, you can also install a plugin with WP-CLI:

$ wp plugin install akismet
Installing Akismet (3.1.8)
Downloading install package from https://downloads.wordpress.org/plugin/akismet.3.1.8.zip...
Unpacking the package...
Installing the plugin...
Plugin installed successfully.

And, because you can also activate plugins from the WordPress admin, you can activate a plugin with WP-CLI:

$ wp plugin activate akismet
Success: Plugin 'akismet' activated.

One key difference between using the WordPress admin and WP-CLI: performing any action takes many fewer clicks. As you become more familiar with the command line, you'll notice performing a given task with WP-CLI is generally much faster than performing the same task through the WordPress admin. Investing time upfront into learning how to better use WP-CLI pays dividends in the long term.

Common Terms

Throughout your usage of WP-CLI, you'll hear certain terms used over and over again.

For instance, a command is an atomic unit of WP-CLI functionality. wp plugin install is one such command, as is wp plugin activate. Commands represent a name (e.g. 'plugin install') and a callback, and are registered with WP_CLI::add_command() (doc).

The synopsis defines which positional and associative arguments a command accepts. Let's take a look at the synopsis for wp plugin install:

$ wp plugin install
usage: wp plugin install <plugin|zip|url>... [--version=<version>] [--force] [--activate] [--activate-network]

In this example, <plugin|zip|url>... is the accepted positional argument. In fact, wp plugin install accepts the same positional argument (the slug, ZIP, or URL of a plugin to install) multiple times. [--version=<version>] is one of the accepted associative arguments. It's used to denote the version of the plugin to install. Notice, too, the square brackets around the argument definition; square brackets mean the argument is optional.

WP-CLI also has a series of global arguments which work with all commands. For instance, including --debug means your command execution will display all PHP errors, and add extra verbosity to the WP-CLI bootstrap process.

Practical Examples

Ready to dive in? Here are some common examples of how WP-CLI is used:

Download and install WordPress in seconds

  1. Download the latest version of WordPress with wp core download (doc).
$ wp core download --path=wpclidemo.dev
Creating directory '/srv/www/wpclidemo.dev/'.
Downloading WordPress 4.6.1 (en_US)...
Using cached file '/home/vagrant/.wp-cli/cache/core/wordpress-4.6.1-en_US.tar.gz'...
Success: WordPress downloaded.
  1. Create a new wp-config.php file with wp config create (doc).
$ cd wpclidemo.dev
$ wp config create --dbname=wpclidemo --dbuser=root --prompt=dbpass
1/10 [--dbpass=<dbpass>]:
Success: Generated 'wp-config.php' file.
  1. Create the database based on wp-config.php with wp db create (doc).
$ wp db create
Success: Database created.
  1. Install WordPress with wp core install (doc).
$ wp core install --url=wpclidemo.dev --title="WP-CLI" --admin_user=wpcli --admin_password=wpcli --admin_email=info@wp-cli.org
Success: WordPress installed successfully.

That's it!

Update plugins to their latest version

Use wp plugin update --all (doc) to update all plugins to their latest version.

$ wp plugin update --all
Enabling Maintenance mode...
Downloading update from https://downloads.wordpress.org/plugin/akismet.3.1.11.zip...
Unpacking the update...
Installing the latest version...
Removing the old version of the plugin...
Plugin updated successfully.
Downloading update from https://downloads.wordpress.org/plugin/nginx-champuru.3.2.0.zip...
Unpacking the update...
Installing the latest version...
Removing the old version of the plugin...
Plugin updated successfully.
Disabling Maintenance mode...
Success: Updated 2/2 plugins.
+------------------------+-------------+-------------+---------+
| name                   | old_version | new_version | status  |
+------------------------+-------------+-------------+---------+
| akismet                | 3.1.3       | 3.1.11      | Updated |
| nginx-cache-controller | 3.1.1       | 3.2.0       | Updated |
+------------------------+-------------+-------------+---------+

Add a user as a super-admin

On multisite, use wp super-admin add (doc) to grant super admin capabilities to an existing user.

$ wp super-admin add wpcli
Success: Granted super-admin capabilities.

Regenerate thumbnails

If you've added or changed an image size registered with add_image_size(), you may want to use wp media regenerate (doc) so your theme displays the correct image size.

wp media regenerate --yes
Found 1 image to regenerate.
1/1 Regenerated thumbnails for "charlie-gpa" (ID 4).
Success: Finished regenerating the image.

Search and replace URLs

If you're moving a database from one domain to another, wp search-replace (doc) makes it easy to update all URL references in your database.

wp search-replace 'http://oldsite.com' 'http://newsite.com' --dry-run

When ready to replace the links, run:

wp search-replace 'http://oldsite.com' 'http://newsite.com'

Wondering what's next? Browse through all of WP-CLI's commands to explore your new world. For more detailed information about creating custom commands, visit the WP-CLI Commands Cookbook. Or, catch up with shell friends to learn about helpful command line utilities.

Guides

Running Commands Remotely

Using an SSH connection

WP-CLI accepts an --ssh=[<scheme>:][<user>@]<host>[:<port>][<path>] global parameter for running a command against a remote WordPress install. This argument works similarly to how the SSH connection is parameterized in tools like scp or git.

Under the hood, WP-CLI proxies commands to the ssh executable, which then passes them to the WP-CLI installed on the remote machine. The syntax for --ssh=[<scheme>:][<user>@]<host>[:<port>][<path>] is interpreted according to the following rules:

Note: you need to have a copy of WP-CLI installed on the remote server, accessible as wp.

Furthermore, --ssh=<host> won’t load your ~/.bash_profile if you have a shell alias defined, or are extending the $PATH environment variable. If this affects you, here’s a more thorough explanation of how you can make wp accessible.

Scheme

You can utilize the scheme component of the ssh argument to define a shorthand for connecting to local containerized or virtualized machines.

The scheme argument is set to ssh by default, but it also accepts vagrant, docker, and docker-compose as alternate options.

docker

To use Docker, the command is: wp rewrite flush --ssh=docker:<name> The Docker container's name can be found by using the docker ps command.

docker-compose

For Docker Compose, the command is: wp option get home_url --ssh=docker-compose:<name> The Docker container's name can be located in the docker-compose.yml file.

vagrant

With Vagrant, you can use the command: wp rewrite flush --ssh=vagrant:<name>

Aliases

WP-CLI aliases are shortcuts you register in your wp-cli.yml or config.yml to effortlessly run commands against any WordPress install.

For instance, when you are working locally, have registered a new rewrite rule and need to flush rewrites inside of your Vagrant-based virtual machine, you can run:

# Run the flush command on the development environment
$ wp @dev rewrite flush
Success: Rewrite rules flushed.

Then, once the code goes to production, you can run:

# Run the flush command on the production environment
$ wp @prod rewrite flush
Success: Rewrite rules flushed.

You don't need to SSH into machines, change directories, and generally spend a full minute to get to a given WordPress install, you can just let WP-CLI know what machine to work with and it knows how to make the actual connection.

It can also easily utilize Vagrant's ssh helper command to figure out the SSH parameters, by piping the WP-CLI command to vagrant ssh using the vagrant scheme like --ssh=vagrant:default where default is the Vagrant machine name/id, or if defined as an alias like the examples below. Some Vagrant boxes ship this by default so you can use WP-CLI from the host machine out-of-the-box.

Additionally, alias groups let you register groups of aliases. If you want to run a command against both configured example sites, you can use a group like @both:

# Run the update check on both environments
$ wp @both core check-update
Success: WordPress is at the latest version.
Success: WordPress is at the latest version.

Aliases can be registered in your project’s wp-cli.yml file, or your user’s global ~/.wp-cli/config.yml file:

@prod:
  ssh: dev_user@example.com~/webapps/production
@dev:
  ssh: vagrant@192.168.50.10/srv/www/example.dev
@local:
  ssh: vagrant:default
@both:
  - @prod
  - @dev

You can find more information about how to set up your configuration files in the Config section.

Running custom commands remotely

If you want to run a custom command on a remote server, that custom command needs to be installed on the remote server, but it does not have to be installed on the local machine you're launching wp from.

You can use the WP-CLI package manager remotely to install custom commands to remote machines.

Example:

# The command is not installed on either local or remote machine
$ wp db ack
Error: 'ack' is not a registered subcommand of 'db'. See 'wp help db'.
$ wp @dev db ack
Error: 'ack' is not a registered subcommand of 'db'. See 'wp help db'.

# To make the command work on the remote machine, we can install it remotely
# through the WP-CLI package manager
$ wp @dev package install runcommand/db-ack
Installing package runcommand/db-ack (dev-master)
Updating /home/vagrant/.wp-cli/packages/composer.json to require the package...
Using Composer to install the package...
---
Loading composer repositories with package information
Updating dependencies
Resolving dependencies through SAT
Dependency resolution completed in 0.311 seconds
Analyzed 4726 packages to resolve dependencies
Analyzed 162199 rules to resolve dependencies
Package operations: 1 install, 0 updates, 0 removals
Installs: runcommand/db-ack:dev-master aff8ccc
 - Installing runcommand/db-ack (dev-master aff8ccc)
Writing lock file
Generating autoload files
---
Success: Package installed.

# Now we can run the command remotely, even though it is not installed locally
$ wp @dev db ack test_email@example.com
wp_users:user_email
9:test_email@example.com

Running from Windows Powershell

Windows Powershell has a special meaning for the @ symbol, meaning it cannot be used without quoting or escaping. To run commands remotely, you need to use one of the following notation.

$ wp `@dev core version
$ wp "@dev" core version

Making WP-CLI accessible on a remote server

Running a command remotely through SSH requires having wp accessible on the $PATH on the remote server. Because SSH connections don’t load ~/.bashrc or ~/.zshrc, you may need to specify a custom $PATH when using wp --ssh=<host>. A few ways to make wp available on the remote server are:

Copy WP-CLI binary to $HOME / bin

In many Linux distros, $HOME/bin is in the $PATH by default, so a way to make wp accessible is to create a $HOME/bin directory, if it doesn't already exist, and move the WP-CLI binary into $HOME/bin/wp:

mkdir -p ~/bin
cd ~/bin
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
mv wp-cli.phar wp

If $HOME/bin is not already in your path, then you can define it in your ~/.bashrc file or equivalent for your remote server's specific shell:

#.bashrc
PATH="$HOME/bin:$PATH"

Specify the $PATH in $HOME / .ssh/ environment

Another way to achieve this is to specify the $PATH in the remote machine user's ~/.ssh/environment file, provided that that machine's sshd has been configured with PermitUserEnvironment=yes (see OpenSSH documentation).

Remote non-interactive shell resolves aliases and runs wp as alias with php

Some web hosts are configured very restrictively:

The solution: On the remote configure ~/.bashrc like this:

Configure the remote non-interactive shell to resolve aliases

Alias wp to php which runs the WP-CLI binary

Using before_ssh hook on client machine

Alternatively, in case you cannot make it work from within the server, you can achieve the same effect by hooking into the before_ssh hook, and defining an environment variable with the command you’d like to run:

WP_CLI::add_hook( 'before_ssh', function() {

    $host = WP_CLI\Utils\parse_ssh_url(
        WP_CLI::get_runner()->config['ssh'],
        PHP_URL_HOST
    );

    switch( $host ) {
        case 'example.com':
            putenv( 'WP_CLI_SSH_PRE_CMD=export PATH=$HOME/bin:$PATH' );
            break;
    }
} );

If you put the code above in a pre-ssh.php file, you can load it for your entire environment by requiring it from your ~/.wp-cli/config.yml file:

require:
  - pre-ssh.php
Guides

Sharing WP-CLI Packages

By default, WP-CLI places installed packages in ~/.wp-cli/packages/, a hidden subdirectory of the user’s home directory.

Because the home directory is different for each user, this naturally means each system user will have a separate directory of installed packages. If you have multiple active shell users on a server, and want to share installed WP-CLI packages between them, there are a couple of supported ways to do this.

WP_CLI_PACKAGES_DIR environment variable

To override the directory WP-CLI uses for installed packages, provide a WP_CLI_PACKAGES_DIR environment variable. If you wish for multiple users to share the same packages directory, you can simply provide the same WP_CLI_PACKAGES_DIR environment variable for each user.

vim /etc/environment
export WP_CLI_PACKAGES_DIR=/usr/local/lib/wp-cli-packages

Similarly, you can make sure the directory is only writable by a specific user to make packages available to all users, but only installable by the specific user.

It’s worth noting the WP_CLI_PACKAGES_DIR environment variable overrides WP-CLI’s default behavior of loading packages installed in the user’s home directory. If you want to support both, you’ll need to take the second approach.

Composer project in a shared directory

WP-CLI’s installed packages directory is simply a Composer project under the hood. Given this architecture, you can create your own Composer project in an arbitrary directory, and load it into scope using WP-CLI’s --require=<path> flag.

First, create your Composer project.

$ mkdir /usr/local/lib/wp-cli-packages
$ cd /usr/local/lib/wp-cli-packages
$ composer init -n --name=runcommand/wp-cli-packages -s=dev
$ composer require wp-cli/server-command

Now, once your Composer project has a dependency or two, you can use wp --require=<path/to/autoload> (or the equivalent config.yml statement) to load the packages into WP-CLI.

Guides

Troubleshooting Guide

Before you start to report a new issue on the GitHub repository, make sure to check your local installation, as some settings may typically result in an unexpected outcome of your WP-CLI commands.

How do I get more verbose information about my WP-CLI installation?

WP-CLI offers the command wp --info, which provides you with a lot of information about your WP-CLI install environment. The output will tell you,

What should I do, before I start debugging issues?

Before you start to debug issues, make sure you are using the latest version of WP-CLI. The latest version may already have solved an issue you experience. The command wp cli update will upgrade your WP-CLI version or confirm you already use the latest version. If the installation hangs, please ensure that you are allowed to connect to GitHub using SSL (port 443) and git (port 9418) for outbound connections.

What should I do if the WP-CLI output is different than expected?

Before starting to investigate a bug, you should be aware of the factors that can change the default behavior of WP-CLI and how you can check whether they might be at the root of the issue. There are five main subsystems for modifying this default behavior: environment variables, configuration files, WP-CLI packages, wp-config.php file and WordPress extensions (plugins, themes, must-use plugins, drop-ins).

Environment Variables

The setup of your working environment is a prerequisite for running WP-CLI. You can check your current environment settings with the shell command env.

If you want to run WP-CLI remotely using SSH, it is required that the command wp is accessible on the path of the remote server. WP-CLI’s behavior can also be changed at runtime through the use of environment variables:

To set an environment variable on demand, you can place the environment variable definition before the WP-CLI command you mean to run (e.g. EDITOR=vim wp post edit 1); to overwrite environment variables, use export VARIABLE=value in your ~/.bashrc or ~/.zshrc.

WP-CLI Configuration Files

Configuration files let you customize the behavior of WP-CLI to adapt it to your personal needs or those of your project. If the configuration file is incorrect or includes unwanted modifications to the default behavior, the output of WP-CLI will most likely reflect the error.

Note that a project configuration file can override settings in a global configuration file.

Rename or delete your configuration file(s) and compare the result, to find out if a configuration setting might have caused the issue.

WP-CLI Packages

WP-CLI packages are community-maintained projects built on WP-CLI. They can contain WP-CLI commands, but they can also just extend WP-CLI in some way. While WP-CLI might be perfectly working, a package with errors can cause unexpected results, too. To skip loading all installed packages, use wp --skip-packages.

WordPress Configuration File (wp-config.php)

Errors may result from moving or editing wp-config.php beyond what WP-CLI supports. If you get a parse error, check the file encoding of your wp-config.php (UTF-8 without BOM).

Make sure that the line require_once(ABSPATH . 'wp-settings.php'); remains in the wp-config.php file and don't modify wp-config.php beyond constant definitions. If you call WordPress functions within wp-config.php, PHP will fail with a fatal error.

If you want to use $_SERVER['HTTP_HOST'] in your wp-config.php, you’ll need to set a default value in WP-CLI context:

if ( defined( 'WP_CLI' ) && WP_CLI && ! isset( $_SERVER['HTTP_HOST'] ) ) {
    $_SERVER['HTTP_HOST'] = 'example.com';
}

Instead of $_SERVER['document_root'] use dirname( __FILE__ ) or similar.

WordPress Extensions

WordPress Themes and Plugins might conflict with the loading process of WP-CLI or interfere e.g. by redirecting users. They often make assumptions that are not true in the WP-CLI context, like using the hostname of the current request (which is a concept that does not exist within command-line context).

You can bypass single plugins and themes (e.g. --skip-plugins=akismet) or skip them entirely (wp --skip-plugins --skip-themes).

What should I do if WP-CLI reports an error?

Common problems are the result of changes in your wp-config.php or web server configuration. The section "Common issues" of the handbook lists the most common error messages and explains how to handle these errors.

Try to reproduce the issue in a fresh installation of WordPress with a default Theme (Twenty …) and no plugins installed. If the issue only reproduces in a custom environment, then the issue is a bug in your environment, not WP-CLI. Also make sure to enable WordPress' debugging mode by setting the constant define( 'WP_DEBUG', true ); in your wp-config.php. It may reveal problems with your existing WordPress installation, not caused by WP-CLI.

Additionally you can use WP-CLI's global parameter --debug to show all PHP errors and add verbosity to WP-CLI bootstrap.

I have checked all above, but still have an issue. Where can I report issues?

If you think you’ve found a bug, we’d love to hear from you to get it fixed.

Bug reporting for WP-CLI is handled on GitHub. Before you create a new issue, please search existing issues to see if there’s an existing resolution to it. If there isn’t an open or fixed issue for your bug, please follow our guidelines for submitting a bug report to make sure it gets addressed in a timely manner. Providing the summary, steps to reproduce, environmental details, and other specifics identified below will help guarantee you are submitting a full bug report.

Please provide us with:

You can find a verbose description of these details of an issue report in the guidelines for submitting a bug report.

Guides

Verifying WP-CLI Downloads

This guide explains how to cryptographically verify the authenticity and integrity of WP-CLI releases after downloading them from the Internet and before installing them on your system.

Why verify downloads?

Verifying downloads ensures:

Quick verification guide

After downloading wp-cli.phar, you can verify it using either GPG signatures or checksums.

Method 1: Verify using GPG signature (Recommended)

This method provides the strongest security as it verifies both the integrity and authenticity of the download.

Step 1: Download the signature file

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar.asc

For a specific version release, download from GitHub releases (replace X.X.X with the desired version):

curl -LO https://github.com/wp-cli/wp-cli/releases/download/vX.X.X/wp-cli-X.X.X.phar
curl -LO https://github.com/wp-cli/wp-cli/releases/download/vX.X.X/wp-cli-X.X.X.phar.asc

For example, to download version 2.12.0:

curl -LO https://github.com/wp-cli/wp-cli/releases/download/v2.12.0/wp-cli-2.12.0.phar
curl -LO https://github.com/wp-cli/wp-cli/releases/download/v2.12.0/wp-cli-2.12.0.phar.asc

Step 2: Import the WP-CLI release signing key

WP-CLI releases are signed with the WP-CLI release signing key. Import the public key:

curl -L https://raw.githubusercontent.com/wp-cli/builds/gh-pages/wp-cli.pgp | gpg --import

The key fingerprint is:

63AF 7AA1 5067 C056 16FD  DD88 A3A2 E8F2 26F0 BC06

You can verify the key fingerprint after importing:

gpg --list-keys --with-fingerprint releases@wp-cli.org

The output should include:

pub   rsa2048 2018-05-31 [SC]
      63AF 7AA1 5067 C056 16FD  DD88 A3A2 E8F2 26F0 BC06
uid           [ unknown] WP-CLI Releases <releases@wp-cli.org>
sub   rsa2048 2018-05-31 [E]

Note: Releases prior to v1.5.1 (July 2016) were signed with a different key (fingerprint: 3B91 91CD 3CF1 B5C5 10EE FAF6 AFAA E4A2 85E0 40A8). If you need to verify older releases, import that key from the builds repository README.

Step 3: Verify the signature

For the latest stable release:

gpg --verify wp-cli.phar.asc wp-cli.phar

For a specific version:

gpg --verify wp-cli-2.12.0.phar.asc wp-cli-2.12.0.phar

If the signature is valid, you'll see output similar to:

gpg: Signature made {DATE}
gpg:                using RSA key 63AF7AA15067C05616FDDD88A3A2E8F226F0BC06
gpg: Good signature from "WP-CLI Releases <releases@wp-cli.org>" [unknown]

You may see a warning about the key not being trusted:

gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.

This is expected if you haven't personally verified and signed the WP-CLI key. The important part is seeing "Good signature" which confirms the file was signed by this key and hasn't been modified.

Method 2: Verify using checksums

If GPG is not available, you can verify the integrity (but not authenticity) using SHA-512 or SHA-256 checksums.

Using SHA-512

Download the SHA-512 checksum file:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar.sha512

Or for a specific release (replace X.X.X with your version):

curl -LO https://github.com/wp-cli/wp-cli/releases/download/vX.X.X/wp-cli-X.X.X.phar.sha512

Verify the checksum by comparing the output:

sha512sum wp-cli.phar
cat wp-cli.phar.sha512

The two hashes should match exactly.

Using SHA-256

Download the SHA-256 checksum file:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar.sha256

Or for a specific release (replace X.X.X with your version):

curl -LO https://github.com/wp-cli/wp-cli/releases/download/vX.X.X/wp-cli-X.X.X.phar.sha256

Verify the checksum by comparing the output:

sha256sum wp-cli.phar
cat wp-cli.phar.sha256

The two hashes should match exactly.

Note: Checksum verification only confirms the file matches the published checksum. It does not verify that the file was created by the WP-CLI maintainers. For complete security, use GPG signature verification.

Where to find verification files

Latest stable and nightly builds

Verification files for the latest stable and nightly builds are available at:

Available files:

Specific version releases

For a specific version, verification files are available on the GitHub releases page:

Each release includes:

Complete installation example with verification

Here's a complete example of downloading, verifying, and installing WP-CLI:

# Download WP-CLI
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

# Download signature
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar.asc

# Import signing key (first time only)
curl -L https://raw.githubusercontent.com/wp-cli/builds/gh-pages/wp-cli.pgp | gpg --import

# Verify signature
gpg --verify wp-cli.phar.asc wp-cli.phar

# If verification succeeds, test it works
php wp-cli.phar --info

# Make executable and move to PATH
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

# Verify installation
wp --info

Additional resources

Guides

Write a check for asserting the value of a given constant

One of the check types included in wp doctor is Constant_Definition, or the ability to assert that a given constant is either defined, a specific value, or falsy. The check type is in use by a couple of the default diagnostic checks, and you can use the Constant_Definition check type in your custom doctor.yml configuration file. As an example, here are two checks using Constant_Definition, one which ensures SAVEQUERIES isn't defined and the other which ensures DISALLOW_FILE_MODS is true:

constant-savequeries-falsy:
  check: Constant_Definition
  options:
    constant: SAVEQUERIES
    falsy: true
constant-disallow-file-mods-true:
  check: Constant_Definition
  options:
    constant: DISALLOW_FILE_MODS
    value: true

Run together, you might see:

$ wp doctor check --config=constant-definition.yml --all
+----------------------------------+---------+--------------------------------------------------+
| name                             | status  | message                                          |
+----------------------------------+---------+--------------------------------------------------+
| constant-savequeries-falsy       | success | Constant 'SAVEQUERIES' is undefined.             |
| constant-disallow-file-mods-true | success | Constant 'DISALLOW_FILE_MODS' is defined 'true'. |
+----------------------------------+---------+--------------------------------------------------+

The Constant_Definition check type accepts the following options:

Guides

Write a check for asserting the value of a given option

One of the check types included in wp doctor is Option_Value, or the ability to assert that a given option is a specific value. The check type is in use by a couple of the default diagnostic checks, and you can use the Option_Value check type in your custom doctor.yml configuration file.

As an example, here are two checks using Option_Value, one which ensures the blog is public and the other which ensures users can't register:

option-blog-public:
  check: Option_Value
  options:
    option: blog_public
    value: 1
option-users-can-register:
  check: Option_Value
  options:
    option: users_can_register
    value: 0

Run together, you might see:

$ wp doctor check --config=option-value.yml --all
+---------------------------+---------+-------------------------------------------------+
| name                      | status  | message                                         |
+---------------------------+---------+-------------------------------------------------+
| option-blog-public        | error   | Site is private but expected to be public.      |
| option-users-can-register | success | Option 'users_can_register' is '0' as expected. |
+---------------------------+---------+-------------------------------------------------+

The Option_Value check type accepts the following options:

Guides

Write a check for verifying contents of WordPress files

One of the check types included in wp doctor is File_Contents, or the ability to check all or a selection of WordPress files for a given regex pattern. The check type is in use by a couple of the default diagnostic checks, and you can use the File_Contents check type in your custom doctor.yml configuration file.

The File_Contents check type is the most efficient way to check WordPress files because it only iterates the filesystem once, regardless of how many file checks you've registered.

As an example, here are two checks using File_Contents, one which ensures sessions aren't used by plugins and the other which ensures $_SERVER['SERVER_NAME'] isn't used in wp-config.php:

file-sessions:
  check: File_Contents
  options:
    regex: .*(session_start|\$_SESSION).*
    only_wp_content: true
file-server-name-wp-config:
  check: File_Contents
  options:
    regex: define\(.+WP_(HOME|SITEURL).+\$_SERVER.+SERVER_NAME
    path: wp-config.php

Run together, you might see:

$ wp doctor check --config=file-contents.yml --all
+----------------------------+---------+-----------------------------------------------------------------------------------------+
| name                       | status  | message                                                                                 |
+----------------------------+---------+-----------------------------------------------------------------------------------------+
| file-sessions              | success | All 'php' files passed check for '.*(session_start|\$_SESSION).*'.                      |
| file-server-name-wp-config | success | All 'php' files passed check for 'define\(.+WP_(HOME|SITEURL).+\$_SERVER.+SERVER_NAME'. |
+----------------------------+---------+-----------------------------------------------------------------------------------------+

The File_Contents check type accepts the following options:

Guides

Write a custom check to perform an arbitrary assertion

Because wp doctor checks are built on top of a foundational abstraction, it's relatively straightforward for you to write your own custom check. The basic requirement is that you create a class extending runcommand\Doctor\Checks\Check that implements a run() method. The run() must set a status and message based on whatever procedural logic As an example, here's an annotated custom check to assert Akismet is activated with a valid API key:

<?php

/**
 * Ensures Akismet is activated with the appropriate credentials.
 */
class Akismet_Activated extends runcommand\Doctor\Checks\Check {

	public function __construct( $options = array() ) {
		parent::__construct( $options );
		// Every check is to run on 'after_wp_load' by default.
		// You could instead use 'before_wp_load' or 'after_wp_config_load'
		$this->set_when( 'after_wp_load' );
	}

	public function run() {
		// If the Akismet isn't activated, bail early.
		if ( ! class_exists( 'Akismet' ) ) {
			$this->set_status( 'error' );
			$this->set_message( "Akismet doesn't appear to be activated." );
			return;
		}
		// Verify that the API exists.
		$api_key = Akismet::get_api_key();
		if ( empty( $api_key ) ) {
			$this->set_status( 'error' );
			$this->set_message( 'API key is missing.' );
			return;
		}
		// Verify that the API key is valid.
		$verification = Akismet::verify_key( $api_key );
		if ( 'failed' === $verification ) {
			$this->set_status( 'error' );
			$this->set_message( 'API key verification failed.' );
			return;
		}
		// Everything looks good, so report a success.
		$this->set_status( 'success' );
		$this->set_message( 'Akismet is activated with a verified API key.' );
	}

}

If the class were placed in an akismet-activated.php file, you could register it with:

plugin-akismet-activated:
  class: Akismet_Activated
  require: akismet-activated.php

Then, run the config file:

$ wp doctor check plugin-akismet-activated --config=doctor.yml
+--------------------------+--------+---------------------+
| name                     | status | message             |
+--------------------------+--------+---------------------+
| plugin-akismet-activated | error  | API key is missing. |
+--------------------------+--------+---------------------+
Guides

Writing Behat Tests for WP-CLI

WP-CLI uses Behat for functional testing. This guide will help you understand how to write and run Behat tests for WP-CLI commands and packages.

Introduction

What are Behat Tests?

Behat is a behavior-driven development (BDD) framework for PHP. In WP-CLI, Behat tests are used as functional tests that execute the entire command and verify it works as expected. Unlike unit tests that test individual functions in isolation, functional tests ensure commands work correctly from end to end.

Why Behat Tests Matter

Stability between releases is an important contract WP-CLI makes with its users. Behat tests help ensure:

Tests are required for most pull requests. If it's a new feature, tests are needed. If it's fixing a bug, tests are needed to prevent regression.

Getting Started

Every WP-CLI repository with commands has a features/ directory containing one or more Gherkin-formatted *.feature files.

Writing Your First Test

Basic Test Structure

Here's a simple example:

Feature: Manage WordPress options

  Scenario: Read an individual option
    Given a WP install

    When I run `wp option get home`
    Then STDOUT should be:
      """
      https://example.com
      """

This test follows the Gherkin syntax with three key components:

In a more human-friendly form:

I have a WordPress installation. When I run wp option get home, then the output from the command should be 'https://example.com'.

Your First Test

Let's write a test for a hypothetical command that displays a greeting:

  1. Create a file features/greeting.feature:
Feature: Test greeting command

  Scenario: Display a greeting
    Given an empty directory

    When I run `wp greeting hello`
    Then STDOUT should contain:
      """
      Hello
      """
    And the return code should be 0

This test:

  1. Starts with an empty directory
  2. Runs your command
  3. Verifies the output contains "Hello"
  4. Verifies the command succeeded (exit code 0)

Common Step Definitions

WP-CLI provides many pre-built step definitions. Here are the most commonly used:

Given (Setup)

When (Actions)

Then (Assertions)

See the Behat Steps reference for a complete list of available steps.

Running Tests

Setting Up Your Test Environment

Before running tests, you need to set up a test database. The composer behat script will automatically detect database availability and fall back to SQLite if there is no running database, making it easy to get started without any setup.

You have two options:

Option 1: SQLite (Recommended for simplicity)

The easiest way to run tests is with SQLite, which requires no database setup:

WP_CLI_TEST_DBTYPE=sqlite composer behat

Or simply run composer behat and let it automatically use SQLite if no database is configured.

Option 2: MySQL/MariaDB

If you prefer to use MySQL or MariaDB, run the setup script to create the test database:

composer prepare-tests

This creates a MySQL user wp_cli_test with password password1 that has full privileges on the wp_cli_test database.

Note: MySQL 8.0 changed the default authentication plugin. If you have connection issues, see this blog post for solutions.

Running the Test Suite

Run all tests:

composer behat

Run tests for a specific feature file:

composer behat -- features/option.feature

Run a specific scenario (by line number):

composer behat -- features/option.feature:10

Run with verbose output:

composer behat -- features/option.feature --format pretty

Re-run only failed tests:

composer behat-rerun

Discovering Available Steps

To see all available step definitions:

composer behat -- --definitions l

This is helpful when writing tests to find the right step for your needs.

Advanced Usage

Writing Effective Tests

Test One Thing at a Time

Each scenario should test a single piece of functionality:

# Good - tests one specific behavior
Scenario: Delete a post
  Given a WP install
  And I run `wp post create --post_title='Test' --porcelain`
  And save STDOUT as {POST_ID}

  When I run `wp post delete {POST_ID}`
  Then STDOUT should contain:
    """
    Success: Trashed post
    """

# Bad - tests multiple unrelated behaviors
Scenario: Create, update, and delete a post
  # Too much in one test...

Use Descriptive Scenario Names

Make it clear what's being tested:

# Good
Scenario: Fail gracefully when post doesn't exist

# Bad
Scenario: Test delete

Test Both Success and Failure

Don't just test the happy path:

Scenario: Successfully create a post
  Given a WP install
  When I run `wp post create --post_title='Test'`
  Then the return code should be 0

Scenario: Fail when required argument is missing
  Given a WP install
  When I try `wp post create`
  Then the return code should be 1
  And STDERR should contain:
    """
    Error
    """

Using Variables

Store command output in variables for later use:

Scenario: Create and then fetch a post
  Given a WP install

  When I run `wp post create --post_title='Test Post' --porcelain`
  Then save STDOUT as {POST_ID}

  When I run `wp post get {POST_ID} --field=title`
  Then STDOUT should be:
    """
    Test Post
    """

Testing Tables and Structured Output

Testing Table Output

Scenario: List posts in table format
  Given a WP install
  And I run `wp post create --post_title='First'`
  And I run `wp post create --post_title='Second'`

  When I run `wp post list --fields=ID,post_title`
  Then STDOUT should be a table containing rows:
    | ID | post_title |
    | 1  | First      |
    | 2  | Second     |

Testing JSON Output

Scenario: List posts in JSON format
  Given a WP install
  And I run `wp post create --post_title='Test' --porcelain`

  When I run `wp post list --format=json`
  Then STDOUT should be JSON containing:
    """
    [{"post_title":"Test"}]
    """

Testing CSV Output

Scenario: Export posts as CSV
  Given a WP install
  
  When I run `wp post list --format=csv`
  Then STDOUT should be CSV containing:
    | ID | post_title | post_status |
    | 1  | Test       | publish     |

Testing Files and Directories

Scenario: Create a plugin file
  Given a WP install

  When I run `wp scaffold plugin my-plugin`
  Then the my-plugin/my-plugin.php file should exist
  And the my-plugin/my-plugin.php file should contain:
    """
    Plugin Name: My Plugin
    """

Background Sections

Use Background: to run common setup for all scenarios:

Feature: Post management

  Background:
    Given a WP install
    And I run `wp post create --post_title='Test'`

  Scenario: Update post title
    When I run `wp post update 1 --post_title='Updated'`
    Then STDOUT should contain:
      """
      Success
      """

  Scenario: Delete post
    When I run `wp post delete 1`
    Then STDOUT should contain:
      """
      Success
      """

Testing Error Messages

Use When I try instead of When I run to allow commands to fail:

Scenario: Error when plugin doesn't exist
  Given a WP install

  When I try `wp plugin activate non-existent-plugin`
  Then the return code should be 1
  And STDERR should contain:
    """
    Error: The 'non-existent-plugin' plugin could not be found.
    """

Using Scenario Outlines

Test multiple inputs with a single scenario:

Scenario Outline: Create posts with different titles
  Given a WP install

  When I run `wp post create --post_title='<title>'`
  Then STDOUT should contain:
    """
    Success
    """

  Examples:
    | title          |
    | Simple Title   |
    | Title's Quotes |
    | UTF-8: 日本語   |

Writing Custom Steps

For advanced use cases, you might need custom step definitions. These are defined in PHP in your package's test bootstrap file.

Example custom step definition:

/**
 * @Given a custom configuration file
 */
public function aCustomConfigurationFile() {
    $config = <<<EOT
custom_setting: value
another_setting: 123
EOT;
    $this->proc( "echo '{$config}' > wp-cli.yml" )->run_check();
}

Custom steps should:

For most packages, the built-in step definitions in wp-cli/wp-cli-tests are sufficient. Only create custom steps when necessary.

Testing with Different PHP and WordPress Versions

WP-CLI tests run against multiple PHP and WordPress versions in CI. Your tests should work across these versions.

Keep in mind:

Database Credentials

By default, MySQL/MariaDB tests use:

Override these with environment variables from wp-cli/wp-cli-tests:

Alternatively, use SQLite by setting WP_CLI_TEST_DBTYPE=sqlite to avoid database configuration entirely.

Best Practices

Do's

Don'ts

Scaffolding Tests for New Packages

When creating a new WP-CLI package, use wp scaffold package-tests to generate the testing infrastructure:

wp scaffold package my-package
cd my-package
wp scaffold package-tests .

This creates:

See the scaffold-package-command documentation for more details.

Troubleshooting

Tests Fail to Connect to Database

If you see database connection errors:

  1. Try using SQLite instead: WP_CLI_TEST_DBTYPE=sqlite composer behat
  2. If using MySQL, verify it's running: mysql -u wp_cli_test -ppassword1 wp_cli_test
  3. Check your MySQL version (MySQL 8.0 has authentication changes)
  4. Verify the database was created: composer prepare-tests

Tests Pass Locally But Fail in CI

Common causes:

Command Not Found Errors

If your command isn't found during tests:

  1. Verify your composer.json properly declares the command
  2. Check that composer install ran successfully
  3. Ensure your command class is properly autoloaded

Slow Tests

If tests are slow:

Additional Resources

Examples from WP-CLI Packages

Real-world examples from WP-CLI core packages:

Study these to see how different types of commands are tested in practice.


Now you're ready to write comprehensive Behat tests for your WP-CLI commands! Remember: good tests make WP-CLI more reliable for everyone.

How to WP-CLI

How to WP-CLI

How to WP-CLI

Introduction

Using WP-CLI, you can perform specific tasks using the command line instead of accessing the WordPress admin panel. In the following section, we will cover step by step some of these tasks.

How to WP-CLI

Figure out why WordPress is slow

wp profile is a WP-CLI command to help you quickly identify what's slow with WordPress. It's designed to work alongside Xdebug and New Relic because it's easy to deploy to any server that has WP-CLI. With wp profile, you gain quick visibility into key performance metrics (execution time, query count, cache hit/miss ratio, etc.) to guide further debugging.

Installation

First, install the wp profile package if you haven't already:

$ wp package install wp-cli/profile-command

Step 1 - Profile the WordPress load process

Dealing with a slow WordPress install you've never worked with before? Run wp profile stage to see metrics for each stage of the WordPress load process. Include the --url=<url> argument to mock the request as a specific URL.

$ wp profile stage --url=runcommand.io
+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+
| stage      | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | hook_time | hook_count | request_time | request_count |
+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+
| bootstrap  | 0.7597s | 0.0052s    | 14          | 93.21%      | 357        | 26           | 0.3328s   | 2717       | 0s           | 0             |
| main_query | 0.0131s | 0.0004s    | 3           | 94.29%      | 33         | 2            | 0.0065s   | 78         | 0s           | 0             |
| template   | 0.7041s | 0.0192s    | 147         | 92.16%      | 2350       | 200          | 0.6982s   | 6130       | 0s           | 0             |
+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+
| total (3)  | 1.477s  | 0.0248s    | 164         | 93.22%      | 2740       | 228          | 1.0375s   | 8925       | 0s           | 0             |
+------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+

When WordPress handles a request from a browser, it's essentially executing as one long PHP script. wp profile stage breaks the script into three stages:

Step 2 - Dive into a specific stage

In the example from above, bootstrap seems a bit slow, so let's dive into it further. Run wp profile stage bootstrap to dive into higher fidelity mode of a given stage. Use the --spotlight flag to filter out the zero-ish results.

$ wp profile stage bootstrap --url=runcommand.io --spotlight
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| hook                     | callback_count | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| muplugins_loaded:before  |                | 0.1644s | 0.0017s    | 1           | 40%         | 2          | 3            | 0s           | 0             |
| muplugins_loaded         | 2              | 0.0005s | 0s         | 0           | 50%         | 1          | 1            | 0s           | 0             |
| plugins_loaded:before    |                | 0.1771s | 0.0008s    | 6           | 77.63%      | 59         | 17           | 0s           | 0             |
| plugins_loaded           | 14             | 0.0887s | 0s         | 0           | 100%        | 104        | 0            | 0s           | 0             |
| after_setup_theme:before |                | 0.043s  | 0s         | 0           | 100%        | 26         | 0            | 0s           | 0             |
| init                     | 82             | 0.1569s | 0.0018s    | 7           | 96.88%      | 155        | 5            | 0s           | 0             |
| wp_loaded:after          |                | 0.027s  | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (7)                | 98             | 0.6575s | 0.0043s    | 14          | 77.42%      | 347        | 26           | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

Each stage is further segmented by wp profile based on its primary actions. For the bootstrap stage, the primary actions include muplugins_loaded, plugins_loaded, and init. You can also see some intermediate actions like plugins_loaded:before and wp_loaded:after. These intermediate actions correspond to script execution before (or after) actual WordPress hooks. They're pseudo hooks in a sense.

Step 3 - Profile specific hooks

When you've found a specific hook you'd like to assess, run wp profile hook <hook>. Include the --fields=<fields> argument to limit output to certain fields.

$ wp profile hook plugins_loaded --url=runcommand.io --fields=callback,time,location
+------------------------------------------------------------+---------+-----------------------------------------------------------------+
| callback                                                   | time    | location                                                        |
+------------------------------------------------------------+---------+-----------------------------------------------------------------+
| wp_maybe_load_widgets()                                    | 0.0046s | wp-includes/functions.php:3501                                  |
| wp_maybe_load_embeds()                                     | 0.0003s | wp-includes/embed.php:162                                       |
| VaultPress_Hotfixes->protect_jetpack_402_from_oembed_xss() | 0s      | vaultpress/class.vaultpress-hotfixes.php:124                    |
| _wp_customize_include()                                    | 0s      | wp-includes/theme.php:2052                                      |
| EasyRecipePlus->pluginsLoaded()                            | 0.0013s | easyrecipeplus/lib/EasyRecipePlus.php:125                       |
| Gamajo\GenesisHeaderNav\genesis_header_nav_i18n()          | 0.0007s | genesis-header-nav/genesis-header-nav.php:61                    |
| DS_Public_Post_Preview::init()                             | 0.0001s | public-post-preview/public-post-preview.php:52                  |
| wpseo_load_textdomain()                                    | 0.0004s | wordpress-seo-premium/wp-seo-main.php:222                       |
| load_yoast_notifications()                                 | 0.0016s | wordpress-seo-premium/wp-seo-main.php:381                       |
| wpseo_init()                                               | 0.0329s | wordpress-seo-premium/wp-seo-main.php:240                       |
| wpseo_premium_init()                                       | 0.0019s | wordpress-seo-premium/wp-seo-premium.php:79                     |
| wpseo_frontend_init()                                      | 0.0007s | wordpress-seo-premium/wp-seo-main.php:274                       |
| Black_Studio_TinyMCE_Plugin->load_compatibility()          | 0.0016s | black-studio-tinymce-widget/black-studio-tinymce-widget.php:206 |
| Jetpack::load_modules()                                    | 0.0564s | jetpack/class.jetpack.php:1672                                  |
+------------------------------------------------------------+---------+-----------------------------------------------------------------+
| total (14)                                                 | 0.1026s |                                                                 |
+------------------------------------------------------------+---------+-----------------------------------------------------------------+

There you have it! We've discovered that wpseo_init() and Jetpack::load_modules() are collectively contributing ~100ms to every page load.

With wp profile, discovering why WordPress is slow becomes the easy part.

How to WP-CLI

How to create a custom plugin

If you want to create your plugins, WP-CLI has a powerful scaffold command that allows us to generate starter code. In this guide we will see how to generate starter code for a basic plugin.

Step 1 - Scaffold the plugin files

The following command uses several options to lets us specify the plugin slug, its name, description, author name and uri as well as the plugin uri. You can replace the values passed to the options below to customize the plugin based on your needs.

    $ wp scaffold plugin wpcli-demo-plugin --plugin_name="WP-CLI Demo Plugin" --plugin_description="This is a wp-cli demo plugin" --plugin_author=wp-cli --plugin_author_uri="https://wp-cli.org" --plugin_uri="https://plugins.wp-cli.org/demo-plugin"
    Success: Created test files.

The above command generates a new folder called wpcli-demo-plugin in the plugins directory, with files such as:

Unless you use the --skip-tests flag, test-related files are generated too. These include:

Step 2 - create a custom post type:

We can now use the scaffold command again to add a custom post type inside our new plugin using the wp scaffold post-type command.

    $ wp scaffold post-type books --label=Book --textdomain=wpcli-demo-plugin --dashicon=dashicons-book-alt --plugin=wpcli-demo-plugin
    Success: Created '/wpcli-demo-plugin/post-types/books.php'.

Step 3 - Write code inside the main file:

The main plugin file wpcli-demo-plugin.php is the starting point that we can use to write our plugin logic.

Inside the main plugin file lets now reference the new post type we just created.

Open in your favourite text editor the file wpcli-demo-plugin.php and under the line saying "your code starts here" add the following:

\\Your code starts here.
require('post-types/books.php');

Step 4 - Activate the plugin

You can now use two wp-cli commands to check the list of plugins and activate your newly created plugin. wp plugin list and wp plugin activate. The first command lists all plugins installed while the second activates a given plugin.

    $ wp plugin list
    +-------------------+----------+-----------+---------+
    | name              | status   | update    | version |
    +-------------------+----------+-----------+---------+
    | akismet           | inactive | available | 4.1.5   |
    | hello             | inactive | none      | 1.7.2   |
    | wpcli-demo-plugin | inactive | none      | 0.1.0   |
    +-------------------+----------+-----------+---------+

From the list above we can see that our plugin wpcli-demo-plugin is inactive. Let's enable it using the other command.

    $ wp plugin activate wpcli-demo-plugin
    Plugin 'wpcli-demo-plugin' activated.
    Success: Activated 1 of 1 plugins.

Our plugin is now active. We can visit the WordPress admin panel and start using our books custom post type.

How to WP-CLI

How to install WordPress

Downloading and installing WordPress using WP-CLI is straightforward. It takes four steps. First, you will need to download WordPress using the wp core download command.

Step 1 - Download WordPress

The syntax of the command to download WordPress is the following: wp core download [--path=<path>] [--locale=<locale>] [--version=<version>] [--skip-content] [--force]

    $ wp core download --path=wpdemo.test --locale=it_IT
    Creating directory '/wpdemo.test/'.
    Downloading WordPress 5.4.1 (it_IT)...
    md5 hash verified: 3fa03967b47cdfbf263462d451cdcdb8
    Success: WordPress downloaded.

The command above creates a wpdemo.test/ folder inside your current working directory and downloads the latest WordPress version. You can replace the --path=wpdemo.test with your desired folder name and the --locale=it_IT with your desired locale. You can omit the --locale option and, that will download by default WordPress in American English using the locale en_US.

Next, navigate into the newly created folder (cd wpdemo.test) before running the subsequent commands.

Step 2 - Generate a config file

In this step, we will generate a config file and set up the database credentials for our installation. The basic syntax of the command is the following: wp config create --dbname=<dbname> --dbuser=<dbuser> [--dbpass=<dbpass>]

    $ wp config create --dbname=your_db_name_here --dbuser=your_db_user_here --prompt=dbpass
    1/10 [--dbpass=<dbpass>]: type_your_password
    Success: Generated 'wp-config.php' file.

The command above generates the wp-config.php file and adds to it the database credentials that you passed. Make sure to replace your_db_name_here with the name you want to assign to the database, replace your_db_user_here with your database user and type the database password when prompted with the following: 1/10 [--dbpass=<dbpass>]:

Step 3 - Create the database

In this step, we are going to create the database based on the information we passed to the wp-config.php file in step 2.

    $ wp db create
    Success: Database created.

Now we are ready to move to the final step where we install WordPress.

Step 4 - Install WordPress

To install WordPress now, we need to run one last command.

    $ wp core install --url=wpclidemo.dev --title="WP-CLI" --admin_user=wpcli --admin_password=wpcli --admin_email=info@wp-cli.org
    Success: WordPress installed successfully.

Remember to replace the values passed to each of the following options with your details:

Congratulations! You have successfully installed WordPress using WP-CLI.

How to WP-CLI

How to put the site in maintenance mode

WP-CLI offers a command to enable, disable maintenance mode and check if maintenance mode is enabled or not.

Step 1 - Check the status

    $ wp maintenance-mode status
    Maintenance mode is not active.

Step 2 - Enable maintenance mode

    $ wp maintenance-mode activate
    Enabling Maintenance mode...
    Success: Activated Maintenance mode.

Step 3 - Disable maintenance mode

    $ wp maintenance-mode deactivate
    Disabling Maintenance mode...
    Success: Deactivated Maintenance mode.
How to WP-CLI

How to start the webserver

You can use the command wp server to launch PHP's built-in web server for a specific WordPress installation. By default, the webserver will start using the localhost and default port 8080 but, you can change them using the --host and --port options.

Step 1 - Start the web server using the default settings

    $ wp server
    PHP 7.2.24-0ubuntu0.18.04.4 Development Server started at Thu Jun  4 17:40:13 2020
    Listening on http://localhost:8080
    Document root is ../wpdemo.test
    Press Ctrl-C to quit.

The command above starts the server using the default settings. We can now open our browser and visit the link http://localhost:8080 to access our WordPress installation.

Step 2 - Start the web server using different settings

If you want to specify a different port number or host, you can pass them to the following flags: --port and --host

    $ wp server --port=9090 --host=192.168.0.4

The command above will start the webserver and listen for requests on http://192.168.0.4:9090. You can open the browser and visit the page to access your WordPress installation.

How to WP-CLI

Identify a Plugin or Theme Conflict

Is WP-CLI not working with your WordPress install, but you don't know why? It might be a plugin or theme conflicting with the WP-CLI load process.

To verify, first run wp --skip-plugins --skip-themes to execute WP-CLI without loading plugins or themes. If WP-CLI runs as expected with plugins and themes skipped, then the problem is caused by one of those plugins or the active theme.

To see if the source of the problem is the active theme, run wp --skip-themes to see if WP-CLI loads as expected.

To see if the source of the problem is an active plugin, run wp --skip-plugins=<plugin-slug> for each active plugin to see which plugin blocks the full execution of WP-CLI. For instance, skipping the Akismet plugin would be:

wp --skip-plugins=akismet

Or, use xargs to try the entire list of active plugins:

wp plugin list --field=name --status=active --skip-plugins | xargs -n1 -I % wp --skip-plugins=% plugin get % --field=name
How to WP-CLI

Plugin Integration Tests

This guide demonstrates how to run integration tests both in continuous integration and locally. The scaffolding uses the WordPress Core "unit tests" that serve to unit-test WordPress Core. Unit tests are useful for testing plugins and themes as well, but if they are used in that way, it turns those tests into "integration tests" - they test the integration between a plugin/theme and WordPress Core. Proper unit tests for a plugin or theme would not load WordPress.

Running tests in continuous integration

The wp scaffold plugin-tests command generates a CircleCI configuration by default. Use --ci=<provider> to generate a configuration for a different supported provider, such as GitHub Actions:

wp scaffold plugin-tests my-plugin --ci=github

All you need to do to enable this is copy and then edit the following files from the WP-CLI sample plugin:

See the docs for an explanation of what each file does. You will then need to specify your unit tests in the tests/ folder.

Running tests locally

Running tests locally can be beneficial during development as it is quicker than committing changes and waiting for CI to run the tests.

We're going to assume that:

So, let's get started:

1. Install PHPUnit

The version of PHPUnit to install depends on both PHP and WordPress versions. See: PHPUnit Compatibility and WordPress Versions – Make WordPress Core

2. Generate the plugin test files

wp scaffold plugin-tests my-plugin

This command will generate all the files needed for running tests, including a .circleci/config.yml file by default. Use --ci=<provider> to generate a configuration for a different supported CI provider.

3. Initialize the testing environment locally

cd into the plugin directory and run the install script (you will need to have wget installed).

bash bin/install-wp-tests.sh wordpress_test root '' localhost latest

The install script first installs a copy of WordPress in the /tmp directory (by default) as well as the WordPress unit testing tools. Then it creates a database to be used while running tests. The parameters that are passed to install-wp-tests.sh setup the test database.

NOTE: This script can be run multiple times without errors, but it will not overwrite previously existing files. So if your DB credentials change, or you want to switch to a different instance of mysql, simply re-running the script won't be enough. You'll need to manually edit the wp-config.php that's installed in /tmp.

4. Run the plugin tests

phpunit

If you have trouble running the install script or PHPUnit, check the common issues guide for help and answers to common issues.

Integrating WP Unit Testing in Windows

Tried and gotten stuck setting up unit tests for your project in Windows?

First, know that some WP-CLI commands don't work in Windows, so you'll want to use Cygwin. Cygwin is also preferred by the WordPress core project.

Second, the bin/install-wp-tests.sh script doesn't work directly in Windows. Windows 10 introduced a Windows Subsystem for Linux but older versions require extra effort. Cygwin is preferred because it runs bash scripts by default.

Third, the bash installation script uses which, a Linux command not available by default in Windows. cURL, svn and wget all can be installed in Windows as separate packages.

Lastly, sometimes the bash script fails to build WordPress because of how it uses tmp folders. If it fails, then you can manually install WordPress to a writable directory, and then manually create wp-tests-config.php.

References

References

References

References

Argument Syntax

WP-CLI commands use a standardized syntax to describe their arguments and options. Understanding this syntax will help you use commands correctly and avoid confusion.

Syntax Conventions

The command synopsis uses specific formatting to indicate whether arguments are required or optional, and what type of values they accept.

Required Arguments

Arguments without square brackets are required and must be provided:

Optional Arguments

Arguments enclosed in square brackets [ ] are optional:

Boolean Flags

Boolean flags are switches that enable or disable a feature:

Associative Arguments

Some commands accept arbitrary key-value pairs:

Repeatable Arguments

Arguments that can be provided multiple times:

Variadic Arguments

Arguments that accept multiple values:

Value Placeholders

The text inside angle brackets describes what kind of value is expected:

Common Patterns

Required Database Connection

Many commands require database connection details:

--dbname=<dbname> --dbuser=<dbuser> [--dbpass=<dbpass>] [--dbhost=<dbhost>]

Format Options

Commands that output data often accept a format option:

[--format=<format>]

Common formats include: table, csv, json, yaml, count

Fields and Filtering

Commands that work with multiple fields:

[--fields=<fields>] [--field=<field>]

Examples

Here are practical examples showing different syntax types:

# Required arguments (no brackets)
wp config create --dbname=mydb --dbuser=root

# Optional arguments with defaults
wp config create --dbname=mydb --dbuser=root --dbhost=localhost

# Boolean flags
wp plugin install hello-dolly --activate --force

# Negated boolean flags
wp plugin list --no-color

# Associative arguments
wp cron event schedule my_hook --foo=bar --baz=qux

# Multiple values
wp plugin install plugin1 plugin2 plugin3

# Optional fields with specific format options
wp post list --format=json --fields=ID,post_title,post_date

Tips

References

Behat Steps

WP-CLI makes use of a Behat-based testing framework and provides a set of custom step definitions to write feature tests.

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


Given

When

Then

References

Config

WP-CLI has a series of global parameters (e.g. --path=<path> and --user=<user>) which work with all commands. They are called global parameters because they affect how WP-CLI interacts with WordPress, and have the same behavior across all commands.

# `--user=<user>` sets request to a specific WordPress user
$ wp --user=wpcli eval 'echo wp_get_current_user()->user_email;'
wpcli@example.com

For repeated usage, WP-CLI can also read options from a YAML configuration file (e.g. wp-cli.yml). WP-CLI automatically discovers configuration files on the filesystem based on rules defined below. These configuration files enable specifying default values for both global parameters and subcommand-specific arguments.

# WordPress develop includes a `wp-cli.yml` to enable easy use of WP-CLI
$ pwd
/srv/www/wordpress-develop.dev
$ cat wp-cli.yml
path: src/

Arguments are interpreted following an order of precedence, from highest priority to lowest:

  1. Command-line arguments.
  2. wp-cli.local.yml file inside the current working directory (or upwards).
  3. wp-cli.yml file inside the current working directory (or upwards).
  4. ~/.wp-cli/config.yml file (path can be changed by setting the WP_CLI_CONFIG_PATH environment variable).
  5. WP-CLI defaults.

Global parameters

The table below lists the available arguments (specified on the command-line) and options (specified in the configuration file).

Description Argument Option
Path to the WordPress files.
Default value: null
--path=<path> path: <path>
Perform operation against a remote server over SSH.
Default value: null
--ssh=[<user>@]<host>[:<port>][<path>] ssh: [<user>@]<host>[:<port>][<path>]
Perform operation against a remote WordPress install over HTTP.
Default value: null
--http=<http> http: <http>
Pretend request came from given URL. In multisite, this argument is how the target site is specified.
Default value: null
--url=<url> url: <url>
Set the WordPress user.
Default value: null
--user=<id|login|email> user: <id|login|email>
Skip loading all or some plugins. Note: mu-plugins are still loaded.
Default value: ""
--skip-plugins[=<plugin>] skip-plugins: <list>
Skip loading all or some themes.
Default value: ""
--skip-themes[=<theme>] skip-themes: <list>
Skip loading all installed packages.
Default value: false
--skip-packages skip-packages: <bool>
Load PHP file before running the command (may be used more than once).
Default value: []
--require=<path> require: <path>
Execute PHP code before running the command (may be used more than once).
Default value: []
--exec=<php-code> exec: <php-code>
Load WordPress in a given context.
Default value: auto
--context[=<context>] context: <context>
(Sub)commands to disable.
Default value: []
Not available as a flag disabled_commands: <list>
Whether to colorize the output.
Default value: "auto"
--[no-]color color: <bool>
Show all PHP errors; add verbosity to WP-CLI bootstrap.
Default value: false
--debug[=<group>] debug: <group>
Prompt the user to enter values for all command arguments, or a subset specified as comma-separated values.
Default value: false
--prompt[=<assoc>] Not available as an option
Suppress informational messages.
Default value: false
--quiet quiet: <bool>
List of Apache Modules that are to be reported as loaded.
Default value: []
Not available as a flag apache_modules: <list>

Config files

WP-CLI can automatically discover and read options from a few configuration file types (when present):

  1. wp-cli.local.yml file inside the current working directory (or upwards).
  2. wp-cli.yml file inside the current working directory (or upwards).
  3. ~/.wp-cli/config.yml file (path can be changed by setting the WP_CLI_CONFIG_PATH environment variable).

Besides the global parameters described above, configuration files can also contain defaults for any subcommand, as well as aliases to one or more WordPress installs.

Here's an annotated example wp-cli.yml file:

# Global parameter defaults
path: wp-core
url: http://example.com
user: admin
color: false
disabled_commands:
  - db drop
  - plugin install
require:
  - path-to/command.php

# Subcommand defaults (e.g. `wp config create`)
config create:
	dbuser: root
	dbpass: 
	extra-php: |
		define( 'WP_DEBUG', true );
		define( 'WP_POST_REVISIONS', 50 );

# Aliases to other WordPress installs (e.g. `wp @staging rewrite flush`)
# An alias can include 'user', 'url', 'path', 'ssh', or 'http'
@staging:
	ssh: wpcli@staging.wp-cli.org
	user: wpcli
	path: /srv/www/staging.wp-cli.org
@production:
	ssh: wpcli@wp-cli.org:2222
	user: wpcli
	path: /srv/www/wp-cli.org

# Aliases can reference other aliases to create alias groups
# Alias groups can be nested
@both:
 - @staging
 - @production

# '_' is a special value denoting configuration options for this wp-cli.yml
_:
	# Merge subcommand defaults from the upstream config.yml, instead of overriding
	merge: true
	# Inherit configuration from an arbitrary YAML file
	inherit: prod.yml

Remote (SSH) configuration

Using the ssh option, WP-CLI can be configured to run on a remote system rather than the current system. Along with the SSH protocol, WP-CLI also supports connecting to Docker containers (including docker-compose) and Vagrant VMs.

The connection type can be passed via the scheme of the --ssh parameter or ssh option.

Supported types are:

All connection types support an optional path suffix to specify a directory to cd to before running WP-CLI; path is a full system path starting with either / or ~. (If WP_CLI_SSH_PRE_CMD is specified, cd is run after this pre-command.)

The SSH connection type also supports two advanced connection configuration options, which must be specified via an alias in the YAML configuration:

See the documentation about running WP-CLI commands remotely for more information.

Context configuration

In WP-CLI v2.6.0, a new global flag --context=<context> was added which allows users to select the WordPress context in which WP-CLI is supposed to execute its command(s).

One of the main goals is to allow WP-CLI to run updates on premium plugins and themes without requiring any special setup. From our initial testing, this allows a large range of popular premium extensions to just work™ with WP-CLI in terms of their update procedures.

Possible values for this flag:

By default, the --context flag was set to cli in the initial release (v2.6.0). In WP-CLI v2.7.0 and later versions, the default was changed to auto. This gradual deployment allowed hosters and site owners to run tests on v2.6.0 by manually setting the context before the default behavior was changed.

If you are still using WP-CLI v2.6.0 but you want to use the default of --context=auto, you can do so by adding the necessary context: auto line to your global wp-cli.yml configuration file. Feel free to check the documentation on WP-CLI configuration files if this is new to you.

Environment variables

WP-CLI's behavior can be changed at runtime through the use of environment variables:

To set an environment variable on demand, simply place the environment variable definition before the WP-CLI command you mean to run.

# Use vim to edit a post
$ EDITOR=vim wp post edit 1

To set the same environment variable value for every shell session, you’ll need to include the environment variable definition in your ~/.bashrc or ~/.zshrc file

# Always use vim to edit a post
export EDITOR=vim
References

Documentation Standards

To promote similarity and consistency between WP-CLI commands, we've produced these documentation standards we'd encourage you to follow. Commonality is a key component of usability, because it reduces the mental overhead required when switching between commands.

Pull requests against the WP-CLI project are reviewed for these standards. Please follow these standards for your custom commands as well.

Command annotation

Here's an example of the PHPdoc annotating the wp cron event schedule command:

/**
 * Schedule a new cron event.
 *
 * ## OPTIONS
 *
 * <hook>
 * : The hook name.
 *
 * [<next-run>]
 * : A Unix timestamp or an English textual datetime description compatible with `strtotime()`. Defaults to now.
 *
 * [<recurrence>]
 * : How often the event should recur. See `wp cron schedule list` for available schedule names. Defaults to no recurrence.
 *
 * [--<field>=<value>]
 * : Associative args for the event.
 *
 * ## EXAMPLES
 *
 *     # Schedule a new cron event.
 *     $ wp cron event schedule cron_test
 *     Success: Scheduled event with hook 'cron_test' for 2016-05-31 10:19:16 GMT.
 *
 *     # Schedule new cron event with hourly recurrence.
 *     $ wp cron event schedule cron_test now hourly
 *     Success: Scheduled event with hook 'cron_test' for 2016-05-31 10:20:32 GMT.
 *
 *     # Schedule new cron event and pass associative arguments.
 *     $ wp cron event schedule cron_test '+1 hour' --foo=1 --bar=2
 *     Success: Scheduled event with hook 'cron_test' for 2016-05-31 11:21:35 GMT.
 */

To break the example down piece by piece:

For more details on how WP-CLI understands the PHPdoc, please see the commands cookbook.

Class annotation

For classes representing a collection of subcommands, we'd encourage you to use class-level annotation to provide an introduction to the collection.

/**
 * Manage options.
 *
 * ## EXAMPLES
 *
 *     # Get site URL.
 *     $ wp option get siteurl
 *     http://example.com
 *
 *     # Add option.
 *     $ wp option add my_option foobar
 *     Success: Added 'my_option' option.
 *
 *     # Update option.
 *     $ wp option update my_option '{"foo": "bar"}' --format=json
 *     Success: Updated 'my_option' option.
 *
 *     # Delete option.
 *     $ wp option delete my_option
 *     Success: Deleted 'my_option' option.
 */

Messages within command execution

$ wp theme activate twentysixteen
Success: Switched to 'Twenty Sixteen' theme.

Command parameter description

If there is a set of allowed values for a command parameter, we have set special format (mostly similar to YAML) for default value and available options.

[--format=<format>]
: Render output in a particular format.
---
default: table
options:
  - table
  - csv
  - json
  - count
  - yaml
---
References

Exit Codes

WP-CLI follows widely-adopted Unix conventions for exit codes: 0 signals success (the command’s primary condition was satisfied), while non-zero values signal that the primary condition was not satisfied. This can represent either an operational error (the command could not perform the requested operation) or an expected negative result that calling scripts are intended to test for (for example, “not installed”). Scripts that call WP-CLI commands can inspect the exit code to determine whether to continue, retry, or abort.

Standard exit codes

Exit code Meaning
0 Command completed successfully. The requested operation was performed as expected.
1 Command failed. WP-CLI could not perform the operation as expected (e.g. invalid arguments, WordPress not found, a fatal error was encountered).

WP_CLI::error() (doc) is the conventional way to report a failure and exit with code 1.

WP_CLI::halt() (doc) can be used to exit with a specific non-zero code when the command needs to communicate a particular outcome to the calling script.

Exit codes for boolean-result commands

Some commands are designed to communicate a binary result directly through the exit code rather than through output. These commands still use the standard 0/1 codes, but 1 means "condition not met" rather than "operational error". For example:

Use exit code 1 to signal "condition not met" (rather than an error) when:

  1. The command result is binary (pass/fail, found/not-found) and is intended to be tested directly in a shell conditional.
  2. The intent is explicitly documented so script authors know the exit code carries a result meaning, not an error meaning.

Do not return a non-zero exit code for conditions that are informational rather than a definitive result (e.g. a list command that finds no items is still a successful operation — it returned an empty list).

Using exit codes in scripts

Because WP-CLI uses standard exit codes, you can use WP-CLI commands directly in shell conditionals:

# Check if a plugin is installed before trying to activate it
if wp plugin is-installed my-plugin; then
    wp plugin activate my-plugin
else
    echo "Plugin not installed"
    exit 1
fi

You can also inspect $? after any WP-CLI command to check whether it succeeded:

wp core update
if [ "$?" -ne 0 ]; then
    echo "Update failed"
fi
References

Given /^&quot;([^&quot;]+)&quot; replaced with &quot;([^&quot;]+)&quot; in the ([^\s]+) file$/

Search and replace a string in a file using regex.


Usage

Scenario: My example scenario
  Given "Foo" replaced with "Bar" in the readme.html file
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^a downloaded Phar with (?:the same version|version &quot;([^&quot;]+)&quot;)$/

Download a specific WP-CLI Phar version from GitHub.


Usage

Scenario: My example scenario
  Given an empty directory
  And a downloaded Phar with version "2.11.0"

Scenario: My other scenario
  Given an empty directory
  And a downloaded Phar with the same version

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^a new Phar with (?:the same version|version &quot;([^&quot;]+)&quot;)$/

Build a new WP-CLI Phar file with a given version.


Usage

Scenario: My example scenario
  Given an empty directory
  And a new Phar with version "2.11.0"

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^a WP multisite (subdirectory|subdomain)?\s?(install|installation)$/

Installs WordPress Multisite.


Usage

Supports either subdirectory or subdomain installation.

Scenario: My example scenario
  Given a WP multisite subdomain installation
  ...

Scenario: My other scenario
  Given a WP subdirectory install
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^a WP( [^\s]+)? install(?:ation)? in ['&quot;]?([^'&quot;]+)['&quot;]?$/

Installs WordPress in a given directory.


Usage

Scenario: My example scenario
  Given a WP installation in 'foo'
  ...

Scenario: My other scenario
  Given a WP install in 'bar'
  ...

Scenario: My version-specific scenario
  Given a WP 6.4.2 installation in 'subdir'
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^a WP( \d[^\s]+)? install(?:ation)?$/

Installs WordPress.


Usage

Scenario: My example scenario
  Given a WP installation
  ...

Scenario: My other scenario
  Given a WP install
  ...

Scenario: My version-specific scenario
  Given a WP 6.4.2 installation
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^an? (&quot;[^&quot;]+&quot;|[^\s]+) (file|cache file):$/

Creates a file with the given contents.


Usage

The file can be created either in the current working directory or in the cache directory.

Scenario: My example scenario
  Given a wp-cli.yml file:
    """
    @foo:
      path: foo
      user: admin
    """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^an? (empty|non-existent) ([^\s]+) directory$/

Creates or deletes a specific directory.


Usage

Scenario: My example scenario
  Given an empty foo-plugin directory
  And a non-existent bar-plugin directory
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^save (STDOUT|STDERR) ([\'].+[^\'])?\s?as \{(\w+)\}$/

Store STDOUT or STDERR contents in a variable.


Usage

Scenario: My example scenario
  When I run `wp package path`
  Then save STDOUT as {PACKAGE_PATH}

Scenario: My other scenario
  When I run `wp core download`
  Then save STDOUT 'Downloading WordPress ([\d\.]+)' as {VERSION}

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^save the (.+) file ([\'].+[^\'])?as \{(\w+)\}$/

Stores the contents of the given file in a variable.


Usage

Scenario: My example scenario
  Given a WP installation with Composer
  And save the {RUN_DIR}/composer.json file as {COMPOSER_JSON}

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given /^that HTTP requests to (.*?) will respond with:$/

Mock HTTP requests to a given URL.


Usage

Scenario: My example scenario
  Given that HTTP requests to https://api.github.com/repos/wp-cli/wp-cli/releases?per_page=100 will respond with:
    """
    HTTP/1.1 200
    Content-Type: application/json

    { "foo": "bar" }
    """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a custom wp-content directory

Configure a custom wp-content directory.


Usage

Defines the WP_CONTENT_DIR, WP_PLUGIN_DIR, and WPMU_PLUGIN_DIR constants.

Scenario: My example scenario
  Given a WP install
  And a custom wp-content directory

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a database

Creates an empty database.


Usage

Has no effect when tests run with SQLite.

Scenario: My example scenario
  Given a database
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a dependency on current wp-cli

Add wp-cli/wp-cli as a Composer dependency.


Usage

Scenario: My example scenario
  Given a WP installation with Composer
  And a dependency on current wp-cli

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a misconfigured WP_CONTENT_DIR constant directory

Modify wp-config.php to set WP_CONTENT_DIR to an empty string.


Usage

Scenario: My example scenario
  Given a WP install
  And a misconfigured WP_CONTENT_DIR constant directory

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a PHP built-in web server

Start a PHP built-in web server in the current directory.


Usage

Scenario: My example scenario
  Given a WP installation
  And a PHP built-in web server

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a PHP built-in web server to serve :subdir

Start a PHP built-in web server in the given subdirectory.


Usage

Scenario: My example scenario
  Given a WP installation
  And a PHP built-in web server to serve 'WordPress'

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a WP install(ation) with Composer

Installs WordPress with Composer.


Usage

Scenario: My example scenario
  Given a WP installation with Composer
  ...

Scenario: My other scenario
  Given a WP install with Composer
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given a WP install(ation) with Composer and a custom vendor directory :vendor_directory

Installs WordPress with Composer and a custom vendor directory.


Usage

Scenario: My example scenario
  Given a WP installation with Composer and a custom vendor directory 'vendor-custom'
  ...

Scenario: My other scenario
  Given a WP install with Composer with Composer and a custom vendor directory 'vendor-custom'
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given an empty cache

Clears the WP-CLI cache directory.


Usage

Scenario: My example scenario
  Given an empty cache
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given an empty directory

Creates an empty directory.


Usage

Scenario: My example scenario
  Given an empty directory
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given download:

Download multiple files into the given destinations.


Usage

Scenario: My example scenario
  Given download:
    | path                | url                                   |
    | {CACHE_DIR}/foo.jpg | https://example.com/foo.jpg           |
    | {CACHE_DIR}/bar.png | https://example.com/another-image.png |

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given these installed and active plugins:

Installs and activates one or more plugins.


Usage

Scenario: My example scenario
  Given a WP installation
  And these installed and active plugins:
    """
    akismet
    wordpress-importer
    """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given WP files

Download WordPress files without installing.


Usage

Scenario: My example scenario
  Given an empty directory
  And WP files

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Given wp-config.php

Create a wp-config.php file using wp config create.


Usage

Scenario: My example scenario
  Given an empty directory
  And WP files
  And wp-config.php

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Hosting Companies

The following is a list of hosting companies that have WP-CLI installed by default for their customers, i.e. when they first SSH into their server, the wp command is already available.

In alphabetical order:

The following is a list of hosting companies that use WP-CLI, whereby their customers can write and request the running of standard and custom WP-CLI commands:

References

Internal API

WP-CLI includes a number of utilities which are considered stable and meant to be used by commands.

This also means functions and methods not listed here are considered part of the private API. They may change or disappear at any time.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


Registration

Output

Input

Execution

System

Misc

References

Shell Friends

As you advance upon your use of WP-CLI, you'll find that a little bit of command line knowledge can have a huge impact on your workflow. Here are some of our favorite shell helper utilities.

You Should Know

Search through your bash history

Did you know that every command you run on your shell is saved to history? Search through your history with CTRL + R:

$ wp core download --version=nightly --force
bck-i-search: wp

When 'bck-i-search' appears, your keystrokes will search against commands saved in your bash history. Hit return to run the current selection.

Or another way to search history is grepping the output from the history command like:

$ history | grep wp

Any of the commands found in that list can be re-executed by bang-number, so for example if the output says your desired command is #218, you just do !218

Combine WP-CLI commands

In many cases, it can be extremely powerful to be able to pass the results of one command to another. Composability is a key philosophy of WP-CLI, and there are two common approaches for composing commands.

Command substitution passes the output of one command to another command, without any transformation to the output.

wp post list only lists posts; it doesn't perform any operation on them. In this example, the command lists page ids as space-separated values.

$ wp post list --post_type='page' --format=ids
1164 1186

Combining wp post list with wp post delete lets you easily delete all posts. In this example, $() lets us pass the space-separated page ids to wp post delete.

$ wp post delete $(wp post list --post_type='page' --format=ids)
Success: Trashed post 1164.
Success: Trashed post 1186.

If you need a bit more flexibility, xargs lets you pass the output of one command to another command, while performing minor transformation on the output.

You may want to assign all editor capabilities to the author role. However, wp cap list lists capabilities separated by newlines, and wp cap add only accepts space-separated capabilities. Enter, xargs, whose default behavior is to split newline output into a space-separated list. Note the | shell operator, which passes the results of wp cap list to xargs. Without |, you'll see a WP-CLI error.

$ wp cap list 'editor' | xargs wp cap add 'author'
Success: Added 24 capabilities to 'author' role.

wp user generate only generates users; it doesn't perform supplemental operations. In this example, wp user generate passes user ids to xargs, which splits the space-separated ids into a list and calls wp user meta add for each.

$ wp user generate --count=5 --format=ids | xargs -0 -d ' ' -I % wp user meta add % foo bar
Success: Added custom field.
Success: Added custom field.
Success: Added custom field.
Success: Added custom field.
Success: Added custom field.

Define aliases, short macros to common commands

If you find yourself running the same commands quite often, you can define aliases to them for easier access.

Run all three status check commands with one check-all alias. In this example, running alias creates a check-all alias for the current shell session. Save the same statement to your ~/.bashrc or ~/.zshrc to always have it available.

$ alias check-all='wp core check-update && wp plugin list --update=available && wp theme list --update=available'
$ check-all
+-----------------+-------------+-----------------------------------------------------------+
| version         | update_type | package_url                                               |
+-----------------+-------------+-----------------------------------------------------------+
| 4.7-beta4-39322 | minor       | https://wordpress.org/nightly-builds/wordpress-latest.zip |
+-----------------+-------------+-----------------------------------------------------------+
+-------------------------+----------+-----------+--------------+
| name                    | status   | update    | version      |
+-------------------------+----------+-----------+--------------+
| akismet                 | inactive | available | 3.1.8        |
| co-authors-plus         | inactive | available | 3.1.1        |
| wp-redis                | inactive | available | 0.2.2        |
| rest-api                | active   | available | 2.0-beta13.1 |
| wp-api-oauth1           | inactive | available | 0.2          |
+-------------------------+----------+-----------+--------------+
+----------------------+----------+-----------+---------+
| name                 | status   | update    | version |
+----------------------+----------+-----------+---------+
| p2                   | inactive | available | 1.4.2   |
+----------------------+----------+-----------+---------+

Save command output

WP-CLI commands send output to both STDOUT and STDERR, depending on the nature of the message. You may not notice there are two destinations, because WP-CLI renders both inside your shell. However, if you want to capture your output to a file, the distinction matters.

Simply using > will capture STDOUT from the command to a file.

$ wp import wordpress.wxr --authors=create > import.log

Redirect STDERR to STDOUT with 2>&1, and then capture STDOUT to a log file.

$ wp import wordpress.wxr --authors=create > import.log 2>&1

When you're capturing output to a file, you won't see the output in your current shell session. However, you can open a second shell session and run tail -f <file> to see the output as it's added to the file.

Alternatively, you can use tee that writes to both standard output and files. You only have to pipe the output to the command and specify the file name.

$ wp import wordpress.wxr --authors=create | tee import.log

This will display the output in the current shell screen and also write it to the log file.

Note that if the file already exists, it will be overwritten. If you want to append the output to the file, use the -a option.

$ wp import wordpress.wxr --authors=create | tee -a import.log

Snippets

Are you an expert in bash or zsh? Share your tips here.

Take a look at the plugin changelog

wget -qO- http://api.wordpress.org/plugins/info/1.0/akismet|php -r '$seri=unserialize(stream_get_contents(STDIN)); echo $seri->sections["changelog"];'|elinks -force-html

Explanation

Start wp-cli with ABSPATH in the current dir and under the current dir's owner

#!/bin/bash

sudo -u $(stat . -c %U) -- wp --path="$PWD" "$@"

Explanation

The stat command returns the owner of the current directory, WordPress root.

Install and Configure WordPress with WP-CLI

wp_install () 
{ 
    wp core download --path=$1;
    cd $1;
    read -p 'name the database:' dbname;
    wp config create --dbname=$dbname --dbuser=root --dbpass=awoods --dbhost=localhost;
    wp db create;
    wp core install --prompt
}

$ source ~/.bashrc
$ wp_install new-site

Explanation

Add this function to your ~/.bashrc are reload your shell (or open a new shell). You'll need to substitute these database credentials with your own. When you need to create a new WordPress site, call this function and specify the name of the directory where you want to create the site. This emulates the web-based install process.

List all image URL-s in posts

wp post list --field=ID|xargs -I % wp post get % --field=post_content|sed -ne 's;.*\(https\?\S\+\(jpe\?g\|png\|gif\)\).*;\1;gp'

Explanation

Create a page from a file and flag it with the file name

wp post create new_page.html --post_type=page --post_title="New Page" --porcelain | xargs -I % wp post meta add % imported_from new_page.html

Explanation

Change to a certain WordPress installation's directory from a menu

#!/bin/bash

WP_TOP_PATH="/home/"
MENU_TEXT="Choose an installation"
GAUGE_TEXT="Searching for WordPress"

declare -a MENU
WPS="$(wp --allow-root find "$WP_TOP_PATH" --field=version_path)"
WP_TOTAL="$(wc -l <<< "$WPS")"
WP_COUNT="0"

while read -r WP; do
    WP_LOCAL="${WP%wp-includes/version.php}"

    NAME="$(cd "$WP_LOCAL"; sudo -u "$(stat . -c %U)" -- wp --no-debug --quiet option get blogname)"
    if [ -z "$NAME" ]; then
        NAME="(unknown)"
    fi
    MENU+=( "$WP_LOCAL" "$NAME" )

    echo "$((++WP_COUNT * 100 / WP_TOTAL))".
done <<< "$WPS" > >(whiptail --gauge "$GAUGE_TEXT" 7 74 0)

WP_LOCAL="$(whiptail --title "WordPress" --menu "$MENU_TEXT"  $((${#MENU[*]} / 2 + 7)) 74 10 "${MENU[@]}" 3>&1 1>&2 2>&3)"

if [ $? -ne 0 ] || [ ! -d "$WP_LOCAL" ]; then
    echo "Cannot find '${WP_LOCAL}'" 1>&2
    exit 100
fi

echo "cd ${WP_LOCAL}"

Explanation

Discard header row from a CSV

By default, --format=csv includes a header row:

$ wp user list --format=csv
ID,user_login,display_name,user_email,user_registered,roles
1,daniel,daniel,daniel@handbuilt.co,"2022-12-21 23:05:16",administrator

If you'd like to discard the header row, tail is pretty helpful:

$ wp user list --format=csv | tail -n +2
1,daniel,daniel,daniel@handbuilt.co,"2022-12-21 23:05:16",administrator

Sort plugins or themes by certain column(s)

References

Then /^(STDOUT|STDERR) should be a number$/

Expect STDOUT or STDERR to be a numeric value.


Usage

Scenario: My example scenario
  Given a WP installation
  When I run `wp db size --size_format=b`
  Then STDOUT should be a number

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^(STDOUT|STDERR) should be a version string (&lt;|&lt;=|&gt;|&gt;=|==|=|!=|&lt;&gt;) ([+\w.{}-]+)$/

Expect STDOUT or STDERR to be a version string comparing to the given version.


Usage

Scenario: My example scenario
  Given a WP install
  When I run `wp core version
  Then STDOUT should be a version string >= 6.8

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^(STDOUT|STDERR) should be empty$/

Expect STDOUT or STDERR to be empty.


Usage

Scenario: My other scenario
  Given a WP install
  When I run `wp plugin install akismet`
  Then STDERR should be empty

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^(STDOUT|STDERR) should not be a number$/

Expect STDOUT or STDERR to not be a numeric value.


Usage

Scenario: My example scenario
  Given a WP installation
  When I run `wp post list --format=json`
  Then STDOUT should not be a number

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^(STDOUT|STDERR) should not be empty$/

Expect STDOUT or STDERR not to be empty.


Usage

Scenario: My example scenario
  When I run `wp user create examplejane jane@example.com`
  Then STDOUT should not be empty

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^(STDOUT|STDERR) should( not)? match (((\/.+\/)|(#.+#))([a-z]+)?)$/

Match STDOUT or STDERR against a regex.


Usage

Scenario: My example scenario
  When I run `wp dist-archive wp-content/plugins/hello-world`
  Then STDOUT should match /^Success: Created hello-world.0.1.0.zip \(Size: \d+(?:\.\d*)? [a-zA-Z]{1,3}\)$/

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^(STDOUT|STDERR) should( strictly)? (be|contain|not contain):$/

Check the contents of STDOUT or STDERR.


Usage

Scenario: My example scenario
  Given an empty directory
  When I run `wp core is-installed`
  Then STDOUT should be empty

Scenario: My other scenario
  Given a WP install
  When I run `wp plugin install akismet`
  Then STDOUT should contain:
    """
    Plugin installed successfully.
    """
  And STDERR should be empty

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^an email should (be sent|not be sent)$/

Expect an email to be sent (or not).


Usage

Scenario: My example scenario
  When I run `wp user reset-password 1`
  Then an email should be sent

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^STDOUT should be a JSON array containing:$/

Expect valid JSON array output in STDOUT.


Usage

Errors when some items are missing from the expected array.

Scenario: My example scenario
  When I run `wp plugin list --field=name --format=json`
  Then STDOUT should be a JSON array containing:
    """
    ["akismet", "hello-dolly"]
    """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^STDOUT should be a table containing rows:$/

Expect STDOUT to be a table containing the given rows.


Usage

Scenario: My example scenario
  Given a WP installation
  When I run `wp config list --fields=name,type`
  Then STDOUT should be a table containing rows:
    | name    | type     |
    | DB_NAME | constant |
    | DB_USER | constant |

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^STDOUT should be CSV containing:$/

Expect STDOUT to be CSV containing certain values.


Usage

Scenario: My example scenario
  When I run `wp term list post_tag --fields=name,slug --format=csv`
  Then STDOUT should be CSV containing:
    | name      | slug |
    | Test term | test |

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^STDOUT should be JSON containing:$/

Expect valid JSON output in STDOUT.


Usage

Scenario: My example scenario
  When I run `wp post meta get 1 meta-key --format=json`
  Then STDOUT should be JSON containing:
    """
    {
    "foo": "baz"
    }
  """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^STDOUT should be YAML containing:$/

Expect STDOUT to be YAML containing certain content.


Usage

Scenario: My example scenario
  When I run `wp cli alias list`
  Then STDOUT should be YAML containing:
    """
    @all: Run command against every registered alias.
    @foo:
      path: {TEST_DIR}/foo
    """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^STDOUT should end with a table containing rows:$/

Expect STDOUT to end with a table containing the given rows.


Usage

Useful when the table is preceded by some other output.

Scenario: My example scenario
  Given a WP installation
  When I run `wp search-replace foo bar --report-changed-only`
  Then STDOUT should contain:
    """
    Success: Made 3 replacements.
    """
  And STDOUT should end with a table containing rows:
    | Table       | Column       | Replacements | Type |
    | wp_options  | option_value | 1            | PHP  |
    | wp_postmeta | meta_value   | 1            | SQL  |
    | wp_posts    | post_title   | 1            | SQL  |

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^the (.+) (file|directory) should( strictly)? (exist|not exist|be:|contain:|not contain:)$/

Expect a certain file or directory to (not) exist or (not) contain certain contents.


Usage

Scenario: My example scenario
  When I run `wp core download`
  Then the wp-settings.php file should exist
  And the wp-content directory should exist
  And the {RUN_DIR} directory should contain:
    """
    index.php
    license.txt
    """
  And the wp-config.php file should contain:
    """
    That's all, stop editing! Happy publishing.
    """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^the contents of the (.+) file should( not)? match (((\/.+\/)|(#.+#))([a-z]+)?)$/

Match file contents against a regex.


Usage

Scenario: My example scenario
  When I run `wp scaffold plugin hello-world`
  Then the contents of the wp-content/plugins/hello-world/languages/hello-world.pot file should match /X-Generator:\s/

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then /^the return code should( not)? be (\d+)$/

Expect a specific exit code of the previous command.


Usage

Scenario: My example scenario
  Given a WP installation
  When I try `wp plugin install`
  Then the return code should be 1

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Then the HTTP status code should be :code

Expect the HTTP status code for visiting http://localhost:8080.


Usage

Scenario: My example scenario
  Given a WP installation with Composer
  And a PHP built-in web server to serve 'WordPress'
  Then the HTTP status code should be 200

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

Tools

The following is a list of projects that integrate with WP-CLI in some form. For installable WP-CLI packages, take a look at Packagist.

Plugins

The following table is an alphabetical list of known commands defined in WordPress plugins:

Command WordPress plugin
acf Advanced Custom Fields wp-cli
amt Add-Meta-Tags command line interface
any-ipsum Any Ipsum
backup BackUpWordPress
backwpup BackWPup
blog-dupe blog-duplicator
camptix CampTix
check-content CheckContent
co-authors-plus Co-Authors Plus
composer Composer
config WP-CFM
crontrol WP-Crontrol
csv Advanced CSV Importer
deploy wp-deploy-flow
developer Developer
edd Easy Digital Downloads
elasticpress ElasticPress
github-updater GitHub Updater
google-sitemap Google Sitemap Generator CLI
image-gen Image Gen
import_sidebar Widget Import Export
itelic Licensing for Exchange
jekyll-export WordPress to Jekyll Exporter
jetpack Jetpack by WordPress.com
liveblog Liveblog
mainwp MainWP
migrate WP Migrate DB CLI Interface
migratedb WP Migrate DB Pro
more-plugin-info More Plugin Info
multi-device Multi Device Switcher
nginx Nginx Cache Controller
optimize WP-Optimize
orderweight Order Weight for WooCommerce
p2-by-email P2 By Email
p2-resolved-posts P2 Resolved Posts
p2p Posts 2 Posts
pdf-light-viewer PDF Light Viewer
phpcompat PHP Compatibility Checker
post-gen Post Gen
quick-mail Quick Mail
rest-api-toolbox REST API Toolbox
redis-cache Redis Object Cache
revisions wp-revisions-cli
revision-strike Revision Strike
safe-redirect-manager Safe Redirect Manager
scheduled-unsticky Scheduled Unsticky
simple-history Simple History
site duplicate MultiSite Clone Duplicator
stream WP Stream
super-cache WP Super Cache CLI
supportpress SupportFlow
themecheck wp-cli-themecheck
thinkup Thinkup Import WP CLI Commands
total-cache W3 Total Cache
updraftplus UpdraftPlus
Unsplash Import images from Unsplash into your Media Library
wp2static Generate & deploy a static HTML version of your site
yoast Reindex Indexables on Yoast

If you implement a WP-CLI command in one of your plugins, please list it here.

Wrappers

Editor plugins

Vagrant boxes

Misc

References

When /^I (run|try) `([^`]+)` from '([^\s]+)'$/

Run or try a given command in a subdirectory.


Usage

run expects an exit code 0, whereas try allows for non-zero exit codes.

Scenario: My example scenario
  When I run `wp core is-installed`
  Then STDOUT should be empty

  When I run `wp core is-installed` from 'foo/wp-content'
  Then STDOUT should be empty

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

When /^I (run|try) `([^`]+)`$/

Run or try a given command.


Usage

run expects an exit code 0, whereas try allows for non-zero exit codes.

So if using run and the command errors, the step will fail.

Scenario: My example scenario
  When I run `wp core version`
  Then STDOUT should contain:
    """
    6.8
    """

Scenario: My other scenario
  When I try `wp i18n make-pot foo bar/baz.pot`
  Then STDERR should contain:
    """
    Error: Not a valid source directory.
    """
  And the return code should be 1

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

When /^I (run|try) the previous command again$/

Run or try the previous command again.


Usage

run expects an exit code 0, whereas try allows for non-zero exit codes.

Scenario: My example scenario
  When I run `wp site option update admin_user_id 1`
  Then STDOUT should contain:
    """
    Success: Updated 'admin_user_id' site option.
    """

  When I run the previous command again
  Then STDOUT should contain:
    """
    Success: Value passed for 'admin_user_id' site option is unchanged.
    """

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

When /^I launch in the background `([^`]+)`$/

Launch a given command in the background.


Usage

Scenario: My example scenario
  Given a WP install
  And I launch in the background `wp server --host=localhost --port=8181`
  ...

Behat steps documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::add_command()

Register a command to WP-CLI.


Usage

WP_CLI::add_command( $name, $callable, $args = [] )
$name (string) Name for the command (e.g. "post list" or "site empty").
$callable (callable|object|string|string[]) Command implementation as a class, function or closure.
$args (array) {
Optional. An associative array with additional registration parameters.
@type callable $before_invoke Callback to execute before invoking the command.
@type callable $after_invoke Callback to execute after invoking the command.
@type string $shortdesc Short description (80 char or less) for the command.
@type string $longdesc Description of arbitrary length for examples, etc.
@type string $synopsis The synopsis for the command (string or array).
@type string $when Execute callback on a named WP-CLI hook (e.g. before_wp_load).
@type bool $is_deferred Whether the command addition had already been deferred.
}
@return (bool) on success, false if deferred, hard error if registration failed.

Notes

WP-CLI supports using any callable class, function, or closure as a command. WP_CLI::add_command() is used for both internal and third-party command registration.

Command arguments are parsed from PHPDoc by default, but also can be supplied as an optional third argument during registration.

# Register a custom 'foo' command to output a supplied positional param.
#
# $ wp foo bar --append=qux
# Success: bar qux

/**
 * My awesome closure command
 *
 * <message>
 * : An awesome message to display
 *
 * --append=<message>
 * : An awesome message to append to the original message.
 *
 * @when before_wp_load
 */
$foo = function( $args, $assoc_args ) {
    WP_CLI::success( $args[0] . ' ' . $assoc_args['append'] );
};
WP_CLI::add_command( 'foo', $foo );

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::add_hook()

Schedule a callback to be executed at a certain point.


Usage

WP_CLI::add_hook( $when, $callback )
$when (string) Identifier for the hook.
$callback (callable) Callback to execute when hook is called.
@return (void)

Notes

Hooks conceptually are very similar to WordPress actions. WP-CLI hooks are typically called before WordPress is loaded.

WP-CLI hooks include:

The parentheses behind the hook name denote the number of arguments being passed into the hook. For such hooks, the callback should return the first argument again, making them work like a WP filter.

WP-CLI commands can create their own hooks with WP_CLI::do_hook().

If additional arguments are passed through the WP_CLI::do_hook() call, these will be passed on to the callback provided by WP_CLI::add_hook().

# `wp network meta` confirms command is executing in multisite context.
WP_CLI::add_command( 'network meta', 'Network_Meta_Command', array(
   'before_invoke' => function ( $name ) {
       if ( !is_multisite() ) {
           WP_CLI::error( 'This is not a multisite installation.' );
       }
   }
) );

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::add_wp_hook()

Add a callback to a WordPress action or filter.


Usage

WP_CLI::add_wp_hook( $tag, $function_to_add, $priority = 10, $accepted_args = 1 )
$tag (string) Named WordPress action or filter.
$function_to_add (callable) Callable to execute when the action or filter is evaluated.
$priority (integer) Priority to add the callback as.
$accepted_args (integer) Number of arguments to pass to callback.
@return (true)

Notes

add_action() without needing access to add_action(). If WordPress is already loaded though, you should use add_action() (and add_filter()) instead.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::colorize()

Colorize a string for output.


Usage

WP_CLI::colorize( $string )
$string (string) String to colorize for output, with color tokens.
@return (string) string.

Notes

Yes, you can change the color of command line text too. For instance, here's how WP_CLI::success() colorizes "Success: "

WP_CLI::colorize( "%GSuccess:%n " )

Uses \cli\Colors::colorize() to transform color tokens to display settings. Choose from the following tokens (and note 'reset'):

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::confirm()

Ask for confirmation before running a destructive operation.


Usage

WP_CLI::confirm( $question, $assoc_args = [] )
$question (string) Question to display before the prompt.
mixed> (array<string,) $assoc_args Skips prompt if 'yes' is provided.
@return (void)

Notes

If 'y' is provided to the question, the script execution continues. If 'n' or any other response is provided to the question, script exits.

# `wp db drop` asks for confirmation before dropping the database.

WP_CLI::confirm( "Are you sure you want to drop the database?", $assoc_args );

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::debug()

Display debug message prefixed with "Debug: " when --debug is used.


Usage

WP_CLI::debug( $message, $group = false )
$message (string|WP_Error|Exception|Throwable) Message to write to STDERR.
$group (string|bool) Organize debug message to a specific group.
Use `false` to not group the message.
@return (void)

Notes

Debug message is written to STDERR, and includes script execution time.

Helpful for optionally showing greater detail when needed. Used throughout WP-CLI bootstrap process for easier debugging and profiling.

# Called in `WP_CLI\Runner::set_wp_root()`.
private static function set_wp_root( $path ) {
    define( 'ABSPATH', Utils\trailingslashit( $path ) );
    WP_CLI::debug( 'ABSPATH defined: ' . ABSPATH );
    $_SERVER['DOCUMENT_ROOT'] = realpath( $path );
}

# Debug details only appear when `--debug` is used.
# $ wp --debug
# [...]
# Debug: ABSPATH defined: /srv/www/wordpress-develop.dev/src/ (0.225s)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::do_hook()

Execute callbacks registered to a given hook.


Usage

WP_CLI::do_hook( $when, $args )
$when (string) Identifier for the hook.
...$args (mixed) Optional. Arguments that will be passed onto the
callback provided by `WP_CLI::add_hook()`.
@return (null|mixed) the first optional argument if optional<br /> arguments were passed, otherwise returns null.

Notes

See WP_CLI::add_hook() for details on WP-CLI's internal hook system. Commands can provide and call their own hooks.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::error()

Display error message prefixed with "Error: " and exit script.


Usage

WP_CLI::error( $message, $exit = true )
$message (string|WP_Error|Exception|Throwable) Message to write to STDERR.
$exit (boolean|int) True defaults to exit(1).
@return (null)

Notes

Error message is written to STDERR. Defaults to halting script execution with return code 1.

Use WP_CLI::warning() instead when script execution should be permitted to continue.

When --debug is enabled, this method will also output a backtrace showing where the error was triggered from, making it easier to identify problematic code.

# `wp cache flush` considers flush failure to be a fatal error.
if ( false === wp_cache_flush() ) {
    WP_CLI::error( 'The object cache could not be flushed.' );
}

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::error_multi_line()

Display a multi-line error message in a red box. Doesn't exit script.


Usage

WP_CLI::error_multi_line( $message_lines )
$message_lines (array<string|\WP_Error|\Exception|\Throwable>) Multi-line error message to be displayed.
@return (void)

Notes

Error message is written to STDERR.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::get_config()

Get values of global configuration parameters.


Usage

WP_CLI::get_config( $key = null )
$key (string) Get value for a specific global configuration parameter.
@return (mixed)

Notes

Provides access to --path=<path>, --url=<url>, and other values of the global configuration parameters.

WP_CLI::log( 'The --url=<url> value is: ' . WP_CLI::get_config( 'url' ) );

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::get_php_binary()

Get the path to the PHP binary used when executing WP-CLI.


Usage

WP_CLI::get_php_binary()
@return (string)

Notes

Environment values permit specific binaries to be indicated.

Note: moved to Utils, left for BC.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::halt()

Halt script execution with a specific return code.


Usage

WP_CLI::halt( $return_code )
$return_code (integer)
@return (never)

Notes

Permits script execution to be overloaded by WP_CLI::runcommand()

When --debug is enabled, this method will also output a backtrace showing where the halt was triggered from, making it easier to identify the cause of early termination.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::has_config()

Confirm that a global configuration parameter does exist.


Usage

WP_CLI::has_config( $key )
$key (string) Config parameter key to check.
@return (bool)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::launch()

Launch an arbitrary external process that takes over I/O.


Usage

WP_CLI::launch( $command, $exit_on_error = true, $return_detailed = false )
$command (string) External process to launch.
$exit_on_error (boolean) Whether to exit if the command returns an elevated return code.
$return_detailed (boolean) Whether to return an exit status (default) or detailed execution results.
@return (int|ProcessRun) command exit status, or a ProcessRun object for full details.

Notes

# `wp core download` falls back to the `tar` binary when PharData isn't available
if ( ! class_exists( 'PharData' ) ) {
    $cmd = "tar xz --strip-components=1 --directory=%s -f $tarball";
    WP_CLI::launch( Utils\esc_cmd( $cmd, $dest ) );
    return;
}

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::launch_self()

Run a WP-CLI command in a new process reusing the current runtime arguments.


Usage

WP_CLI::launch_self( $command, $args = [], $assoc_args = [], $exit_on_error = true, $return_detailed = false, $runtime_args = [] )
$command (string) WP-CLI command to call.
string> (array<int,) $args Positional arguments to include when calling the command.
mixed> (array<string,) $assoc_args Associative arguments to include when calling the command.
$exit_on_error (bool) Whether to exit if the command returns an elevated return code.
$return_detailed (bool) Whether to return an exit status (default) or detailed execution results.
mixed> (array<string,) $runtime_args Override one or more global args (path,url,user,allow-root)
@return (int|ProcessRun) command exit status, or a ProcessRun instance

Notes

Use WP_CLI::runcommand() instead, which is easier to use and works better.

Note: While this command does persist a limited set of runtime arguments, it does not persist environment variables. Practically speaking, WP-CLI packages won't be loaded when using WP_CLI::launch_self() because the launched process doesn't have access to the current process $HOME.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::line()

Display informational message without prefix, and ignore --quiet.


Usage

WP_CLI::line( $message = '', $newline = true )
$message (string) Message to display to the end user.
$newline (bool) Optional. Whether to append a newline to the end of the message. Default true.
@return (void)

Notes

Message is written to STDOUT. WP_CLI::log() is typically recommended; WP_CLI::line() is included for historical compat.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::log()

Display informational message without prefix.


Usage

WP_CLI::log( $message, $newline = true )
$message (string) Message to write to STDOUT.
$newline (bool) Optional. Whether to append a newline to the end of the message. Default true.
@return (void)

Notes

Message is written to STDOUT, or discarded when --quiet flag is supplied.

# `wp cli update` lets user know of each step in the update process.
WP_CLI::log( sprintf( 'Downloading from %s...', $download_url ) );

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::read_value()

Read a value, from various formats.


Usage

WP_CLI::read_value( $raw_value, $assoc_args = [] )
$raw_value (string)
mixed> (array<string,) $assoc_args
@return (mixed)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::run_command()

Run a given command within the current process using the same global parameters.


Usage

WP_CLI::run_command( $args, $assoc_args = [] )
string> (array<int,) $args Positional arguments including command name.
mixed> (array<string,) $assoc_args
@return (void)

Notes

Use WP_CLI::runcommand() instead, which is easier to use and works better.

To run a command using a new process with the same global parameters, use WP_CLI::launch_self(). To run a command using a new process with different global parameters, use WP_CLI::launch().

ob_start();
WP_CLI::run_command( array( 'cli', 'cmd-dump' ) );
$ret = ob_get_clean();

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::runcommand()

Run a WP-CLI command.


Usage

WP_CLI::runcommand( $command, $options = [] )
$command (string) WP-CLI command to run, including arguments.
mixed> (array<string,) $options {
Configuration options for command execution.
@type bool $launch Launches a new process (true) or reuses the existing process (false). Default: true.
@type bool $exit_error Halts the script on error. Default: true.
@type bool|string $return Returns output as an object when set to 'all' (string), return just the 'stdout', 'stderr', or 'return_code' (string) of command, or print directly to stdout/stderr (false). Default: false.
@type bool|string $parse Parse returned output as 'json' (string); otherwise, output is unchanged (false). Default: false.
@type array $command_args Contains additional command line arguments for the command. Each element represents a single argument. Default: empty array.
}
@return (mixed)

Notes

Launches a new child process to run a specified WP-CLI command. Optionally:

$options = array(
  'return'       => true,                // Return 'STDOUT'; use 'all' for full object.
  'parse'        => 'json',              // Parse captured STDOUT to JSON array.
  'launch'       => false,               // Reuse the current process.
  'exit_error'   => true,                // Halt script execution on error.
  'command_args' => [ '--skip-themes' ], // Additional arguments to be passed to the $command.
);
$plugins = WP_CLI::runcommand( 'plugin list --format=json', $options );

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::success()

Display success message prefixed with "Success: ".


Usage

WP_CLI::success( $message )
$message (string) Message to write to STDOUT.
@return (void)

Notes

Success message is written to STDOUT, or discarded when --quiet flag is supplied.

Typically recommended to inform user of successful script conclusion.

# wp rewrite flush expects 'rewrite_rules' option to be set after flush.
flush_rewrite_rules( \WP_CLI\Utils\get_flag_value( $assoc_args, 'hard' ) );
if ( ! get_option( 'rewrite_rules' ) ) {
    WP_CLI::warning( "Rewrite rules are empty." );
} else {
    WP_CLI::success( 'Rewrite rules flushed.' );
}

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI::warning()

Display warning message prefixed with "Warning: ".


Usage

WP_CLI::warning( $message )
$message (string|WP_Error|Exception|Throwable) Message to write to STDERR.
@return (void)

Notes

Warning message is written to STDERR, or discarded when --quiet flag is supplied.

Use instead of WP_CLI::debug() when script execution should be permitted to continue.

# `wp plugin activate` skips activation when plugin is network active.
$status = $this->get_status( $plugin->file );
// Network-active is the highest level of activation status
if ( 'active-network' === $status ) {
  WP_CLI::warning( "Plugin '{$plugin->name}' is already network active." );
  continue;
}

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\basename()

Locale-independent version of basename()


Usage

WP_CLI\Utils\basename( $path, $suffix = '' )
$path (string)
$suffix (string)
@return (string)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\esc_like()

First half of escaping for LIKE special characters % and _ before preparing for MySQL.


Usage

WP_CLI\Utils\esc_like( $text )
$text (string) The raw text to be escaped. The input typed by the user should have no
extra or deleted slashes.
@return (string) in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare()<br /> or real_escape next.

Notes

Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.

Copied from core "wp-includes/wp-db.php". Avoids dependency on WP 4.4 wpdb.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\format_items()

Render a collection of items as an ASCII table, JSON, CSV, YAML, list of ids, or count.


Usage

WP_CLI\Utils\format_items( $format, $items, $fields )
$format (string) Format to use: 'table', 'json', 'csv', 'yaml', 'ids', 'count'.
array<string, (array<int,) mixed>|object>|iterable|object> $items An array of items to output.
$fields (array<string>|string) Named fields for each item of data. Can be array or comma-separated list.
@return (void)

Notes

Given a collection of items with a consistent data structure:

$items = array(
    array(
        'key'   => 'foo',
        'value'  => 'bar',
    )
);

Render $items as an ASCII table:

WP_CLI\Utils\format_items( 'table', $items, array( 'key', 'value' ) );

# +-----+-------+
# | key | value |
# +-----+-------+
# | foo | bar   |
# +-----+-------+

Or render $items as YAML:

WP_CLI\Utils\format_items( 'yaml', $items, array( 'key', 'value' ) );

# ---
# -
#   key: foo
#   value: bar

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\get_flag_value()

Return the flag value or, if it's not set, the $default value.


Usage

WP_CLI\Utils\get_flag_value( $assoc_args, $flag, $default = null )
mixed> (array<string|int,) $assoc_args Arguments array.
$flag (string|int) Flag to get the value.
$default (mixed) Default value for the flag. Default: NULL.
@return (mixed)

Notes

Because flags can be negated (e.g. --no-quiet to negate --quiet), this function provides a safer alternative to using isset( $assoc_args['quiet'] ) or similar.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\get_home_dir()

Get the home directory.


Usage

WP_CLI\Utils\get_home_dir()
@return (string)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\get_named_sem_ver()

Compare two version strings to get the named semantic version.


Usage

WP_CLI\Utils\get_named_sem_ver( $new_version, $original_version )
$new_version (string)
$original_version (string)
@return (string) 'minor', 'patch'

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\get_php_binary()

Get the path to the PHP binary used when executing WP-CLI.


Usage

WP_CLI\Utils\get_php_binary()
@return (string)

Notes

Environment values permit specific binaries to be indicated.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\get_temp_dir()

Get the system's temp directory. Warns user if it isn't writable.


Usage

WP_CLI\Utils\get_temp_dir()
@return (string)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\http_request()

Make a HTTP request to a remote URL.


Usage

WP_CLI\Utils\http_request( $method, $url, $data = null, $headers = [], $options = [] )
$method (string) HTTP method (GET, POST, DELETE, etc.).
$url (string) URL to make the HTTP request to.
$data (array|null) Data to send either as a query string for GET/HEAD requests,
or in the body for POST requests.
$headers (array) Add specific headers to the request.
$options (array) {
Optional. An associative array of additional request options.
@type bool $halt_on_error Whether or not command execution should be halted on error. Default: true
@type bool|string $verify A boolean to use enable/disable SSL verification
or string absolute path to CA cert to use.
Defaults to detected CA cert bundled with the Requests library.
@type bool $insecure Whether to retry automatically without certificate validation.
@type int $max_retries Maximum number of retries of failed requests. Default 3.
}
$method (string) HTTP method.
$url (string) URL to request.
mixed>|string|null (array<string,) $data Array of data to send or string.
string> (array<string,) $headers Array of headers to send.
mixed> (array<string,) $options Array of options for the request.
@return (\Requests_Response|Response)

Notes

Wraps the Requests HTTP library to ensure every request includes a cert.

# `wp core download` verifies the hash for a downloaded WordPress archive

$md5_response = Utils\http_request( 'GET', $download_url . '.md5' );
if ( 20 != substr( $md5_response->status_code, 0, 2 ) ) {
     WP_CLI::error( "Couldn't access md5 hash for release (HTTP code {$response->status_code})" );
}

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\is_stream()

Check if a path is a PHP stream URL.


Usage

WP_CLI\Utils\is_stream( $path )
$path (string) The resource path or URL.
@return (bool) if the path is a PHP stream URL, false otherwise.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\isPiped()

Checks whether the output of the current script is a TTY or a pipe / redirect


Usage

WP_CLI\Utils\isPiped()
@return (bool)

Notes

Returns true if STDOUT output is being redirected to a pipe or a file; false is output is being sent directly to the terminal.

If an env variable SHELL_PIPE exists, the returned result depends on its value. Strings like 1, 0, yes, no, that validate to booleans are accepted.

To enable ASCII formatting even when the shell is piped, use the ENV variable SHELL_PIPE=0.

SHELL_PIPE=0 wp plugin list | cat

Note that the db command forwards to the mysql client, which is unaware of the env variable. For db commands, pass the --table option instead.

wp db query --table "SELECT 1" | cat

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\launch_editor_for_input()

Launch system's $EDITOR for the user to edit some text.


Usage

WP_CLI\Utils\launch_editor_for_input( $input, $title = WP-CLI, $ext = tmp )
$input (string) Some form of text to edit (e.g. post content).
$title (string) Title to display in the editor.
$ext (string) Extension to use with the temp file.
@return (string|bool) text, if file is saved from editor; false, if no change to file.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\make_progress_bar()

Create a progress bar to display percent completion of a given operation.


Usage

WP_CLI\Utils\make_progress_bar( $message, $count, $interval = 100 )
$message (string) Text to display before the progress bar.
$count (integer) Total number of ticks to be performed.
$interval (int) Optional. The interval in milliseconds between updates. Default 100.
@return (\cli\progress\Bar|\WP_CLI\NoOp)

Notes

Progress bar is written to STDOUT, and disabled when command is piped. Progress advances with $progress->tick(), and completes with $progress->finish(). Process bar also indicates elapsed time and expected total time.

# `wp user generate` ticks progress bar each time a new user is created.
#
# $ wp user generate --count=500
# Generating users  22 % [=======>                             ] 0:05 / 0:23

$progress = \WP_CLI\Utils\make_progress_bar( 'Generating users', $count );
for ( $i = 0; $i < $count; $i++ ) {
    // uses wp_insert_user() to insert the user
    $progress->tick();
}
$progress->finish();

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\make_temp_dir()

Create a unique temporary directory safely without following symlinks.


Usage

WP_CLI\Utils\make_temp_dir( $prefix = wp-cli- )
$prefix (string) Optional. Prefix for the temporary directory name. Default 'wp-cli-'.
@return (string) to the created temporary directory with a trailing slash.

Notes

The directory is only accessible by its owner on non-Windows systems.

The directory is created in the directory returned by WP-CLI's get_temp_dir() (based on sys_get_temp_dir()), not WordPress's get_temp_dir(), and is not removed automatically; callers are responsible for cleaning it up.

On failure, this function exits via WP_CLI::error() rather than throwing.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\make_temp_file()

Create a unique temporary file safely without following symlinks.


Usage

WP_CLI\Utils\make_temp_file( $prefix = wp-cli-, $suffix = '' )
$prefix (string) Optional. Prefix for the temporary file name. Default 'wp-cli-'.
$suffix (string) Optional. Suffix for the temporary file name. Default ''.
@return (string) to the created temporary file.

Notes

The file is only readable and writable by its owner on non-Windows systems.

The file is created in the directory returned by WP-CLI's get_temp_dir() (based on sys_get_temp_dir()), not WordPress's get_temp_dir(), and is not removed automatically; callers are responsible for cleaning it up.

On failure, this function exits via WP_CLI::error() rather than throwing.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\normalize_path()

Normalize a filesystem path.


Usage

WP_CLI\Utils\normalize_path( $path )
$path (string) Path to normalize.
@return (string) path.

Notes

On Windows systems, replaces backslashes with forward slashes and forces upper-case drive letters. Allows for two leading slashes for Windows network shares, but ensures that all other duplicate slashes are reduced to a single one. Ensures upper-case drive letters on Windows systems. Allows for PHP file wrappers.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\parse_ssh_url()

Parse a SSH url for its host, port, and path.


Usage

WP_CLI\Utils\parse_ssh_url( $url, $component = -1 )
$url (string)
$component (int)
@return (mixed)

Notes

Similar to parse_url(), but adds support for defined SSH aliases.

host OR host/path/to/wordpress OR host:port/path/to/wordpress

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\parse_str_to_argv()

Parse a string of command line arguments into an $argv-esqe variable.


Usage

WP_CLI\Utils\parse_str_to_argv( $arguments )
$arguments (string)
@return (array<string>)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\proc_open_compat()

Windows compatible proc_open(). Works around bug in PHP, and also deals with *nix-like ENV_VAR=blah cmd environment variable prefixes.


Usage

WP_CLI\Utils\proc_open_compat( $cmd, $descriptorspec, $pipes, $cwd = null, $env = null, $other_options = null )
$cmd (string) Command to execute.
list<string>|resource> (array<int,) $descriptorspec Indexed array of descriptor numbers and their values.
resource> (array<int,) &$pipes Indexed array of file pointers that correspond to PHP's end of any pipes that are created.
$cwd (string) Initial working directory for the command.
string> (array<string,) $env Array of environment variables.
bool>|null (array<string,) $other_options Array of additional options (Windows only).
array<int, (@param-out) resource> $pipes
@return (resource|false) stripped of any environment variable settings, or false on failure.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\report_batch_operation_results()

Report the results of the same operation against multiple resources.


Usage

WP_CLI\Utils\report_batch_operation_results( $noun, $verb, $total, $successes, $failures, $skips = null )
$noun (string) Resource being affected (e.g. plugin).
$verb (string) Type of action happening to the noun (e.g. activate).
$total (integer) Total number of resource being affected.
$successes (integer) Number of successful operations.
$failures (integer) Number of failures.
$skips (null|integer) Optional. Number of skipped operations. Default null (don't show skips).
@return (void)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\trailingslashit()

Appends a trailing slash.


Usage

WP_CLI\Utils\trailingslashit( $string )
$string (string) What to add the trailing slash to.
@return (string) with trailing slash added.

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


References

WP_CLI\Utils\write_csv()

Write data as CSV to a given file.


Usage

WP_CLI\Utils\write_csv( $fd, $rows, $headers = [] )
$fd (resource) File descriptor.
mixed>> (array<string[]>|iterable<array<string,) $rows Array of rows to output.
$headers (array<string>) List of CSV columns (optional).
@return (void)

Internal API documentation is generated from the WP-CLI codebase on every release. To suggest improvements, please submit a pull request.


Contributions

Contributions

Contributions

Contributions

Bug Reports

Think you've found a bug? We'd love for you to help us get it fixed.

Before you create a new issue, you should:

Once you've done a bit of searching and discovered there isn't prior art for your bug, please create a new GitHub issue in the appropriate repository. Providing the summary, steps to reproduce, environmental details, and other specifics identified below will help guarantee you are submitting a full bug report.

Summary

In narrative form, such as “While testing feature x, I encountered z.”

Summary:

After installing the following package on newest nightly version of the WP-CLI, I receive "is not a registered wp command error"

Steps to reproduce the bug

Please be as detailed and specific as possible. It's a good idea to go back and follow your steps a second time to make sure another user can recreate the issue by following the steps directly without assuming any actions.

Steps to reproduce:

  1. Run command 'wp package install wp-cli/scaffold-package-command:@stable'
  2. Allow installation process to complete
  3. 'Success: Package installed.' is displayed
  4. Run 'wp help scaffold package'
  5. Observe 'Error: 'scaffold package' is not a registered wp command.'

If you have a great deal of output to share, please create a GitHub gist and link to it in the issue.

Environment

Your bug may also be environment-specific. Because WP-CLI is a tool whose behavior is different from system to system, please include some environmental details in your issue if you think they're relevant.

# What PHP environment does WP-CLI run in?
wp cli info
# Are you running suhosin? If so, make sure you've added `suhosin.executor.include.whitelist = phar` to your php.ini
php -m | grep -i suhosin
# Can you share the results of which wp ?
which -a wp
stat $(which wp)
# Are you running any packages? If one is causing a problem, you can use `--skip-packages` to skip loading them
wp package list

Results and impacts

Explain how the bug affects your usage, including severity level (what were the expected results, what were the actual results? who, how and to what degree affected?).

Severity - High

Expected Results: Running wp help scaffold package displays help information for installed command.

Actual Results: 'Error: 'scaffold package' is not a registered wp command' is displayed.

This command is crucial for the daily function of our business. 1.2k other users also have installed this package and will be affected.

The installed package works without issue using WP-CLI 1.2.0-alpha-3182ac4

Workarounds

Do any workarounds exist? If so, what are they?

Workaround: Rollback to working version of WP-CLI

Relevant diagnostics

Include any crash reports, stack traces or debug output relevant to the issue. Any screenshots or gifs of issue are helpful too!

Have a lot of output? Make your issue easier to understand by creating a GitHub gist and linking to it in the issue.

If you include the --debug flag when executing WP-CLI (e.g. wp post list --debug), you may get more verbosity into the source of the error to include in the report.

$ wp package install wp-cli/scaffold-package-command:@stable
Installing package wp-cli/scaffold-package-command (@stable)
Updating /root/.wp-cli/packages/composer.json to require the package...
Using Composer to install the package...
---
Loading composer repositories with package information
Updating dependencies
Resolving dependencies through SAT
Dependency resolution completed in 0.001 seconds
Analyzed 421 packages to resolve dependencies
Analyzed 96 rules to resolve dependencies
Package operations: 1 install, 0 updates, 0 removals
Installs: wp-cli/scaffold-package-command:1.2.0
 - Installing wp-cli/scaffold-package-command (1.2.0)
Writing lock file
Generating autoload files
---
Success: Package installed.
$ wp help scaffold package --debug
Debug (bootstrap): Loading packages from: /root/.wp-cli/packages/vendor/autoload.php (0.044s)
Debug (bootstrap): No readable global config found (0.193s)
Debug (bootstrap): No project config found (0.194s)
Debug (bootstrap): argv: /usr/local/bin/wp help scaffold package --debug (0.194s)
Debug (bootstrap): ABSPATH defined: /home/fu/public_html/ (0.195s)
Debug (bootstrap): Begin WordPress load (0.195s)
Debug (bootstrap): wp-config.php path: /home/fu/public_html/wp-config.php (0.196s)
Debug (bootstrap): Loaded WordPress (1.323s)
Debug (bootstrap): Running command: help (1.323s)
Error: 'scaffold package' is not a registered wp command.

wp scaffold package does not work either:

$ wp scaffold package
Error: 'scaffold package' is not a registered subcommand of 'scaffold'. See 'wp help scaffold'.
Contributions

Code Review

Code review is a core part of the WP-CLI project's software development workflow. Code review and pairing on code are necessary to maintain quality output, as well as build cohesive styles in our work. Having a well-understood and cohesive style can make all our work easier to approach and maintain for new as well as established developers. If done right, code review can also be a learning process for all involved. Here's a typical code review workflow:

A typical code review workflow

We're currently using GitHub for everything. Read up on our GitHub workflow for details of process, naming, usage of issues and pull requests. A moderately complex issue will typically be addressed across multiple pull requests, each tackling a distinct part of the issue. This makes review simpler because each review stage will be looking at a small code.

What code review is

A good way to visualize the objectives of code review is this analogy to Maslow's "Hierarchy of Needs" pyramid. From the most basic to the highest level, a reviewer checks that code is Correct, Secure, Readable, Elegant, and Altruistic. It's important to keep this sense of priorities: if a change introduces unhandled edge cases, bugs, or security vulnerabilities, those issues need to be addressed before coding style guidelines or beautification practices preferences will matter.

code-review-pyramid

How to review code

As a reviewer, your first job is to get an understanding of what the proposed change does and why it's essential. There's no point in critiquing anything until you understand what it does, why it's necessary, and what decisions went into the way this was built.

Next, look it over for correctness. Are all functions that take parameters and produce output covered by functional unit tests? Do they actually do what they're supposed to? Can you picture an edge case where a function would error unexpectedly or return something other than the expected result? If this command has output, does it render properly in all situations? Are all global flags handled? Does it have unsurprising fallbacks for uncommon situations? Does it handle errors with clear output messages?

If it addresses the business and UX requirements, the next thing to check for is security. Does it follow basic sanitization and escaping practices for all untrusted input? If it interacts with other aspects of the codebase, is it liberal in the inputs it accepts and conservative in its output, making sure to only pass expected values? Think like an attacker. If there's any way a malicious agent could exploit this code, or an unlucky user could trigger a bug that fatals or looks bad, it's your job to find it.

Next, check for readability. Functions, variables, and files should be named clearly according to their meaning. Everything should adhere to the surrounding code style.

All code in any code-base should look like a single person typed it, no matter how many people contributed.

-- Principles of writing Idiomatic JavaScript

As readability is inherently subjective, this requires the ability to look back from the immediate code changes to the bigger picture. Think of someone months down the road trying to trace a given code path through the current changeset. Are there unnecessary steps that could be simplified? Are code comments and inline documentation robust enough to recreate the thought process behind the code?

Finally, check for elegance and overall quality. Code should follow existing and known patterns so that others can understand it at a glance. If a change introduces a chance to refactor surrounding functionality, to abstract and standardize old code into new patterns, suggest those opportunities.

How to receive code review

As the person receiving the code review, your job is to learn from suggestions. Defensiveness, stubbornness, or impatience can prevent you from getting the most out of suggestions. It's OK to explain yourself if you feel the reviewer seemed to misunderstand your intent. Arguing semantics or insisting on the correctness of one approach is almost always a bad habit and distracts from the team process. If something isn't clear to someone familiar with the codebase who's reviewing your work today, it will definitely be unclear to a new developer being on-boarded six months from now.

It's best to respond to the issues addressed by the reviewer as quickly as possible. Keep in mind that the review process requires context switching on the part of the reviewer as well as on your part, and the more immediate that process is, the less disruptive that context switch will be.

Additional readings

Contributions

Committers Credo

Some people have write access to WP-CLI repositories. These people are identified in the following teams:

This “Committers credo” is a living document. It’s meant to establish generally agreed upon standards for committers, and will continue to evolve over time.

Product quality

WP-CLI is a project depended upon by the entire WordPress ecosystem. This trust is earned by producing a consistent, dependable, and high quality product. WP-CLI committers hold product and code quality in the highest regard.

Practically-speaking:

Stellar judgement

A great product is a reflection of thoughtful, deliberate, and considered decision-making. WP-CLI committers exhibit stellar judgement when making decisions on new features, fixing bugs, merging others' pull requests, and generally working on the project.

The basis of this decision-making ensures:

Consistent participation

Effective involvement with the WP-CLI project requires consistent involvement, clarity of communication, and a propensity to help others.

As a widely adopted project, WP-CLI has a diverse user base. Helping this user base has tremendous opportunity for impact. WP-CLI committers prioritize clearing others’ blockers over their own work, knowing this is a key contributor to a stable community.

Notably, the idea that "pending code reviews represent blocked threads of execution" can be applied broadly across all forms of collaboration. Prioritize providing feedback to others over your own work, if your feedback is blocking their progress. Request a code review for a pull request by assigning the @wp-cli/committers team for review.

Committer participation isn’t just committing code. Often, you can have a huge amount of impact by investing a little bit of your time into:

Contributions

Contributing

Welcome and thanks!

We appreciate you taking the initiative to contribute to WP-CLI. It’s because of you, and the community around you, that WP-CLI is such a great project.

Contributing isn’t limited to just code. We encourage you to contribute in the way that best fits your abilities, by writing tutorials, giving a demo at your local meetup, helping other users with their support questions, or revising our documentation.

Please take a moment to read these guidelines in depth. Following the guidelines helps to communicate that you respect the time of the other contributors to the project. In turn, they’ll do their best to reciprocate that respect when working with you, across timezones and around the world.

Should you have any questions about contributing, please join the #cli channel in the WordPress.org Slack.

Reporting Security Issues

The WP-CLI team and WordPress community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.

To report a security issue, please visit the WordPress HackerOne program.

Reporting a bug

Think you’ve found a bug? We’d love for you to help us get it fixed.

Before you create a new issue, you should search existing issues to see if there’s an existing resolution to it, or if it’s already been fixed in a newer version of WP-CLI. You should also check our documentation on common issues and their fixes.

Once you’ve done a bit of searching and discovered there isn’t an open or fixed issue for your bug, please follow our guidelines for submitting a bug report to make sure it gets addressed in a timely manner.

Creating a pull request

Want to contribute a new feature? WP-CLI is a mature project, and already chock-full of useful functionality. Please first open an ideas issue to suggest new commands, or open an issue in the appropriate repository to suggest enhancements to existing commands. Opening an issue before submitting a pull request helps us provide architectural and implementation guidance before you spend too much time on the code.

New to the WP-CLI codebase? Check out issues labeled 'good-first-issue' for a place to start. These issues are specially earmarked for new contributors.

Once you've decided to commit the time to seeing your pull request through, please follow our guidelines for creating a pull request to make sure it's a pleasant experience. See "Setting up" for details on making local modifications to WP-CLI. Keep in mind pull requests are expected to have tests covering the scope of the change. Read through our code review guidelines for a better understanding of how your pull request will be evaluated.

Improving our documentation

Is documentation your strength? Take a look at the currently open documentation issues and see if you can tackle any of those.

There are a couple different types of documentation currently part of WP-CLI:

Contributing in other ways

Feel free to create an issue with your question, and we'll see if we can find an answer for it.

Alternatively, if you have a WordPress.org account, you may also consider joining the #cli channel on the WordPress.org Slack organization.

Contributions

Governance

This document describes the governance structure of the WP-CLI project.

Who's in charge?

WP-CLI has one maintainer: schlessera. On a day to day basis, final decision-making authority resides with him.

The project roadmap is an organic, evolving vision developed between the project's maintainers, committers, and contributors. Generally, we try to make decisions in alignment with the project's philosophy and committers credo.

Why are some people members of the WP-CLI organization?

On occasion, we grant write access to contributors who have demonstrated, over a period of time, that they are capable and invested in moving the project forward. These committers are expected to:

Becoming a committer can seem glamorous, but it also comes with expectations of responsibility, commitment, and humility.

andreascreten is the original author of WP-CLI. scribu and danielbachhuber have been long time maintainers. schlessera is the current maintainer.

The maintainer is the person most directly responsible for the reliability and longevity of the project. They are expected to:

What's the connection between WP-CLI and WordPress?

As of December 2017, WP-CLI is a formal WordPress project.

Practically speaking, this means we get to take advantage of the WordPress project's people and software infrastructure:

Other than this, WP-CLI operates independently.

How does all of this work?

The WP-CLI GitHub organization contains all project code repositories. Some commands are bundled with WP-CLI, while others are independently installable. Commands are bundled with WP-CLI when they're stable, closely aligned with the project philosophy, and useful to a majority of users.

wp-cli/wp-cli is the main project repository, which pulls in command packages and other dependencies through Composer. Composer defines which version of which dependencies is included in the build. Changes to command packages are included in WP-CLI proper when a stable release is tagged for the package.

Build artifacts are published to the builds repository. Releases are prepared manually in accordance with the release checklist.

The wp-cli.org domain is currently owned by andreascreten. DNS is managed through a Cloudflare account that danielbachhuber holds credentials to.

Much of the WP-CLI documentation (command pages, etc.) is editable through wp-cli/handbook, and then synced to WordPress.org. The WP-CLI.org homepage is hosted on GitHub Pages.

From time to time, you may see a pull request from the wp-make-coffee bot. These originate from a donated WebFaction server running some cron jobs calling bash scripts:

5 4 * * 1,3,5 source ~/.bash_profile; WP_CLI_DIR=~/wp-cli bash ~/wp-cli/utils/auto-composer-update.sh > ~/auto-composer-update.log 2>&1
5 6 * * 1,3,5 source ~/.bash_profile; WP_CLI_DIR=~/wp-cli-bundle bash ~/wp-cli-bundle/utils/auto-composer-update.sh > ~/auto-composer-update-bundle.log 2>&1

The wpcli Twitter account is managed by schlessera.

If you subscribe for email updates, your email address is registered with WordPress.com through Automattic's Jetpack plugin.

Contributions

Implementation Details

This page contains some history on various implementation details of WP-CLI.

Bootstrapping WordPress

On a normal web request, your web server calls the index.php file in the root of the web directory to bootstrap the WordPress load process:

<?php
/**
 * Front to the WordPress application. This file doesn't do anything, but loads
 * wp-blog-header.php which does and tells WordPress to load the theme.
 *
 * @package WordPress
 */

/**
 * Tells WordPress to load the WordPress theme and output it.
 *
 * @var bool
 */
define('WP_USE_THEMES', true);

/** Loads the WordPress Environment and Template */
require( dirname( __FILE__ ) . '/wp-blog-header.php' );

You'll notice index.php calls wp-blog-header.php, which then calls wp-load.php, which then calls wp-config.php, which then calls wp-settings.php.

This last file, wp-settings.php, is WordPress' primary bootstrap file. It loads your plugins, active theme, and calls the init action.

On the command line, WP-CLI follows a similar process to bootstrap WordPress. However, instead of loading index.php, using the wp command starts with this:

<?php

// Can be used by plugins/themes to check if WP-CLI is running or not
define( 'WP_CLI', true );
define( 'WP_CLI_VERSION', trim( file_get_contents( WP_CLI_ROOT . '/VERSION' ) ) );
define( 'WP_CLI_START_MICROTIME', microtime( true ) );

// Set common headers, to prevent warnings from plugins
$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
$_SERVER['HTTP_USER_AGENT'] = '';
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';

include WP_CLI_ROOT . '/php/utils.php';
include WP_CLI_ROOT . '/php/dispatcher.php';
include WP_CLI_ROOT . '/php/class-wp-cli.php';
include WP_CLI_ROOT . '/php/class-wp-cli-command.php';

\WP_CLI\Utils\load_dependencies();

WP_CLI::get_runner()->start();

WP-CLI includes a good amount of setup code prior to calling wp-settings.php. Its bootstrapping process is different than a web request in a couple of notable ways.

wp-config.php is parsed, and then executed

Rather than calling wp-config.php directly, WP-CLI gets the contents of wp-config.php, parses out the require_once ABSPATH . 'wp-settings.php'; statement, and loads the constants into scope with eval(). Read "How WP-CLI loads WordPress" for a narrative on the historical reasons. After that, WP-CLI used a custom wp-settings-cli.php until v0.24.0 [#2278], but parsing wp-config.php was kept for backwards compatibility purposes. See also #1631.

WordPress is loaded inside of a function

WP-CLI loads WordPress with the WP_CLI::get_runner()->load_wordpress() method, meaning WordPress plugins and themes aren't loaded in global scope. Any global variables used in plugins or themes need to be explicitly globalized. See #2089 for the history of this decision.

Once WP_CLI::get_runner()->load_wordpress() calls wp-settings.php, WordPress handles the rest of the bootstrap process.

Command Help Text

The wp help <command> has been through several incarnations.

Since WP-CLI 0.3, it invoked a static help() method in the command class, if it existed. (48a8887d)

Since WP-CLI 0.6, it looked for a <command>.1 ROFF file and displayed it using man. The ROFF file was compiled from a corresponding <command>.txt markdown file and from PHPDoc metadata. (#24).

Since WP-CLI 0.11, it generates the help text on the fly. (#548)

WP_ADMIN

Most WP-CLI commands perform administrative actions and they need access to code defined in wp-admin/includes. This code can be loaded on-demand or preemptively.

The question is: should the WP_ADMIN constant be set to true or false?

Initially, WP-CLI just loaded the wp-admin code and didn't mess with the WP_ADMIN constant at all.

Then, it sort of pretended it was doing a front-end page load, for doing integration testing (#69). [1]

Then it pretended it was loading wp-admin, to side-step caching plugins (#164).

Then it stopped pretending it was loading wp-admin (#352), because we found a better way to side-step caching plugins. [2]


Contributions

Licensing

WP-CLI is licensed under the MIT license. This document outlines WP-CLI's licensing expectations for third-party code.

Accepting third-party code into the official repository/organization

All code accepted into repositories under the official WP-CLI GitHub organization must be compatible with the MIT license. This ensures that WP-CLI can remain freely usable, modifiable, and distributable by anyone.

Bundling third-party code

When bundling third-party code directly into a WP-CLI package (i.e., copying code into the repository rather than declaring it as a dependency), the code must:

  1. Be licensed under an MIT-compatible license
  2. Include the original license file or header - The third-party code's license terms must be preserved
  3. Maintain copyright notices - Original copyright notices must remain intact
  4. Be clearly identified - It should be clear which code is third-party and where it came from

When in doubt about whether to bundle third-party code, consider using it as a dependency through Composer instead.

Depending on third-party code

Third-party packages declared as dependencies (through Composer or other package managers) must be licensed under MIT-compatible licenses. This applies to both:

Checking dependency licenses

Before adding a new dependency:

  1. Check the dependency's LICENSE file or composer.json for license information
  2. Verify that all of the dependency's own dependencies are also compatible
  3. Use composer show -t to view the dependency tree with license information, or install additional tools like composer/satis to run composer licenses for detailed license auditing

Why these requirements?

These licensing requirements ensure that:

  1. WP-CLI remains free and open - Users can use, modify, and distribute WP-CLI without legal concerns
  2. Commercial use is permitted - Companies can use and integrate WP-CLI into their products
  3. No license conflicts - All code in the WP-CLI ecosystem is legally compatible
  4. Simplicity for users - Users don't need to track multiple license requirements

Questions?

If you have questions about licensing or need clarification on whether a specific license is compatible, please open an issue or contact the WP-CLI maintainers.

Contributions

Philosophy

WP-CLI is the command-line interface for WordPress. It provides commands for actions that can be performed in the WordPress backend (e.g. wp theme activate and wp user create), as well as commands for actions that don't have an equivalent web UI (e.g. wp cron event run, wp search-replace, and wp scaffold child-theme).

WP-CLI's guiding principle is to be the fastest and canonical way to manage WordPress:

This page contains a list of guidelines that should inform decisions related to scope, command organization and behavior:

Good commands are simple, interact with WordPress, and solve one unique problem.

When a command implements existing WordPress functionality (e.g. wp export), it should mirror and re-use existing WordPress behavior as much as possible. Doing so ensures it meets the user's assumed expectations of behavior.

When a command adds functionality not present in the WordPress admin (e.g. wp search-replace), it should solve one unique problem and interact with WordPress in some way. Good commands are born of true user need; for a new proposed WP-CLI command, there probably should be existing prior art in the ecosystem.

If a command provides useful functionality, but it doesn't have anything to do with WordPress, it doesn't belong in WP-CLI. Similarly, an idea for a command is sometimes better implemented as a change to documentation instead.

Consistency makes for an enjoyable user experience.

Whenever possible, commands should emulate one another with consistent usage behavior, argument names, and documentation.

Decisions, not options.

WordPress' philosophy states:

[The end user is considered first when making implementation decisions]. A great example of this consideration is software options. Every time you give a user an option, you are asking them to make a decision. When a user doesn’t care or understand the option this ultimately leads to frustration. As developers we sometimes feel that providing options for everything is a good thing, you can never have too many choices, right? Ultimately these choices end up being technical ones, choices that the average end user has no interest in. It’s our duty as developers to make smart design decisions and avoid putting the weight of technical choices on our end users.

The same principle applies to WP-CLI. In every case, we should first try to improve the default behavior. Options should be added sparingly, only when a solution is required and no other solutions are available.

As a rule of thumb, once a command has a half-dozen options or more, it becomes difficult to understand how the command will operate under each condition.

Composability is always a good idea.

One of the most useful ideas in UNIX was that of pipelines.

WP-CLI commands should be composable, i.e. the output from one command should be easily pipe-able to another command.

A corollary of this is that commands should be orthogonal, which means that there should be no overlapping functionality between commands. (wp plugin install --activate is an exception).

See Shell Friends for specific examples of how this philosophy is applied.

Readability trumps number of keystrokes.

Most of the commands in WP-CLI will be used non-interactively, so make the parameter names self-documenting.

Don't assume anything.

Bundled commands MUST work on any given WordPress install (provided that it's a new enough version), no matter how it's configured and no matter how much data it has.

As a corollary, bundled commands should not assume the presence of any plugin or theme.

Contributions

Pull Requests

WP-CLI follows a pull request workflow for changes to its code (and documentation). Whether you want to fix a bug or implement a new feature, the process is pretty much the same:

  1. Search existing issues; if you can't find anything related to what you want to work on, open a new issue in the appropriate repository so that you can get some initial feedback.
    1. Opening an issue before submitting a pull request helps us provide architectural and implementation guidance before you spend too much time on the code.
  2. Fork the repository you'd like to modify, either the framework or one of the command packages.
    1. See Setting Up for more details on configuring the codebase for development.
  3. Create a branch for each issue you'd like to address. Commit your changes.
  4. Push the code changes from your local clone to your fork.
  5. Open a pull request. It doesn't matter if the code isn't perfect. The idea is to get it reviewed early and iterate on it.
  6. Respond to code review feedback in a timely manner, recognizing development is a collaborative process.
  7. Once your pull request has passed code review, it will be merged into the default branch and be in the pipeline for the next release.

New to WP-CLI commands? You may want to start with the commands cookbook to learn more about how commands work.

There are three classes of repos you might want to edit:

Expectations

When submitting a pull request, there are several expectations to keep in mind.

Tests are required

Most of the time, we'll ask that functional or unit tests be added to cover the change. If it's a new feature, the pull request needs tests. If it's fixing a bug, the pull request needs tests.

See the documentation below for more information on writing and running tests.

Follow WordPress Coding Standards

While not yet strictly enforced, the WP-CLI project generally follows the WordPress Coding Standards. We may ask you to clean up your pull request if it deviates too much.

Please refrain from unnecessary code churn

Code refactoring should not be done just because we can. With a years-old codebase, there's an infinite number of best practice, readability, or consistency improvements that could be made. However, engaging on any of them has non-obvious costs: our time and attention, making Git history more difficult to review, etc. Any code changes should have clear and obvious value.

Contributions are atomic

To make it far easier to merge your code, each pull request should only contain one conceptual change. Keeping contributions atomic keeps the pull request discussion focused on one topic and makes it possible to approve changes on a case-by-case basis.

If you submit a pull request with multiple conceptual changes, we'll ask you to resubmit as separate pull requests.

Make regular progress on your contribution

Through our code review process, we'll work with you to make sure your pull request is ready for merge. But if changes are needed and we haven't heard from you in two weeks, we'll consider the pull request abandoned. Someone else may pick it up and make the changes required. Or it may be closed.

If you need to step away for any reason, make a comment on the pull request or the related issue so we can pick things up or put things on hold when needed.

Setting up

If you haven't submitted a pull request before, you'll want to install WP-CLI for local development. Depending on whether you want to work on a particular command/package or on the entire project as a whole, the process is slightly different.

Working on a specific command/package

  1. Install Composer if you don't already have it.
  2. Clone the git repository of the command/package you want to work on to your local machine. As an example for working on the wp core command: git clone https://github.com/wp-cli/core-command.git
  3. Change into the cloned directory: cd core-command
  4. Install all Composer dependencies: composer install
  5. Verify WP-CLI was installed properly: vendor/bin/wp --info

Within this package, you should preferably use vendor/bin/wp to run the command. Just using wp should work as well, but by doing that you might run the command through a different version of the framework and thus get an unexpected result.

Working on the project as a whole

  1. Install Composer if you don't already have it.
  2. Clone the WP-CLI git repository to your local machine: git clone git@github.com:wp-cli/wp-cli.git ~/wp-cli
  3. Change into the cloned directory: cd ~/wp-cli. If you are going to work on the core framework itself, fork the repository on GitHub through the web UI and add it as a remote: git remote add fork git@github.com:yourusername/wp-cli.git
  4. Install all Composer dependencies: composer install --prefer-source
  5. Alias the wp command to your new WP-CLI install: alias wp='~/wp-cli/bin/wp'
  6. Verify WP-CLI was installed properly: wp --info

Commands bundled with WP-CLI (e.g. wp scaffold plugin) will be editable from the vendor/wp-cli directory (e.g. vendor/wp-cli/scaffold-command). The --prefer-source flag when installing WP-CLI ensures each command is installed as a Git clone, making it easier to commit to.

Commands available for standalone installation (e.g. wp dist-archive) can be installed from source (e.g. wp package install git@github.com:wp-cli/dist-archive-command.git). Run wp package path <package-name> to find the appropriate directory to edit.

Importantly, you'll need to fork each repository in order to have a remote to push to. For each repository you want to contribute to:

  1. Fork the repository on GitHub through the web UI

  2. Add your fork as a remote:

    $ cd vendor/wp-cli/scaffold-command $ git remote add fork git@github.com:yourusername/scaffold-command.git $ git remote -v origin https://github.com/wp-cli/scaffold-command.git (fetch) origin https://github.com/wp-cli/scaffold-command.git (push) fork git@github.com:yourusername/scaffold-command.git (fetch) fork git@github.com:yourusername/scaffold-command.git (push)

Once you've done so, you'll have a fork in your GitHub account and a new remote you can push to. You can then push changes to your fork with git push fork branch-name.

Optional: The GitHub CLI (gh) can help streamline some of these tasks, such as forking repositories and creating pull requests from the command line.

Running and writing tests

There are three types of automated tests:

Code style sniffers

The sniffers ensure that the code adheres to a given code style, to avoid unneeded discussions about less relevant details like spacing or alignments.

They also check for known sources of bugs and PHP compatibility problems.

To run the sniffs:

composer phpcs

To fix the errors and warnings that can be automatically fixed:

vendor/bin/phpcbf

Functional tests

WP-CLI uses Behat for functional testing. Functional tests ensure commands work as expected by executing them end-to-end.

For a comprehensive guide on writing and running Behat tests, see Writing Behat Tests.

Here's a quick example of what a Behat test looks like:

Feature: Manage WordPress options

  Scenario: Read an individual option
    Given a WP install

	When I run `wp option get home`
	Then STDOUT should be:
      """
	  https://example.com
	  """

In this example:

Creating a test database

Before running the functional tests, you'll need a MySQL (or MariaDB) user called wp_cli_test with the password password1 that has full privileges on the MySQL database wp_cli_test. To override these credentials you can make use of the database credentials constants of wp-cli-tests

If your user has the correct permissions, the database can also be set up by running composer prepare-tests. This will create the database and the user and configure the necessary privileges. Note that this operation is not needed for every test run, it only needs to be run the first time for the initial setup.

Note: If you are using MySQL >= 8.0, you may experience inconsistencies with WP-CLI successfully connecting to the database. MySQL 8.0 changed the default authentication plugin and some clients (such as PHP) do not yet support this change. More information can be found on this blog post.

Running the test suite

Then, to run the entire test suite:

composer behat

Or to test a scenario at a specific line:

composer behat -- features/core.feature:10

Or to test a single feature:

composer behat -- features/core.feature

Or to test a single feature with more verbosity:

composer behat -- features/core.feature --format pretty

To run only the tests that failed during the previous run:

composer behat-rerun

When writing new tests, to see which step definitions are available:

composer behat -- --definitions l

More info can be found by using composer behat -- --help.

Unit tests

The unit test files are in the tests/ directory.

To run the unit tests, execute:

composer phpunit

To run a specific unit test, you can use:

composer phpunit -- --filter=<method name>

Running all tests in one go

To run all tests in one go:

composer test

This will run all the tests that the package is set up to use, based on the presence of the respective configuration files.

Each repository is configured to run all of its active tests on every code push. The wp-cli/automated-tests repository runs all tests for all repositories on a regular basis.

Finally...

Thanks! Hacking on WP-CLI should be fun. If you find any of this hard to figure out, let us know so we can improve our process or documentation!

Contributions

Release Checklist

There are two different release checklists available from within the wp-cli/wp-cli repository:

As a first step for initiating a release process, the corresponding checklist should be generated and added to the milestone it relates to.

Contributions

Repository Management

Package Names

Package names for the official wp-cli GitHub organization need to be discussed upfront with the maintainers.

Before creating a new repository, it's important to discuss the repository first. This discussion helps establish consensus on what purpose the codebase serves, and where it lives best in the WP-CLI ecosystem.

Package names for WP-CLI commands must end with the -command suffix for clarity, and also to distinguish them from the non-command repositories.

Before new repositories are created for commands, a decision needs to be made whether the command deserves its own repository or whether it should be included within an existing repository instead.

Example: wp-cli/scaffold-package-command.

Package Versions

Package versions follow the Semantic Versioning Specification (SemVer).

In layman's terms for us, this typically means:

Milestones

Open milestones usually point to the next version to be released.

As the release notes are built using the milestones, both pull requests and issues that are to be merged need to be set to the upcoming milestone, so that these pull requests will be part of the release notes.

Labels

Labels are an important organizational tool to communicate the state and progress of the work being done. Committers are required to keep these up-to-date.

Keeping issues and pull requests accurately labeled helps us make sense of issue tracker usage in aggregate and more easily search closed issues and pull requests for past discussion.

Groups Of Labels

Labels can be part of a label group, a concept that is applicable to all packages. The actual labels that are available can depend on the actual package, but the groups always have the same semantic role.

Command

The labels that define what specific command a given issue/pull request is related to are prefixed with command:.

Some examples:

Scope

The labels that define what scope the current issue/pull request applies to are prefixed with scope:.

Used scopes:

State

The labels that define what state a given issue/pull request is in are prefixed with state:.

Used states:

Required Labels

Some labels have a special meaning and/or might be used for automated workflows down the road. These are required across all official packages.

Bug

The bug label denotes an issue that is a confirmed bug where the code does not produce the expected result.

Bugs are specific to unexpected behavior of existing functionality. Functionality perceived to be missing is not a bug, but an enhancement.

Upstream Bug

The upstream-bug label denotes an issue that is a bug in upstream software (e.g. WordPress core, PHP, etc.) that won't be fixed in WP-CLI.

Good First Issue

The good-first-issue label denotes an issue that is a good entry point for new contributors wanting to get their feet wet.

Good first issues are small in scope and don't require extensive technical expertise or historical project knowledge to get started.

Commits

No direct commits are to be done against the default (main/master) branch of the packages. All code changes need to go through a pull request workflow.

Pull Requests

All code changes go through a pull request workflow.

Every submitted pull request needs to go through a code review, and needs to be approved by at least one of the committers.

Merge Pre-Conditions

Non-trivial pull requests should be preceded by a related issue that defines the problem to solve and allows for discussion of the most appropriate solution before actually writing code.

If a pull request is submitted by one of the committers, the submitter should set the "Reviewers" for that pull request to wp-cli/committers if a general code review is needed, or to one or more specific committer profiles if the expertise of a specific person is needed/wanted.

If a pull request is submitted by an external contributor, the committers should be responsive and provide quick feedback to encourage further contributions.

Unmergeable Contributions

Sometimes, a pull request may not be mergeable, no matter how much additional effort is applied to it (e.g. out of scope, etc.). In these cases, it's best to let the contributor down as softly and firmly as possible, both to encourage future involvement and avoid flame wars.

Make sure to:

  1. Thank the contributor for their time and effort.
  2. Fully explain the reasoning behind the decision to close the pull request.
  3. Link to as much supporting documentation as possible.

If you'd like a template to follow:

Thanks ________ for the time you've spent on this pull request.

I'm closing this pull request because ________. To clarify further, ________.

For more details, please see ________ and ________.

Merge Conditions

Apart from needing to be approved, pull requests also need to have their tests in a passing state before they can be merged. When both of these conditions are true, any committer can merge the pull request by:

  1. Ensuring that all applicable labels have been set.
  2. Ensuring that the correct milestone has been set.
  3. Ensuring that the branch is deleted after the merge (if applicable).
  4. Adapting the title of the pull-request as needed. The ideal pull request title is one that can be copied and pasted into release notes as is.

Once a pull request is merged, the committer should make sure any corresponding issues are also closed and assigned to the correct milestone.

Releases

WP-CLI Releases

WP-CLI releases are scheduled and communicated well in advance by the maintainers.

When the release is ready, it is prepared and executed according to the release checklist by one of the maintainers.

Package Releases

Package releases happen on an as-needed basis and are executed by the maintainers. A package is tagged for a release when:

Tagging a release should be postponed (for a few to several days) if there's an open pull request that makes sense to include in the release.

Package release notes should include the subject of and a link to each merged pull request. It's not necessary to include minor documentation, coding standard, and test suite changes. Feel free to edit the subject for clarity. See wp-cli/scaffold-command v1.0.6 for an example.

Importantly, tagging a package release is also an opportunity to perform a final review of product quality:

Sometime after a new package version is tagged, the wp-make-coffee bot submits a pull request against wp-cli/wp-cli with the results of composer update. When this pull request is merged, the package release will end up in the WP-CLI nightly build.

Tips & Tools

GitHub Searches

There are a lot of useful ways to search GitHub. These searches can be put into bookmarks or embedded into pages as a link.

Here are some examples:

Contributions

Roadmap

WP-CLI is released every 3-4 months, typically around the beta period of WordPress's release cycle:

Patch versions are released on an as-needed basis, usually to address bugs or regressions. The current version of WP-CLI is the only officially supported version.

The current longer-term goals of the project are:

New features are developed as packages first. Doing so enables:

To suggest new ideas, head on over to the wp-cli/ideas repository.

For more details on how WP-CLI releases are produced, please see Philosophy and Governance. For more information about how WP-CLI handles its requirements, please see WP-CLI PHP Requirements Strategy.

Contributions

Security Vulnerability Reporting for WP-CLI

The WP-CLI team appreciates security vulnerability reports and takes them seriously. If you believe you have discovered a security vulnerability in WP-CLI, please read this page in full before following our responsible disclosure process. A valid report can be granted a CVE and may be rewarded with a bounty payment via the HackerOne program.

Report validity

A growing number of the vulnerability reports we receive are not genuine vulnerabilities. They describe behavior that may be technically accurate in isolation but requires an attacker to already possess more power than the reported vulnerability would grant, or that depends on a victim being socially engineered into compromising themselves. Triaging these reports takes up maintainer time that would be better spent elsewhere.

What follows is a set of triage maxims borrowed in spirit from Brocards for vulnerability triage by the maintainer of Zizmor, adapted to WP-CLI's threat model. A brocard is a concise legal maxim that captures the essence of a principle. None of these are universally true, and any given report may rebut any of them, but each lets us evaluate a report quickly when it covers a category we routinely decline.

Understand WP-CLI's threat model first

Most invalid reports we receive share a single root cause: a misunderstanding of who WP-CLI trusts.

WP-CLI is a tool that an operator runs from a shell on a server in order to administer a WordPress installation. To run wp at all, you must already be able to execute arbitrary code on that server. It is a tool for people who are already inside the trust boundary.

This means the operator running WP-CLI is not a party we must defend against because WP-CLI's job is not to protect the server from the people who administer it.

A report only describes a vulnerability if it shows how someone outside that boundary gains something they could not otherwise have.

No vulnerability without a threat model

A report must explain who the attacker is and what they gain. "This function could behave unexpectedly" or "this input is not sanitized" is not a vulnerability report, it is an observation. Without a plausible attacker who ends up with more access than they began with, there is nothing to fix from a security standpoint (though it may still be a good bug report).

The team will decline reports that demonstrate no actual exploit. This may include:

No exploit from the heavens

If exploiting the reported behavior requires the attacker to already hold a capability equal to or greater than the one they gain, there is no vulnerability.

A common form of this report requires that the attacker can already write files to the target server, and then demonstrates that WP-CLI will trust a malicious file they placed there. An attacker who can write a malicious file to the server has already won, and the WP-CLI team considers this to be true regardless of the form that the malicious file takes.

The same applies to a report that requires the attacker to already have access to the database, the filesystem, a server control panel, or a shell, and then frames WP-CLI's normal trust of that environment as the flaw.

The user is not their own attacker

A vulnerability in the form of "a remote attacker can convince a WP-CLI user to run a malicious command" is not a vulnerability in WP-CLI, it is social engineering.

Any command-line tool will do what the person at the terminal tells it to do. WP-CLI cannot, and does not attempt to, defend the operator against their own keystrokes, including input that's obviously malicious or cleverly obfuscated.

A broken neighbour is not our vulnerability

Some reports are only exploitable on shared hosting where multiple accounts are not properly isolated from one another. This is a failure of isolation in a multi-tenant environment. The classic problem is account A being able to read account B's files or write into a location account B's WP-CLI will read.

When the operating system or hosting platform fails to isolate tenants, the vulnerability lives in that misconfiguration, not in WP-CLI. It is expected that WP-CLI can assume that the server enforces safe user and file permissions.

Denial of self is not denial of service

Reports of denial of service, memory exhaustion, runaway CPU usage, infinite loops, or processes that consume too many resources are, for WP-CLI, generally not security vulnerabilities.

If a command consumes too much memory or runs too long, the operator can press Ctrl-C or set a memory or time limit. No privilege boundary is crossed if a weakness results in runaway resource usage.

No vulnerability from an unsupported configuration

If the behavior can only be triggered under an unusual, unsupported, or deliberately insecure configuration of WordPress, the server, or WP-CLI, the problem lies in that configuration.

Examples include reports that depend on:

No cure worse than the disease

When a report identifies real but marginal behavior, the team will weigh the cost of the fix against the risk it removes.

This includes weaknesses where the fix would:

Such fixes may do more harm than the issue they address. Where the downside of fixing such an issue exceeds the vulnerability's impact, the team may instead choose to clearly document the behavior.

If your report still stands

The above are filters, not walls. If you have read this far and your report still demonstrates that an attacker outside of WP-CLI's trust boundary can gain access they should not have, then please disclose it responsibly and the team will endeavour to handle it accordingly.

Contributions

WordCamp Contributor Day

Welcome to WordCamp Contributor Day! We appreciate you sharing your time with WP-CLI.

We'd love to help you submit at least one pull request, so we put together this guide to make it as straightforward as possible. We're here to support you however we can.

When you submit your pull request, add Related https://github.com/wp-cli/wp-cli/issues/6295 so we can see all pull requests created during the day. We'll give these some special promotion in the next release notes.

Your table lead for the day is: thelovekesh

A few seasoned WP-CLI contributors are also helping out: Abhishek Sharma, Deepak Kumar

Getting Started

If you normally use WP-CLI on your web host or via Brew, you're most likely using the Phar executable. The Phar executable is the "built", single-file version of WP-CLI. It's compiled from a couple dozen repositories in the WP-CLI GitHub organization, so modifying WP-CLI requires working amongst those repositories.

Before you can work on WP-CLI, you'll need to first make sure you have PHP and a functioning MySQL or MariaDB server. Once those prerequisites are met, install the wp-cli-dev development environment to start contributing:

git clone https://github.com/wp-cli/wp-cli-dev
cd wp-cli-dev
composer install

The wp-cli-dev installation process clones all of WP-CLI's repositories to your local machine. After it's complete, you'll be able to make changes in whichever repository you'd like. However, you'll need to fork the repository and add it as a remote in order to push your feature branch.

All WP-CLI pull requests are expected to have tests. See running and writing tests for a complete introduction.

Suggested Tickets

To help you be successful during Contributor Day, we curated a list of reasonably approachable and actionable issues. Feel free to comment directly on the issue if you plan to work on it. We don't usually assign issues, so no need to worry about that.

New contributors

Seasoned contributors

You're obviously welcome to work on any other issue you'd like too! Contributor Day can be a good opportunity to discuss trickier issues and brainstorm approaches.

Contributions

WP-CLI Contributor Badge

The WP-CLI Contributor Badge recognizes individuals who have made meaningful contributions to the WP-CLI project. This badge is displayed on a contributor’s WordPress.org profile and highlights their involvement in maintaining and improving WP-CLI.

Eligibility criteria

You may be eligible for the WP-CLI badge if you have made consistent and valuable contributions, including but not limited to:

There is no strict minimum requirement. Contributions are evaluated based on quality, impact, and consistency.

Badge assignment process

WP-CLI maintainers regularly review contributor activity across all WP-CLI repositories, typically after every release, and award badges to eligible contributors.

Note: Please do not ping maintainers about the badges or try to request it yourself.

Contributions

WP-CLI Hack Day

Welcome to WP-CLI Hack Day! We appreciate you taking time to contribute to the project.

We'd love to help you submit at least one pull request today. Given this goal, you'll likely want to start with something small and attainable. After you've submitted your first pull request for the day, you're welcome to work on something more ambitious.

When you submit a pull request during Hack Day, please add Related https://github.com/wp-cli/wp-cli/issues/5935 so we can easily keep track of them. We'll include them in a recap post at the end of the day.

We put together this guide to make contributing as straightforward as possible. Please also join the #cli channel in WordPress.org Slack (sign up instructions) to chat with other contributors, for questions, etc.

Your leads for the day are: schlessera, danielbachhuber, swissspidy

Open Video Sessions

During the Hack Day, we’ll have two open video chat sessions:

These sessions are a great opportunity to discuss remaining issues live, chat about the progress we’ve made, and to connect with the community. Feel free to join these to talk through any challenges or share your updates!

Getting Started

If you normally use WP-CLI on your web host or via Brew, you're most likely using the Phar executable (wp-cli.phar). This Phar executable file is the "built", singular version of WP-CLI. It is compiled from a couple dozen repositories in the WP-CLI GitHub organization.

In order to make code changes to WP-CLI, you'll need to set up the wp-cli-dev development environment on your local machine. Before you can proceed, though, you'll need to make sure you have Composer, PHP, and a functioning MySQL or MariaDB server on your local machine.

Once the prerequisites are met, clone the GitHub repository and run the installation process:

git clone https://github.com/wp-cli/wp-cli-dev wp-cli-dev
cd wp-cli-dev
composer install
composer prepare-tests

The wp-cli-dev installation process clones all of WP-CLI's repositories to your local machine. After the installation process is complete, you can make changes in whichever repository you like. You'll need to fork the repository in order to push your feature branch, however. GitHub's CLI is pretty helpful for this:

cd core-command
gh repo fork

We have started a video tutorial series that you can watch here: WP-CLI Contribution Tutorials. These videos will provide an overview of the packages and then go through setting up the development environment to allow you to run automated tests locally.

All WP-CLI pull requests are expected to have tests. Watch a ~10 minute video introduction, or see running and writing tests for the written version.

Suggested Tickets

To help you be successful during the day, we curated a list of reasonably approachable and actionable issues.

Feel free to comment directly on the issue if you plan to work on it. We don't usually assign issues, so no need to worry about that.

New contributors

See issues labeled 'good-first-issue' or issues labeled 'contributor-day' for a broader list.

Seasoned contributors

You're obviously welcome to work on any other issue you'd like too! Bug fixes in particular are very welcome. This day can be a good opportunity to discuss trickier issues and brainstorm approaches.

Thank You for Contributing

We appreciate your taking the time to participate in the Hack Day and contribute to wp-cli. Have fun, and we look forward to seeing you on Slack!

Support

WP-CLI's maintainers and contributors have limited availability to answer general support questions. The current version of WP-CLI is the only officially supported version.

Before asking for help, search for your question in these resources:

If you do not find an answer in those resources:

If you have found a reproducible bug in WP-CLI, please follow the bug report guidelines before opening an issue.