I got the badge Popular question
for this question, so I feel it's about time I elaborate how I did my REST-soluton.
I did look at both Laravel, Sympfony2 and Codeigniter for this REST Api. They all had some elements I liked and some I disliked. My main concern was how to do the authentication because I had a rather complex algorithm where my users can log in with the apps' access_token or access_tokens served by Google or Facebook. I also perfer being in complete control of my framework and the frameworks mentioned above had some elements I felt unnecessary and hard to work around. Because of this I decided to make my own REST-solution. It is not as hard as one might expect, and it can be done in several ways. The way I did it requires some knowledge about OOP-programming.
Okey, so starting out a made a base-class called REST. This class takes care of all the stuff that is in common for every call. Like authentication, parsing the requested path to a method, checking access_token
etc.
One of the central things in this class is the requested path and how this is translated into a method. I did this inspired by Laravel. I have a array with key
=> value
where the key is the url it should match and the value is the actual method to call. I also included the way Lavavel parses variables in the URL like so:
'/user/(:id)' => 'user_id',
This would match any /user/[number]. It also checks what type of request this is, so if this is a simple get-method it would try to call get_user_id
. Anything parsed with (:id)
would be used as an argument when calling that method (so it is actually calling get_user_id($id)
).
After authentication the actual method-call gets evaluated. I did not want all the methods (like get_user_id
) in the REST-class itself, so I broke these up in different controllers that extends the REST-class. This is done by looking at the url being requested. If it is /user/(:id)
the script will check if there is a controller named userController.php
. If it exists, check if the method we are going to call exists. If it does, check if the number of arguments matches what we have. If everything is good, execute the method, if not return an error message. Structure and error-messages are very important when making a API like this.
In the different controllers I call the constructor for the REST-class to get the authentication, parsing of the url etc resolved. The tricky part here is that I did not want to do:
$controller = new MyController();
$controller->printResponse();
In the bottom of every controller. So I made a little hack and a script called run.php
that does this dynamically for every controller-class. Before I include the run.php
I save the path for the controller by simply doing $path = explode('/',__FILE__);
. This is used in the run-script. The run-script looks like this:
// Splitting the file-name, removing the extension
$name = explode('.',$path[count($path)-1]);
// Uppercasing the first letter to be nice and OOP-ish
$classToCall = ucfirst($name[0]);
// Creating a new instance
$controller = new $classToCall();
// Logging
$controller->doLog();
// Printing the final response
$controller->printResponse();
I found this to be a perfect solution for how I wanted to build my API. I can easily add new methods by supplying it in the array that parses urls to methods, and I can add new methods in the nicely broken apart controllers for maximum cleanness.
Some people might think this is too much work, but it actually took me only a few hours to get it up and running. I'd also call this highly dynamic as I can just add new controllers and the system will recognize them if they are valid url-patterns.
A few friendly advices.
If you decide to go with something resembling this solution, these can be some good tips. In each controller, do something like this:
public function __construct() {
// Loading the class-name, setting it in the REST-class, so we can check if it holds the method being called
$this->className = get_class($this);
// Calling RESTs constructor
parent::__construct();
}
We will need to store what class we are currently working from. This would be UserController
or something like that.
In the REST-class I can then use this variable to check if the actual method getting called does exist in this controller. I've done that this way:
// Checking if the method exists
if (method_exists($this->className,$method_name)) {
// Check to see if we have the required number of arguments represented
$ReflectionClass = new ReflectionClass($this->className);
if ($ReflectionClass->getMethod($method_name)->getNumberOfParameters() == count($this->methodUrl['args'])) {
$this->response['response'] = call_user_func_array(array($this, $method_name), $this->methodUrl['args']);
I hope that can get you all going.
Happy codin'