To create RESTful APIs, Elefant provides the Restful class. Here's how it works:
<?php
namespace myapp;
use blog\Post;
class API extends \Restful {
/**
* Fetch a blog post by its ID.
*/
public function get_blogpost ($id) {
$post = new Post ($id);
if ($post->error) {
return $this->error ('Not found');
}
return $post->orig ();
}
}
?>
Save this to the file apps/myapp/lib/API.php. This example exposes one method which
fetches a blog post and returns it as a JSON-encoded object.
To expose your API, you simply need to initialize it in a handler, like this:
<?php
$this->restful (new myapp\API);
?>
Save this to apps/myapp/handlers/api.php and your RESTful API will be available at
/myapp/api. To test it out, try visiting the URL /myapp/api/blogpost/1 on your site.
Method names are based on the request method (GET, POST, PUT, and DELETE), and the first
extra parameter in the request URI. For example, if your API is exposed at the address
/myapp/api, making a GET request to /myapp/api/blogpost/123 would call
get_blogpost(123).
If no method matches the requested URI, Elefant will look for a default method for the
request method named in the form ${request_method}__default() (note the double
underscore), for example:
<?php
namespace myapp;
class API extends \Restful {
public function get__default () {
}
}
?>
Also note that the request method must be lowercase in the method name as well.
Failing to find a default method, an error of "Invalid action name" will be returned to the client.
Sometimes you need more customizable routes than the above allows for. In this case,
you can define additional routes in the $custom_routes property, like this:
<?php
namespace myapp;
class API extends \Restful {
public $custom_routes = array (
'GET blogpost/%d/comment/%d' => 'blogpost_comment'
);
public function blogpost_comment ($id, $comment_id) {
// matches /myapp/api/blogpost/123/comment/456
}
}
?>
In this way, you can have as much flexibility as needed in your RESTful routing.
Method parameters are the extra path elements in your request URI, after the method name has been resolved.
You can also access the $_GET and $_POST arrays separately, and PUT data can be
accessed via:
$data = $this->get_put_data ();
Or to automatically decode JSON-formatted PUT data, use:
$data = $this->get_put_data (true);
Similarly, if the POST data is JSON-formatted, you can return it via:
$data = $this->get_raw_post_data (true);
To return a data structure, simply call:
return $data;
If $data is an object with property foo that has the value bar, then Restful will
wrap your output as follows:
{"success": true, "data": {"foo": "bar"}}
You can disable the wrapping and simply return the JSON-encoded data as-is via the $wrap property:
public $wrap = false;
To return an error, call error() like this:
return $this->error ('Error message');
This will send the following JSON structure to the client:
{"success": false, "error": "Error message"}
To add authentication, simply add it to the handler:
<?php
$this->require_login ();
$this->restful (new myapp\API);
?>
The API endpoint above now requires a user to be logged in before they can access it.
Similarly, you can implement HMAC
token-based authentication using the user\Auth\HMAC class:
<?php
$this->require_auth (user\Auth\HMAC::init ($this, $cache, 3600));
$this->restful (new myapp\API);
?>
To create an HMAC token and key for a user accounts, call Api::create_token() like this:
<?php
list ($token, $key) = Api::create_token ($user_id);
?>
To test requests to HMAC-protected endpoints, you can use the ./elefant api/get and ./elefant api/post commands like this:
$ ./elefant api/get http://www.example.com/myapp/api/blogpost/123 $TOKEN $KEY
Replace $TOKEN and $KEY with the results of calling Api::create_token(), which can also be found in the elefant_api table.
As an API grows, there are a couple things to keep in mind:
As an example of the second point, say the following URLs make up part of your API:
GET /myapp/api/blog/123GET /myapp/api/blog/post/456GET /myapp/api/blog/tags/456You could move these to their own class, such as:
<?php // apps/myapp/lib/API/Blog.php
namespace myapp\API;
class Blog extends \Restful {
public function get__default ($id) {
}
public function get_post ($id) {
}
public function get_tags ($id) {
}
}
?>
These could then be made available in a separate handler like this:
<?php // apps/myapp/handlers/api/blog.php
$this->restful (new myapp\API\Blog);
?>
In this way, you can maintain a more organized codebase, and further control your URIs as well.
Next: Users and access control