Posts

Example of a simple REST API in PHP

This post is an example of the simplest REST API in PHP without using any framework or other means.

The goal is to provide an overall picture of how it all works.

Since no frameworks with routers will be used in the example, you should start by redirecting all requests to the “entry point” – index.php. For a server on Apache, this can be done in the .htaccess file, which should be located in the root of the project:

Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on

# Redirection from DOMAIN to DOMAIN/api
RewriteCond %{REQUEST_URI} ^/$
RewriteRule ^(.*)$ /api/$1 [R=301]

# If the URI starts with api/ then redirect all requests to index.php
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^api/(.*)$ /index.php

According to the rules, the link should start with /api and, for example, for the API working with the users table should have this look:

DOMAIN/api/users

Example file index.php:

<?php

require_once 'UsersApi.php';

try {
    $api = new usersApi();
    echo $api->run();
} catch (Exception $e) {
    echo json_encode(Array('error' => $e->getMessage()));
}

As you can see from the code – we will work with the usersApi object, i.e. with users (users table). As for the simplicity of the example, I don’t use Composer or any other mechanism for autoloading classes here, just connect the class file using:

require_once 'UsersApi.php';

In addition to users, it may be necessary to do api for other entities, so all classes of different APIs must have one common backbone that will define the request method, the action to perform, etc. We create an Api.php file with an abstract Api class:

<?php

abstract class Api
{
    public $apiName = ''; //users

    protected $method = ''; //GET|POST|PUT|DELETE

    public $requestUri = [];
    public $requestParams = [];

    protected $action = ''; //Название метод для выполнения


    public function __construct() {
        header("Access-Control-Allow-Orgin: *");
        header("Access-Control-Allow-Methods: *");
        header("Content-Type: application/json");

        // Array of GET parameters separated by a slash
        $this->requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
        $this->requestParams = $_REQUEST;

        // Defining the request method
        $this->method = $_SERVER['REQUEST_METHOD'];
        if ($this->method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) {
            if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') {
                $this->method = 'DELETE';
            } else if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') {
                $this->method = 'PUT';
            } else {
                throw new Exception("Unexpected Header");
            }
        }
    }

    public function run() {
        // The first 2 elements of the URI array must be "api" and the table name
        if(array_shift($this->requestUri) !== 'api' || array_shift($this->requestUri) !== $this->apiName){
            throw new RuntimeException('API Not Found', 404);
        }
        // Defining the action to be processed
        $this->action = $this->getAction();

        // If the method (action) is defined in the API child class
        if (method_exists($this, $this->action)) {
            return $this->{$this->action}();
        } else {
            throw new RuntimeException('Invalid Method', 405);
        }
    }

    protected function response($data, $status = 500) {
        header("HTTP/1.1 " . $status . " " . $this->requestStatus($status));
        return json_encode($data);
    }

    private function requestStatus($code) {
        $status = array(
            200 => 'OK',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            500 => 'Internal Server Error',
        );
        return ($status[$code])?$status[$code]:$status[500];
    }

    protected function getAction()
    {
        $method = $this->method;
        switch ($method) {
            case 'GET':
                if($this->requestUri){
                    return 'viewAction';
                } else {
                    return 'indexAction';
                }
                break;
            case 'POST':
                return 'createAction';
                break;
            case 'PUT':
                return 'updateAction';
                break;
            case 'DELETE':
                return 'deleteAction';
                break;
            default:
                return null;
        }
    }

    abstract protected function indexAction();
    abstract protected function viewAction();
    abstract protected function createAction();
    abstract protected function updateAction();
    abstract protected function deleteAction();
}

It remains to implement abstract methods and the $apiName property, which is unique for each individual API. For this purpose we create the file UsersApi.php:

<?php
require_once 'Api.php';
require_once 'Db.php';
require_once 'Users.php';

class UsersApi extends Api
{
    public $apiName = 'users';

    /**
     * Метод GET
     * Вывод списка всех записей
     * http://ДОМЕН/users
     * @return string
     */
    public function indexAction()
    {
        $db = (new Db())->getConnect();
        $users = Users::getAll($db);
        if($users){
            return $this->response($users, 200);
        }
        return $this->response('Data not found', 404);
    }

    /**
     * GET method
     * Viewing a separate entry (by id)
     * http://DOMAIN/users/1
     * @return string
     */
    public function viewAction()
    {
        // id should be the first parameter after /users/x.
        $id = array_shift($this->requestUri);

        if($id){
            $db = (new Db())->getConnect();
            $user = Users::getById($db, $id);
            if($user){
                return $this->response($user, 200);
            }
        }
        return $this->response('Data not found', 404);
    }

    /**
     * POST method
     * Create new record
     * http://DOMAIN/users + request details name, email
     * @return string
     */
    public function createAction()
    {
        $name = $this->requestParams['name'] ?? '';
        $email = $this->requestParams['email'] ?? '';
        if($name && $email){
            $db = (new Db())->getConnect();
            $user = new Users($db, [
                'name' => $name,
                'email' => $email
            ]);
            if($user = $user->saveNew()){
                return $this->response('Data saved.', 200);
            }
        }
        return $this->response("Saving error", 500);
    }

    /**
     * PUT method
     * Updating a single post (by its id)
     * http://DOMAIN/users/1 + request details name, email
     * @return string
     */
    public function updateAction()
    {
        $parse_url = parse_url($this->requestUri[0]);
        $userId = $parse_url['path'] ?? null;

        $db = (new Db())->getConnect();

        if(!$userId || !Users::getById($db, $userId)){
            return $this->response("User with id=$userId not found", 404);
        }

        $name = $this->requestParams['name'] ?? '';
        $email = $this->requestParams['email'] ?? '';

        if($name && $email){
            if($user = Users::update($db, $userId, $name, $email)){
                return $this->response('Data updated.', 200);
            }
        }
        return $this->response("Update error", 400);
    }

    /**
     * DELETE method
     * Deleting a single record (by its id)
     * http://ДОМЕН/users/1
     * @return string
     */
    public function deleteAction()
    {
        $parse_url = parse_url($this->requestUri[0]);
        $userId = $parse_url['path'] ?? null;

        $db = (new Db())->getConnect();

        if(!$userId || !Users::getById($db, $userId)){
            return $this->response("User with id=$userId not found", 404);
        }
        if(Users::deleteById($db, $userId)){
            return $this->response('Data deleted.', 200);
        }
        return $this->response("Delete error", 500);
    }

}

Methods related to the database and obtaining data from it are just for example.

/ August, 10 at 13:00

I create web projects, develop, optimize and promote websites. If you have any ideas or want to suggest something, then write to me and my team.

Top ↑

Leave a Reply