Skip to content

More on WP-CLI (updated)

We’re in the process of moving the second of our big multisite installations. But along with that come some changes that have to be made, and on a big scale.

Change an option setting for a plugin

For example, we’ve been using the WP Mail SMTP plugin for a long time, to connect with our on-campus mail server. Individual machines aren’t provisioned to send mail directly, instead they have to route it through our central hub. So the plugin is configured to point to that server. But now that we’re moving to a cloud-based server, the nice folks in IT are content to let individual machines use localhost instead.

But what to do with the thousand or so individual blogs that are configured to use the old server? Enter WP-CLI to the rescue again. Here are the steps, and I’ll follow with a complete chunk of code. What we need to do is to change the option smtp_host wherever it’s set to the old value. We’ll look at each blog first to see if the plugin is in fact activated (not every blog needs to send mail), what the setting for smtp_host is, and if it’s the old host change it to the new one.

The documentation page gives you this:

$ wp site list --field=url | xargs -n1 -I {} sh -c 'wp --url={} option update my_option my_value'

Which is a little too brute-force for my taste, besides by cryptic.

I’ll keep a set of global variables at the top so that it’s easier to repurpose this script for other plugins and options:

$plugin = 'wp-mail-smtp';
$option = 'smtp_host';
$old_value = '';
$desired_value = 'localhost';

I keep all my CLI scripts in a separate directory, away from the server root. So first I have to change the working directory. This is all in PHP:


Then generate a list of all the sites’ URLs:

exec('wp site list --field=url --archived=0 --skip-themes --skip-plugins', $output);*

*I always skip themes and plugins. Some of them are badly coded and throw annoying warnings that clog up the results screens and make it hard to see what I want to know.

This stores the results of the wp command to an array, $output. Then we’ll iterate through the array of sites and do a few things.

For each site we want clean arrays to hold the results of the three commands we’re going to make, for the plugin status ($active), the current setting of an option ($message), and the results of the change ($updated). The first command checks to see if our plugin is active.

exec("wp plugin status $plugin --skip-themes --skip-plugins --url=$url", $active);

If it’s active, the script stores the result in an unfortunately messy array, something like this:

Plugin wp-mail-smtp details:
  Name: WP Mail SMTP
  Status: Active
  Version: 1.4.2
  Author: WPForms
  Description: Reconfigures the <code>wp_mail()</code> function to use Gmail/Mailgun/SendGrid/SMTP instead of the default <code>mail()</code> and creates an options page to manage the settings.

If the plugin isn’t active, you’ll only see the URL:

My result is actually an array, and the piece we want is stored in $active[2], which is the third line. It has a bunch of leading spaces, so we can’t do a straight “==” comparison. I’m using this instead:

if ( strpos( $active[2], 'Status: Active' ) ) { /* do something */ }

The “do something” is actually two things. First, check the current setting:

exec("wp option get $option --skip-themes --skip-plugins --url=$url", $message);

Then check to see if it’s the value you want to change from. I’m specifying this because you might use different mail servers for different blogs, and you don’t want to necessarily change all of them.

if ( $message[0] == $old_value ) { /* change the setting */ }

For some, the smtp_host was never configured. That could be for a lot of reasons, which I’m not really going to worry about.

Error: Could not get 'smtp_host' option. Does it exist?

Then we’ll switch the option setting to the new one:

exec("wp option update $option $desired_value --skip-themes --skip-plugins --url=$url", $updated);

$updated[0] contains the return value of the exec, which should be:

[0] => Success: Updated 'smtp_host' option.

Breaking it down then:

  1. Get the list of sites
  2. Iterate over them
  3. Is the plugin activated?
    1. Check that it has the option setting you want to change from
    2. Change it to the new one

Here’s the complete code set:

$plugin = 'wp-mail-smtp';
$option = 'smtp_host';
$old_value = '';
$desired_value = 'localhost';
exec('wp site list --field=url --archived=0 --skip-themes \
--skip-plugins', $output);
foreach($output as $url) {
$active = '';
$message = '';
$updated = '';
echo "\n$url\n";
exec("wp plugin status $plugin --skip-themes --skip-plugins --url=$url", $active);
if ( strpos( $active[2], 'Status: Active' ) ) {
echo "$plugin is active for $url\n";
exec("wp option get $option --skip-themes --skip-plugins --url=$url", $message);
if ( $message[0] == $old_value ) {
echo "Set to $old_value. Resetting...\n";
exec("wp option update $option $desired_value --skip-themes --skip-plugins --url=$url", $updated);

It will show you the URL and current settings, and the change result.
wp-mail-smtp is active for
Set to Resetting...
[0] => Success: Updated 'smtp_host' option.

But see the update below…

Selectively activating or de-activating a plugin

After the above, this code is a little more self-explanatory. Some themes have required, or at least highly-recommended, plugins. Once upon a time theme A was activated which required plugin B. Since then the site owner has migrated over to theme C and doesn’t need plugin B anymore, but didn’t bother to de-activate it.

Chaos ensues.

The steps are:

  1. Generate a list of sites
  2. Check to see if the theme is Twenty Eleven (aka theme A)
    • No, we’re not going to force a change!
  3. If it is
    1. Activate the Twenty Eleven Theme Extensions plugin (plugin B)
  4. If it is not
    1. De-activate the Twenty Eleven Theme Extensions plugin

In this one, I save all the results into a string and print that out at the end. Here’s the code:

exec('wp site list');exec('wp site list --field=url --archived=0 --ignore-themes --ignore-plugins', $output);
foreach($output as $url) {
$messages .= $url . "\n";
$active_theme = exec("wp theme list --status=active --url=$url --fields=name --format=csv");
if ( 'twentyeleven' == $active_theme ) {
$messages .= "TwentyEleven is active for $url\n";
$messages .= exec("wp plugin activate twenty-eleven-theme-extensions --url=$url") . "\n\n";
else {
$messages .= "TwentyEleven is NOT active for $url\n";
$messages .= exec("wp plugin deactivate twenty-eleven-theme-extensions --url=$url") . "\n\n";
echo $messages;

Enjoy, and as always post a comment if you have a question. I’m usually responsive.


In testing out the results of the options update script, in most of the blogs the setting on the dashboard page was unchanged. No error was thrown, as you’d expect if you tried to change a non-existent option. But it stubbornly persisted. Running wp option get from the command line showed the proper new value, localhost; but the dashboard showed the old value.

So there’s some kind of disconnect between wp option get and wp option set. It might be in wp-cli, it might be in the plugin code. I’m not going to worry about whether that’s a bug or not right now.

I dug into the (poorly documented) wp option pluck and wp option update commands for information. It turns out that the wp-mail-smtp options are nested. wp option get is pulling a value from somewhere, but wp option set is working against something else. There’s a much better explanation and set of examples here, and the command structure is really pretty simple.

The way to make the change that works is by using the wp option patch command. By passing this:

wp option patch update wp_mail_smtp smtp host localhost --url= --skip-themes --skip-plugins

I get the correct result on the dashboard.

One Comment

  1. YK YK

    Exactly what I was looking for. Going to give this a try. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

Share This