Initialize WordPress from a PHP CLI script

I sometimes run scripts from the command line and still need WordPress functionality and the regular WordPress environment. Here’s a very basic script that I usually use as a structure for this kind of actions.

<?php
/** 
 * Dummy CLI script that loads the WordPress environment
 * Author: Thorsten Ott 
 * Author URI: http://hitchhackerguide.com
 */
set_time_limit( 0 );
ini_set( "memory_limit", "64M" );
$_SERVER['HTTP_HOST'] = 'wp_trunk'; // set this to the apache vhost name of your WordPress install

ob_start();
require_once( dirname( __FILE__ ) . '/wp-load.php' ); // you need to adjust this to your path
require_once( ABSPATH . 'wp-admin/includes/admin.php' );
ob_end_clean();

$cli_script = new WordPress_CLI;
$cli_script->set_required_arg( 'blog_id', 'blog_id of blog to use' );
$cli_script->set_required_arg( 'action', 'what shall I do' );

$cli_script->set_argument_validation( '#^blog_id$#', 'cli_init_blog', 'blog_id invalid' );
$cli_script->init();

abstract class WordPress_CLI_Factory {
	public $args;
	private $validate_args = array();
	private $required_args = array();
	
	public function __construct() {
		$this->args = $this->get_cli_arguments();
	}
	
	public function init() {
		if ( !$this->validate_args() )
			exit;
		$this->dispatch();
	}

	abstract function dispatch();
	
	public function set_required_arg( $name, $description='' ) {
		$this->required_args[$name] = $description;
	}
	
	public function set_argument_validation( $name_match, $value_match, $description='argument validation error' ) {
		$this->validate_args[] = array( 'name_match' => $name_match, 'value_match' => $value_match, 'description' => $description );
	}

	private function validate_args() {
		$result = true;
		if ( empty( $this->args ) && !empty( $this->required_args ) ) {
			$this->show_help();
			$result = false;
		} else { 
			foreach( $this->required_args as $name => $description ) {
				if ( !isset( $this->args->$name) ) {
					$this->raise_required_argument_error( $name, $description );
					$result = false;
				}
			}
		}
		foreach( $this->validate_args as $validator ) {
			foreach( $this->args as $name => $value ) {
				$name_match_result = preg_match( $validator['name_match'], $name );
				if ( ! $name_match_result ) {
					continue;
				} else {
					$value_match_result = $this->dispatch_argument_validator( $validator['value_match'], $value );
					if ( ! $value_match_result ) {
						$this->raise_argument_error( $name, $value, $validator );
						$result = false;
						continue;
					}
				}
			}
		}
		
		return $result;
	}
	
	private function dispatch_argument_validator( $match, $value ) {
		$match_result = false;
		if ( is_callable( array( &$this, $match ) ) ) {
			$_match_result = call_user_func( array( &$this, $match ), $value );
		} else if ( is_callable( $match ) ) {
			$_match_result = call_user_func( $match, $value );
		} else {
			$_match_result = preg_match( $match, $value );
		}
		return $_match_result;
	}
	
	private function raise_argument_error( $name, $value, $validator ) {
		printf( "Validation of %s with value %s failed: %s\n", $name, $value, $validator['description'] );
	}
	
	private function raise_required_argument_error( $name, $description ) {
		printf( "Argument %s is required: %s\n", $name, $description );
	}
	
	private function show_help() {
		printf( "Please provide the following arguments: %s\n", print_r( $this->required_args, true ) );
	}
	private function cli_init_blog( $value ) {
		$blog_address = get_blogaddress_by_id( (int) $value );
		if ( $blog_address == 'http://' )
			return false;
		switch_to_blog( (int) $value );
		return true;
	}
	
	private function get_cli_arguments() {
		$_ARG = new StdClass;
		$argv = $_SERVER['argv'];
		array_shift( $argv );
		foreach ( $argv as $arg ) {
			if ( preg_match( '#--([^=]+)=(.*)#', $arg, $reg ) ) 
				$_ARG->$reg[1] = $reg[2];
			elseif( preg_match( '#-([a-zA-Z0-9])#', $arg, $reg ) ) 
				$_ARG->$reg[1] = 'true';
		}
		return $_ARG;
	}

}

class WordPress_CLI extends WordPress_CLI_Factory {
	
	public function dispatch() {
		print_r( get_bloginfo() );
	}

}
Advertisements

7 Responses to “Initialize WordPress from a PHP CLI script”

  1. Nice one!

    I want to run a script by real cron (WordPress cron is too unreliable). In this script I’ll be using $wpdb to insert data into WordPress tables.

    I believe I can use your script!

  2. This is great and all, but there’s no documentation. It’s a fairly sophisticated script to try to parse manually. I think this could find a lot of use for WPcom / VIP, but its operation is very opaque.

    1) This only works in a multi-site environment. It fails silently outside of one, or if HTTP_HOST is not set correctly.

    2) What is the action parameter for? It’s required, but how do I tell what valid options are?

    • Hi Ben, this was more meant as a snippet to build upon not as a ready solution. The action parameter is just an example for further implementations and could be used to determine what the script should do. I did not test this in a non-multi-site environment but if there is interest I’m happy to revise this further.

  3. Thanks! I agree with Ben. Sample usage would be cool. Specifically, how can I setup a wpdb object and start making calls?

    I’m having a hard time getting wpdb to load on the command line. This looks like a possible solution but not showing sample usage doesn’t make it easy to get my head around it quickly.

    How do I actually use the “action” argument on line 18? Sample?

    Thanks again

    • As $this->args would hold an array of the validated arguments you could simply use a action parameter which would then call different functions from your dispatch() method. This could look like this:

      class WordPress_CLI extends WordPress_CLI_Factory {
          public function dispatch() {
              switch( $this->args->action ) {
                  case 'hello_world':
                      echo 'hello world';
                      break;
                  case 'count100':
                      for( $i=0; $i<100; $i++ )
                          echo $i;
                      break;
                  default:
                      echo 'all done';
                      break;
              }
          }
      }
      

      In this case a call such as
      php scriptname.php --blog_id=1 --action=hello_world
      would cause dispatch() to run the “hello world” echo

  4. Thanks for this. Was dealing with a script that worked fine if run through the browser, but that did nothing from the command line. Adding $_SERVER[‘HTTP_HOST’] = ‘blah’ made all the difference.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: