Designing a class: WordPress API client

As a WordPress developer, learning object-oriented programming seems daunting at first. It’s one thing to learn all the concepts, but theory will only get you so far.

You want to see how you can do it in practice. How do you start from nothing and end up with a working class. That’s why I’m going to share how I built a client for the WordPress JSON REST API. You’ll get to see how I use object-oriented design in practice.

Some background

I’ve been working on a project with the WordPress JSON REST API over the last few weeks. The goal of the project was to do a complex cross-site user importer. I decided to use that API because I’m more confortable with it than with the XML-RPC API.

The importer itself needed a generic php client to connect with the API. The WP-API project does offer a PHP client. That said, it wasn’t designed to leverage internal WordPress functionalities. The project team build the client for the larger PHP community.

This prompted me to design a WordPress API client. I had two goals in mind for it. It had to:

1) Leverage WordPress APIs
2) Work without an external library

Adding some limitations

Before we get into it, let’s talk about the two limitations that I’m imposing on the article. The point of those limitations is to limit the amount of code in the class. It also makes it easier for you to understand the design decisions.

You’ll see that there’s plenty to cover already with those limitations.

One API method

The client will only have one API method. That’s how to retrieve users. That said, that one method will give you the foundation you need for creating the other ones yourself.

Basic authentication

There’s several authentication methods available for the API. We’re going to use basic authentication for the client. It isn’t recommended for a production environment, but it’s fine for the context of this example.

Authentication is only a small part of the client functionality. That means you can reuse a lot of the code here with the other authentication methods.

There’s a few ways you could handle the different authentication methods. You could create classes for each with a common interface. You could also create an abstract class and extend it for each authentication method. Both have their advantages and disadvantages.

How will others interact with the client?

In most cases, this is the first thing you should ask yourself. How is another programmer going to interact with it. It forces you to think about the job your class needs to do. You also need to figure out what it needs to have to do its job.

This ties into the single responsibility principle. You want to focus on the essential and not have your class do everything. Let’s go over the decisions I took.

Constants

I wanted the path to the endpoint for retrieving users to be a constant. We want to use a constant when we know a value won’t change ever. The endpoint fits that idea. It shouldn’t change or the client will break.

Constructor

I designed the standard constructor to take three parameters. Two of those are straight forward. You need the base URL to the WordPress site you are connecting to. You also need the authorization token used by the basic authentication.

I’d like to point out we pass the encoded token. You could pass the username and the password instead. The constructor would need to encoded them.

The final parameter is an instance of “WP_Http”. It’s a WordPress class that most developers don’t know exists. It powers the HTTP API. It hides the complex logic WordPress does behind the scenes to allow you to do an HTTP request.

The reason you want to pass that class to the constructor is to follow SOLID principles. It isn’t the job of our API class to handle all the logic related to performing an HTTP request. You want to let “WP_Http” do that job for you and pass it in the constructor.

Create an instance from WordPress globals

Now that you have a constructor, you want to add a layer to decouple the WordPress functionality from your class. You do this by using a static method to construct the object. The job of that static method is to create the client from the WordPress globals.

A client method that matches the API method

This was an important design consideration. The client method needed to match the API method in the plugin. This meant using the same name and parameters as the API method. By doing this, you make it easy for another developer to use the client by looking up the API documentation.

It’s worth noting that there was a lot of back and forth here. The parameters on the documentation site weren’t working as described. Some were also missing. It’s not unusual for the documentation to lag behind.

Whenever you encounter an issue like that, you want to dig into the code to see what’s happening. You also want to run some tests to determine how the API is working in practice. This is what I did here.

You’ll notice that I left the inside of the method blank. This is intentional. We’ll cover it in the next section.

What’s going on behind the scene

Now, that we’ve defined what is accessible from outside the class. It’s time to work on what’s going on inside the class. The idea is to work your way from the public method and create internal methods to help it do its job.

I’ve tried to break these down into categories in the order I tackled them. You’ll notice that it usually involves diving one level down from the previous category.

Doing a HTTP request

Let’s go back to the “get_users” method. What do you need to code to make it work? It’s important to take a step back and think about it before you start coding. So take a second to think about it before reading on.

Building the request URL

We need a way to build the request URL to communicate with the API. We have several components that we need to combine to create the URL. They are:

  • The base URL
  • The path to the endpoint
  • The query string arguments

Let’s take a look at the method to see the design decisions I took.

Let’s talk about the parameters. We don’t pass the base URL because the idea is to build an URL to that points to it. So we only pass the endpoint and the query string arguments as an array. The query string arguments are optional since you might not always have any.

Building the URL is pretty straight forward. I want to combine the base URL and the endpoint path. If we have query string arguments, we want to add them to the URL. PHP has a built-in function to do this for you.

Sending a GET request

We want to send a GET request with the URL that “build_url” built. You’ll need an internal method to send it with the “WP_Http” object we passed in the constructor.

So now that we filled out our “get_users” method, let’s dig down to HTTP API.

Working with the HTTP API

The HTTP API is great for hiding away the complexity of doing an HTTP request with WordPress. There’s still a lot of work that our client needs to do once we get a response back. You need to convert the response into something the client can use.

We get our response by calling the “get” method from the “WP_Http” class. You want to pass it the URL we built in “get_users”. You also want to pass it some arguments. That’s how the API builds its request.

We need to add our own “build_args” method to add the basic authentication token from the constructor. This is how we’ll authenticate with the API since we’re using basic authentication. This code would be different depending on your chosen authentication method.

Processing the response

At this point, I took an important design decision. I wanted two possible return values from the client. If we had a valid response from the API, the return value should be the decoded JSON array.

Otherwise, we should be getting an instance of “WP_Error”. This is to stay in the spirit of how WordPress handles errors.

Getting information from the response

Before we proceed further, let’s talk about the response we get from the HTTP API. The HTTP API always gives you either an array or an instance of “WP_Error”. The fact that the response is an array adds some extra work you need to take care of.

You shouldn’t assume that your array is correctly formatted. You’ll need to add some methods to validate and extract that information. You want to be able to handle if the information isn’t there. If you try to work with the array without validating, your code could generate you warnings or flat out break.

For the client, I needed to extract the response headers and the status code. You’ll see why when we start working with the response.

This shows you the advantage of using an object over an array. You need to repeat this code in any class that uses the HTTP API. If we had a response class, you wouldn’t need to write it anywhere.

Decoding the response

Let’s go back to the “get” method. The first thing we want to do is handle a successful response from the API.

A successful response from the HTTP API will be an array. We check for that first. We also want the response to have a successful status code.

If we have an array and the response is successful, we want to decode it.

Before we do anything, you want to check that the response is actually JSON. You’ll want to check the headers to see if we have “application/json” in the “content-type”. You want to enforce that the content of a response is JSON. This prevents errors where the response was something else like HTML.

Now that we validated the content type, we want to decode the response. You want to do a quick check to see if there’s a response body first though.

json_decode” will return null if it there was an error decoding the JSON. You want to catch that error and return a “WP_Error” instead. If you make it to the end, you’ll have either an empty array or the decoded JSON array.

Converting API errors

The other situation we want to handle is an unsuccessful response from the API. That’s because if there’s an error, the API will return the JSON encoded “WP_Error”. I wanted to convert those back to a “WP_Error” object.

So the new condition is that we want the response to be an array, but “is_successful” needs to be false.

Converting a response to an instance of “WP_Error” starts by decoding the response. It’s possible that “decode_response” creates an instance of “WP_Error”. If that happens, we want to return that error instead of the empty default error.

If we get an array, we want to merge that array of error codes and messages into our empty default error. I do this using a PHP function called “array_walk“. Using it allows me to extract the logic for adding an error to the instance of “WP_Error”. For each element of the array, “array_walk” will call another function “add_response_error”.

This method’s purpose is to attempt to validate and add part of a response into an instance of “WP_Error”. Before doing anything, it checks that we have an array. The expected array will have two keys:

  • code
  • message

We use the ternary operator to prevent PHP warnings. The ternary operator returns an empty string if either keys isn’t set.

Final notes

Holy moly, that was a lot! Like I said in the limitations, there was plenty for you to cover. Let’s go over a few final points.

Power of object-oriented programming

The goal of this design example was to show you the power of using objects for solving complex problems. That said, you were also able to see the extra work that you needed to do when you didn’t.

If WordPress core used more classes instead of arrays, you could avoid a lot of the work that we did. You could also cut a few internal methods like “get_response_headers”, “get_response_status_code” and “is_successful”. You also wouldn’t need to do as much validation.

How you can proceed from here

I want to discuss how you could continue based on the work you saw here. We took time to break down a lot of the internal methods. This allows us to reuse them with the other API methods.

Let’s look at an example. Retrieving the current user would only need that you create a new public method and a new endpoint constant. Retrieving posts would be quite similar as well.

Creating a new user would be more work. You’d need to work create an internal method for POST requests. You’d have new status codes to support. New outcomes.

That said, the foundation you need is there to help you to move ahead.

The complete picture

You can find the documented code on a GitHub project I created for it. I plan on fleshing out the client in the future. So feel free to use it and check back on it.

I’ll leave you with an exercise. If you had to extract part of the class into a new one, what would you split out? Let me know in the comments!

  • As always, a great article. I submitted a pull request against the REST API docs site to include this in the tutorials section: https://github.com/WP-API/WP-API.github.io/pull/26

    • Hadn’t seen that it existed! Thanks for doing it Josh!

  • Great stuff Carl, very informative. Have you ever tried the wp_remote_retrieve_header(), wp_remote_retrieve_response_code() and wp_remote_retrieve_body() functions? You catn pass in a WP_HTTP response array and get back the data you need from it. I think you could remove a couple of your methods and simplify is_successful() with them.

    • I actually missed those functions! I still think they make sense in the example to get a larger picture, but you could optimize that way for sure.