rzaripov.kz

Разработка REST API на PHP с использованием Slim Framework

REST API остаётся удобным способом связать мобильное приложение, веб-клиент и серверную часть. Для Android, iOS, Windows, macOS и Linux можно создать единый набор HTTP-методов, который возвращает данные в формате JSON и скрывает внутреннюю реализацию базы данных. Такой подход особенно полезен в кроссплатформенных проектах, где клиентские приложения должны работать с одинаковой бизнес-логикой.

Разработка REST API на PHP с использованием Slim Framework позволяет собрать лёгкий и понятный сервер без избыточной инфраструктуры. Slim отвечает за маршрутизацию, обработку HTTP-запросов и middleware, а разработчик самостоятельно выбирает ORM или уровень доступа к данным, контейнер зависимостей, систему авторизации и инструменты тестирования.

Подготовка проекта и зависимостей

Slim 4 устанавливается через Composer. Для базового API достаточно PHP 8.1 или более новой версии, самого фреймворка и реализации PSR-7 для HTTP-сообщений. В качестве такой реализации часто используется пакет nyholm/psr7, а для запуска приложения в окружении PHP может применяться php-di/php-di.

composer require slim/slim:"4.*" nyholm/psr7

После установки удобно разделить проект на каталоги public, src, config и tests. В папке public размещается единственная точка входа, обычно файл index.php. Исходный код маршрутов и обработчиков находится в src, а настройки подключения к базе данных и окружению не должны смешиваться с логикой контроллеров.

Минимальный экземпляр приложения создаётся так:

use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/health', function ($request, $response) {
    $response->getBody()->write(json_encode(['status' => 'ok']));
    return $response->withHeader('Content-Type', 'application/json');
});

$app->run();

Проектирование маршрутов и ресурсов

REST-подход строится вокруг ресурсов, а не отдельных действий. Например, сущность пользователя может быть доступна по адресу /api/users, а конкретная запись — по /api/users/{id}. Метод GET используется для чтения, POST — для создания, PUT или PATCH — для изменения, а DELETE — для удаления.

В Slim маршрут связывается с анонимной функцией, классом контроллера или invokable-объектом. Параметры пути доступны через $args, а тело запроса читается методом getParsedBody(), если перед этим подключено корректное декодирование JSON. Для единообразия полезно сразу определить префикс версии, например /api/v1, чтобы в будущем менять контракт без резкого нарушения работы мобильных клиентов.

$app->group('/api/v1', function ($group) {
    $group->get('/users/{id}', UserController::class . ':show');
    $group->post('/users', UserController::class . ':store');
});

Контроллер не должен содержать всю бизнес-логику. Его задача — принять входные данные, вызвать сервис, преобразовать результат в ответ и установить HTTP-статус. Такое разделение упрощает поддержку, повторное использование кода и написание автоматических тестов.

Формирование JSON-ответов и обработка ошибок

API должен возвращать предсказуемые ответы. Успешный запрос обычно сопровождается объектом или массивом в JSON, а ошибка — структурой с кодом, сообщением и дополнительными сведениями. Заголовок Content-Type: application/json следует устанавливать для каждого ответа, включая сообщения об ошибках.

Практично вынести сериализацию в отдельный метод или небольшой класс:

function jsonResponse($response, array $data, int $status = 200)
{
    $payload = json_encode(
        $data,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    $response->getBody()->write($payload);

    return $response
        ->withStatus($status)
        ->withHeader('Content-Type', 'application/json');
}

Для отсутствующего ресурса используется статус 404, для некорректных входных данных — 400 или 422, для отсутствующей авторизации — 401, а для недостатка прав — 403. В производственной среде внутренние тексты исключений нельзя отправлять клиенту: они могут раскрыть структуру базы данных или пути файловой системы. Подробности записываются в журнал, а наружу передаётся безопасное сообщение.

Middleware и жизненный цикл запроса

Middleware в Slim располагается между HTTP-сервером и обработчиком маршрута. Через него удобно реализовать проверку токена, CORS, журналирование, ограничение частоты запросов, установку идентификатора операции и обработку исключений. Каждый слой получает объект запроса и следующий обработчик, после чего может изменить запрос, остановить выполнение или преобразовать ответ.

Порядок подключения имеет значение. Middleware для обработки тела запроса должен выполняться до контроллера, а слой перехвата ошибок — охватывать приложение целиком. Для JSON API часто подключают BodyParsingMiddleware, чтобы данные из application/json были доступны через $request->getParsedBody().

$app->addBodyParsingMiddleware();

$app->addErrorMiddleware(
    $displayErrorDetails = false,
    $logErrors = true,
    $logErrorDetails = true
);

CORS следует настраивать осознанно: в заголовке Access-Control-Allow-Origin лучше указывать конкретные доверенные домены, а не использовать * вместе с авторизационными cookie. Для сложных запросов необходимо обрабатывать метод OPTIONS и разрешать только требуемые заголовки и HTTP-методы.

Подключение базы данных через PDO

Slim не навязывает конкретную систему хранения, поэтому для небольшого REST-сервиса достаточно PDO, а для сложной предметной области можно использовать Doctrine или другой ORM. Подключение к базе данных следует создавать через фабрику или контейнер зависимостей, чтобы контроллеры не создавали соединение самостоятельно.

Параметры подключения хранятся в переменных окружения, а не в исходном коде. Подготовленные выражения защищают от SQL-инъекций и позволяют передавать значения отдельно от SQL-команды:

$stmt = $pdo->prepare(
    'SELECT id, name, email FROM users WHERE id = :id'
);
$stmt->execute(['id' => $id]);

$user = $stmt->fetch(PDO::FETCH_ASSOC);

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

Авторизация и защита конечных точек

Для мобильных и настольных клиентов часто используется токен Bearer, передаваемый в заголовке Authorization. Middleware извлекает токен, проверяет его подпись или ищет соответствующую сессию, а затем добавляет сведения о пользователе в объект запроса. Контроллер получает уже проверенную личность и не повторяет код аутентификации.

Пароли необходимо хранить только в виде хешей, созданных функцией password_hash(). Передача данных выполняется по HTTPS, а секретные ключи, токены и строки подключения не включаются в репозиторий. Для чувствительных операций полезно применять короткое время жизни access-токена, обновляемые refresh-токены и отзыв скомпрометированных сессий.

Одной проверки личности недостаточно. Сервер должен контролировать права на уровне ресурса: пользователь может редактировать собственный профиль, но не чужие данные, а администратор получает отдельные разрешения. Дополнительно стоит ограничить количество запросов, защититься от массового присваивания полей и явно описать разрешённые свойства входного JSON.

Тестирование и публикация сервиса

Проверять REST API можно с помощью PHPUnit, инструмента curl, Postman или автоматизированного набора запросов. Тесты должны охватывать успешные сценарии, неверный JSON, отсутствие обязательного поля, неизвестный идентификатор, недостаток прав и ошибки базы данных. Для Slim удобно создавать приложение в тестовом окружении и отправлять ему PSR-7-запросы без запуска полноценного веб-сервера.

Перед публикацией на сервере настраиваются PHP-FPM, Nginx или Apache, HTTPS, переменные окружения и миграции базы данных. Веб-сервер должен направлять запросы через public/index.php, а каталог vendor устанавливается командой composer install --no-dev --optimize-autoloader. Логи приложения отделяются от системных логов и регулярно контролируются.

Производительность повышают индексы базы данных, кэширование редко меняющихся данных и ограничение размера входящего запроса. Документацию API удобно вести в формате OpenAPI, чтобы клиенты на Kotlin, Swift, Delphi или других языках получали единый контракт. Версионирование маршрутов, понятные статусы и стабильная схема JSON существенно упрощают развитие сервиса и интеграцию с несколькими платформами.