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 to index.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-Type header 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 ApiView class - 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!

35 thoughts on “Building a RESTful PHP Server: Output Handlers

  1. Pingback: Building A RESTful PHP Server: Routing the Request | LornaJane

  2. Pingback: Building a RESTful PHP Server: Output Handlers | LornaJane | PHP Web Development | Scoop.it

  3. Pingback: Lorna Mitchell’s Blog: Building A RESTful PHP Server: Output Handlers | Scripting4You Blog

  4. Pingback: Building a RESTful PHP Server: Output Handlers | LornaJane | v3k.net | Scoop.it

  5. Pingback: Building a RESTful PHP Server: Output Handlers | LornaJane | Next Web App | Scoop.it

  6. Lorna,
    I've not used MVC pattern before so I'm still struggling with it a bit. Is there a source code the the sample somewhere I can download and play with.
    Thanks for the great tutorial.
    JB

  7. I'm interested in the full code as well. It seemed to me that there was something missing. So I added the following to create a Request Object instance, and populate the $url_elements array:

    $ro = new Request;
    $url_elements = $ro->url_elements;

    But still, something is wrong for me. Admittedly, I'm operating on very little sleep, and it might be obvious, but I don't think my version can see the "UsersController" class.

    • Hmmm, I think the issue that I'm dealing with is the base class: myController. I don't see where that's defined. Did I miss it?

  8. For what it's worth, I got mine working, although there were several issues I found:
    1. My system doesn't know what __DIR__ is. I substitute "dirname(__FILE__)" in the include function
    2. I created base classes for MyController and ApiView and included them before I pulled in the extended classes
    3. A bug I found in your code is in UsersController::getAction() there is a switch statement that doesn't have a closing curly bracket.

    • Also, thanks for the Tut! I'm not trying to be negative, just scratching my head on a few details there. Great stuff!

      • Thanks for the kind words, hopefully now the whole code is posted it will make more sense. Or, more likely, you already solved the issues :)

  9. LOTS of people asked me for the code, and I'm sorry it took me a while to upload it. You can find it on bitbucket: https://bitbucket.org/lornajane/restful-php-examples (just look for the "get source" button over on the right if you don't want to clone the repo). Hope that helps and answers some questions where I didn't have the full code in the blog posts.

    Many thanks for all your comments, it's great to get some feedback!

  10. Pingback: Adding Custom Headers to Every Request with Chrome | LornaJane

  11. I've been playing around a bit with your code here but can't get it to work even when i download your source code and try to run it.

    All i get is a few php warnings when i try to run it.

    Warning: include(/Applications/XAMPP/xamppfiles/htdocs/restfulphp/library/Controller.php) [function.include]: failed to open stream: No such file or directory in /Applications/XAMPP/xamppfiles/htdocs/restfulphp/index.php on line 16

    Warning: include() [function.include]: Failed opening '/Applications/XAMPP/xamppfiles/htdocs/restfulphp/library/Controller.php' for inclusion (include_path='.:/Applications/XAMPP/xamppfiles/lib/php:/Applications/XAMPP/xamppfiles/lib/php/pear') in /Applications/XAMPP/xamppfiles/htdocs/restfulphp/index.php on line 16

    Am i missing something here? I even tried to include a few url elements but when i do that i just get an Object not found! page from apache.

    • I'm not sure what exactly is wrong there, PHP can't find the files that are being included. This is likely to be related either to permissions, or to the fact that the autoloader makes assumptions about how the paths will work - so if I were you I'd start there for debugging. Good luck :)

  12. I'm new to MVC and the RESTful model so I've read through the series and *think* I'm following it ok... I'm just not sure how to test the example. Do I need to send a JSON string as the body of a POST request to get anything back? Is there a simple way to test quickly (that it's doing anything) from a browser with ? params?

    Thanks again for your work..

    • I found my problem, but not sure what the specific issue is. the MyControllers class is not registered and therefore the UsersController class could not extend it. I tried manually including it at the top of index.php to no avail and ultimately dropped MyControllers and took out the "extends MyControllers" clause from UsersController.php.

      (tail -f is your friend)

      • Are you working from the code in the posts or the downloaded example? I'm not sure the MyController class (and be careful whether it is plural or not) was declared in the examples, but the code I linked in the earlier comments should work. Good luck!

    • I don't have an example but you'd just make another view class that extends ApiView and sits alongside the other options. It would just need to serialize the data instead of json encoding it - and for a header you might send application/php but that's not widely used so I'd also be happy to see text/plain given. Hope that helps

  13. I come to this with some coding and design experience, but no PHP. Within a couple of hours my database was available by a RESTful api on a public site. Thanks very much!

      • Hi, great tutorial. I hate to ask, but could you post an example of hire the client calls the service ?

        I know for experienced folds that's elementary, but unfortunately I need to see it in order to pull it all together.

        Thanks

        • From PHP I would call this by doing something like:

          file_get_contents("http://api.yourdomain.com/users");

          Hopefully that helps you get started!

  14. Really appreciate you taking the time to write all this out. I know all to well how hard it is to come up with the time to put things up on the net for others, so I just wanted to drop you a line to tell you its appreciated!

    I know there were some critical comments early on on one of the other pages, and validity of those comments aside, I think it's in everyone's best interest to take everything with a grain of salt, research lots of sources, compare, contrast and pick the best approach that works in their individual circumstances, I think this is particularly true in the context of REST :)

    In the end yours and one other are the basis for the API I've just built, just in case it helps anyone else the other is here: http://www.gen-x-design.com/archives/create-a-rest-api-with-php/ and although it's a bit aged at 2009, it was still a good starting point.

    All the best, keep up the good work, and Cheers!

Leave a Reply

Please use [code] and [/code] around any source code you wish to share.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>