REST is one of the most common ways to consume a Web API these days. As AmfPHP is all about exposing services, it should support for RESTful API calls. This would be convenient for advanced developers and a good way to get started for newcomers to AmfPHP.
How would a RESTful API be mapped to an AmfPHP service?
Calls
Each service is considered a resource, and the HTTP methods(GET, PUT, POST, DELETE, etc.) is a service method. These methods are converted to lower case to respect usual coding conventions. What is actually done by the methods and which methods are implemented is left to the developer.
So for a example:
parameters
An array of parameters for the call is constructed from both the URL and the request body.
Suppose you want to update a user record in your database.
- The first parameter is $id, an int value giving the id of the record. Example value: 123
- The second parameter is an associative array with the new record values. Example value: {"firstName": "John", "surname":"Doe"}
The following calls are equivalent:
Error Codes
The developer can return an error code like with any other PHP code. For example:
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
Would return a 'HTTP 500 Internal Server Error' response.
Example syntax for a service
The following code is an example implementation for a 'User' Amfphp service implementing HTTP GET, PUT, POST, and DELETE calls.
class User{
/**
* mapped from HTTP GET to http://yourserver.com/amfphp/User/
* if URL is http://yourserver.com/amfphp/User/123 then $id shall be set to 123
* load all users if $id is null
* otherwise load user with an id equal to $id
*/
function get($id = null){
//TODO
}
/**
* mapped from HTTP POST to http://yourserver.com/amfphp/User/
* Creates a User
* An array is expected, so that multiple parameters are supported.
* Here is an example of valid data:
* [ {"firstName": "John", "surname":"Doe"} ]
*/
function post($user){
//TODO
}
/**
* mapped from HTTP PUT to http://yourserver.com/amfphp/User
* updates a User
* An array is expected, so that multiple parameters are supported.
* Here is an example of valid data:
* [ 123, {"firstName": "John", "surname":"Doe"} ]
*/
function put($id, $user){
//TODO
}
/**
* mapped from HTTP DELETE to http://yourserver.com/amfphp/User/123
* deletes a user. If the user doesn't exist, return HTTP error code.
*/
function delete($id){
//TODO
header($_SERVER['SERVER_PROTOCOL'] . ' User not found', true, 500);
}
}
Data Formats
As elsewhere the content type http header is used to determine the incoming format. These are AMF or JSON by default. Amfphp shall return the data using the same format as the incoming data.
Exceptions
Exceptions shall be caught and transformed into a 'HTTP 500 Internal Server Error exception.' , unless the error code is set in the exception. In this case the passed error code shall be used as the HTTP return code.
Suppose you put the following code in your service method:
throw new Exception('Forbidden', 403);
This shall result in an HTTP 403 response.
Implementation
A .htaccess file would handle the necessary URL rewriting, and a plugin similar to the AmfphpJson plugin would handle the consequent mapping.
Here is an example .htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?uri=%{REQUEST_URI} [QSA,L]
It shall need to be next to the entry point file(Amfphp/index.php) rather than in the plugin folder, which would be more elegant but is unfortunately impossible.
The plugin called 'AmfphpRest' shall use the existing serializer/deserializer mechanisms. It shall however need to use the FILTER_DESERIALIZED_REQUEST_HANDLER and FILTER_EXCEPTION_HANDLER filters to handle the request mapping and return codes.