Hello, this is Jan Lehnardt and you're visiting my blog. Thanks for stopping by.
plok — It reads like a blog, but it sounds harder!
↑ Archives
I keep coming up with ideas for projects that would benefit from a REST architecture. I am now laying out some foundations to build those projects on top. One of the projects involves very little code and I do not want to use a full fledged framework there. Another one is using Code Igniter which does not handle REST by default (as far as I can tell). So I wrote a minimal REST application controller that easy to integrate and very flexible.
It provides a base class that you would extend your actual controller from and you implement only the request types your are going to need.
<?php
class My_Controller extends REST_Controller
{
function _get()
{
// handle get request
}
function _post()
{
// handle post request
}
function _delete()
{
// handle delete request
}
// do not care for put
}
?>
Quite easy so far. The REST_Controller
defines two convenience methods.
_respond($body, $headers = array())
lets you send a HTTP response custom headers.
_respondError($error)
allows you to send a HTTP status message back to the client. It will detect the request’s HTTP version and will create an appropriate reply.
The only thing you need to do is call the _dispatch()
method when you want to start processing a request. I do happen to have an index()
method for that:
<?php
class My_Controller extends REST_Controller
{
function index()
{
$this->_dispatch();
}
function _get()
{
// handle get request
}
...
}
?>
The REST_Controller
is just a very bare-bones class that doesn’t handle input filtering or making request data available. That is all up to you.
In Code Igniter My_Controller
looks like this:
<?php
// my parent class definition class lives next door
include dirname(__FILE__)."/rest_controller.php";
class MY_Controller extends REST_Controller
{
function My_Controller()
{
// as per Code Igniter setup guide
parent::REST_Controller();
}
function index($id)
{
// let's have others do the work
$this->_dispatch($id);
}
function _get($id)
{
$this->load->model("json");
$resource = $this->json->fetch_resource($id);
$this->_respond($resource, $headers = array("Content-Type" => "application/json"));
}
function _delete($id)
{
$this->load->model("json");
$resource = $this->json->fetch_resource($id);
if(!$id || !$resource) {
$this->_respondError("404 Not Found");
}
$this->json->delete($id);
}
...
}
?>
Finally, here is the code. This is public domain free. Please report bugs or enhancements. Contact information can be found on my about page.
<?php
/*
Public domain PHP REST Controller Base Class.
Originally by Jan Lehnardt <jan@php.net>
Usage instructions: http://jan.prima.de/~jan/plok/archives/115-REST-Controller-for-PHP-Applications.html
*/
class REST_Controller extends Controller
{
var $method = 'get'; //default
function REST_Controller()
{
parent::Controller();
}
function _dispatch($id = false)
{
if(isset($_SERVER['REQUEST_METHOD'])) {
$this->method = strToLower($_SERVER["REQUEST_METHOD"]);
}
$dispatch_method = "_{$this->method}";
if(!is_callable(array($this, $dispatch_method))) {
$this->_respondError("501 Not Implemented");
return false;
}
$this->{$dispatch_method}($id);
}
function _getProtocol()
{
$protocol = "HTTP/1.1";
if(isset($_SERVER['SERVER_PROTOCOL'])) {
$protocol = $_SERVER['SERVER_PROTOCOL'];
}
return $protocol;
}
function _respondError($error)
{
$protocol = $this->_getProtocol();
header("$protocol $error");
}
function _respond($body, $headers = array())
{
foreach($headers AS $header => $value) {
header("{$header}: {$value}");
}
echo $body;
}
}
Nice. Does this require PHP 5 or work with PHP 4?
I’m working on my own REST framework for PHP at the moment, but your REST framework is solving a different aspect of the problem domain than I am tackling. Once I get it to a point I can package it up I’d like to send it along to you so we can see if we can (hopefully) make sure our two approaches are compatible.
Heya Mike, this should run on 4 and 5. I’m very interested in what you come up with, too.
Cheers,
Jan
Hi Jan:
Though I’ve uploaded no code yet (and not sure when I’ll be able to), I started a project a Google Code and you can read more about it here:
http://code.google.com/p/restian
Contact me via gmail (mikeschinkel) if you want to collaborate in the mean time and/or see what I have.
BTW, Drupal 5.x will be the database schema targeted for examples.
PHP4 is dead so f* it :]
Tell that to the Drupal community.
this is brilliant, thankyou so much! Currently about to implement an app using EXTJS with CI/REST on the backend, wasnt liking the idea of munging something together myself.
oh, two things with CI.
you can put your custom controller in the libraries folder. eg ‘My_controller.php’
there is a class method to handle headers.
$this->output->set_header();
thnx again,
I have a similar, but slightly different approach.
http://blog.medryx.org/2008/10/03/codeigniter-rest/
Great Post!
This is a great class. Its proving very useful to me.
One change I have made is to support overloaded post for clients that cant make put/delete requests. Let me know what you think.
I wish you cared for PUT.
Hi Nick,
you can just implement
function _put() {}
and be ready.Cheers
Jan