2017-12-10 20:45:08 +00:00
```yaml
module: api
version: 3.0
requires:
- http: 1.0
- error: 2.0
```
2017-12-12 22:27:03 +00:00
Plan
2017-12-10 20:45:08 +00:00
====
[**I.** Overview ](#i-overview )
- [**1** Introduction & features ](#1-introduction--features )
- [**2** Basic knowledge ](#2-basic-knowledge )
[**II.** Usage ](#ii-usage )
2017-12-12 22:57:19 +00:00
- [**1** REST API ](#1-rest-api )
- [**2** Internal use (PHP) ](#2-internal-use )
2017-12-10 20:45:08 +00:00
[**III.** Configuration ](#iii-configuration )
2017-12-12 22:57:19 +00:00
- [**1** Configuration format ](#1---configuration-format )
- [**2** Method definition ](#2---methoddefinition )
- [**2.1** Method description ](#21---methoddescription )
- [**2.2** Method permissions ](#22---methodpermissions )
- [**2.3** Method parameters ](#23---methodparameters )
- [parameter name ](#parametername )
- [parameter description ](#parameterdescription )
- [parameter type ](#parameterchecker_type )
- [parameter optional vs. required ](#parameteris_optional )
- [parameter rename ](#parameterrename )
- [parameter default value ](#parameterdefault_value )
- [**2.4** Method output format ](#24---methodoutput_format )
- [**2.5** Method options ](#25---methodoptions )
2017-12-10 20:45:08 +00:00
[**IV.** Implementation ](#iv-implementation )
2017-12-12 23:00:30 +00:00
- [**1** Permissions : AuthSystem ](#1---permissions--authsystem )
- [**2** Core implementation ](#2---core-implementation )
2017-12-12 23:01:13 +00:00
- [Classes ](#classes )
- [Methods ](#methods )
- [Method arguments ](#Method-arguments )
2017-12-12 23:00:30 +00:00
- [Return statement ](#return-statement )
- [Example ](#example )
2017-12-10 20:45:08 +00:00
2017-12-12 20:32:20 +00:00
# **I.** Overview
## **1** Introduction & features
2017-12-10 20:45:08 +00:00
2017-12-12 20:32:20 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 20:32:20 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 20:32:20 +00:00
Things you **have** to do :
2017-12-12 22:27:03 +00:00
- write the configuration file (cf. [configuration ](#iii-configuration ))
2017-12-10 20:45:08 +00:00
- implement your authentication system (cf. [AuthSystem ](#1-permissions--authsystem ))
2017-12-12 22:27:03 +00:00
- implement your processes (obviously)
2017-12-10 20:45:08 +00:00
Things you **don't have** to do :
- input type check (cf. [Checker ](#4-checker ))
2017-12-12 20:32:20 +00:00
- multiple permission management
- optional or required input
- multipart and file input
- URL input
2017-12-10 20:45:08 +00:00
- before and after scripts
2017-12-12 20:32:20 +00:00
- and a lot more ...
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **2** Basic knowledge
2017-12-10 20:45:08 +00:00
2017-12-12 20:32:20 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
So each of your functionalities must have a dedicated `HTTP method` and a corresponding a `path` .
2017-12-10 20:45:08 +00:00
< u > Example:< / u >
* the module `article` contains methods:
2017-12-12 20:32:20 +00:00
* `GET` to get article data
* `POST` to post a new article
* `PUT` to edit an existing article
* `DELETE` to delete an exisint article
2017-12-10 20:45:08 +00:00
2017-12-12 20:32:20 +00:00
*Note that each method must be a valid HTTP METHOD*.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
# **II.** Usage
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **1** REST API
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
In order for the API to catch **URI** , you must use a router. It will allow the API to get the requested URI.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
*Important*: You must use `$_SERVER['REQUEST_URI']` because it will (instead of other methods) keep some useful format.
2017-12-10 20:45:08 +00:00
```php
2017-12-12 22:27:03 +00:00
< ?php
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
require_once '../autoloader.php';
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
/* (1) Create the request */
$request = \api\core\Loader::remote($_SERVER['REQUEST_URI']);
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
/* (2) Process the response (execute your implementation) */
$response = $request->dispatch();
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
/* (3) Serialize the response */
echo $response->serialize();
```
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
*Note:* `Request` or `Response` errors will propagate to *serialize()* method into the JSON output.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **2** Internal use
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
You can also use the API from within your code (not from URI).
2017-12-10 20:45:08 +00:00
```php
2017-12-12 22:27:03 +00:00
< ?php
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
require_once '../autoloader.php';
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
/* (1) Emulate API data */
$emu_uri = '/some/target/uri';
$emu_params = [ 'p1' => 'param1', 'URL0' => 'uri param 0' ];
$emu_method = 'DELETE';
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
/* (2) Create the request */
$request = new \api\core\Request($emu_uri, $emu_params, $emu_method);
/* (3) Process the response (execute your implementation) */
2017-12-10 20:45:08 +00:00
$response = $request->dispatch();
2017-12-12 22:27:03 +00:00
/* (4) [OPTIONAL] Check for errors */
if( $response->error->get() != \error\core\Err::Success )
die('encountered error: '.$response->error->explicit());
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
/* (5) Fetch response data */
$all_response_fields = $response->getAll();
$specific_response_field = $response->get('specific_field_name');
2017-12-10 20:45:08 +00:00
```
2017-12-12 22:27:03 +00:00
# **III.** Configuration
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **1** - configuration format
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
The configuration is a set of `uri` paths that can contain up to 4 method types: **POST** , **DELETE** , **PUT** , **GET** or `uri` subpaths.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
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..
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
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` .
2017-12-10 20:45:08 +00:00
```json
2017-12-12 22:27:03 +00:00
{
"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": {}
}
2017-12-12 22:51:47 +00:00
},
"uri5/uri6/uri7": {
"GET": method.definition,
"POST": method.definition,
"PUT": method.definition,
"DELETE": method.definition
2017-12-12 22:27:03 +00:00
}
2017-12-10 20:45:08 +00:00
}
```
2017-12-12 20:32:20 +00:00
2017-12-12 22:27:03 +00:00
**Note**: It is possible to trigger the *root uri* (`/`), so you can set methods directly at the root of the JSON file.
2017-12-12 20:32:20 +00:00
2017-12-12 22:27:03 +00:00
## **2** - method.definition
2017-12-10 20:45:08 +00:00
```json
2017-12-12 22:27:03 +00:00
{
"des": method.description,
"per": method.permissions,
"par": method.parameters,
"out": method.output_format,
"opt": method.options
2017-12-10 20:45:08 +00:00
}
```
2017-12-12 22:27:03 +00:00
## **2.1** - method.description
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
The *description* field must be a **string** containing the human-readable description of what the method does.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **2.2** - method.permissions
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
The *permissions* field must be an array. You can manage **OR** and **AND** permission combinations.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
- **OR** is applied between each **0-depth** array
- **AND** is applied between each **1-depth** array
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
For instance the following permission `[ ["a","b"], ["c"] ]` means you need the permissions **a** and **b** combined or only the permission **c** .
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **2.3** - method.parameters
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
The *parameters* field must be an object containing each required or optional parameter needed for the implementation.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
```json
"parameter.name": {
"des": parameter.description,
"typ": parameter.checker_type,
"opt": parameter.is_optional,
"ren": parameter.rename,
"def": parameter.default_value,
2017-12-10 20:45:08 +00:00
}
```
2017-12-12 22:27:03 +00:00
#### 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
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
#### parameter.rename
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
If ommited, by default, `parameter.name` will be used.
#### parameter.default_value
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
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
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
By default, each optional parameter will exist and will be set to `null` to the implementation.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **2.4** - method.output_format
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **2.5** - method.options
The *options* field must be an **object** containing the available options.
The only option available for now is:
```json
"download": true
```
Your implementation must return 2 fields:
- `body` a string containing the file body
- `headers` 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
2017-12-10 20:45:08 +00:00
In order to implement your _Authentification System_ you have to implement the **interface** `AuthSystem` located in `/build/api/core/AuthSystem` .
2017-12-12 22:27:03 +00:00
You must register your custom authentification system before each api call.
For instance, lets suppose your implementation is called `myAuthSystem` .
2017-12-10 20:45:08 +00:00
```php
2017-12-12 22:27:03 +00:00
\api\core\Request::setAuthSystem(new myAuthSystem);
2017-12-10 20:45:08 +00:00
```
2017-12-12 22:27:03 +00:00
**Note**: By default the API will try to find `\api\core\AuthSystemDefault` .
2017-12-10 20:45:08 +00:00
2017-12-12 22:27:03 +00:00
## **2** - core implementation
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
### classes
2017-12-10 20:45:08 +00:00
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** .
2017-12-12 22:51:47 +00:00
Also the **namespace** must match, it corresponds to the path (starting at `/api` ).
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
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` .
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
### methods
Each method is represented as a class' method with the same name as the associated *HTTP method* .
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
### method arguments
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
Arguments are passed to the method as a single argument which an associative array according to the configuration.
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
_Notes_:
* Optional parameters if not given are set to the `default` value given in the configuration (`null` if)
* parameters of type `FILE` are given by reference but the use is the same as normal parameters
* URI parameters are called `URL0` , `URL1` and so on according to their order if no `rename` set in the configuration.
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
### return statement
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
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.
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
The list of available errors are set in the class `\error\core\Err` .
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
If you don't return the 'error' field, by default to
2017-12-10 20:45:08 +00:00
```php
2017-12-12 22:51:47 +00:00
[ 'error' => new \error\core\Error(\error\core\Err::Success) ]
2017-12-10 20:45:08 +00:00
```
2017-12-12 22:51:47 +00:00
### example
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
For instance here, we manage the call `GET /article/2` where `2` is the argument `URL0` renamed to `id_article` .
2017-12-10 20:45:08 +00:00
```php
2017-12-12 22:51:47 +00:00
use \error\core\Error;
use \error\core\Err;
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
public function GET($parameters){
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
/* (1) Set parameters available in the method scope */
extract($parameters);
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
/* (2) You can now use the variable `id_article` */
$article_data = get_article_from_database($id_article);
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
/* (3) Return error if error in process */
if( has_error($article_data) )
return [ 'error' => new Error(Err::ModuleError, 'Cannot fetch data from db.') ];
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
/* (4) Return data on success */
return [
'id_article' => $id_article,
'article' => $article_data
];
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
}
2017-12-10 20:45:08 +00:00
```
2017-12-12 22:51:47 +00:00
*Note*: Functions `get_article_from_database()` and `has_error()` do not exist, it was in order for all to understand the example
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
## **3** Type Checker
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
`\api\core\Checker` checks the input values according to the type given in the configuration.
2017-12-10 20:45:08 +00:00
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.
### 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)|
2017-12-12 22:51:47 +00:00
|`hash`|`"4612473aa81f93a878..."`|String with a length of 128, containing only hexadecimal characters|
|`alphanumeric`|`"abc029.-sd9"`|String containing only alphanumeric, _, _ -_, and _._ characters|
2017-12-10 20:45:08 +00:00
|`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|
### Complex type : chainable array
|Type|Sub-Type|Description|
|---|---|---|
|`array< a > `|`a`|Array containing only entries matching the type `a` |
> **Note:** It is possible to chain `array` type as many as needed.
2017-12-12 22:51:47 +00:00
**Ex.:** `array<array<id>>` - Will only match an array containing arrays that only contains `id` entries.
2017-12-10 20:45:08 +00:00
2017-12-12 22:51:47 +00:00
## **4** Advanced
2017-12-10 20:45:08 +00:00
### 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.