PHP API REST framework using annotations, and Swagger 2.0 support.
- PHP >=7.2
- nrk/predis (Redis) - https://github.com/nrk/predis Or
- Pecl APC - Alternative PHP Cache - https://pecl.php.net/package/apc
- yaml pecl extension https://pecl.php.net/package/yaml
Use composer to manage your dependencies and download PHP-API-REST:
composer require diomac/php-api-rest
- 1 - Place an .htaccess file in the root folder of your Rest API (base url), to redirect all routes to the API initialization file.
Example API folders:
.htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_URI} !init\.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* init.php [L,QSA]
- 2 - Use the initialization file to import the dependencies of your project and...
- Instantiate a Diomac\API\AppConfiguration object;
- Set the base url of your API;
- Add the Resources Class Names of your API (See Implementing API Rest Resources);
- Instantiate a Diomac\API\App object using the configuration object;
- Finally call exec method.
init.php file:
use Diomac\API\App;
use Diomac\API\AppConfiguration;
/**
* Initializing API configuration
*/
$config = new AppConfiguration();
$config->setBaseUrl('/php-api-rest/example/v1');
/**
* Adding resources classes
*/
$config->addResource(\example\v1\ExampleResource::class);
$config->addResource(\example\v1\ExampleSwaggerJson::class);
/**
* Execute API
*/
try{
$app = new App($config);
$app->exec();
}catch (Exception $ex){
...
}
- 3 - In the resource class, enter the inheritance of the Resource class:
namespace example;
use Diomac\API\Resource;
use Diomac\API\Response;
use Diomac\API\UnauthorizedException;
class ExampleResource extends Resource
{
...
}
- 4 - For each method of the resource class, enter PHP annotation to identify routes (@route), HTTP methods (@method) and if you need a Class implementing Guard Interface to protect the route (@guard - See how implement guards in topic Implementing a Guard Class ):
/**
* @method get
* @route /auth/usr-data/id/{id}
* @guard(
* className="example\core\secure\ExampleGuard",
* @parameters(operationId="GETUSERDATA")
* )
*/
function getUsrData()
{
// ... your code
$this->response->setCode(Response::OK); // set HTTP response code
$this->response->setBodyJSON($jsonSerializable); // set responde data
return $this->response; // return response object
}
- 5 - Whenever a route is executed, the class holding the route will be instantiated and inherit the following attributes:
A Diomac\API\Request object with the methods:
$this->request->getParams() // returns the parameters of the URL and $_GET
$this->request->getData() // returns an object sent by the front end
$this->request->getRoute() // returns the executed route
$this->request->getMethod() // returns the executed HTTP method
A Diomac\API\Response object with the methods:
$this->response->setHeader('name', 'value'); // set HTTP header response
$this->response->setCode(Response::BAD_REQUEST); // set HTTP response code
$this->response->setBody('string'); // set response body
$this->response->setBodyJSON(\JsonSerializable object); // set response body to convert in JSON
$this->response->setContentType(''); // set content type response (for setBodyJSON not needed)
And it inherits methods from the Diomac\API\Resource class:
$this->getRoute(); // returns the configured route
$this->getParams(); // returns the parameters of the URL and $_GET
$this->getParam('name'); // returns a parameter by name
- 6 - For the output simply return the response object:
return $this->response;
API get method:
/**
* @method get
* @route /example/v1/pet/{petId}
*
* @return Response
* @throws Exception
*/
function getPet(): Response
{
try {
$petId = $this->getParam('petId');
$pet = new Pet($petId);
/**
* API Rest best practices - selecting returned fields and aliases
*/
$fields = $this->getParam('fields');
if($fields){
Response::setFields(explode(',', $fields), Pet::class);
}
$this->response->setCode(Response::OK);
$this->response->setBodyJSON($pet);
} catch (Exception $ex) {
throw new Exception('Internal Server Error', Response::INTERNAL_SERVER_ERROR);
}
return $this->response;
}
/**
* @method get
* @route /auth/usr-data/id/{id}
* @guard(
* className="example\core\secure\ExampleGuard"
* )
* @guard(
* className="example\core\secure\ExampleGuardWithParams",
* @parameters(operationId="GETUSERDATA", operationName="GETUSERDATA")
* )
*/
function getUsrData()
{
// ... your code
$this->response->setCode(Response::OK); // set HTTP response code
$this->response->setBodyJSON($ this->request->getData()); // set responde data
return $this->response; // return response object
}
namespace api\secure;
use Diomac\API\Exception;
use Diomac\API\ForbiddenException;
use Diomac\API\UnauthorizedException;
use Diomac\API\Response;
use Diomac\API\Guard;
/**
* Class AuthGuard
* @package api\secure
*/
class AuthGuard implements Guard
{
/**
* @param object|null $guardParams
* @return bool
* @throws Exception
* @throws ForbiddenException
* @throws UnauthorizedException
*/
public function guard(object $guardParams = null) : bool
{
$func = $guardParams->func;
$access = checkAccess($func);
switch ($access) {
case Response::OK:
return true;
break;
case Response::UNAUTHORIZED:
throw new UnauthorizedException();
break;
case Response::FORBIDDEN:
throw new ForbiddenException();
break;
default:
throw new Exception('Server Error!', Response::INTERNAL_SERVER_ERROR);
}
}
}
Cache settings save the mapped routes into a list, and when used (set to true), skip the route mapping step for better performance.
**
* Setting use cache for caching of annotations mapping
*/
$config->setUseCache(true);
/**
* Execute API
*/
try{
$app = new App($config);
$app->exec();
}catch (Exception $ex){
...
}
/**
* Set Redis for cache
*/
$redis = new RedisConfig();
$redis->setScheme('tcp');
$redis->setHost('redis');
$redis->setPort(6379);
**
* Setting use cache for caching of annotations mapping
*/
$config->setUseCache(true, $redis);
Create a route in a resource like the example below:
PHP Class:
use Diomac\API\Resource;
use Diomac\API\Response;
use example\v1\doc\ExampleSwaggerDoc;
class ExampleSwaggerJson extends Resource
{
/**
* @method get
* @route /swagger.json
*/
function swaggerJson()
{
$this->response->setCode(Response::OK);
$swagger = new ExampleSwaggerDoc();
/**
* JSON
*/
$this->response->setBodySwaggerJSON($swagger);
/**
* Or YAML
*/
//$this->response->setBodySwaggerYAML($swagger);
return $this->response;
}
}
Route result:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Swagger Sample App",
"description": "This is a sample server Petstore server.",
"termsOfService": "http:\/\/swagger.io\/terms\/",
"contact": {
"name": "API Support",
"email": "[email protected]",
"url": "http:\/\/swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http:\/\/www.apache.org\/licenses\/LICENSE-2.0.html"
}
},
"host": "localhost",
"basePath": "\/php-api-rest\/vendor\/example\/v1",
"schemes": [
"http",
"https"
],
"consumes": [
"text\/plain; charset=utf-8"
],
"produces": [
"application\/json",
"text\/html"
],
"tags": [
{
"name": "ExampleAPIDocSwagger",
"description": "ExampleAPIDocSwagger",
"externalDocs": {
"description": "Externaldocsexample",
"url": "http:\/\/example_php_api_rest.com"
}
}
],
"paths": {
...
To document the information (Swagger Info) of your API, you can use Swagger and SwaggerInfo classes. Just implement an "ExampleSwaggerDoc" class that inherits from Swagger as in the example below:
PHP Class:
use Diomac\API\swagger\Swagger;
use Diomac\API\swagger\SwaggerInfo;
class ExampleSwaggerDoc extends Swagger
{
public function info(): SwaggerInfo
{
$this->setInfo(new SwaggerInfo());
$this->info->setVersion('1.0.0');
$this->info->setTitle('Swagger Sample App');
$this->info->setDescription('This is a sample server Petstore server.');
$this->info->setTermsOfService('http://swagger.io/terms/');
$this->info->getContact()->setName('API Support');
$this->info->getContact()->setEmail('[email protected]');
$this->info->getContact()->setUrl('http://swagger.io');
$this->info->getLicense()->setName('Apache 2.0');
$this->info->getLicense()->setUrl('http://www.apache.org/licenses/LICENSE-2.0.html');
return $this->getInfo();
}
...
}
Use @tag in PHPDoc Class to document Resources's routes with Swagger Tag Object.
PHPDoc:
/**
* Class ExampleResource
* @package example\v1
* @tag(
* name="Example API Doc Swagger",
* description="Example API Doc Swagger",
* @externalDocs(description="External docs example", url="http://example_php_api_rest.com")
* )
*/
class ExampleResource extends Resource
{
...
}
Swagger json result:
...
"tags": [
{
"name": "ExampleAPIDocSwagger",
"description": "ExampleAPIDocSwagger",
"externalDocs": {
"description": "Externaldocsexample",
"url": "http:\/\/example_php_api_rest.com"
}
}
],
...
PHPDoc:
/**
* @method get
* @route /example/api/value1/{value1}/value2/{value2}
* @tag More one tag
*/
function getUsrData(): Response
{
...
}
Swagger json result:
...
"tags": [
"More one tag",
"ExampleAPIDocSwagger"
],
...
Use @contentType in PHPDoc function to document your route with Swagger produces [string].
PHPDoc:
/**
* @method get
* @route /example/api/value1/{value1}/value2/{value2}
* @contentType application/json
* @contentType text/html
*/
function getUsrData(): Response
{
...
}
Swagger json result:
...
"produces": [
"application\/json",
"text\/html"
],
...
Use @summary in PHPDoc function to document your route with Swagger summary string.
PHPDoc:
/**
* @method get
* @route /example/api/value1/{value1}/value2/{value2}
* @summary Example api rest php
*/
function getUsrData(): Response
{
...
}
Swagger json result:
...
"get": {
"summary": "Example api rest php",
...
Use @description in PHPDoc function to document your route with Swagger description string.
PHPDoc:
/**
* @method get
* @route /example/api/value1/{value1}/value2/{value2}
* @description A example api rest php
*/
function getUsrData(): Response
{
...
}
Swagger json result:
...
"get": {
"description": "A example api rest php",
...
Use @operationId in PHPDoc function to document your route with Swagger operationId string.
PHPDoc:
/**
* @method get
* @route /example/api/value1/{value1}/value2/{value2}
* @operationId GETUSERDATA
*/
function getUsrData(): Response
{
...
}
Swagger json result:
...
"get": {
"operationId": "GETUSERDATA",
...
Use @consumeType in PHPDoc function to document your route with Swagger consumes [string].
PHPDoc:
/**
* @method get
* @route /example/api/value1/{value1}/value2/{value2}
* @consumeType text/plain; charset=utf-8
*/
function getUsrData(): Response
{
...
}
Swagger json result:
...
"post": {
"consumes": [
"text\/plain; charset=utf-8"
],
...
Use @response(...) in PHPDoc function to document your route with Swagger response object.
PHPDoc:
/**
* @method get
* @route /example/api/value1/{value1}/value2/{value2}
* @response(
* code=200,
* description="Success",
* @schema(
* type="object",
* @items($ref="#/definitions/sucess")
* )
* )
*/
function getUsrData(): Response
{
...
}
Swagger json result:
...
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "object",
"items": {
"$ref": "#\/definitions\/sucess"
}
}
},
...
This project is licensed under the MIT License - see the LICENSE file for details