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:


 * 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." );
		// 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.' );
		// 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.' );
		// 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:

  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. |

Profile key performance metrics for a WP REST API response

Using the eval-file command in wp profile, you can profile key performance metrics for a WP REST API response by mocking, executing, and evaluating the request with a one-off file. These key performance metrics include execution time, query count, cache hit/miss ratio, and more.

For example, to mock a call to GET /wp/v2/posts?per_page=100, you’d create a get-posts.php file like this:

$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
$request->set_param( 'per_page', 100 );
$server = rest_get_server();
$server->dispatch( $request );

Then, profile get-posts.php by running wp profile eval-file get-posts.php:

$ wp profile eval-file get-posts.php
| time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
| 0.9429s | 0.002s     | 5           | 96.39%      | 7599       | 285          | 0s           | 0             |

Always require a specific file when running WP-CLI

WP-CLI can read default 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).

To always require a specific file when running WP-CLI, you can include a require statement in one of the aforementioned configuration files like so:

  - path/to/file.php

Learn more about WP-CLI’s config system on the WP-CLI website.

RESTful WP-CLI: The final update?

This post originally appeared on the WP-CLI blog.

Last November, I published a Kickstarter, and was completely blown away by the support. This month, the funding ran out, so I thought I’d post one last RESTful WP-CLI update.

Actually, the story doesn’t end here. I’m writing a massive retrospective post about using Kickstarter to fund open source, so keep an eye out for that. Also, WP-CLI v0.24.0 is due out a week from now, July 27th, and it’s looking to be the largest release ever. When you do a Kickstarter, it’s really just the beginning of something bigger.

Enough with the superlatives, let’s dive into some new features. Remember: RESTful WP-CLI features require under the hood changes to WP-CLI. You’ll want to wp cli update --nightly to play with this new functionality locally. Once you’ve done so, you can wp package install wp-cli/restful to install the latest.

Effortlessly use WP-CLI against any WordPress install

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, if I’m working locally on the runcommand theme, have registered a new rewrite rule, and need to flush rewrites inside my Vagrant-based virtual machine, I can run:

$ wp @dev rewrite flush
Success: Rewrite rules flushed.

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

$ wp @prod rewrite flush
Success: Rewrite rules flushed.

Look ma! No more SSH’ing into machines, changing directories, and generally spending a full minute to get to a given WordPress install.

Additionally, alias groups let you register groups of aliases. If I want to run a command against both runcommand WordPress instances, I can use @both:

$ 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:

  ssh: [email protected]~/webapps/production
  ssh: [email protected]/srv/www/runcommand.dev
  - @prod
  - @dev

But wait, what’s the ‘ssh’ in there?

WP-CLI now natively supports a --ssh=<host> global parameter for running a command against a remote WordPress install. Many thanks to XWP and their community for paving the way with WP-CLI SSH.

Under the hood, WP-CLI proxies commands to the ssh executable, which then passes them to WP-CLI installed on the remote machine. Your syntax for -ssh=<host> can be any of the following:

  • Just the host (e.g. wp --ssh=runcommand.io), which means the user will be inferred from your current system user, and the path will be the SSH user’s home directory.
  • The user and the host (e.g. wp [email protected]).
  • The user, the host, and the path to the WordPress install (e.g. wp [email protected]~/webapps/production). The path comes immediately after the TLD of the host.

Or, if you use a ~/.ssh/config, <host> can be any host alias stored in the SSH config (e.g. wp --ssh=rc for me).

Note you do need a copy of WP-CLI on the remote server, accessible as wp. Futhermore, --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.

RESTful WP-CLI v0.2.0 and beyond

Today marks the release of RESTful WP-CLI v0.2.0. Among 43 closed issues and pull requests, I’d like to highlight two new features.

First, use wp rest (post|user|comment|*) generate to create an arbitrary number of any resource:

$ wp @wpdev rest post generate --count=50 --title="Test Post"
Generating items  100% [==============================================] 0:01 / 0:02

When working on a site locally, you often need dummy content to work with. There are a myriad of ways custom post types can store data in the database though, so generating dummy content can be a painstaking process. Because the WP REST API represents a layer of abstraction between the client (e.g. WP-CLI in this case) and the database, it’s much easier to produce a general purpose content generation command.

In the future, I’d love to see dummy data generated for each field based on the resource schema.

Second, use wp rest (post|user|comment|*) diff to compare resources between two enviroments:

# "command" isn't a typo in this example; "command" is a content type expressed through the WP REST API on runcommand.io
$ wp @dev rest command diff @prod find-unused-themes --fields=title
(-) http://runcommand.dev/api/ (+) https://runcommand.io/api/
  + title: find-unused-themes

When working with multiple WordPress environments, you may want to know how these environments differ. Because the WP REST API represents a higher-level abstraction on top of WordPress, computing the difference between two environments becomes a matter of fetching the data and producing a comparison.

There are a number of ways the diff command could be improved, so consider this implementation to be the prototype.

What’s next?

More immediately, I’d like to start looking at how well RESTful WP-CLI works with plugins and themes. If you’ve written custom endpoints for the WP REST API, please weigh in on this Github issue so I can check it out.

Ultimately, the goal is for wp rest post to replace wp post, but there are many months between here and there. In this future where WP-CLI packages are first-class citizens amongst the commands in WP-CLI core, RESTful WP-CLI gets to serve as a testbed for figuring out how that actually works. We shall see, we shall see.

As always, thanks for your support!

Summary of the bootstrap / load updates coming in WordPress 4.6

Summary of the bootstrap / load updates coming in WordPress 4.6. Notable, after WordPress 4.6, WP-CLI will be fully compatible with any future changes to wp-settings.php. Previously, changes to wp-settings.php would break WP-CLI. Many thanks to Aaron Jorbin for helping work through the core changes.

How to use WP-CLI without SSH access to the server

This micro-tutorial is now continuously revised on runcommand.

Need to use WP-CLI on a WordPress site, but don’t have SSH access to its server? Simply change your local WordPress install’s database credentials to use the remote site’s configuration, such that the local WordPress install is using the remote database. You’re now free to run commands to your heart’s content.

Keep in mind:

  • The remote WordPress site’s database will need to accept public connections, or you’ll need to be able to whitelist your IP address.
  • Your local WordPress install should use the exact same codebase as the remote install.
  • Any filesystem operations will be happening on your local machine, not the remote server. If you install and activate a plugin, the remote WordPress site will then try to run a plugin that doesn’t exist on its server.
  • Bad things can happen. Be very careful.

But, I used this trick for data transformation on a Flywheel-hosted site earlier this week, and it worked like a charm.