Building an Object-Oriented WordPress Plugin (xkcd-shortcode) – Part 2

This is Part 2 of a Tutorial Series on writing an Object-Oriented
WordPress Plugin from scratch.

Series Navigation:

  1. Design
  2. Making Remote Requests
  3. Generating HTML
  4. Connecting to WordPress’s Shortcode API
  5. Implementing Caching

In Part 1 you did a bit of planning and set the stage to start coding. You identified 3 objects XKCDLoader, XKCDShortcode and XKCDPlugin that make up this plugin. The XKCDLoader does the bulk of the work. That’s where you will be starting today.

In the preliminary design, you decided that the XKCDLoader Class has a load method. It takes one argument, the number of the XKCD comic to fetch. Further, this argument can be null, to support fetching the current comic instead.

Somehow this method needs to return a PHP object that contains the comic’s meta information, fetched from the XKCD.com servers.

XKCD’s JSON API

First let’s get acquainted with the API you will be using. XKCD.com provides a handy JSON API that suits this plugin nicely. It allows us to fetch metadata about a specific comic or the current one.

To fetch the JSON for a specific comic you make an HTTP request to the URL,

http://xkcd.com/614/info.0.json (comic #614)

For the current comic, the number may be skipped.

http://xkcd.com/info.0.json (current comic)

The JSON returned contains top-level keys for the alt, title & img attributes. You will also use the num key to link back to the comic’s page on xkcd.com. The full response looks like this,

{
  "month": "3",
  "num": 1339,
  "link": "",
  "year": "2014",
  "news": "",
  "safe_title": "When You Assume",
  "transcript": "",
  "alt": "You know what happens when you assert--you make an ass out of the emergency response team.",
  "img": "http://imgs.xkcd.com/comics/when_you_assume.png",
  "title": "When You Assume",
  "day": "7"
}

XKCDLoader#load

It would be tempting to implement everything into the load
method itself and be done with. This is a mistake. And it is how you end up with a bloated function!

There are a number of things that make up the loading process. You need to think of each of these as steps that will make up the load method. Each step gets you closer to your goal, which is to return a PHP object with the required metadata.

Here’s an outline of what the load method does.

function load($num = null) {
  /* build a URL for the XKCD comic to fetch */

  /* make a request to that URL */

  /* verify that the server returned a valid response */

  /* convert the JSON response into a PHP object */

  /* return that PHP object */
}

For each of these steps you will utilize a helper function. When creating such helpers, it is important that they are immutable. In order words they take some input and return a result. They do not have any side-effects. And they do not modify anything outside the function.

Here’s the same outline in a sequence diagram. Note how the load method calls out to each of helper functions and then
proceeds to the next step.

If any of these steps failed, you must throw an Exception and indicate the cause of the failure.

XKCDLoader#build

Before you do any actual loading, you need to build the URL to request. And you need to build a slightly different URL for the current comic vs a numbered comic. An if conditional with an is_null will suffice.

function build($num) {
  if (is_null($num)) {
    return 'http://xkcd.com/info.0.json';
  } else {
    return 'http://xkcd.com/' . $num . '/info.0.json';
  }
}

XKCDLoader#fetch

Now that you have a URL, the next step is to fetch a response for this URL. This is where you will use WordPress’s wp_remote_get API function.

Wrapping this into a fetch function clarifies intent. A Loader object with a fetch method suggests, this is where the request is executed. And once you know fetching is working, testing the load function becomes much easier.

function fetch($url) {
  return wp_remote_get($url);
}

XKCDLoader#verify

You now have a URL and you’ve fetched the response. It’s time to verify that the response was valid! WordPress returns a WP_Error object if the wp_remote_get failed due an older PHP version for instance. It is also a good idea to check that the server returned a valid response with an HTTP Status Code of 200.

Both these checks however don’t mean anything if the HTTP Response body is empty. So, you also need to verify that the response body wasn’t empty.

If things went ok you can return the body you extracted out using wp_remote_retrieve_body.

And if any of these checks failed, you must throw an Exception with a message indicating the cause of the error.

function verify($result) {
  if (is_wp_error($result)) {
    throw new Exception('XKCDLoader Failed: Could not wp_remote_get');
  }

  $code = $result['response']['code'];
  if ($code != 200) {
    throw new Exception('XKCDLoader Failed: Invalid HTTP Response from server - ' . $code);
  }

  $body = wp_remote_retrieve_body($result);
  if ($body == '') {
    throw new Exception('XKCDLoader Failed: JSON body was empty.');
  }

  return $body;
}

XKCDLoader#parse

Now that you have a JSON response from the server, the next step is to convert it into a PHP object. The PHP function json_decode comes in handy here.

The server could also have returned an invalid JSON response, in which case you must throw an Exception that indicates this error.

function parse($body) {
  $json = json_decode($body);
  if (json_last_error() !== JSON_ERROR_NONE) {
    throw new Exception('XKCDLoader Failed: Invalid JSON returned by server - ' . json_last_error());
  }

  return $json;
}

Putting things together in XKCDLoader#load

Now that everything is Mise en place, it’s time to bring it all together! Following the sequence from the earlier outline you have,

function load($num = null) {
  $url    = $this->build($num);
  $result = $this->fetch($url);
  $body   = $this->verify($result);

  return $this->parse($body);
}

This load function reads clearly. It is very apparent what steps it follows. If you were to return to this method at a later date it will be easy to catch up. Things would not be as obvious if all that code was in the load method itself.

Using the XKCDLoader

At this point you’ve succeeded in your goal. You have a working Loader object that can fetch the JSON for any valid XKCD number.

When you use this class you need not worry about how the fetching or parsing happens. Just use the load method and trap any errors.

$loader = new XKCDLoader();

try {
  $json = $loader->load(5);
} catch (Exception $e) {
  /* show the error */
}

Moving On

Things are moving along nicely. The biggest part of the xkcd-shortcode plugin is now working. In the next part you will use this XKCDLoader object in the XKCDShortcode class and assemble the rest of your plugin.

Stay tuned for Part 3!