REST API стал стандартом де-факто для обмена данными между приложениями. Если вы пишете на PHP и хотите создать свой первый или улучшить существующий REST-сервис, эта статья для вас. Я поделюсь практическим опытом, покажу код и расскажу, как избежать типичных граблей.
Что такое REST и с чем его едят
REST (Representational State Transfer) — это архитектурный стиль, а не протокол. Простыми словами, это набор правил, как клиент и сервер общаются через HTTP. Каждый URL — это ресурс. Действия с ресурсом определяются HTTP-методами: GET — получить, POST — создать, PUT — обновить, DELETE — удалить. Всё просто, как в жизни: пришёл в магазин (URL), взял товар (GET), положил в корзину (POST), изменил количество (PUT), выкинул (DELETE).
Главное преимущество REST — единообразие. Клиент точно знает, как запросить данные, а сервер знает, как ответить. Формат данных обычно JSON — легко читается и парсится.
Скелет REST-сервиса на чистом PHP
Многие сразу тянут фреймворки вроде Laravel или Slim. Но чтобы понять суть, лучше начать с чистого PHP. Создадим точку входа — index.php. Она будет принимать все запросы и направлять их в нужный обработчик.
<?php
// index.php
// Разрешаем CORS (если нужно)
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
// Получаем метод и путь
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = rtrim($path, '/');
// Простейшая маршрутизация
switch (true) {
case $method === 'GET' && $path === '/api/users':
getUsers();
break;
case $method === 'GET' && preg_match('#^/api/users/(\d+)$#', $path, $matches):
getUser($matches[1]);
break;
case $method === 'POST' && $path === '/api/users':
createUser();
break;
case $method === 'PUT' && preg_match('#^/api/users/(\d+)$#', $path, $matches):
updateUser($matches[1]);
break;
case $method === 'DELETE' && preg_match('#^/api/users/(\d+)$#', $path, $matches):
deleteUser($matches[1]);
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Not Found']);
}
Это минимальный роутинг. В реальном проекте лучше вынести его в отдельный класс, но для старта сойдёт.
Обработка запросов и ответов
Теперь напишем функции-обработчики. Данные будем хранить в массиве (для примера), в реальности — база данных. Важно правильно читать тело запроса для POST и PUT.
function getUsers() {
// Здесь мог бы быть запрос к БД
$users = [
['id' => 1, 'name' => 'Алиса'],
['id' => 2, 'name' => 'Боб']
];
echo json_encode($users);
}
function getUser($id) {
// Эмуляция поиска
$users = [
1 => ['id' => 1, 'name' => 'Алиса'],
2 => ['id' => 2, 'name' => 'Боб']
];
if (isset($users[$id])) {
echo json_encode($users[$id]);
} else {
http_response_code(404);
echo json_encode(['error' => 'User not found']);
}
}
function createUser() {
$input = json_decode(file_get_contents('php://input'), true);
if (!isset($input['name'])) {
http_response_code(400);
echo json_encode(['error' => 'Name is required']);
return;
}
// Логика сохранения (например, в БД)
$newUser = ['id' => 3, 'name' => $input['name']];
http_response_code(201);
echo json_encode($newUser);
}
function updateUser($id) {
$input = json_decode(file_get_contents('php://input'), true);
// Проверяем существование и обновляем
echo json_encode(['id' => $id, 'name' => $input['name'] ?? 'Обновлено']);
}
function deleteUser($id) {
// Удаляем
http_response_code(204); // No Content
}
Обратите внимание на коды ответов: 200 — успех, 201 — создано, 204 — удалено, 400 — неверный запрос, 404 — не найдено, 500 — ошибка сервера. Это важно для клиента.
json_decode с проверкой на null. Иначе получите фатальную ошибку и белый экран.
Продвинутая маршрутизация и автозагрузка
Когда маршрутов становится много, switch превращается в кашу. Лучше использовать регулярные выражения и вынести всё в отдельный класс. Также подключим автозагрузку через Composer (если ещё не используете — пора начать).
Пример простого роутера с поддержкой параметров:
class Router {
private $routes = [];
public function add($method, $pattern, $callback) {
$this->routes[] = compact('method', 'pattern', 'callback');
}
public function dispatch($method, $uri) {
foreach ($this->routes as $route) {
if ($route['method'] !== $method) continue;
$pattern = '#^' . $route['pattern'] . '$#';
if (preg_match($pattern, $uri, $matches)) {
array_shift($matches);
return call_user_func_array($route['callback'], $matches);
}
}
http_response_code(404);
echo json_encode(['error' => 'Not Found']);
}
}
// Использование
$router = new Router();
$router->add('GET', '/api/users', 'getUsers');
$router->add('GET', '/api/users/(\d+)', 'getUser');
$router->add('POST', '/api/users', 'createUser');
$router->dispatch($_SERVER['REQUEST_METHOD'], $path);
Такой подход гораздо гибче. Можно легко добавлять новые маршруты, не трогая логику.
Аутентификация и безопасность
REST API часто требует авторизации. Самый простой способ — токены. Клиент передаёт токен в заголовке Authorization: Bearer <token>. Сервер проверяет его валидность. Вот пример проверки:
function authenticate() {
$headers = getallheaders();
if (!isset($headers['Authorization'])) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
$token = str_replace('Bearer ', '', $headers['Authorization']);
// Проверяем токен в БД или расшифровываем JWT
if ($token !== 'secret-token') {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
exit;
}
}
// Вызываем перед обработкой защищённых маршрутов
authenticate();
Не забывайте про SQL-инъекции, XSS и другие атаки. Используйте подготовленные запросы (prepared statements) при работе с БД. Никогда не доверяйте данным от клиента.
Сравнение подходов: чистый PHP vs фреймворки
Когда стоит писать на чистом PHP, а когда брать фреймворк? Вот таблица для сравнения.
| Критерий | Чистый PHP | Фреймворк (Laravel, Slim) |
|---|---|---|
| Скорость разработки | Низкая | Высокая |
| Гибкость | Полная | Ограничена правилами |
| Порог входа | Низкий | Средний/высокий |
| Производительность | Высокая | Средняя |
| Безопасность | Всё на ваших плечах | Многое из коробки |
Для маленьких проектов или обучения чистый PHP — отличный выбор. Для крупных систем лучше взять фреймворк, чтобы не изобретать велосипед.
Что в итоге
Создать RESTful сервис на PHP — задача не rocket science. Главное — понять принципы: ресурсы, методы, коды ответов. Начните с простого роутинга, постепенно добавляйте функциональность. Не забывайте про безопасность и валидацию. И помните: даже самый навороченный API строится из простых кусочков.
Практикуйтесь, пишите код, экспериментируйте. И всё получится.



