PHP API Simple Layout
Phasil stands for PHP Api SImple Layout, but also is pronounced like the Spanish word "fácil" that means easy.
New ERA model for API development (ERA: Endpoint-Response API).
Don't get lost on hard to code REST definitions, you just need to define an endpoint and write a response for it. Easy as that!.
Do you need to connect a database? Sure, MySQL configuration and JWT security is out of the box. The project has no deep roots so no limits on what you are able to modify.
Add the api folder in your root projects and access it using http://www.myproject.dev/api/
In /api/index.php set a new route (method, endpoint, response):
Route::Create('POST', '/myEndpoint', 'myClass/myMethod');
Create the /api/responses/MyClassResponse.php:
<?php
use Base\Response;
class MyClass extends Response {
public function myMethod(): array {
return [
'name' => 'Phasil',
'description' => 'ERA Layout (Endpoint-Response API)',
'link' => 'https://andresrobert.github.io/Phasil-Framework/',
'github' => 'https://github.com/AndresRobert/Phasil-Framework'
];
}
}
Call the endpoint:
curl --location --request POST \
--header 'Content-Type: application/json' \
'https://www.mywebsite.dev/api/myEndpoint'
You should be seeing:
200 - HTTP OK
{
"status": "OK",
"response": [
{
"name": "Phasil",
"description": "ERA Layout (Endpoint-Response API)",
"link": "https://andresrobert.github.io/Phasil-Framework/",
"github": "https://github.com/AndresRobert/Phasil-Framework"
}
]
}
That's it!
...
"Wait a second!, you said something about some database sh... stuff!!"
...
That's... true!
You can set your mySQL credentials in /api/config/Core.php file:
<?php
...
// DATABASE
define('DB_HOST', 'localhost');
define('DB_NAME', 'phasil');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', 'root');
define('DB_TABLE_PREFIX', '');
...
If you are just starting, create a simple table:
CREATE TABLE users (
id int(255) NOT NULL AUTO_INCREMENT,
user_name varchar(255) NOT NULL,
password varchar(255) NOT NULL,
email varchar(255) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE users
ADD PRIMARY KEY (id),
ADD UNIQUE KEY user_name (user_name),
ADD UNIQUE KEY email (email);
INSERT INTO users (id, user_name, password, email) VALUES
(1, 'andres', '$2y$10$...', 'andres@acode.cl'),
(2, 'robert', '$5t$87$...', 'robert@acode.cl');
Create this file /api/models/UsersModel.php and extend the base model (the important part is to define the table name):
<?php
use Base\Model;
class Users extends Model {
public function __construct () {
$this->table = 'users';
parent::__construct();
}
}
As you might have already guessed, you can obviously redefine everything, add more methods, etc... you're welcome #Hela'sVoice
Using the ERA model you can easily expose this data:
Here /api/index.php add a new route:
Route::Create('GET', '/users', 'users/list');
Create the /api/responses/UsersResponse.php and import your model:
<?php
use Base\Response;
require_once MDL.'UsersModel.php' as UserModel;
class Users extends Response {
/**
* List all users
*
* @param array $filters: passed by payload ;)
* @return array
*/
function list(array $filters = []): array {
/* Use filter([SELECT], [FROM]) */
return (new UserModel())->filter(['user_name', 'email'], $filters);
}
}
Call the endpoint (try a filter):
curl --location --request GET \
--header 'Content-Type: application/json' \
'https://www.mywebsite.dev/api/users?id=1'
You should be seeing:
200 - HTTP OK
{
"status": "OK",
"response": [
{
"user_name": "andres",
"email": "andres@acode.cl"
}
]
}
That's it!
...
"Wait another second!, that's way too unsafe! Anyone can access users' data!!"
...
That's... also true... but we got your back!
JWT Library (firebase/php-jwt) is pre-implemented by using the Auth kit. Kits are just plugins wrappers or helpers for easy tooling.
First, you need to change the JWT_SECRET in /api/config/Core.php or else everyone who uses this layout will "know your secret" #ifYouKnowWhatIMean:
<?php
...
// JWT
define('JWT_SECRET', 'wLdkrBuQ3...');
define('JWT_ISSUER', 'PHASIL');
define('JWT_AUDIENCE', 'MY_AUDIENCE');
define('JWT_NOT_BEFORE', 5); // delay in seconds
define('JWT_EXPIRE', 600); // duration in seconds
...
Make your response safe in api/models/UsersResponse.php using the authorization wrapper:
<?php
use Base\Response;
require_once MDL.'UsersModel.php' as UserModel;
class Users extends Response {
function list(array $filters = []): array {
return self::RequiresAuthorization(function () use ($filters) {
return (new User())->filter(['user_name', 'email'], $filters);
});
}
}
And call the endpoint again to get:
401 - HTTP OK
{
"status": "Unauthorized",
"response": [
{
"status": "fail",
"id": "-1",
"message": "Not Authorized",
"response_code": "401"
}
]
}
Great!, protected already!, lets try to call it using a token:
curl --location --request GET \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ0eXAiOiJ...' \
'https://www.mywebsite.dev/api/users?id=1'
And you'll get:
401 - HTTP OK
{
"status": "Unauthorized",
"response": [
{
"status": "fail",
"id": "-1",
"message": "Expired token",
"response_code": "401"
}
]
}
Sorry, just kidding, I was using a expired token XD:
200 - HTTP OK
{
"status": "OK",
"response": [
{
"user_name": "andres",
"email": "andres@acode.cl"
}
]
}
That's a lot better (actually, it's the same as before but with the "security extras")
...
"Wait yet another second, something feels wrong... how did you get that token?"
...
You are right, I was trying to avoid the full documentation (like most of us programmers do LOL).
Add the route:
Route::Create('POST', '/login', 'users/login');
In the Users response add the login method:
...
public function login(array $userPayload): array {
$User = new User();
if ($User->readBy(['user_name' => $userPayload['user_name']])) {
if (Auth::Match($userPayload['password'], $User->get('password'))) {
$tokenData = Auth::JWToken($User);
return [
'response_code' => 200,
'token' => $tokenData['token']
];
}
}
return [
'response_code' => 400,
'token' => 'notoken'
];
}
...
Call it:
curl --location --request POST 'https://www.mywebsite.dev/api/login' \
--header 'Content-Type: application/json' \
--data-raw '{"user_name": "andres","password": "$2y$10$..."}'
Get:
200 - HTTP OK
{
"status": "OK",
"response": {
"response_code": 200,
"token": "eyJ0eXAiOiJ..."
}
}
That's it! Notice that the token came back by one line of code: Auth::JWToken($User)
...
Cool! I wanna know more!!!
This line in the index.php gets the job done by getting the METHOD used, the REQUESTed endpoint and the BODY payload:
echo Route::Read(METHOD, REQUEST, BODY);
Insert, Select, Update, Delete & ComplexSelect (custom queries) are out of the box, but you are also encouraged to add more at api/kits/Database.php
Of course! check /api/config/Core.php out!
Sure we do! they are called Kits /api/kits/ and there are some in there already (and more will be added): Session, Cookie, File, Text, etc., check them all out!.
Check the status of your configuration calling /api/status
curl --location --request POST 'https://www.mywebsite.dev/api/status' \
--header 'Content-Type: application/json'