Using WordPress in a non-interactive “batch” CLI process

Sometimes you need to write a repetitive process that loops thru large quantities of data and performs WordPress API calls. Back near the dawn of time we used to call these “batch” processes, a term from the old IBM mainframe days of decades past. Nowadays you’ll probably call this a non-interactive CLI process. Whatever you call it, using WordPress normally thru its web-based interface isn’t desirable in this case; rather than having WordPress contain your process in a plugin or whatever, you want your process to simply have the ability to make WordPress API calls in addition to whatever else it’s doing.

You could extend the formal WP-CLI mechanism, but this might feel too constraining or overly complex. In these cases, it’s still possible to use the WP API by having your process load (or contain) WP itself. Here’s a code snippet that shows how:

function setup_wordpress() {
    /*
     * Need to setup WordPress since we're not running "under" it...
     */
    if ( in_array( php_sapi_name(), [ 'cli', 'cli-server' ] ) ) {
        foreach( $_SERVER as $key => $val ) {
            if ( ! getenv( $key ) ) {
                if ( is_scalar( $val ) ) {
                    putenv( $key . '=' . $val );
                } else { // Especially for $argc
                //  echo "What? \$_SERVER[ '$key' ] is: "  .  print_r( $val, true )  .  "\n";
                }
            }
        }

        if ( ! getenv( 'HTTP_HOST' ) ) {
            putenv( 'HTTP_HOST=' . gethostname() );
        }

        if ( ! getenv( 'SERVER_ADDR' ) ) {
            putenv( 'SERVER_ADDR=' . gethostbyname( gethostname() ) );
        }

        if ( ! getenv( 'REQUEST_URI' ) ) {
            putenv( 'REQUEST_URI=/' );
        }

        if ( ! getenv( 'REQUEST_METHOD' ) ) {
            putenv( 'REQUEST_METHOD=GET' );
        }

        define('WP_USE_THEMES', false);

        require_once( './wp-load.php' );
    }
}

How do you use this? Simply call setup_wordpress() once prior to any WordPress API calls. That’s all.

Let’s discuss what this does:

  • php_sapi_name() indicates how PHP was invoked. We check this to make sure we’re coming from the command-line; we don’t want to invoke WordPress this way in interactive use via a web browser because it has already been loaded in that case.
  • Next we loop thru the $_SERVER associative array and copy all those values into environment variables in case they are referenced that way later on. This emulates what happens when PHP invokes WP thru a webserver. Note that we skip any entries that are other than a simple scalar value (for example, the parameters passed to this script are contained in an array in the argv entry).
  • If missing, we create several environment variables that define the otherwise-missing HTTP request.
  • wp-includes/template-loader.php includes code to conditionally output the theme’s HTML. To suppress this, we set WP_USE_THEMES = false.
  • Finally, we bring in the WordPress API by including wp-load.php. However, we do not kick off  the normal WordPress flow of control, as would normally be done by calling wp() next. We’re simply defining the API and will allow our own code to control the flow of the process.

Hope this helps! Leave any questions in the comments.

Leave a Reply

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