Daniele Scasciafratte

WP-CLI

What you can do with this tool?

Daniele Scasciafratte / @Mte90Net

Daniele Scasciafratte

  • Co Founder/CTO Codeat
  • Open Source Addicted
  • Mozillian & Mozilla Reps & Mozilla TechSpeaker
  • WordPress Core Contributor/Developer/Translator
  • Podcast 🇮🇹 Daniele.tech - Opinioni in open source
  • VVV Co-Mantainer
  • Project Translation Editor in WordPress Italia

Few info

WordPress 3.7 or later

No root privileges required

No login required

SSH support (make.wordpress.org/cli/handbook/running-commands-remotely/)

Extendable by plugin or packages

FAST

How to install it


curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
###### OR #######
composer global require wp-cli/wp-cli
###### OR #######
Deb/RPM builds: https://github.com/wp-cli/builds
					

Update it


wp cli update
					

By make.wordpress.org/cli/handbook/installing/ with Bash/ZSH completions docs

Install WordPress


wp core download --locale=it_IT
wp core config --dbuser="wp" --dbpass="wp" --dbprefix="wp_"
wp core install --siteurl="https://yourdomain.tld"
wp @demosite cli update # Will use settings from wp-cli for the alias
##### OR #####
wp core install --prompt
					

By developer.wordpress.org/cli/commands/core/install/

Why


$ wp post list --post_type='page' --format=ids
1164 1186
					

$ wp post delete $(wp post list --post_type='page' --format=ids)
Success: Trashed post 1164.
Success: Trashed post 1186.
					

Shell Alias


$ alias wp-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        |
+-------------------------+----------+-----------+--------------+
+----------------------+----------+-----------+---------+
| name                 | status   | update    | version |
+----------------------+----------+-----------+---------+
| p2                   | inactive | available | 1.4.2   |
+----------------------+----------+-----------+---------+
					

By make.wordpress.org/cli/handbook/shell-friends/

Real examples!

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'
					

By make.wordpress.org/cli/handbook/shell-friends/

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
					

By make.wordpress.org/cli/handbook/shell-friends/

Remove the featured image from the article of a category

for id in $(wp post list --category_name='news' --format=ids); do wp post meta update $id '_thumbnail_id' ''; done

Delete all the post of a specific category

wp post delete $(wp post list --cat=1 --post_status=published --format=ids)

Change a constant on wp-config.php


wp config set WP_DEBUG true --type=constant --raw
wp config set WP_DISABLE_FATAL_ERROR_HANDLER true --type=constant --raw

Create posts with dummy content

wp post generate --count=10

Load PHP file before running the command

--require=fixer.php

Import image by url

wp media import url --post_id=123 --title="It's me an image"

Migrate Taxonomy term between taxonomies

wp term migrate apple --by=slug --from=category --to=post_tag

Generate WXR file from a list of IDs

wp export --post__in=$(cat id_export.txt | tr '\n' ',') --with_attachments

Search & Replace


wp search-replace http://domain.com https://domain.com
wp search-replace http://domain.com https://domain.com --network
##### OR #####
wp search-replace http://olddomain.com https://newdomain.com 
 \ --export=newdb.sql --all-tables
##### Remove JS code inside post_content
wp search-replace '<script(.*?)>(.*?)<\/script>' '' --regex 
 \ --include-columns=post_content --skip-columns=gui 
 \ --skip-themes --skip-plugins wp_posts --regex-flags='ms'
					

Upgrade all the websites in a folder


#!/bin/bash
declare -a wp_sites=('/usr/local/web/site1/htdocs' '/usr/local/web/site2/htdocs')

for site in "${wp_sites[@]}"; do
    echo Upgrading $site...
    wp --path=$site db export $site/db-backup.sql
    wp --path=$site core update
done
					

Disable commands for a site with wp-cli.yml


disabled_commands:
    - db drop
    - search-replace
					

By make.wordpress.org/cli/handbook/config/

Database operation


wp db query "show tables;"
wp db export
wp db reset --yes # Yes reset the DB!
wp db clean --yes # Only the WP tables will be dropped
wp db import ~/db-export.sql
wp db search 'http:\/\/' --regex --stats
wp db size --tables --human-readable
					

Plugin management


wp plugin install glossary-by-codeat --activate
##### OR #####
wp plugin activate glossary-by-codeat
wp plugin deactivate glossary-by-codeat
wp plugin update glossary-by-codeat
wp plugin list
					

Options


wp option get option-name
wp option add option-name
wp option delete option-name
wp option pluck plugin_with_serialized_values parameter sub-parameter
wp option patch update plugin_with_serialized_values parameter sub-parameter
wp option get plugin_with_serialized_values --format=yaml
					

Various tasks


wp transient delete --expired
wp cache flush # Redis Memcached or PHP-APCu
wp media regenerate 
wp transient list
wp config shuffle-salts
wp site meta get 123 bio # Website id and field
wp user check-password admin adminpass # Check if the password of user is ok
EDITOR=vim wp config edit
					

Gets the post ID for a given URL


wp post url-to-id https://example.com/?p=1
					

List of WooCommerce's orders


wp wc shop_order list --customer=1 --user=1 --fields=id,status
					

By robotninja.com/blog/wp-cli-woocommerce-development/

Profile package


wp package install wp-cli/profile-command
wp profile stage --allow-root --all
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| hook                     | callback_count | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+      |
| plugins_loaded:before    |                | 0.2855s | 0.0041s    | 19          | 83.95%      | 136        | 26           | 0s           | 0             |
| plugins_loaded           | 34             | 0.2442s | 0.0009s    | 3           | 98.31%      | 116        | 2            | 0s           | 0             |
| setup_theme:before       |                | 0.0005s | 0s         | 0           | 100%        | 4          | 0            | 0s           | 0             |
| setup_theme              | 1              | 0s      | 0s         | 0           |             | 0          | 0            | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (31)               | 242            | 1.4571s | 0.0284s    | 78          | 90.8%       | 3323       | 84           | 0s           | 0             |
+--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
wp profile hook --all --spotlight --url=https://yourdomain.com --allow-root
+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| callback                                     | location                                            | time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| wpseo_init()                                 | wordpress-seo/wp-seo-main.php:252                   | 0.0136s | 0s         | 0           | 100%        | 34         | 0            | 0s           | 0             |
| wp_enqueue_scripts()                         | wp-includes/script-loader.php:1294                  | 0.015s  | 0s         | 0           | 100%        | 348        | 0            | 0s           | 0             |
| wp_trim_excerpt()                            | wp-includes/formatting.php:3289                     | 0.0186s | 0s         | 0           | 100%        | 16         | 0            | 0s           | 0             |
+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| total (7)                                    |                                                     | 0.0927s | 0.0006s    | 6           | 100%        | 1054       | 0            | 0s           | 0             |
+----------------------------------------------+-----------------------------------------------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
					

Profile every plugin


#!/bin/bash
echo "Loop of curl request, with plugin disabled for the page $1"

for p in $(wp plugin list --fields=name --status=active)
do
echo "Plugin: $p"
    wp plugin deactivate $p
    for i in {1..5}
    do
        curl -so /dev/null -H "Pragma: no-cache" -w "%{time_total}\n" $1 
           | sed 's/\,/./'
    done | awk '{ sum +=$1; n++; print $1 } END { if (n > 0) print "AVG: " sum / n; }'
    wp plugin activate $p
done
					

Remove tags with 0 posts


wp term list post_tag --format=json > tags.json

#!/usr/bin/python
import json
import os

with open('tags.json') as f:
    tags = json.load(f)
    for taxonomy in tags:
        if taxonomy['count'] == 0:
            os.system("wp term delete post_tag " + str(taxonomy['term_id']))
					

List all the URLs with content empty


#!/bin/sh

site=$(wp option get home)
ids=$(wp db query "SELECT ID FROM $(wp db prefix)posts WHERE post_content='' AND post_status!='auto-draft' AND post_status!='draft' AND post_status!='trash' AND (post_type='post' OR post_type='page')" | tr '\r\n' ' ')

for id in $ids
do
    slug=$(wp post get "$id" --field=post_name)
    echo $site/$slug
done
					

PHP binaries that run in the WordPress context


#!/bin/env wp eval-file

					

Create a command


# Register a custom 'foo' command to output a supplied positional param.
# $ wp foo bar --append=qux
/**
 * My awesome closure command
 *
 * Message
 * : An awesome message to display
 *
 * --append="your message"
 * : An awesome message to append to the original message.
 *
 * @when before_wp_load
 */
if ( defined( 'WP_CLI' ) && WP_CLI ) {
	$foo = function( $args, $assoc_args ) {
		WP_CLI::success( $args[0] . ' ' . $assoc_args['append'] );
	};
	WP_CLI::add_command( 'foo', $foo );
}
					

Remove Comments by year


#!/usr/bin/python

# Generate the list of comments
# wp comment list --fields=ID,comment_date --format=json > comments.json

# This script require a parameter that is the year

import json, os, datetime, sys

i = 0
ids = ''
with open('comments.json') as f:
    comments = json.load(f)
    print('Found ' + str(len(comments)) + ' comments')
    print('Processing...')
    for comment in comments:
        date = datetime.datetime.strptime(comment['comment_date'], '%Y-%m-%d %H:%M:%S')
        if date.year == int(sys.argv[1]):
            i += 1
            ids += comment['comment_ID'] + ' '

os.system("wp comment delete " + ids + ' --force')
print('Removed ' + str(i) + ' comments')
					

By https://github.com/CodeAtCode/WPCli-scripts/

Remove revisions older then 5 days


wp revisions clean --before-date=$(date --date="5 days ago" +%Y-%m-%d) --dry-run
					

By https://github.com/trepmal/wp-revisions-cli/

Cool packages to discover for you

Contribute to WP CLI with VVV

utilities:
    core:
        - wpcli-dev

WPDB-Status

WPDB-Status repository

Prometheus Metrics

- WordPress Plugin
- Grafana Dashboard

THE END

- wp-cli.org
- VVV
- guides.wp-bullet.com/category/wp-cli/