WP-CLI From A to Z
Ryan Kanner (@codeprokid)
What is wp-cli?
What is WP-CLI?
- It's WordPress... on the command line
- Open source project backed by WordPress.org, and collaborated on in Github.
- Allows you to talk to WordPress without going through the browser.
- Has an easy to use API for extending it with your own commands.
- Available from wp-cli.org
Why should I use it?
- ...Because it's awesome.
- Allows you to automate monotonous tasks.
- Gives you an easy way to do bulk operations like deleting posts, or migrating data.
- Provides an easy way to touch some hard-to-control WordPress internals (transients, rewrite rules)
- It's consistent and repeatable.
When should I use it?
- Update/Install: Core/Themes/Plugins
- Search & Replace your DB
- Automating deployments or maintenance
- Troubleshoot issues with some WordPress internals (cron, cache, transients)
- Long running batch processes
- Data migrations
- Scaffolding themes & plugins
Getting going
Download the .phar file
$ curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
Check if it's working
$ php wp-cli.phar --info
Make it executable, and put it in your PATH
$ chmod +x wp-cli.phar
$ sudo mv wp-cli.phar /usr/local/bin/wp
That's it!
$ wp --info
PHP binary: /usr/bin/php5
PHP version: 5.5.9-1ubuntu4.14
php.ini used: /etc/php5/cli/php.ini
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.1.0
Basic usage
install & Update all the things
# Download a fresh copy of WordPress
wp core download
# Update Core
wp core update
# Update a plugin
wp plugin update jetpack
# Install a theme
wp theme install twentyseventeen --activate
CRUD Data
# Create a term
wp term create category Rabbits --parent=animals
# Get information on a user
wp user get 10
# Update an option
wp option update my_favorite_pet Rabbits
# Delete post ID 123
wp post delete 123 --force=true
Managing UI-less Features
# Regenerate all thumbnails
wp media regenerate
# Delete expired transients
wp transient delete --expired
# Delete all transients
wp transient delete --all
# List of scheduled cron events
wp cron event list
# Run all cron events in queue
wp cron event run --due-now
# Flush rewrite rules
wp rewrite flush
# List of rewrites
wp rewrite list --format=csv
# Flush the cache
wp cache flush
Scaffold Plugins!
Global Parameters
--path=<path>
//Path to the WordPress files.
--ssh=[<user>@]<host>[:<port>][<path>]
//Perform operation against a remote server over SSH.
--http=<http>
//Perform operation against a remote WordPress install over HTTP.
--url=<url>
//Pretend request came from given URL. In multisite, this
//argument is how the target site is specified.
--skip-plugins[=<plugin>]
//Skip loading all or some plugins. Note: mu-plugins are still loaded.
--skip-themes[=<theme>]
//Skip loading all or some themes.
--skip-packages
//Skip loading all installed packages.
--require=<path>
//Load PHP file before running the command (may be used more than once).
--[no-]color
//Whether to colorize the output.
--quiet
//Suppress informational messages.
And More!
Advanced Usage
Combining commands
// Delete all pages
$ wp post delete $(wp post list --post_type='page' --format=ids)
// Delete all posts in the trash
$ wp post delete $(wp post list --post_status=trash --format=ids)
// Update an option for all sites in a network
$ wp site list --field=url | xargs -n1 -I {} sh -c 'wp --url={} option update my_option my_value'
// Import tags from a CSV
#Usage ./import_tags <file-name>
while IFS=, read line
do
eval wp term create post_tag "\""$line"\""
done < $1
Generate Dummy Data for testing
# Generate 100 dummy posts
wp post generate --format=ids --count=100 | xargs -n1 -I % wp term meta add % generated_data true
# Generate 10 dummy categories
wp term generate category --format=ids --count=10 | xargs -n1 -I % wp term meta add % generated_data true
# Generate 100 dummy users
wp user generate --format=ids --count=100 | xargs -n1 -I % wp term meta add % generated_data true
# Clean It Up
wp term list category --field=term_id --meta_key=generated_data | xargs wp term delete category
wp user list --field=user_id --meta_key=generated_data | xargs wp user delete
wp post list --format=ids --meta_key=generated_data | xargs wp post delete
Bash shortcuts
# Add this to your ~/.bashrc
syn_prod_db() {
wp search-replace $1 $2 --export=db.sql
wp @production db export backup.sql
wp @production db import db.sql
wp @production db optimize
}
# Usage
sync_prod_db mysite.dev mysite.com
Using wp-cli .yml files
- Set configuration defaults for all of your projects, or on a project by project basis
- Create alias's for all of your environments
- Uses hierarchical inheritance
- ~/.wp-cli/config.yml Global System settings
- wp-cli.yml Working directory, or upwards
- wp-cli.local.yml Project specific
Parameter & command defaults
# 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 core config`)
core config:
dbuser: root
dbpass:
extra-php: |
define( 'WP_DEBUG', true );
define( 'WP_POST_REVISIONS', 50 );
Aliases
// Project level
@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
// Global locally
@site1
path: /projects/project_1_root
@site2
path: /projects/project_2_root
@site3
path: /projects/project_3_root
@site4
path: /projects/project_4_root
Export & Import data
# Search & Replace Local DB & export to .sql file
wp search-replace mysite.dev mysite.com --export=db.sql
# Take a backup of the old DB
wp @production db export backup.sql
# Import the new DB
wp @production db import db.sql
# Optimize the DB after import
wp @production db optimize
Extending
The package index
- Like the plugin repository, but for WP-CLI commands
- Official repository is here -> http://wp-cli.org/package-index/
- You can install a package with wp package install <PACKAGE_NAME>
- You can also create your own packages. Get started by installing the scaffold package... package.
Anatomy of a command
// Class to contain our command
Class My_Command extends WP_CLI {
// Any public method within the class becomes a command
public function say_hello( $args, $assoc_args ) {
$name = ( isset( $args[0] ) ) : $args[0] : 'Anonymous';
WP_CLI::success( 'Hello ' . $name );
}
}
// Registers all commands under the `sample` sub-command
WP_CLI::add_command( 'sample', 'My_Command' );
# How to run this command:
wp sample say_hello Ryan
Success: Hello Ryan
Keeping commands reusable & accepting input
- In order to make commands more reusable, they should accept input arguments.
- 2 ways to accept them, normal args & flags.
- Allows scripts to work for multiple environments (ex. ID's being different), and allows for consistent repeatability
Fun with flags
// Class to contain our command
Class My_Command extends WP_CLI {
// Any public method within the class becomes a command
public function say_hello( $args, $assoc_args ) {
$name = ( isset( $args[0] ) ) : $args[0] : 'Anonymous';
$excited = WP_CLI\Utils\get_flag_value( $assoc_args, 'excited', false );
$message = 'Hello ' . $name;
$message .= ( 'true' === $excited ) ? '!' : '';
WP_CLI::success( $message );
}
}
// Registers all commands under the `sample` sub-command
WP_CLI::add_command( 'sample', 'My_Command' );
# Running the following:
wp sample say_hello Ryan --excited=true
# Will return
Success: Hello Ryan!
Controlling commands with doc blocks
/**
* Command for saying hello to people
*
* # OPTIONS
*
* <name>
* : Who we should say hello to
*
* [--excited]
* : Whether or not to append an exclamation mark to the message
* ---
* default: false
* options:
* - false
* - true
* ---
*
* @subcommand say-hello
* @when before_wp_load
*/
public function say_hello( $args, $assoc_args ){ /* code */ };
Run WordPress code
class Term_Mover extends WP_CLI {
public function move_terms( $args, $assoc_args ) {
$delete_old_term = WP_CLI\Utils\get_flag_value( $assoc_args, 'delete-old-term', false );
// Get all the terms from the old taxonomy
$old_terms = get_terms( array( 'taxonomy' => $args[0] ) );
// Make sure there are terms to move
if ( empty( $old_terms ) || is_wp_error( $old_terms ) ) {
WP_CLI::error( 'Could not find any terms to move' );
}
foreach ( $old_terms as $term ) {
$term_id = wp_insert_term( $term->name, $args[1] );
if ( false !== $term_id && ! is_wp_error( $term_id ) ) {
WP_CLI::success( 'Successfully inserted term id: ' . $term_id );
if ( 'true' === $delete_old_term ) {
wp_delete_term( $term->term_id, $args[0] );
}
} else {
WP_CLI::error( 'Could not insert new term' );
}
}
}
}
WP_CLI::add_command( 'sample', 'Term_Mover' );
Include commands in a theme or plugin
// Only load our CLI command when loaded via WP_CLI.
if ( defined( 'WP_CLI' ) && WP_CLI ) {
require_once dirname( __FILE__ ) . '/my-cli-class.php';
}
Tips & Tricks
writing your own
- All args passed to the command actually get passed as strings, so if you are expecting an integer, typeset it to be one.
- Including a `dry-run` flag for commands manipulating data, so you don't accidentally nuke your DB.
- Even though you are on the command line, you should still consider timeouts as a possibility, try to do things in batches for large datasets.
Adding a command to an existing subcommand
You can actually add a new command to an existing sub-command. See below:
// Class to contain our command
Class My_Command extends WP_CLI {
// Any public method within the class becomes a command
public function say_hello( $args, $assoc_args ) {
$name = ( isset( $args[0] ) ) : $args[0] : 'Anonymous';
WP_CLI::success( 'Hello ' . $name );
}
}
// Registers all commands under the `plugin` sub-command
WP_CLI::add_command( 'plugin', 'My_Command' );
# Usage
wp plugin say_hello Ryan
Use WP_parse_Args for setting defaults
$assoc_args = wp_parse_args( $assoc_args, array(
'foo' => true,
'bar' => array(),
);
Progress bars are sweet
$count = 100;
$progress = \WP_CLI\Utils\make_progress_bar( 'Generating users', $count );
for ( $i = 0; $i < $count; $i++ ) {
// Your code
$progress->tick();
}
$progress->finish();
Progress bars are sweet
$count = 100;
$progress = \WP_CLI\Utils\make_progress_bar( 'Generating users', $count );
for ( $i = 0; $i < $count; $i++ ) {
// Your code
$progress->tick();
}
$progress->finish();
Use other WP-CLI commands within your command
// Run an existing CLI command within another command
$plugins = WP_CLI::runcommand( 'plugin list --format=json', array(
'return' => true, // Return `STDOUT` from command
'parse' => 'json', // Convert the `STDOUT` to a json obj
'launch' => false, // re-use same process so we can capture result
'exit_error' => true, // Halt the entire script on error
) );
Save the output of a command
You can easily save the output of any command that gives you a STDOUT with the > operator
# Save all of the terms in the post_tag taxonomy to a CSV
wp term list post_tag --field=name --format=csv > mytags.csv
DEMO!
WP-CLI From A to Z
By Ryan Kanner
WP-CLI From A to Z
In this presentation, I will take you from installing wp-cli to writing your own commands.
- 3,056