
Недавно я уже опубликовал статью, в которой описан процесс создания REST API для проекта на Yii2.
Т.к. никакие фреймворки с маршрутизаторами в примере использоваться не будут, нужно начать с перенаправления всех запросов на "точку входа" - файл index.php. Для сервера на Apache это можно сделать в файле .htaccess который должен располагаться в корне проекта:
Options +FollowSymLinks IndexIgnore */* RewriteEngine on # Перенаправление с ДОМЕН на ДОМЕН/api RewriteCond %{REQUEST_URI} ^/$ RewriteRule ^(.*)$ /api/$1 [R=301] #Если URI начинается с api/ то перенаправлять все запросы на index.php RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^api/(.*)$ /index.php
Согласно правил, ссылка должна начинаться на /api и ,например, для API работающего с таблицей users должна иметь такой вид:
ДОМЕН/api/users
Пример файла index.php
<?php require_once 'UsersApi.php'; try { $api = new usersApi(); echo $api->run(); } catch (Exception $e) { echo json_encode(Array('error' => $e->getMessage())); }
Как видно из кода - будем работать с объектом usersApi, т.е. с пользователями (таблица users). Т.к. для простоты примера я не использую тут Composer или другой механизм для автозагрузки классов, просто подключим файл класса с помощью
require_once 'UsersApi.php';
Кроме пользователей, может потребоваться сделать api и для других сущностей, поэтому все классы различных API должны иметь один общий костяк, который будет определять метод запроса, действие для выполнения и тд. Создаем файл Api.php c абстрактным классом Api:
<?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"); //Массив GET параметров разделенных слешем $this->requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/')); $this->requestParams = $_REQUEST; //Определение метода запроса $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() { //Первые 2 элемента массива URI должны быть "api" и название таблицы if(array_shift($this->requestUri) !== 'api' || array_shift($this->requestUri) !== $this->apiName){ throw new RuntimeException('API Not Found', 404); } //Определение действия для обработки $this->action = $this->getAction(); //Если метод(действие) определен в дочернем классе API 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(); }
Осталось реализовать абстрактные методы и свойство $apiName, которое уникально для каждого отдельного API. Для этого создаем файл 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 * Просмотр отдельной записи (по id) * http://ДОМЕН/users/1 * @return string */ public function viewAction() { //id должен быть первым параметром после /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 * Создание новой записи * http://ДОМЕН/users + параметры запроса 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 * Обновление отдельной записи (по ее id) * http://ДОМЕН/users/1 + параметры запроса 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 * Удаление отдельной записи (по ее 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); } }Методы связанные с базой данных и получением данных из нее просто для примера.