Write a check for verifying contents of WordPress files

One of the check types included in wp doctor is File_Contents, or the ability to check all or a selection of WordPress files for a given regex pattern. The check type is in use by a couple of the default diagnostic checks, and you can use the File_Contents check type in your custom doctor.yml configuration file.

The File_Contents check type is the most efficient way to check WordPress files because it only iterates the filesystem once, regardless of how many file checks you’ve registered.

As an example, here are two checks using File_Contents, one which ensures sessions aren’t used by plugins and the other which ensures $_SERVER['SERVER_NAME'] isn’t used in wp-config.php:

file-sessions:
  check: File_Contents
  options:
    regex: .*(session_start|\$_SESSION).*
    only_wp_content: true
file-server-name-wp-config:
  check: File_Contents
  options:
    regex: define\(.+WP_(HOME|SITEURL).+\$_SERVER.+SERVER_NAME
    path: wp-config.php

Run together, you might see:

$ wp doctor check --config=file-contents.yml --all
+----------------------------+---------+-----------------------------------------------------------------------------------------+
| name                       | status  | message                                                                                 |
+----------------------------+---------+-----------------------------------------------------------------------------------------+
| file-sessions              | success | All 'php' files passed check for '.*(session_start|\$_SESSION).*'.                      |
| file-server-name-wp-config | success | All 'php' files passed check for 'define\(.+WP_(HOME|SITEURL).+\$_SERVER.+SERVER_NAME'. |
+----------------------------+---------+-----------------------------------------------------------------------------------------+

The File_Contents check type accepts the following options:

  • ‘regex’: Regex pattern to check against each file’s contents.
  • ‘extension’. File extension to check. Defaults to ‘php’. Separate multiple file extensions with a ‘|’.
  • ‘path’. Check a specific file path. Value should be relative to ABSPATH (e.g. ‘wp-content’ or ‘wp-config.php’).
  • ‘only_wp_content’: Only check the wp-content directory.

Write a check for asserting the value of a given option

One of the check types included in wp doctor is Option_Value, or the ability to assert that a given option is a specific value. The check type is in use by a couple of the default diagnostic checks, and you can use the Option_Value check type in your custom doctor.yml configuration file.

As an example, here are two checks using Option_Value, one which ensures the blog is public and the other which ensures users can’t register:

option-blog-public:
  check: Option_Value
  options:
    option: blog_public
    value: 1
option-users-can-register:
  check: Option_Value
  options:
    option: users_can_register
    value: 0

Run together, you might see:

$ wp doctor check --config=option-value.yml --all
+---------------------------+---------+-------------------------------------------------+
| name                      | status  | message                                         |
+---------------------------+---------+-------------------------------------------------+
| option-blog-public        | error   | Site is private but expected to be public.      |
| option-users-can-register | success | Option 'users_can_register' is '0' as expected. |
+---------------------------+---------+-------------------------------------------------+

The Option_Value check type accepts the following options:

  • ‘option’: Name of the option.
  • ‘value’: Option is expected to be a specific value.

Write a check for asserting the value of a given constant

One of the check types included in wp doctor is Constant_Definition, or the ability to assert that a given constant is either defined, a specific value, or falsy. The check type is in use by a couple of the default diagnostic checks, and you can use the Constant_Definition check type in your custom doctor.yml configuration file.

As an example, here are two checks using Constant_Definition, one which ensures SAVEQUERIES isn’t defined and the other which ensures DISALLOW_FILE_MODS is true:

constant-savequeries-falsy:
  check: Constant_Definition
  options:
    constant: SAVEQUERIES
    falsy: true
constant-disallow-file-mods-true:
  check: Constant_Definition
  options:
    constant: DISALLOW_FILE_MODS
    value: true

Run together, you might see:

$ wp doctor check --config=constant-definition.yml --all
+----------------------------------+---------+--------------------------------------------------+
| name                             | status  | message                                          |
+----------------------------------+---------+--------------------------------------------------+
| constant-savequeries-falsy       | success | Constant 'SAVEQUERIES' is undefined.             |
| constant-disallow-file-mods-true | success | Constant 'DISALLOW_FILE_MODS' is defined 'true'. |
+----------------------------------+---------+--------------------------------------------------+

The Constant_Definition check type accepts the following options:

  • ‘constant’: Name of the constant.
  • ‘defined’: Constant is expected to be defined.
  • ‘value’: Constant is expected to be a specific value.
  • ‘falsy’: Constant is expected to be undefined or falsy.

Check status of a given plugin

One of the check types included in wp doctor is Plugin_Status, or the ability to assert that a given plugin should be active, installed, or uninstalled. Although the check type isn’t use by any of the default diagnostic checks, you can use the Plugin_Status check type in your custom doctor.yml configuration file.

As an example, here are two checks using Plugin_Status, one which ensures Akismet is active on the system and other which ensures Hello Dolly is uninstalled:

plugin-akismet-active:
  check: Plugin_Status
  options:
    name: akismet
    status: active
plugin-hello-uninstalled:
  check: Plugin_Status
  options:
    name: hello
    status: uninstalled

Run together, you might see:

$ wp doctor check --config=plugin-status.yml --all
+--------------------------+---------+----------------------------------------------------------------+
| name                     | status  | message                                                        |
+--------------------------+---------+----------------------------------------------------------------+
| plugin-akismet-active    | success | Plugin 'akismet' is 'active' as expected.                      |
| plugin-hello-uninstalled | error   | Plugin 'hello' is 'inactive' but expected to be 'uninstalled'. |
+--------------------------+---------+----------------------------------------------------------------+

The Plugin_Status check type accepts the following options:

  • ‘name’: Name of the plugin as it appears in wp plugin list.
  • ‘status’: Expected plugin status as one of ‘active’, ‘installed’, or ‘uninstalled’.

Customize diagnostic checks run by wp doctor

Even though wp doctor comes with a number of default diagnostic checks, it’s designed with extensibility at its core. Checks are defined at runtime, read from a doctor.yml configuration file naming each check with its options.

doctor.yml format

Let’s take a look at the first two checks in the included doctor.yml:

autoload-options-size:
  check: Autoload_Options_Size
constant-savequeries-falsy:
  check: Constant_Definition
  options:
    constant: SAVEQUERIES
    falsy: true

In this example:

  • ‘autoload-options-size’ and ‘constant-savequeries-falsy’ are the names for the checks. Names must be unique amongst all registered checks.
  • Autoload_Options_Size and Constant_Definition are reusable check classes in the runcommand\Doctor\Checks namespace. You can use them too, or you can write your own class extending runcommand\Doctor\Checks\Check and supply it as ‘class: yourNamespace\yourClassName’.
  • ‘constant’ and ‘falsy’ are configuration options accepted by the Constant_Definition class. In this case, we’re telling doctor to ensure SAVEQUERIES is either false or undefined.

For the sake of completeness, it’s also worth noting Autoload_Options_Size accepts ‘threshold_kb’ as an optional configuration option. The default value for ‘threshold_kb’ is 900, so it doesn’t needed be included in the doctor.yml.

Custom doctor.yml configuration files

Run your own doctor checks by creating a doctor.yml and supplying it with wp doctor check --config=doctor.yml. Use different configurations for different environments by creating separate prod.yml and dev.yml files.

If you want your custom file to extend an existing doctor config, you can use the magical _ config file option to define which config file to inherit. ‘default’ is a magic reference to the bundled doctor.yml; you also specify an entire file path.

Take a look at this example:

_:
  inherit: default
  skipped_checks:
    - autoload-options-size
constant-disallow-file-mods-falsy:
  check: Constant_Definition
  options:
    constant: DISALLOW_FILE_MODS
    falsy: true
plugin-akismet-active
  check: Plugin_Status
  options:
    plugin: akismet
    status: active
plugin-akismet-valid-api-key:
  class: Akismet_Valid_API_Key
  require: akismet-valid-api-key.php

This custom doctor.yml file:

  • Inherits all default diagnostic checks except for ‘autoload-options-size’.
  • Defines a ‘constant-disallow-file-mods-falsy’ check to ensure the DISALLOW_FILE_MODS constant is falsy.
  • Defines a ‘plugin-akismet-active’ check to ensure Akismet is active.
  • Defines a ‘plugin-akismet-valid-api-key’ custom check in a akismet-valid-api-key.php file to ensure Akismet has a valid API key.

Available check types

Some wp doctor check types are configurable, meaning the default setting can be changed, while other check types are abstracted in such a way that they can be reusable. For instance, the Autoload_Options_Size check accepts an option ‘threshold_kb’ while Plugin_Status accepts ‘plugin’ and ‘status’ as options.

The configurable check types include:

  • check: Autoload_Options_Size: Accepts ‘threshold_kb’ as an option to set the threshold in kilobytes. Default value is 900.
  • check: Cron_Count: Accepts ‘threshold_count’ as an option to set the threshold of total cron jobs. Default value is 50.
  • check: Cron_Duplicates: Accepts ‘threshold_count’ as an option to set the threshold of duplicate cron jobs. Default value is 10.
  • check: Plugin_Active_Count: Accepts ‘threshold_count’ as an option to set the threshold of total active plugins. Default is 80.
  • check: Plugin_Deactivated: Accepts ‘threshold_percentage’ as an option to set the threshold of percentage deactivated plugins. Default is 40.

The abstracted check types include:

  • check: Constant_Definition: Assert a given constant as defined, a specific value, or falsy. Learn more.
  • check: File_Contents: Check all or a selection of WordPress files for a given regex pattern. Learn more.
  • check: Option_Value: Assert a given option as a specific value. Learn more.
  • check: Plugin_Status: Assert a given plugin as active, installed, or uninstalled. Learn more.

Of course, some check types don’t need configuration options:

  • check: Core_Update: Errors when new WordPress minor release is available; warns for major release.
  • check: Core_Verify_Checksums: Verifies WordPress files against published checksums; errors on failure.
  • check: Plugin_Update: Warns when there are plugin updates available.
  • check: Theme_Update: Warns when there are theme updates available.

You can write your own custom check type by extending the runcommand\Doctor\Checks\Check class.

Default diagnostic checks included with wp doctor

Although it’s power comes from its ability to be customized, wp doctor includes a number of default diagnostic checks considered to be recommendations for production websites.

Use wp doctor list to view these default checks:

name description
autoload-options-size Warns when autoloaded options size exceeds threshold of 900 kb.
constant-savequeries-falsy Confirms expected state of the SAVEQUERIES constant.
constant-wp-debug-falsy Confirms expected state of the WP_DEBUG constant.
core-update Errors when new WordPress minor release is available; warns for major release.
core-verify-checksums Verifies WordPress files against published checksums; errors on failure.
cron-count Errors when there’s an excess of 50 total cron jobs registered.
cron-duplicates Errors when there’s an excess of 10 duplicate cron jobs registered.
file-eval Checks files on the filesystem for regex pattern eval\(.*base64_decode\(.*.
option-blog-public Confirms the expected value of the ‘blog_public’ option.
plugin-active-count Warns when there are greater than 80 plugins activated.
plugin-deactivated Warns when greater than 40% of plugins are deactivated.
plugin-update Warns when there are plugin updates available.
theme-update Warns when there are theme updates available.

To explain these further:

  • Autoloaded options are options that are automatically loaded in every request to WordPress. A size exceeding the recommended threshold could be a symptom of a larger problem.
  • Because SAVEQUERIES causes WordPress to save a backtrace for every SQL query, which is an expensive operation, using SAVEQUERIES in production is discouraged.
  • WordPress minor versions are typically security releases that should be applied immediately.

If you create a custom doctor.yml config file, you can use wp doctor list --config=<file> to view the diagnostic checks listed in the file.

Install a WP-CLI package

Packages are to WP-CLI as plugins are to WordPress: modular additional functionality. There are a couple of ways you can install a WP-CLI package.

Use WP-CLI’s built-in installer

For packages listed in the WP-CLI package index, you can install the package with wp package install (doc).

wp package install runcommand/hook
Installing package runcommand/hook (dev-master)
Updating /home/runcommand/.wp-cli/packages/composer.json to require the package...
Using Composer to install the package...
---
Loading composer repositories with package information
Updating dependencies
Resolving dependencies through SAT
Dependency resolution completed in 0.001 seconds
Analyzed 361 packages to resolve dependencies
Analyzed 73 rules to resolve dependencies
 - Installing runcommand/hook (dev-master e0c6487)
Writing lock file
Generating autoload files
---
Success: Package installed successfully.

Require the package on your local machine

If the package isn’t listed in the WP-CLI package index, you’ll need to download it locally and require it through a WP-CLI config file.

Given a package located in ~/.wp-cli/runcommand-profile with a main loader file command.php, you can edit (or create) ~/.wp-cli/config.yml and include the following require statement:

require:
  - runcommand-profile/command.php

Profile key performance metrics for an admin-ajax request

Using the eval-file command in wp profile, you can profile key performance metrics for an admin-ajax request 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 the oembed_cache admin-ajax callback in WordPress core, you’d create an oembed-cache.php file like this:

<?php
// Load the wp_ajax_oembed_cache() function
require_once dirname( __FILE__ ) . '/wp-admin/includes/ajax-actions.php';

// Mock data expected in the $_GET request
$_GET = array(
	'post'   => 1,
);

// Ensure WordPress doesn't halt script execution
define( 'DOING_AJAX', true );
add_filter( 'wp_die_ajax_handler', function(){
	return '__return_false';
});

// Finally, call the function we're profiling
wp_ajax_oembed_cache();

Then, profile oembed-cache.php by running wp profile eval-file oembed-cache.php:

$ wp profile eval-file oembed-cache.php
+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| time    | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count |
+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+
| 0.0214s | 0s         | 0           | 100%        | 1          | 0            | 0s           | 0             |
+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+

Switch themes based on query argument

Jonathan asks:

> So for lack of a better explanation of “it will make my life easier in the short term” I’m in a situation where I’d love to be able to switch themes at runtime if viewing a single CPT entry. Do I need to be talked off a ledge before spending time researching the various theme switcher plugins out there and implementing such a thing?

WordPress looks at the `template` and `stylesheet` options to determine which theme to load. You can filter these at runtime based on a query argument.

Create a mu-plugin called `switch-themes.php` and place this inside:

“`
if ( ! empty( $_GET[‘theme’] ) ) {
foreach( array( ‘stylesheet’, ‘template’ ) as $option ) {
add_filter( “pre_option_{$option}”, function(){
return $_GET[‘theme’];
});
}
unset( $option );
}
“`

You should probably add a bit of security around this if you want to use it in a public environment.