Steve Grunwell@stevegrunwell
We're glad you're here!
If a script is only meant to be run once, making it available via wp-admin would be insane.
# Install Son of Clippy $ wp plugin install son-of-clippy --activate # Upgrade Yoast SEO $ wp plugin update wordpress-seo # Remove TwentyThirteen $ wp theme delete twentythirteen
# Add a new editor named Hypnotoad. $ wp user create hypnotoad hypnotoad@example.com --role=editor # ALL GLORY TO THE HYPNOTOAD! $ wp user set-role hypnotoad admin
# Export WXR files for all post types in 5MB chunks $ wp export --max_file_size=5 # Export only posts from 2016 $ wp export --start_date=2016-01-01 --post_type=post # Import WXR files $ wp import my-wxr-file.xml
# Dump the database $ wp db export my-backup.sql # Import that database file $ wp db import my-backup.sql # Optimize the database $ wp db optimize # Repair the database $ wp db repair
# Update production URLs for staging $ wp search-replace example.dev example.com # Replace all instances of "foo" with "bar" *only* in wp_options $ wp search-replace foo bar wp_options
Intelligently handles PHP serialized data!
WP-CLI will inherently be used by more technical users, but that doesn't mean we shouldn't make them friendly
The output of one command should be "pipe-able"
$ wp post list | grep "hello" > 1 Hello world! hello-world 2014-11-11 20:46:47 publish
Make it obvious what the user is doing!
# ¯\_(ツ)_/¯ $ wp my-command --launch # Fffffuuuuuuuuuuuuu...... $ wp my-command --launch-nukes
class My_Awesome_Command extends WP_CLI_Command {
// Magic will happen in here!
}
// Tell WP-CLI that My_Awesome_Command is 'awesome'.
WP_CLI::add_command( 'awesome', 'My_Awesome_Command' );
$ wp awesome
class My_Awesome_Command extends WP_CLI_Command {
/**
* Convert terms from taxonomy A to taxonomy B.
*
* ...
*/
public function convert_terms( $args, $assoc_args ) {
// All sorts of fancy logic.
}
}
$ wp awesome convert_terms
A one-line description of what the command does.
/** * Convert terms from taxonomy A to taxonomy B. *
Arguments accepted by the command.
/** * ... * * ## OPTIONS * * <origin> * : The original taxonomy. * * <destination> * : The destination taxonomy. *
How people might use your command.
/** * ... * * ## EXAMPLES * * wp awesome convert-terms category post_tag *
/** * ... * * @synopsis <origin> <destination> * @subcommand convert-terms * @alias do-conversion */
/** * Demonstrate how arguments work. * * ## OPTIONS * * <required> * : This is a required, positional argument. * * [<optional>] * : This positional argument is optional. * * ...
/**
* ...
*
* --option=<required>
* : This is a required, associative argument.
*
* [--option2=<optional>]
* : This associative argument is optional.
*
* [--flag]
* : An optional flag.
*/
public function my_command( $args, $assoc_args ) {
// ...
public function my_command( $args, $assoc_args ) {
if ( isset( $assoc_args['option2'] ) ) {
// The user passed some value to --option2.
}
}
# Confirm an action. WP_CLI::confirm( 'Are you sure you want to do this?' );
> Are you sure you want to do this? [y/n]
Request additional information from the user.
$email = cli\prompt( 'Email address', 'user@example.com' );
Email address [user@example.com]:
Present the user with a list of options.
$options = array(
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz',
);
$selected = cli\menu( $options, 'foo', 'What to do?' );
1. Foo 2. Bar 3. Baz What to do? [Foo]:
# Simply output text. WP_CLI::log( 'A normal message' ); # Prepend the text with a colored "Warning:". WP_CLI::warn( '¯\_(ツ)_/¯' ); # Prepend the message with "Error:" and return. WP_CLI::error( '(╯°□°)╯︵ ┻━┻' ); # Prepend the text with a colored "Success:"! WP_CLI::success( 'GREAT SUCCESS!' );
$table = new cli\Table;
$table->setHeaders( array(
'id' => 'ID',
'title' => 'Title'
) );
foreach ( $posts as $post ) {
$table->addRow( array(
'id' => $post->ID,
'title' => $post->post_title,
) );
}
$table->display();
# $ wp my-plugin get-posts +----+----------------------+ | ID | Title | +----+----------------------+ | 1 | Hello World! | +----+----------------------+ | 2 | Goodbye Cruel World! | +----+----------------------+
# $ wp my-plugin get-posts > posts.csv ID Title 1 Hello World! 2 Goodbye Cruel World!
Shortcut for table construction, also works for YAML, JSON, and more:
$headers = array(
'id' => 'ID',
'title' => 'Title'
);
$rows = array();
foreach ( $posts as $post ) {
$rows[] = array(
'id' => $post->ID,
'title' => $post->post_title,
);
}
WP_CLI\Utils\format_items( 'table', $rows, $headers );
Show the user that progress is being made.
$progress = WP_CLI\Utils\make_progress_bar( 'Making progress', 10 );
for ( $i = 0; $i < 10; $i++ ) {
sleep( 1 ); // Do something real here, please.
$progress->tick();
}
Making progress 100%[=================================] 0:10 / 0:10
Don't forget the PHP error log!
error_log( 'Something went wrong!' );
When performing destructive options, consider a --dry-run flag
$dry_run = isset( $assoc_args['dry-run'] );
if ( ! $dry_run ) {
do_something_destructive();
}
WP_CLI::log( 'Something destructive happened' );
Great scripts will include options for --verbose, --quiet, or both.
// Define the value once.
$verbose = isset( $assoc_args['verbose'] );
// Simple conditional around output.
if ( $verbose ) {
WP_CLI::log( 'Thank you for reading me!' );
}
Always remember your audience and build your command around their needs.
WP-CLI commands make a great addition to most plugins!
// Only load our CLI command when loaded via WP_CLI.
if ( defined( 'WP_CLI' ) && WP_CLI ) {
require_once dirname( __FILE__ ) . '/my-cli-class.php';
}
The actions and filters you use on your site are still active, so use them!
// Trigger some action in my theme do_action( 'my_theme_some_action' ); // Remove filters you won't be needing. remove_filter( 'the_content', 'wpautop' );
Don't forget useful WordPress functions like wp_parse_args() for setting defaults!
$assoc_args = wp_parse_args( $assoc_args, array(
'foo' => true,
'bar' => 'baz',
) );
if ( ! defined( 'WP_IMPORTING' ) ) {
define( 'WP_IMPORTING', true );
}
Useful constant for debugging, major bottleneck when running large scripts!
Activated by default in VIP Quickstart!
When a variable, instance, etc. is no longer being used, PHP will try to clean up the memory in a process known as garbage collection
unset( $instance_var ); $my_global_var = null;
Normally handled at end of request, but CLI commands can run far longer!
protected function stop_the_insanity() {
global $wpdb, $wp_object_cache;
$wpdb->queries = array();
if ( is_object( $wp_object_cache ) ) {
$wp_object_cache->group_ops = array();
$wp_object_cache->stats = array();
$wp_object_cache->memcache_debug = array();
$wp_object_cache->cache = array();
if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
$wp_object_cache->__remoteset(); // important
}
}
}
When working with WordPress Multisite, know what site you're working on!
# Runs on the default site. $ wp cache empty # Runs on a specific site. $ wp cache empty --url=http://subsite.example.com
Remember: calling die() or exit will kill the entire script, so use with care!
If in doubt, return early!
# Launch an external process. WP_CLI::launch( WP_CLI\Utils\esc_cmd( $command ) ); # Call another WP-CLI command. WP_CLI::launch_self( $command, $args, $assoc_args );
Steve Grunwellstevegrunwell.comgrowella.com