Перейти к содержимому

Как сделать rest api на php

  • автор:

Пример простого создания REST API на PHP v.2

Пример простого создания REST API на PHP v.2

Restful API является одним из наиболее популярных способов взаимодействия между клиентами и серверами. Он позволяет взаимодействовать с сервером и передавать данные в унифицированном формате, который поддерживается большинством платформ.

Давайте разберемся, как создать Restful API на PHP.

Шаг 1: Настройка окружения

Перед тем, как начать использовать Restful API, нам нужно установить все необходимые инструменты и настроить окружение. Для начала установите PHP, Apache и MySQL. Нам также понадобится Composer, чтобы управлять зависимостями. Вы можете скачать его с официального сайта: https://getcomposer.org/. Чтобы установить необходимые библиотеки, создайте файл composer.json в корневой директории проекта и добавьте следующий код:

Затем запустите команду: composer install Он установит Slim Framework, который мы будем использовать для создания нашего Restful API.

Шаг 2: Создание маршрутов

Теперь мы можем начать создавать маршруты для нашего API. Для этого создайте файл index.php и добавьте следующий код:

require 'vendor/autoload.php'; $app = new \Slim\App; $app->get('/api/', function ($request, $response, $args) < $name = $args['name']; return $response->write("Hello, $name"); >); $app->run(); 

Здесь мы создаем маршрут для нашего API, который передает имя в качестве параметра. Это простой пример, который показывает, как работают маршруты.

Шаг 3: Создание контроллеров

Теперь, когда мы создали наш маршрут, давайте добавим контроллер, который выполняет определенные действия в зависимости от запроса. Создайте новый файл в папке controllers и назовите его UsersController.php. В этом файле мы будем определять наши методы контроллера. Добавьте следующий код:

namespace App\Controllers; use Psr\Http\Message\RequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; class UsersController < public function index(Request $request, Response $response) < $response->getBody()->write("Index page"); return $response; > public function show(Request $request, Response $response, $args) < $id = $args['id']; $response->getBody()->write("Show page: $id"); return $response; > public function store(Request $request, Response $response) < $data = $request->getParsedBody(); $response->getBody()->write(json_encode($data)); return $response; > public function update(Request $request, Response $response, $args) < $id = $args['id']; $data = $request->getParsedBody(); $response->getBody()->write("Update page: $id"); return $response; > public function delete(Request $request, Response $response, $args) < $id = $args['id']; $response->getBody()->write("Delete page: $id"); return $response; > > 

В этом коде мы определяем методы контроллера для каждого из действий: index, show, store, update, delete.

Шаг 4: Использование контроллеров в маршрутах

Теперь мы можем использовать контроллеры в маршрутах, чтобы обрабатывать запросы к нашему API. Для этого откройте файл index.php и замените имеющийся маршрут на следующий:

$app->get('/api/user', 'App\Controllers\UsersController:index'); $app->get('/api/user/', 'App\Controllers\UsersController:show'); $app->post('/api/user', 'App\Controllers\UsersController:store'); $app->put('/api/user/', 'App\Controllers\UsersController:update'); $app->delete('/api/user/', 'App\Controllers\UsersController:delete'); 

Теперь мы используем методы контроллера, чтобы обрабатывать каждый из запросов.

Шаг 5: Использование базы данных

Чаще всего при создании RESTful API используется база данных для сохранения и доступа к информации. Давайте добавим базу данных MySQL в наш проект. Создайте новую базу данных MySQL и добавьте таблицу пользователей.

CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 

Теперь добавим класс DB в наш проект, который будет использоваться для установки соединения с базой данных и выполнения запросов. Создайте новый файл в папке lib и назовите его DB.php. Добавьте следующий код:

namespace App\Lib; use PDO; class DB < private static $instance; private static $db; private function __construct() < $host = "localhost"; $dbname = "restful_api"; $username = "root"; $password = ""; self::$db = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password); >public static function getInstance() < if (!self::$instance) < self::$instance = new DB(); >return self::$instance; > public static function query($query) < return self::$db->query($query); > > 

Теперь мы можем использовать этот класс для выполнения запросов к базе данных. В UsersControllers.php замените index метод на следующий:

public function index(Request $request, Response $response) < $query = "SELECT * FROM users"; $result = \App\Lib\DB::query($query)->fetchAll(PDO::FETCH_ASSOC); $response->getBody()->write(json_encode($result)); return $response; > 

Здесь мы выполняем запрос SELECT на таблицу пользователей и возвращаем результат в качестве JSON. Аналогично обновите остальные методы контроллера, используя соответствующие запросы для CRUD операций.

Шаг 6: Защита API ключом

Последний шаг — добавление аутентификации API. Есть много способов, чтобы обеспечить безопасность вашего Restful API, но простейшим способом является добавление API ключа. Возможность использования ключа API означает, что только авторизованные клиенты могут получить доступ к вашему API. Самый простой способ сделать это — добавить ключ API в запрос. Создайте новый файл в папке lib и назовите его Auth.php. Добавьте следующий код:

namespace App\Lib; class Auth < public static function isValid($apiKey) < $validKeys = ["abc123", "123abc", "qwerty"]; return in_array($apiKey, $validKeys); >> 

Здесь мы предполагаем, что у нас есть несколько действительных ключей API. Поэтому мы проверяем, есть ли ключ api в массиве действительных ключей. Откройте файл index.php и добавьте следующий код непосредственно перед $app->run() :

$app->add(function ($request, $response, $next) < $apiKey = $request->getQueryParam('apiKey'); if (!\App\Lib\Auth::isValid($apiKey)) < $response->getBody()->write("Unauthorized"); return $response->withStatus(401); > $response = $next($request, $response); return $response; >); 

Здесь мы добавляем middleware, который проверяет, является ли переданный ключ API действительным. Если ключ не является действительным, мы возвращаем статус 401 «Unauthorized». Теперь мы можем протестировать наш Restful API с помощью Postman или другого инструмента для тестирования API. Вот и все! Теперь вы знаете, как создать Restful API на PHP.

Простой RESTful-сервис на нативном PHP

Я не знаю ни одного php-фреймворка. Это печально и стыдно, но законом пока не запрещено. А при этом поиграться с REST API хочется. Проблема в том, что php по умолчанию поддерживает только $_GET и $_POST. А для RESTful-сервиса надобно уметь работать еще и с PUT, DELETE и PATCH. И не очень очевидно, как культурно обработать множество запросов вида GET http://site.ru/users, DELETE http://site.ru/goods/5 и прочего непотребства. Как завернуть все подобные запросы в единую точку, универсально разобрать их на части и запустить нужный код для обработки данных?

Почти любой php-фреймворк умеет делать это из коробки. Например, Laravel, где роутинг реализован понятно и просто. Но что если нам не нужно прямо сейчас заниматься изучением новой большой темы, а хочется просто быстро завести проект с поддержкой REST API? Об этом и пойдет речь в статье.

Что должен уметь наш RESTful-сервис?

1. Поддерживать все 5 основных типов запросов: GET, POST, PUT, PATCH, DELETE.
2. Разруливать разнообразные маршруты вида
POST /goods
PUT /goods/
GET /users//info
и прочие сколь угодно длинные цепочки.

Внимание: это статья не про основы REST API
Я предполагаю, что Вы уже знакомы с REST-подходом и понимаете, как это работает. Если нет, то в интернетах много замечательных статей по основам REST — я не хочу дублировать их, моя идея — показать, как с REST работать на практике.

Какой функционал мы будем поддерживать?

Рассмотрим 2 сущности — товары и пользователи.

Для товаров возможности следующие:

  • 1. GET /goods/ — Получение информации о товаре
  • 2. POST /goods — Добавление нового товара
  • 3. PUT /goods/ — Редактирование товара
  • 4. PATCH /goods/ — Редактирование некоторых параметров товара
  • 5. DELETE /goods/ — Удаление товара

По пользователям для разнообразия рассмотрим несколько вариантов с GET

  • 1. GET /users/ — Полная информация о пользователе
  • 2. GET /users//info — Только общая информация о пользователе
  • 3. GET /users//orders — Список заказов пользователя

Как это заработает на нативном PHP?

Первое, что мы сделаем — это настроим .htaccess так, чтобы все запросы перенаправлялись на файл index.php. Именно он и будет заниматься извлечением данных.

  • 1. Метод запроса (GET, POST, PUT, PATCH или DELETE)
  • 2. Данные из URL-a, например, users//info — нужны все 3 параметра
  • 3. Данные из тела запроса

.htaccess

Создадим в корне проекта файл .htaccess

RewriteEngine On RewriteCond % !-f RewriteRule ^(.+)$ index.php?q=$1 [L,QSA]

Этими загадочными строками мы повелеваем делать так:
1 — направить все запросы любого вида на царь-файл index.php
2 — сделать строку в URL-е доступной в index.php в get-параметре q. То есть данные из URL-а вида /users//info мы достанем из $_GET[‘q’].

index.php

Рассмотрим index.php строка за строкой. Для начала получим метод запроса.

// Определяем метод запроса $method = $_SERVER['REQUEST_METHOD'];

Затем данные из тела запроса

// Получаем данные из тела запроса $formData = getFormData($method);

Для GET и POST легко вытащить данные из соответствующих массивов $_GET и $_POST. А вот для остальных методов нужно чуть извратиться. Код для них вытаскивается из потока php://input, код легко гуглится, я всего лишь написал общую обертку — функцию getFormData($method)

// Получение данных из тела запроса function getFormData($method) < // GET или POST: данные возвращаем как есть if ($method === 'GET') return $_GET; if ($method === 'POST') return $_POST; // PUT, PATCH или DELETE $data = array(); $exploded = explode('&', file_get_contents('php://input')); foreach($exploded as $pair) < $item = explode('=', $pair); if (count($item) == 2) < $data[urldecode($item[0])] = urldecode($item[1]); >> return $data; >

То есть мы получили нужные данные, скрыв все детали в getFormData — ну и отлично. Переходим к самому интересному — роутингу.

// Разбираем url $url = (isset($_GET['q'])) ? $_GET['q'] : ''; $url = rtrim($url, '/'); $urls = explode('/', $url);

Выше мы узнали, что .htaccess подложит нам параметры из URL-a в q-параметр массива $_GET. То есть в $_GET[‘q’] попадет примерно такая строка: users/10. Независимо от того, каким методом мы запрос дергаем.

А explode(‘/’, $url) преобразует нам эту строку в массив, с которым уже можно работать. Таким образом, составляйте сколько угодно длинные цепочки запросов, например,
GET /goods/page/2/limit/10/sort/price_asc
И будьте уверены, получите массив

$urls = array('goods', 'page', '2', 'limit', '10', 'sort', 'price_asc');

Теперь у нас есть все данные, нужно сделать с ними что-нибудь полезное. А сделают это всего лишь 4 строки кода

// Определяем роутер и url data $router = $urls[0]; $urlData = array_slice($urls, 1); // Подключаем файл-роутер и запускаем главную функцию include_once 'routers/' . $router . '.php'; route($method, $urlData, $formData);

Улавливаете? Мы заводим папку routers, в которую складываем файлы, манипулирующие одной сущностью: товарами или пользователями. При этом договариваемся, что название файлов совпадают с первым параметром в urlData — он и будет роутером, $router. А из urlData этот роутер нужно убрать, он нам больше не нужен и используется только для подключения нужного файла. array_slice($urls, 1) и вытащит нам все элементы массива, кроме первого.

Теперь осталось подключить нужный файл-роутер и запустить функцию route с тремя параметрами. Что же это за function route? Условимся, что в каждом файле-роутере будет определена такая функция, которая по входным параметрам определит, какое действие инициировал пользователь, и выполнит нужный код. Сейчас это станет понятнее. Рассмотрим первый запрос — получение данных о товаре.

GET /goods/

// Роутер function route($method, $urlData, $formData) < // Получение информации о товаре // GET /goods/if ($method === 'GET' && count($urlData) === 1) < // Получаем id товара $goodId = $urlData[0]; // Вытаскиваем товар из базы. // Выводим ответ клиенту echo json_encode(array( 'method' =>'GET', 'id' => $goodId, 'good' => 'phone', 'price' => 10000 )); return; > // Возвращаем ошибку header('HTTP/1.0 400 Bad Request'); echo json_encode(array( 'error' => 'Bad Request' )); >

Содержимое файла — это одна большая функция route, которая в зависимости от переданных параметров выполняет нужные действия. Если метод GET и в urlData передан 1 параметр (goodId), то это запрос о получении данных о товаре.

Внимание: пример очень упрощенный
В реале, конечно же, нужно дополнительно проверять входные параметры, например, что goodId — это число. Вместо того, чтобы писать код здесь, Вы, вероятно, подключите нужный класс. И для получения товара создадите объект оного класса и вызовете у него какой-то метод.
А может быть, передадите управление какому-то контроллеру, который уже озаботится инициализацией нужных моделей. Вариантов много, мы рассматриваем только общую структуру кода.

В ответе клиенту мы выводим нужные данные: название товара и его цену. id товара и метод в реальном приложении совершенно не обязательны. Покажем их только, чтобы убедится, что вызывается нужный метод с правильными параметрами.

Давайте попробуем на примере: откройте консоль браузера и выполните код

$.ajax(>)

Код отправит запрос на сервер, где я развернул подобное приложение и выведет ответ. Убедитесь, что интересующий наш маршрут /goods/10 действительно отработал. На вкладке Network Вы заметите такой же запрос.
И да, /examples/rest — это корневой путь нашего тестового приложения на webdevkin.ru

Если Вам привычнее пользоваться curl-ом в консоли, то запустите в терминале это — ответ будет тот же самый, да еще и с заголовками от сервера.

curl -X GET https://webdevkin.ru/examples/rest/goods/10 -i

В конце функции мы написали такой код.

// Возвращаем ошибку header('HTTP/1.0 400 Bad Request'); echo json_encode(array( 'error' => 'Bad Request' ));

Он значит, что если мы ошиблись с параметрами или запрашиваемый маршрут не определен, то вернем клиенту 400-ю ошибку Bad Request. Добавьте, например, к URL-у что-то вроде goods/10/another_param и увидите ошибку в консоли и ответ 400 — кривой запрос не прошел.

По http-кодам ответов сервера
Мы не будем заморачиваться с выводом разных кодов, хотя по REST-у это и стоит делать. Клиентских ошибок много. Даже в нашем простом случае уместна 405 в случае неправильно переданного метода. Намеренно не хочу усложнять.
В случае успеха сервер у нас всегда вернет 200 ОК. По хорошему, при создании ресурса стоит отдавать 201 Created. Но опять-таки в плане упрощения эти тонкости мы отбросим, а в реальном проекте Вы их легко реализуете сами.

По совести говоря, статья закончена. Думаю, Вы уже поняли подход, каким образом разруливаются все маршруты, вынимаются данные, как это протестировать, как добавлять новые запросы и т.д. Но я для завершения образа приведу реализацию оставшихся 7 запросов, которые мы обозначили в начале статьи. Попутно приведу пару интересных замечаний, а в конце выложу архив с исходниками.

POST /goods

Добавление нового товара

// Добавление нового товара // POST /goods if ($method === 'POST' && empty($urlData)) < // Добавляем товар в базу. // Выводим ответ клиенту echo json_encode(array( 'method' =>'POST', 'id' => rand(1, 100), 'formData' => $formData )); return; >

urlData сейчас пустой, но зато используется formData — мы ее просто выведем клиенту.

Как сделать «правильно»?
Согласно канонам REST в post-запросе следует отдавать обратно только id созданной сущности или url, по которому эту сущность можно получить. То есть в ответе будет или просто число — , или /goods/ .
Почему я написал «правильно» в кавычках? Да потому, что REST — это набор не жестких правил, а рекомендаций. И как будете реализовывать именно Вы, зависит от Ваших предпочтений или уже принятых соглашений на конкретном проекте.
Просто имейте в виду, что другой программист, читающий код и осведомленный о REST-подходе, будет ожидать в ответе на post-запрос id созданного объекта или url, по которому можно get-запросом вытащить данные об этом объекте.

Тестим из консоли

$.ajax(, dataType: 'json', success: function(response)>)
curl -X POST https://webdevkin.ru/examples/rest/goods/ --data "good=notebook&price=20000" -i

PUT /goods/

// Обновление всех данных товара // PUT /goods/ if ($method === 'PUT' && count($urlData) === 1) < // Получаем id товара $goodId = $urlData[0]; // Обновляем все поля товара в базе. // Выводим ответ клиенту echo json_encode(array( 'method' =>'PUT', 'id' => $goodId, 'formData' => $formData )); return; >

Здесь уже все данные используются по-полной. Из urlData вытаскивается id товара, а из formData — свойства.

Тестим из консоли

$.ajax(, dataType: 'json', success: function(response)>)
curl -X PUT https://webdevkin.ru/examples/rest/goods/15 --data "good=notebook&price=20000" -i

PATCH /goods/

Частичное обновление товара

// Частичное обновление данных товара // PATCH /goods/ if ($method === 'PATCH' && count($urlData) === 1) < // Получаем id товара $goodId = $urlData[0]; // Обновляем только указанные поля товара в базе. // Выводим ответ клиенту echo json_encode(array( 'method' =>'PATCH', 'id' => $goodId, 'formData' => $formData )); return; >

Тестим из консоли

$.ajax(, dataType: 'json', success: function(response)>)
curl -X PATCH https://webdevkin.ru/examples/rest/goods/15 --data "price=25000" -i

К чему эти понты с PUT и PATCH?
Разве одного PUT не достаточно? Разве не выполняют они одно и то же действие — обновляют данные объекта?
Именно так — внешне действие одно. Разница в передаваемых данных.
PUT предполагает, что на сервер передаются все поля объекта, а PATCH — только измененные. Те, которые переданы в теле запроса. Обратите внимание, что в предыдущем PUT мы передали и название товара, и цену. А в PATCH — только цену. То есть мы отправили на сервер только измененные данные.
Нужен ли Вам PATCH — решайте сами. Но помните о том читающем код программисте, о котором я упоминал выше.

DELETE /goods/

// Удаление товара // DELETE /goods/ if ($method === 'DELETE' && count($urlData) === 1) < // Получаем id товара $goodId = $urlData[0]; // Удаляем товар из базы. // Выводим ответ клиенту echo json_encode(array( 'method' =>'DELETE', 'id' => $goodId )); return; >

Тестим из консоли

$.ajax(>)
curl -X DELETE https://webdevkin.ru/examples/rest/goods/20 -i

С DELETE-запросом все понятно. Теперь давайте рассмотрим работу с пользователями — роутер users и соответственно, файл users.php

GET /users/

Получение всех данных о пользователе. Если GET-запрос вида /users/ , то мы вернем всю информацию о пользователе, если дополнительно указывается /info или /orders, то соответственно, только общую информацию или список заказов.

// Роутер function route($method, $urlData, $formData) < // Получение всей информации о пользователе // GET /users/if ($method === 'GET' && count($urlData) === 1) < // Получаем id товара $userId = $urlData[0]; // Вытаскиваем все данные о пользователе из базы. // Выводим ответ клиенту echo json_encode(array( 'method' =>'GET', 'id' => $userId, 'info' => array( 'email' => 'webdevkin@gmail.com', 'name' => 'Webdevkin' ), 'orders' => array( array( 'orderId' => 5, 'summa' => 2000, 'orderDate' => '12.01.2017' ), array( 'orderId' => 8, 'summa' => 5000, 'orderDate' => '03.02.2017' ) ) )); return; > // Возвращаем ошибку header('HTTP/1.0 400 Bad Request'); echo json_encode(array( 'error' => 'Bad Request' )); >

Тестим из консоли

$.ajax(>)
curl -X GET https://webdevkin.ru/examples/rest/users/5 -i

GET /users//info

Общая информация о пользователе

// Получение общей информации о пользователе // GET /users//info if ($method === 'GET' && count($urlData) === 2 && $urlData[1] === 'info') < // Получаем id товара $userId = $urlData[0]; // Вытаскиваем общие данные о пользователе из базы. // Выводим ответ клиенту echo json_encode(array( 'method' =>'GET', 'id' => $userId, 'info' => array( 'email' => 'webdevkin@gmail.com', 'name' => 'Webdevkin' ) )); return; >

Тестим из консоли

$.ajax(>)
curl -X GET https://webdevkin.ru/examples/rest/users/5/info -i

GET /users//orders

Получение списка заказов пользователя

// Получение заказов пользователя // GET /users//orders if ($method === 'GET' && count($urlData) === 2 && $urlData[1] === 'orders') < // Получаем id товара $userId = $urlData[0]; // Вытаскиваем данные о заказах пользователя из базы. // Выводим ответ клиенту echo json_encode(array( 'method' =>'GET', 'id' => $userId, 'orders' => array( array( 'orderId' => 5, 'summa' => 2000, 'orderDate' => '12.01.2017' ), array( 'orderId' => 8, 'summa' => 5000, 'orderDate' => '03.02.2017' ) ) )); return; >

Тестим из консоли

$.ajax(>)
curl -X GET https://webdevkin.ru/examples/rest/users/5/orders -i

Итоги и исходники

Исходники из примеров статьи — здесь

Как видим, организовать поддержку REST API на нативном php оказалось не так уж и сложно и вполне законными способами. Главное — это поддержка маршрутов и нестандартных для php методов PUT, PATCH и DELETE.

Основной код, реализовывающий эту поддержку, уместился в 3 десятка строк index.php. Остальное — это уже обвязка, которую можно реализовать как угодно. Я предложил это сделать в виде подключаемых файлов-роутеров, имена которых совпадают с сущностями Вашего проекта. Но можно подключить фантазию и найти более интересное решение.

Что еще почитать по теме
  • Наноагрегатор новостей на примере футбольного сайта
  • Кроссдоменные ajax-запросы и причем здесь php
  • mysql-миграции: что это и как реализовать
  • Фильтры в интернет-магазине
  • Админка для сайта, основанная на файлах
  • Админка интернет-магазина на Vue.js. REST API на чистом php

Заходите в группу в контакте — https://vk.com/webdevkin

Анонсы статей, обсуждения интернет-магазинов, vue, фронтенда, php, гита.
Истории из жизни айти и обсуждение кода.

Пример простого REST API на PHP.

В данной заметке пример самого простого REST API на PHP без использования какого-либо фреймворка и других средств. Целью есть предоставить общую картину — как это все работает.
Недавно я уже опубликовал статью, в которой описан процесс создания REST API для проекта на Yii2.

Т.к. никакие фреймворки с маршрутизаторами в примере использоваться не будут, нужно начать с перенаправления всех запросов на «точку входа» — файл index.php. Для сервера на Apache это можно сделать в файле .htaccess который должен располагаться в корне проекта:

Options +FollowSymLinks IndexIgnore */* RewriteEngine on # Перенаправление с ДОМЕН на ДОМЕН/api RewriteCond % ^/$ RewriteRule ^(.*)$ /api/$1 [R=301] #Если URI начинается с api/ то перенаправлять все запросы на index.php RewriteEngine On RewriteCond % !-f RewriteCond % !-d RewriteRule ^api/(.*)$ /index.php

Согласно правил, ссылка должна начинаться на /api и ,например, для API работающего с таблицей users должна иметь такой вид:
ДОМЕН/api/users

Пример файла index.php

run(); > catch (Exception $e) < echo json_encode(Array('error' =>$e->getMessage())); >

Как видно из кода — будем работать с объектом usersApi, т.е. с пользователями (таблица users). Т.к. для простоты примера я не использую тут Composer или другой механизм для автозагрузки классов, просто подключим файл класса с помощью

require_once 'UsersApi.php';

Кроме пользователей, может потребоваться сделать api и для других сущностей, поэтому все классы различных API должны иметь один общий костяк, который будет определять метод запроса, действие для выполнения и тд. Создаем файл Api.php c абстрактным классом Api:

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->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:

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 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 not found", 404); > if(Users::deleteById($db, $userId))< return $this->response('Data deleted.', 200); > return $this->response("Delete error", 500); > >

Методы связанные с базой данных и получением данных из нее просто для примера.

Автор: Сергей Дата публикации: 05.10.2018

  • Использование YouTube Data API для своего WEB-сервиса. Выборка нужных видеозаписей, получение информации.
  • Определение страны и города посетителя по его IP. Расширение GeoIP.
  • Установка, настройка и базовое использование фреймворка для тестирования «Codeception».
  • Основы PHPUnit — 1 часть.
  • Использование событий в PHP.

Пример простого создания REST API на PHP

Пример простого создания REST API на PHP

В данной статье рассказывается пример простого REST API реализация которого на PHP без использования каких-либо фреймворков. Из-за того, что никак фреймворки применяться не будут, необходимо начать с того, что перенаправления всех запросов на точку входа, это index.php . Сервер где используется Apache , это можно реализовать в файле htaccess который должен находится в корне проекта:

Options +FollowSymLinks IndexIgnore */* RewriteEngine on # Перенаправление с domain.by на domain.by/api RewriteCond % ^/$ RewriteRule ^(.*)$ /api/$1 [R=301] #Если адрес начинается с api/ , то перенаправлять все запросы на index.php RewriteEngine On RewriteCond % !-f RewriteCond % !-d RewriteRule ^api/(.*)$ /index.php 

Исходя из правил, ссылка должна начинаться с /api , допустим, для API работающего с таблицей people должна иметь такой вид: domain.by/api/people Пример файла index.php :

require_once 'PeopleApi.php'; try < $api = new usersApi(); echo $api->run(); > catch (Exception $e) < echo json_encode(Array('error' =>$e->getMessage())); > 

Как видно из примера, будет идти работа с объектом peopleApi , в примере показано подключение файла c классом непосредственно через require_once .

require_once 'PeopleApi.php'; 

Кроме людей еще возможно нужно будет сделать API и по другим сущностям, и поэтому все классы различных API должны иметь один общий костяк, который будет определять метод запроса, действие для выполнения и так далее. Создаем файл API с абстрактным классом Api :

abstract class Api < public $apiName = ''; //people 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->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 . Для примера создаем файл PeopleApi.php :

require_once 'Api.php'; require_once 'Db.php'; require_once 'People.php'; class PeopleApi extends Api < public $apiName = 'people'; /** * Метод GET * Вывод списка всех записей * https://domain.by/people * @return string */ public function indexAction() < $db = (new Db())->getConnect(); $people = People::getAll($db); if($people)< return $this->response($people, 200); > return $this->response('Data not found', 404); > /** * Метод GET * Просмотр отдельной записи (по id) * https://domain.by/people/1 * @return string */ public function viewAction() < //id должен быть первым параметром после /people/x $id = array_shift($this->requestUri); if($id)< $db = (new Db())->getConnect(); $user = People::getById($db, $id); if($user)< return $this->response($user, 200); > > return $this->response('Data not found', 404); > /** * Метод POST * Создание новой записи * https://domain.by/people + параметры запроса name, email * @return string */ public function createAction() < $name = $this->requestParams['name'] ?? ''; $email = $this->requestParams['email'] ?? ''; if($name && $email)< $db = (new Db())->getConnect(); $user = new People($db, [ 'name' => $name, 'email' => $email ]); if($user = $user->saveNew())< return $this->response('Data saved.', 200); > > return $this->response("Saving error", 500); > /** * Метод PUT * Обновление отдельной записи (по ее id) * https://domain.by/people/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 || !People::getById($db, $userId))< return $this->response("User with not found", 404); > $name = $this->requestParams['name'] ?? ''; $email = $this->requestParams['email'] ?? ''; if($name && $email)< if($user = People::update($db, $userId, $name, $email))< return $this->response('Data updated.', 200); > > return $this->response("Update error", 400); > /** * Метод DELETE * Удаление отдельной записи (по ее id) * https://domain.by/people/1 * @return string */ public function deleteAction() < $parse_url = parse_url($this->requestUri[0]); $userId = $parse_url['path'] ?? null; $db = (new Db())->getConnect(); if(!$userId || !People::getById($db, $userId))< return $this->response("User with not found", 404); > if(People::deleteById($db, $userId))< return $this->response('Data deleted.', 200); > return $this->response("Delete error", 500); > > 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *