Simple REST server in PHP – Supports JSON & AMF

After building a couple of RESTful services using the Zend Framework, I decided to create a dead simple REST server that allowed me to skip all the features I didn’t need as well as a tons of classes that came with Zend Framework MVC. There are still useful features to add (XML support for example), but overall I’m quite happy with what I’ve come up with.

My solution, RestServer, is a JSON REST server, so far. It should be trivial to add support for XML or other formats, but there would have to be assumptions on what your object would look like in XML (XML-RPC style, your own custom XML format, etc). First we’ll look at the classes that you write to handle the requests, then we’ll look at how to tie it together in your index.php file.

REST Controllers

The RestServer class assumes you are using URL rewriting and looks at the URL from the request to map to the necessary actions. The map that gets a request from URL to class method is all in the doc-comments of the classes. Here is an example of a class that would handle some user actions:

class TestController
	 * Returns a JSON string object to the browser when hitting the root of the domain
	 * @url GET /
	public function test()
		return "Hello World";

	 * Logs in a user with the given username and password POSTed. Though true
	 * REST doesn't believe in sessions, it is often desirable for an AJAX server.
	 * @url POST /login
	public function login()
		$username = $_POST['username'];
		$password = $_POST['password'];
		// validate input and log the user in

	 * Gets the user by id or current user
	 * @url GET /users/:id
	 * @url GET /users/current
	public function getUser($id = null)
		if ($id) {
			$user = User::load($id); // possible user loading method
		} else {
			$user = $_SESSION['user'];

		return $user; // serializes object into JSON

	 * Saves a user to the database
	 * @url POST /users
	 * @url PUT /users/:id
	public function saveUser($id = null, $data)
		// ... validate $data properties such as $data->username, $data->firstName, etc.
		$data->id = $id;
		$user = User::saveUser($data); // saving the user to the database
		return $user; // returning the updated or newly created user object

Let’s walk through the above TestController class to talk about the features demonstrated. First we’ll look at the test method. You’ll notice there is a new kind of doc-comment tag in the docblock. @url maps a URL to the method below it and is in the form:


In this particular example, when someone does a GET on (assuming is where our service is located) it will print out:

"Hello World"

which is a valid representation of a string in JSON.

Moving on to the next method, login, we see the @url maps any POSTs to to the login method. Getting data from a regular web-type POST is the same as any PHP application, allowing you to use your own validation or other framework in conjunction with this REST server. Sessions can also be kept if desired (credit andrew). Though keeping sessions isn’t true REST style, often all we want a REST server for is to serve up data to our ajax application, and it can be easier to just use sessions than something more RESTful.

Next we have our getUser method (you’ll notice that it doesn’t really matter what I name my methods because our @url directives define what URLs map to the method). You can see a couple of things here. First, we have multiple @url mappings for this method. And second, there is an odd “/:id” in that first URL mapping. The RestServer treats any “:keyword” placeholders as wildcards in the URL and will take that section of the URL and pass it into the parameter with the same name in the method. In this example, when hitting, $id will equal 1234. When hitting, $id will equal null. It doesn’t matter what order your parameters are in, so long as they have the same name as the placeholder (:id and $id, :username and $username). You’ll also want to be sure to make your parameters optional ($id = null) when you have several URL mappings that don’t all require a parameter. Otherwise you’ll have an error thrown telling you that you didn’t pass in a required parameter.

One last thing to note in getUser is that this method simply returns a User object. This gets serialized into JSON (or potentially XML) and printed out for consumption by the application.

Finally we get to saveUser. You see here we have multiple URL mappings again. This time they also have different HTTP methods (POST and PUT) for creating and updating a user. The new thing here is the $data variable. This is a special keyword parameter that will contain the value of whatever was POSTed or PUT to the server. This is different than your regular web POST in that it doesn’t need to only be name-value pairs, but can be as robust as JSON, sending complex objects. For example, the body of a regular web POST, let’s say the login request, might look like this:


but POSTing a new user object for our saveUser method could look like this:

{ "username": "bob", "password": "supersecretpassword", "firstName": "Bob", "lastName": "Smith" }

So you’re able to allow POSTing JSON in addition to regular web style POSTs.

I call these classes that handle the requests Controllers. And they can be completely self-contained with their URL mappings, database configs, etc. so that you could drop them into other RestServer services without any hassle.

REST index.php

In order to get the whole server kicked off, you’ll want to create an index.php file, have your URL rewriting direct requests to it (another topic which you can learn about elsewhere), and create the RestServer and add controller classes to it for handling. RestServer will cache the URL mappings between requests using APC or a file to speed up requests. You won’t have to load every controller file on every request if you use autoload and this cache, only the one needed for the request. The cache only runs in production mode. Here is an example index.php file:

spl_autoload_register(); // don't load our classes unless we use them

$mode = 'debug'; // 'debug' or 'production'
$server = new RestServer($mode);
// $server->refreshCache(); // uncomment momentarily to clear the cache if classes change in production mode

$server->addClass('ProductsController', '/products'); // adds this as a base to all the URLs in this class


That’s it. You can add as many classes as you like. If there are conflicts, classes added later will overwrite duplicate URL mappings that were added earlier. And the second parameter in addClass can be a base URL which will be prepended to URL mappings in the given class, allowing you to be more modular.

You can view the RestServer class, copy it and use it for your own purposes. It is under the MIT license. Features to be added include XML support and HTTP Authentication support. If you make this class better please share your updates with everyone by leaving a comment. I will try and keep this class updated with new features as they are shared. I hope you enjoy!

I changed the title of this post to remove the AMF portion but was asked to cover it, so I will quickly talk about the AMF support. RestServer supports the AMF format in addition to the JSON format. This is a binary format used by Adobe Flash in their remoting services, but because their remoting services are not RESTful, you can’t use classes such as RemoteObject with REST.

In order to use the AMF format with this service, you’ll need to have the Zend Framework in your classpath so that the classes to serialize and deserialize AMF are present (e.g. Zend/Amf/Parse/Amf3/Serializer.php). Then you’re ready so server up AMF. The way you consume AMF in Flash is using URLLoader or URLStream to load the data and ByteArray to convert it into an object. To tell the server you want AMF rather than JSON your URLRequest object will need to add an Accept header of “application/x-amf”. Below I will show you how this could be done.

public function getUser():void
	var loader:URLLoader = new URLLoader();
	loader.dataFormat = URLLoaderDataFormat.BINARY;
	var request:URLRequest = new URLRequest("");
	request.requestHeaders.push(new URLRequestHeader("Accept", "application/x-amf"));
	loader.addEventListener(Event.COMPLETE, onComplete);

private function onComplete(event:Event):void
	var loader:URLLoader = as URLLoader;
	var byteArray:ByteArray = as ByteArray;
	var user:Object = byteArray.readObject();
	// do something with the user

You can even make your PHP objects cast into their actionscript equivalents using the $_explicitType property or getASClassName method in PHP as defined in the documentation and by using registerAlias in Flash.

Good luck and let me know if you end up using it!

Update: I am including an example .htaccess file for anyone who might need it. It will only rewrite requests to files that don’t exist, so you can have images, css, or other PHP files in your webroot and they will still work. Anything that would give a 404 will redirect to your index.php file.

DirectoryIndex index.php
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteRule ^$ index.php [QSA,L]
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteRule ^(.*)$ index.php [QSA,L]

Update 2: I’ve put the code for RestServer up on github at It has some changes and may not work exactly like I’ve described here in this post. Be sure to read the documentation, when I get around to writing it. ;)

115 Responses to “Simple REST server in PHP – Supports JSON & AMF”

  1. vinay Says:

    could any one please explain how ot write this .htaccess file.i have the same error as KRISS.

  2. kirby Says:

    Be careful if you are running this rest server on linux

    Maybe you can’t get rid of these 404 errors, (and I experimented this for hours),
    try this, because it resolved my case :

    on RestServer.php, inside the __construct method, after this line :

    $dir = dirname(str_replace($_SERVER[‘DOCUMENT_ROOT’], ”, $_SERVER[‘SCRIPT_FILENAME’]));

    Add :

    $dir = ($dir[0] == ‘/’ ? substr($dir, 1) : $dir);

    The problem was that on Linux, for $_SERVER[‘DOCUMENT_ROOT’], i got “/var/www” whereas on windows i got “C:/wamp/www/”

    So on Linux the root was miscalculated because there is no ‘/’ at the end of $_SERVER[‘DOCUMENT_ROOT’], so $this->url was miscalculated too, and nothing could match… hope that helped

  3. Andi Says:

    I get the same problem here too…
    I can define a @url GET /foo/bar and that works well.
    However the @url GET /foo/:id always comes back with a JSON formatted 404

    I believe it is also running on a linux server.

    Any ideas on how to fix this ?

  4. Kelly Jones Says:

    Awesome post. Here’s a tool that lets your build your online database without programming. There is no need to hand code PHP. Cut your development time by 90%

  5. ketan Says:

    RestServer is nice one…
    but i can’t bind my controller to index.php or my method with specific url…
    for example:
    i try to put
    RestServer.php and index.php and sample.htaccess at root
    and my controller at root/demosite/rest/

    change @url /product/ for test method

    in index.php i add
    $server->addClass(‘DemoController’, ‘/demosite/rest’);

    and i call localhost/product/ ==== it gives
    Object not found! that apache is not found error…

    what is my mistake? can anyone help me…..?

  6. Andrew Says:

    Hi, trying to use the RestServer in a sub-directory ( – the rewrite in my .htaccess is set up correctly (phpinfo in the index.php file prints correctly given any url in that directory) – but as soon as I try to use the RestServer with a test controller, I get a json formatted 404 no matter what.


    require “RestServer.php”;
    require “AccountsController.php”;


    $server = new RestServer($mode);
    $server->root = ‘/rest/’;



    class AccountsController
    * Returns all accounts
    * @url GET /
    public function getAllAccounts()
    return “Hello World”;

    What am I missing?

  7. Cristián Says:

    First of all, Great Post Jacob

    I’ve been working around your RestServer this week end and I’ve found it very helpfull as an introductory class to REST

    I have had then same problem that Andrew (last post) , I didn’t figure out until today that the comments where been scanned for parameters.

    Andrew, you have to modify the comments in the controller, the following line should be modified:

    * @url GET /

    * @url GET /rest

    So you will be able to run this url and get your results :)

  8. thessoro Says:


    It seems the author has changed the format of the placeholder. Reading the code is clear it looks for a dollar sign:

    So, instead of:

    * @url GET /users/:id


    * @url GET /users/$id

    Hope it helps.

  9. A REST service for Meaningful Healthcare | I'm Wesley Brand and I work as a freelance software developer in Birmingham, AL. Says:

    […] If you plan to write a full end-to-end application in PHP, to include a REST service, I’d recommend using the Zend Framework.  That buys you an MVC setup, and REST formatting/routing without much effort on your part.  However, if you just need a quick and dirty service, have a look at this one page php REST solution (in depth explanation here). […]

  10. James Says:

    Firstly thanks for this :)

    Took a while to get this going without docs hehe. But for other guys reference, to get this going in a subdirectory (api in this case), the index file needs a change. After this line:

    $server = new RestServer($mode);

    Add this link:

    $server->root = ‘api’;

    Note NO slashes. There was an example above with slashes but this broke it. This makes requests work at

  11. James Says:

    Actually, with my previous comment, no slashes makes it work for the directory root path endpoint (‘/’), but breaks all others. I fixed this with a trailing slash only

    $server->root = ‘api/’;

  12. Luis Says:

    Jacod, thanks for your work.
    I am trying to use your restful server but I could not make it works.
    My server is configured with Apache 2.2 and php 5.2.11 on windows xp, url rewriting is configures on xxx and it is working fine.
    It doesn’t matter if the url is xxx/login or xxx/ or xxx/user I always get:

    Warning: Call-time pass-by-reference has been deprecated in C:\Apache2.2\htdocs\www\tomaestado\RestServer.php on line 365
    “error”: {
    “code”: 404,
    “message”: “Not Found”

  13. Stephan Says:

    I think you have to replace

    if(preg_match(‘/\.(\w+)$/i’, $_SERVER[‘REQUEST_URI’], &$matches)) {


    if(preg_match(‘/\.(\w+)$/i’, $_SERVER[‘REQUEST_URI’], $matches)) {

    to make it work.

  14. Klaas Says:

    Good tutorial, I am about to implement my first REST webservice for an Android app. I choose REST above SOAP for several reasons but good tutorials are scarce.
    I have this tutorial working right now on my Linux/Apache system and can surely use it for my project!

    One question, my project involves logging in to a MySQL database. In an normal Web application, a HTTP session is used for the user keeping logged in (until he explicitly logs off).
    But (REST) Webservices are stateless, so how to implement this? Am I forced to send the credentials in each subsequent request? I would not like to send the password, or even the MD5 hash, as a GET request parameter while pulling user information.

    Probably I am not the first to do this but I have not found a satisfying solution, so hints are more than welcome.

  15. Jeremy Says:

    With REST you have to send the credentials up with each request if you are going to keep a true to REST, but you can cache state as a session if you are opposed to sending up credentials all the time.