14 KiB
module: api
version: 3.0
requires:
- http: 1.0
- error: 2.0
Plan
I. Overview
1 Introduction & features
The api
package (v3.0) allows you to easily create and manage an API for your applications. It can be used as an HTTP API (REST, or other kind), and you can use it as an internal core for your system.
The aim of this package is to make your life easier working with APIs and internal delegation. The only things you have to do is to implement your processes and write the configuration, the package will do the rest.
Things you have to do :
- write the configuration file (cf. configuration)
- implement your authentication system (cf. AuthSystem)
- implement your processes (obviously)
Things you don't have to do :
- input type check (cf. Checker)
- multiple permission management
- optional or required input
- multipart and file input
- URL input
- before and after scripts
- and a lot more ...
2 Basic knowledge
The api is made of paths that binds to a php class, each path
can manage multiple HTTP METHODS each will correspond to a method of the bound class.
So each of your functionalities must have a dedicated HTTP method
and a corresponding a path
.
Example:
- the module
article
contains methods:GET
to get article dataPOST
to post a new articlePUT
to edit an existing articleDELETE
to delete an exisint article
Note that each method must be a valid HTTP METHOD.
II. Usage
1 REST API
In order for the API to catch URI, you must use a router. It will allow the API to get the requested URI.
Important: You must use $_SERVER['REQUEST_URI']
because it will (instead of other methods) keep some useful format.
<?php
require_once '../autoloader.php';
/* (1) Create the request */
$request = \api\core\Loader::remote($_SERVER['REQUEST_URI']);
/* (2) Process the response (execute your implementation) */
$response = $request->dispatch();
/* (3) Serialize the response */
echo $response->serialize();
Note: Request
or Response
errors will propagate to serialize() method into the JSON output.
2 Internal use
You can also use the API from within your code (not from URI).
<?php
require_once '../autoloader.php';
/* (1) Emulate API data */
$emu_uri = '/some/target/uri';
$emu_params = [ 'p1' => 'param1', 'URL0' => 'uri param 0' ];
$emu_method = 'DELETE';
/* (2) Create the request */
$request = new \api\core\Request($emu_uri, $emu_params, $emu_method);
/* (3) Process the response (execute your implementation) */
$response = $request->dispatch();
/* (4) [OPTIONAL] Check for errors */
if( $response->error->get() != \error\core\Err::Success )
die('encountered error: '.$response->error->explicit());
/* (5) Fetch response data */
$all_response_fields = $response->getAll();
$specific_response_field = $response->get('specific_field_name');
III. Configuration
The documentation consists of a chain of urls each one can contain several HTTP method specifications. Note that each url can be chained apart the method specifications.
1 - configuration format
The configuration is a set of uri
paths that can contain up to 4 method types: POST, DELETE, PUT, GET or uri
subpaths.
For instance the 4 methods directly inside uri1
will be triggered when the calling URI is /uri1
, the ones directly inside uri2
will be triggered when calling /uri1/uri2
and so on..
You can also set full paths if you don't need transitional methods, for instance the path uri5/uri6/uri7
will be triggered by the url /uri5/uri6/uri7
.
Example: The example file loaded with the default configuration can be found here.
{
"uri1" : {
"GET": method.definition,
"POST": method.definition,
"PUT": method.definition,
"DELETE": method.definition,
"uri2": {
"GET": method.definition,
"POST": method.definition,
"PUT": method.definition,
"DELETE": method.definition,
"uri3": {}
}
},
"uri5/uri6/uri7": {
"GET": method.definition,
"POST": method.definition,
"PUT": method.definition,
"DELETE": method.definition
}
}
Note: It is possible to trigger the root uri (/
), so you can set methods directly at the root of the JSON file.
2 - method.definition
{
"des": method.description,
"per": method.permissions,
"par": method.parameters,
"out": method.output_format,
"opt": method.options
}
2.1 - method.description
The description field must be a string containing the human-readable description of what the method does.
2.2 - method.permissions
The permissions field must be an array. You can manage OR and AND permission combinations.
- OR is applied between each 0-depth array
- AND is applied between each 1-depth array
For instance the following permission [ ["a","b"], ["c"] ]
means you need the permissions a and b combined or only the permission c.
2.3 - method.parameters
The parameters field must be an object containing each required or optional parameter needed for the implementation.
"parameter.name": {
"des": parameter.description,
"typ": parameter.checker_type,
"opt": parameter.is_optional,
"ren": parameter.rename,
"def": parameter.default_value,
}
parameter.name
The name field must be a string containing variable name that will be asked for the caller.
Note that you can set any string for body parameters, but GET parameters must be named URL#
, where #
is the index within the URI, beginning with 0.
parameter.description
The description field must be a string containing the human-readable description of what the parameter is or must be.
parameter.checker_type
The checker_type field must be a string corresponding to a \api\core\Checker
type that will be checked before calling the implementation.
parameter.is_optional
The is_optional field must be a boolean set to true
if the parameter can be ommited. By default, the field is set to false
so each parameter is required.
parameter.rename
The rename field must be a string corresponding to the variable name given to the implementation. It is useful for GET parameters because they need to be called URL#
, where #
is the position in the URI. (cf. paramter.name))
If ommited, by default, parameter.name
will be used.
parameter.default_value
The default_value field must be of any type according to the checker_type field, it will be used only for optional parameters when ommited by the caller
By default, each optional parameter will exist and will be set to null
to the implementation.
2.4 - method.output_format
The output_format field must have the same format as method.parameters
but will only be used to generate a documentation or to tell other developers what the method returns if no error occurs.
2.5 - method.options
The options field must be an object containing the available options.
The only option available for now is:
"download": true
Your implementation must return 2 fields:
body
a string containing the file bodyheaders
an array containing as an associative array file headers
If the API is called with HTTP directly, it will not print the json response (only on error), but will instead directly return the created file.
AJAX: If called with ajax, you must give the header HTTP_X_REQUESTED_WITH
set to XMLHttpRequest
. In that case only, it will return a normal JSON response with the field link
containing the link to call for downloading the created file. You must take care of deleting not used files - there is no such mechanism.
IV. Implementation
1 - permissions : AuthSystem
In order to implement your Authentification System you have to implement the interface AuthSystem
located in /build/api/core/AuthSystem
.
You must register your custom authentification system before each api call.
For instance, lets suppose your implementation is called myAuthSystem
.
\api\core\Request::setAuthSystem(new myAuthSystem);
Note: By default the API will try to find \api\core\AuthSystemDefault
.
2 - core implementation
classes
Each module's implementation is represented as a file so as a class located in /build/api/module/
. In order for the autoloader to work, you must name the file the same name as the class.
Also the namespace must match, it corresponds to the path (starting at /api
).
For instance if you have in your configuration a path called /uri1/uri2/uri3
, you will create the file /build/api/module/uri1/uri2/uri3.php
.
Specific: For the root (/
) path, it will trigger the class \api\module\root
, so you have to create the file /build/api/module/root.php
. (cf. configuration format)
methods
Each method is represented as a class' method with the same name as the associated HTTP method.
method arguments
Arguments are passed to the method as a single argument which an associative array according to the configuration.
Notes:
- Optional parameters if not given are set to their default value (cf. parameter.optional, parameter.default_value)
- parameters of type
FILE
are given by reference but the use is the same as normal parameters (cf. complex types) - URI parameters are called
URL0
,URL1
and so on according to their order if norename
set in the configuration. (cf: parameter.name, parameter.rename)
return statement
You MUST return an associative array containing at least the field error
containing an instance of /api/core/Error
, then you can add whatever you want to return in the array.
The list of available errors are set in the class \error\core\Err
.
If you don't return the 'error' field, by default to
[ 'error' => new \error\core\Error(\error\core\Err::Success) ]
Before and After scripts
Each time a method is called, the api creates an instance from the class, and after the execution, the class is destroyed. So you can implement the methods __construct
and __destruct
to add before and after scripts.
example
For instance here, we manage the call GET /article/2
where 2
is the argument URL0
renamed to id_article
.
use \error\core\Error;
use \error\core\Err;
public function GET($parameters){
/* (1) Set parameters available in the method scope */
extract($parameters);
/* (2) You can now use the variable `id_article` */
$article_data = get_article_from_database($id_article);
/* (3) Return error if error in process */
if( has_error($article_data) )
return [ 'error' => new Error(Err::ModuleError, 'Cannot fetch data from db.') ];
/* (4) Return data on success */
return [
'id_article' => $id_article,
'article' => $article_data
];
}
Note: Functions get_article_from_database()
and has_error()
do not exist, it was in order for all to understand the example
V. Type Checker
\api\core\Checker
checks the input values according to the type given in the configuration.
The default types below are available in the default package.
To add a new type, just open the file /build/api/Checker.php
and add an entry in the switch
statement.
1 - Default types
Type | Example | Description |
---|---|---|
mixed |
[9,"a"] , "a" |
Any content (can be simple or complex) |
id |
10 , "23" |
Positive integer number between 0 and 2147483647 |
numeric |
-10.2 , "23" |
Any number, null and the string "null" |
text |
"Hello!" |
String that can be of any length (even empty) |
hash |
"4612473aa81f93a878..." |
String with a length of 128, containing only hexadecimal characters |
alphanumeric |
"abc029.-sd9" |
String containing only alphanumeric, _, -, and . characters |
letters |
"abc -sd" |
String containing only letters, -, and space characters |
mail |
"a.b@c.def" |
Valid email address |
array |
[1, 3] |
Non-empty array |
object |
works only within php | Non-empty object |
boolean |
true , false |
Boolean |
varchar(a,b) |
"Hello!" |
String with a length between a and b (included) |
varchar(a,b,c) |
"abc" |
String with a length between a and b (included) and matching the c type |
2 - Complex types
Type | Sub-Type | Description |
---|---|---|
array<a> |
a |
Array containing only entries matching the type a |
FILE |
_a raw file send in multipart/form-data |
A raw file sent by multipart/form-data |
Note: It is possible to chain
array
type as many as needed.
Ex.: array<array<id>>
- Will only match an array containing arrays that only contains id
entries.