We Are Communication Architects

Building brand awareness through content creation and community engagement.

December 20th, 2010

How to Setup an Interactive WordPress Shell

One of my favorite features of (most) interpreted programming languages is the Read-Eval-Print-Loop or REPL.

REPLs allow for much quicker feedback during experimentation, and are invaluable tools for learning a language.

When I started working with PHP more extensively, I quickly became frustrated with the Edit-Save-Refresh workflow I had gotten into, especially when I was just experimenting with some built-in PHP functions.

PHP 5.1 and up has “interactive” mode, but that didn’t work on the version of PHP that ships with Mac OS X and left me to seek out alternatives.

I quickly found Facebook’s Python based phpsh, set it up, and began trying to load WordPress in it.

I’ve put together a tutorial if you wish to setup your own interactive WordPress shell:

What you will need:

  • phpsh – an interactive shell for PHP, by Facebook
  • ctags – builds a tag index for a codebase, allowing tab completion for WordPress core and theme functions
  • a working WordPress install

NOTE: This tutorial was tested on Mac OS X 10.6.5, but should work in most *nix environments

Setting up phpsh

Download the latest version of phpsh from http://www.phpsh.org

Extract the archive

tar -xzf facebook-phpsh-8438f3f.tar.gz

Install readline (this enables tab completion!)

sudo easy_install readline

Enter your password when prompted

Build and Install

Here I’m using /usr/local as my prefix:

cd facebook-phpsh-8438f3f

python setup.py build

sudo python setup.py install --prefix=/usr/local

Setting up ctags

Download the latest version of ctags from http://ctags.sourceforge.net/

Extract the archive

tar -xzf ctags-5.8.tar.gz

Configure, Make and Install

Using my preferred prefix of /usr/local again:

./configure --prefix=/usr/local && make && sudo make install

Loading your WordPress blog in phpsh

Test phpsh

$ phpsh
Starting php
type 'h' or 'help' to see instructions & features
php>

Let’s try a simple PHP command to make sure it’s all good

php> = ucwords('voce communications')
"Voce Communications"
php>

As shown above, in order to simple display the return of a function in phpsh, prefix the call with an equals sign.

Loading WordPress

While phpsh is an extremely useful tool on its own, our goal is to make an interactive WordPress playground. Onward!

Navigate to the root of a WordPress project

cd ~/WordPress/wordpress-trunk

Generate the ctags

ctags -R

Create a WordPress shell startup script

Paste the following into a new file, I’ve named mine wpshell:

#!/bin/bash
if [ ! -f ./.wp-shell.php ];
then
  echo '<?php define("WP_USE_THEMES", false); require("./wp-blog-header.php"); ?>' > ./.wp-shell.php
fi
echo "Rebuilding ctags..."
ctags -R --languages=PHP
echo "Launching WP Shell..."
phpsh ./.wp-shell.php

What this script does is create a simple PHP startup file that tells WordPress not to load your theme (your functions.php is still loaded), which avoids issues with phpsh and HTTP redirects.

It then rebuilds your ctag index so that each time you load your WP Shell, it has the latest functions from your theme.

Setting WP_USE_THEMES to false avoids any redirects that will muck with phpsh.

Make the wpshell file executable, and move it to a directory in your $PATH. I’ve chosen /usr/local/bin

$ chmod +x wpshell
$ sudo mv wpshell /usr/local/bin

Run it!

$ wpshell
Rebuilding ctags...
Launching WP Shell...
Loading ctags (in background)
Starting php with extra includes: ['./.wp-shell.php']

type 'h' or 'help' to see instructions & features
php>

You may see some PHP notices and warnings here depending on your log level.

Now things get interesting..

What you can do

PHP function documentation

php> d ucwords
# ucwords
(PHP 4, PHP 5)
ucwords -- Uppercase the first character of each word in a string
### Description
string ucwords ( string $str )
Returns a string with the first character of each word in str capitalized, if that character is alphabetic.
The definition of a word is any string of characters that is immediately after a whitespace (These are: space, form-feed, newline, carriage return, horizontal tab, and vertical tab).
### Parameters
str
The input string.
### Return Values
Returns the modified string.
###
php>

WordPress function documentation

php> d get_post_meta

[{'type': 'f', 'context': 'function get_post_meta($post_id, $key, $single = false) {', 'file': 'wp-includes/post.php'}]

/WordPress/wordpress-trunk/wp-includes/post.php, lines 1407-1420:

/**
* Retrieve post meta field for a post.
*
* @since 1.5.0
* @uses $wpdb
* @link http://codex.wordpress.org/Function_Reference/get_post_meta
*
* @param int $post_id Post ID.
* @param string $key The meta key to retrieve.
* @param bool $single Whether to return a single value.
* @return mixed Will be an array if $single is false. Will be value of meta data field if $single
*  is true.
*/


php>

Tab Completion

WordPress builtins:

php> wp_get_a [TAB][TAB]
wp_get_active_and_valid_plugins   wp_get_attachment_image_src       wp_get_attachment_thumb_url
wp_get_archives                   wp_get_attachment_link            wp_get_attachment_url
wp_get_associated_nav_menu_items  wp_get_attachment_metadata
wp_get_attachment_image           wp_get_attachment_thumb_file
php> wp_get_a

Theme functions:

php> twentyten_ [TAB][TAB]
twentyten_admin_header_style            twentyten_posted_in
twentyten_auto_excerpt_more             twentyten_posted_on
twentyten_comment                       twentyten_remove_gallery_css
twentyten_continue_reading_link         twentyten_remove_recent_comments_style
twentyten_custom_excerpt_more           twentyten_setup
twentyten_excerpt_length                twentyten_widgets_init
twentyten_page_menu_args
php> twentyten_

NOTE: The above would require a new scan of the theme directory using ctags each time the theme code changes.

Experimentation

Retrieving the most recent post:

php> = get_posts(array('posts_per_page'=>1))
array(
  0 => <object #288 of type stdClass> {
    ID => 1,
    post_author => "1",
    post_date => "2010-12-17 13:52:59",
    post_date_gmt => "2010-12-17 13:52:59",
    post_content => "Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!",
    post_title => "Hello world!",
    post_excerpt => "",
    post_status => "publish",
    comment_status => "open",
    ping_status => "open",
    post_password => "",
    post_name => "hello-world",
    to_ping => "",
    pinged => "",
    post_modified => "2010-12-17 13:52:59",
    post_modified_gmt => "2010-12-17 13:52:59",
    post_content_filtered => "",
    post_parent => 0,
    guid => "http://wordpress.trunk/?p=1",
    menu_order => 0,
    post_type => "post",
    post_mime_type => "",
    comment_count => "1",
    filter => "raw",
  },
)
php>

Trying a new function:

php> function get_posts_with_thumbnails() {
... return get_posts(array('meta_key'=>'_thumbnail_id'));
... }
php> $thumbnail_posts = get_posts_with_thumbnails();
php> = count($thumbnail_posts)
1

php>

Troubleshooting

No multiline input

This particular issue took me almost an hour to debug, so hopefully it will save you the headache should you run into it yourself.

If all your attempts at multiline input result in phpsh giving you “false” after you’ve entered the first line, check your php.ini file.

You need to make sure that display_errors is set to on!

Internally, phpsh uses PHP from the command line to test each line of your input, looking at the standard error output to determine what to do next (evaluate, wait for valid syntax completion, etc).

WordPress Multisite

Kudos to fellow Vocian Chris Scott for helping get wpshell working on a Multisite install.

Add this to your wp-config.php, above the wp-settings.php include, but after the DOMAIN_CURRENT_SITE constant has been defined (thanks William!):

if (empty($_SERVER['HTTP_HOST'])) {
    $_SERVER['HTTP_HOST'] = DOMAIN_CURRENT_SITE;
}
if (empty($_SERVER['SERVER_PROTOCOL'])) {
    $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
}

Done?

So there you have it. There are a lot of steps involved, but the process is fairly simple.

For most basic new-to-WordPress experimentation, this really is a great tool.

I’m excited to see ways in which this setup may break down when advanced PHP and WordPress techniques are used, so please comment if you find any!

About the Author
Jeff Stieler is an application developer for the Platforms team. When not coding, he pretends to be a beer snob and tries to figure out what's wrong with his car. On twitter he's @jeffstieler

Filed in Development, WordPress

Add Your Comment5 Responses to “How to Setup an Interactive WordPress Shell”

William on March 5th, 2012 at 4:31 pm

This is a brilliant walkthrough. Thanks, Jeff.

Troubleshooting question (for you or perhaps Chris Scott): When I run the wpshell script from my multi-site directory, phpsh throws:

“phpsh failed to initialize php Fix the problem and hit enter to reload or ctrl-C to quit.”

I’ve included the script suggested by Mr. Scott in my multisite’s wp-config.php.

I’ve tested the wpshell script on another non-multisite wordpress installation on the same server, and it works like a dream. So, I don’t think it’s a problem with my phpsh configuration. I think it’s specifically a multisite thing.

So… I suppose the question is: any hunches? Can you describe what aspects of multisites break phpsh? Is it redirects? The rewrites in .htaccess, maybe? Something completely different?

I’ll continue to poke around. It looks like yours is the only guide out there about getting set up with phpsh and WordPress.

Thanks! William

William on March 5th, 2012 at 4:41 pm

Aaaand, nevermind, solved it already.

Mr. Scott’s code should go before this:

require_once(ABSPATH . ‘wp-settings.php’);

But of course, after DOMAIN_CURRENT_SITE has been defined within wp-config.php.

Again, thanks for this awesome walkthrough.

Jeff Stieler on March 5th, 2012 at 5:13 pm

Thanks for the Multisite troubleshooting tips William!

I’ve revised the end of the post to include your recommendations.

Ben Doherty on June 11th, 2012 at 10:06 am

Hey,

I’ve used the concepts in this blog for my talk “WordPress on the CLI – It’s not so scary!” Thanks a lot to you guys for coming up with this approach. It’s been very helpful.

My MU fix was to extend the wpsh launcher code to add in a value for the HTTP_HOST environment if specified to the wpsh script via a -h option. This will allow you to automagically switch to a blog via hostname (for subdomain installs.) There’s also some additional sanity checks to ensure that HTTP_HOST is supplied for MU installs, but I see that William had a good tip to use the DOMAIN_CURRENT_SITE constant also.

You can read about the talk here,

http://www.thinkoomph.com/thinking/2012-06/wcnyc-2012-wordpress-on-the-command-line/

Ben Doherty on June 11th, 2012 at 10:08 am

Also, for anyone who’s getting the error:

“phpsh failed to initialize php Fix the problem and hit enter to reload or ctrl-C to quit.”

From phpsh, please note that ANY output to STDOUT or STDERR while the WP environment and any initialization script loads will cause phpsh to consider it a fatal error and give up. Kind of poor error handling if you ask me.