This website recently got a major rebuild; if you're missing something, let Lorna know.

Building a RESTful PHP Server: Output Handlers



This is the third installment in my series about writing a RESTful web service in PHP (the previous entries are about understanding the request and routing it. It is probably the last one but there are a few other things I'd like to cover such as error handling, so I might keep adding to it, especially if I get any particular requests or interesting questions in the comments. So far we've covered parsing requests to determine exactly what the user is asking for, and also looked at routing to a controller to obtain the data or perform the action required. This post gives examples of how to return the data to the client in a good way.

Output Handlers Instead of Views

We'll have as many output handlers as we have supported output formats. The joy of having all the controllers return the data toindex.php is that we can then add common output handling to all the data. In our example system, we can remove that ugly print_r from index.php and instead detect which output format is needed and load the relevant view. My code looks like this:

$view_name = ucfirst($request->format) . 'View';
if(class_exists($view_name)) {
    $view = new $view_name();
    $view->render($result);
}

The most simple example is a JsonView which looks like this:

class JsonView extends ApiView {
    public function render($content) {
        header('Content-Type: application/json; charset=utf8');
        echo json_encode($content);
        return true;
    }
}
As you can see here, it's pretty simple! We send the Content-Typeheader first to let the consumer know what's in the response, then we just encode the JSON and echo it out.
To support other formats, you might loop over your array (remember it might be nested – things usually get recursive at this point for something like an XML format) and transform it into the new format. Between two PHP systems, it might be simpler to support serialised PHP as a format as it is a little more verbose than the JSON.

I also like to support HTML as an output format - for example the joind.in API does this; just go to http://api.joind.in with your browser and it will realise you're a web browser and return you the HTML version. Joind.in is open source and since I wrote its RESTful API, it looks a lot like this one! You can see the HTML output handler at: https://github.com/joindin/joind.in/blob/master/src/api-v2/views/HtmlView.php

One format that I often get asked about is JSONP – so that other sites can make calls to the API from JavaScript and get around the cross-domain policy. The way that I've implemented this in the past is to check for a parameter called "callback" with a request for JSON format, and if I get it, then just wrap the function around the response I would normally send. I blogged about it here and again, this feature is in joind.in's codebase if you want to take a look.

Along with the data from the controller, it can also be useful to add some additional information to your output handlers, such as a count of how many top-level records were returned, and sometimes some pagination (I think I could write a whole other post about hypermedia and pagination - if you want it, leave me a comment). This additional functionality should be added in the parent ApiViewclass - this just has a few helper functions which the various output formats can use if they want to. Having it all central means that the responses will be very consistent across all the requests made to this service, and between formats. Consistency is absolutely the key to a good API so more shared functionality is always good.

There you have it, one functioning RESTful (ish! we skated over a few issues here, read the comments on all the posts in the series) server. You can test it by calling your service from cURL or writing a script to consume it from PHP instead. Since this was a simple tutorial there are some areas where I'd have liked to have gone in to more detail but it is already quite long-winded, so I'll stop there and if I get any good questions in the comments, I may add some followup posts!


In: php
Tags: #api #json #MVC #output #php #rest2 #RESTful