Разработка открытого API на языке PHP
Основные подходы к разработке открытого API на PHP
Открытое API (Application Programming Interface) предоставляет внешним разработчикам возможность взаимодействовать с системой через HTTP. PHP остается популярным языком для создания таких интерфейсов благодаря обширной экосистеме. Рассмотрим эффективное решение и альтернативные варианты.
Как построить масштабируемое API без тяжелых фреймворков?
Наиболее эффективный подход для небольших и средних проектов - использование композиции легковесных библиотек: маршрутизатора FastRoute, PSR-7 реализации (например, laminas-diactoros) и обработчика ошибок. Это позволяет получить полный контроль над процессом без избыточных зависимостей.
Пример реализации:
<?
require 'vendor/autoload.php';
use FastRoute\RouteCollector;
use function FastRoute\simpleDispatcher;
$dispatcher = simpleDispatcher(function(RouteCollector $r) {
$r->addRoute('GET', '/api/users', 'get_users');
$r->addRoute('POST', '/api/users', 'create_user');
});
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND:
http_response_code(404);
echo json_encode(['error' => 'Not Found']);
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
break;
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
// Вызов функции обработчика
echo $handler($vars);
break;
}
function get_users($vars) {
header('Content-Type: application/json');
return json_encode([['id' => 1, 'name' => 'Alice']]);
}
function create_user($vars) {
$input = json_decode(file_get_contents('php://input'), true);
// валидация и сохранение
header('Content-Type: application/json');
return json_encode(['success' => true, 'id' => 2]);
}
?>Api https php (api на php с https)
Пояснение: маршрутизатор определяет, какой обработчик вызвать в зависимости от метода и URI. Обрабатываются случаи 404 и 405. Ответ формируется в JSON.
Типичные проблемы и их решения
- Отсутствие CORS-заголовков: клиентские запросы с другого домена блокируются. Необходимо добавить заголовки в каждом ответе:
header('Access-Control-Allow-Origin: *');. - Ошибки при больших входящих данных: проверять лимиты памяти и времени выполнения. Использовать
set_time_limit(0)при загрузке файлов. - Некорректная обработка PUT/DELETE: необходимо читать тело запроса для этих методов так же, как для POST.
Как сделать API без сторонних библиотек?
Простейший вариант - использование нативного PHP с ручной маршрутизацией через $_SERVER['REQUEST_URI'] и $_SERVER['REQUEST_METHOD']. Подходит для прототипирования или очень малых проектов.
<?
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ($path === '/api/hello' && $method === 'GET') {
echo json_encode(['message' => 'Hello World']);
} elseif ($path === '/api/echo' && $method === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
echo json_encode(['echo' => $data]);
} else {
http_response_code(404);
echo json_encode(['error' => 'Not found']);
}
?>Api php action (действия api в php)
Код прост, но с ростом числа эндпоинтов становится неудобным. Нет поддержки параметров пути (например, /api/users/{id}).
Проблемы и ограничения
- Отсутствие автомаршрутизации - каждый новый эндпоинт требует ручного дописывания условия.
- Невозможность использовать атрибуты или middleware.
- Сложность тестирования.
Как ускорить разработку API с помощью Slim?
Микрофреймворк Slim предоставляет готовую маршрутизацию, PSR-7, middleware и контейнер зависимостей. Установка через Composer: composer require slim/slim:^4.0.
<?
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/vendor/autoload.php';
$app = AppFactory::create();
$app->get('/api/users', function (Request $request, Response $response) {
$payload = json_encode([['id' => 1, 'name' => 'Bob']]);
$response->getBody()->write($payload);
return $response->withHeader('Content-Type', 'application/json');
});
$app->post('/api/users', function (Request $request, Response $response) {
$data = $request->getParsedBody();
// сохранение...
$response->getBody()->write(json_encode(['created' => true]));
return $response->withHeader('Content-Type', 'application/json');
});
$app->run();
?>
Json api php (php: создание json api)
Преимущество: разделение логики, легкое добавление middleware (например, для аутентификации).
Возможные сложности
- Необходимость изучения документации Slim для интеграции с ORM и валидацией.
- Ограниченная встроенная поддержка HATEOAS или документации (требуются дополнительные библиотеки).
Как использовать Laravel для создания открытого API?
Laravel предлагает полноценный набор инструментов: Eloquent, валидацию, пагинацию, ресурсы API. Установка: composer create-project laravel/laravel.
// routes/api.php
Route::get('/users', function () {
return UserResource::collection(User::all());
});
Route::post('/users', function (Request $request) {
$validated = $request->validate([
'name' => 'required|string',
'email' => 'required|email|unique:users',
]);
$user = User::create($validated);
return new UserResource($user);
});
API Resources упрощают форматирование ответов. Встроенная аутентификация через Laravel Sanctum или Passport.
Проблемы и их решения
- Высокое потребление памяти при большом количестве запросов - используйте кэширование через Redis.
- Сложность настройки CORS для специфичных заголовков - пакет fruitcake/laravel-cors.
- Склонность к “магии” - при ошибках сложно отследить источник.
Расширенные примеры создания открытого API
Аутентификация с использованием JWT
JWT (JSON Web Token) позволяет бесстатусную аутентификацию. Для PHP используйте библиотеку firebase/php-jwt. Пример выдачи и проверки токена.
<?
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secretKey = 'super-secret-key';
$payload = [
'iss' => 'example.com',
'iat' => time(),
'exp' => time() + 3600,
'sub' => 123
];
$token = JWT::encode($payload, $secretKey, 'HS256');
echo 'Token: ' . $token . PHP_EOL;
// Проверка
try {
$decoded = JWT::decode($token, new Key($secretKey, 'HS256'));
print_r($decoded);
} catch (Exception $e) {
echo 'Ошибка: ' . $e->getMessage();
}
?>
Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
stdClass Object
(
[iss] => example.com
[iat] => 1710000000
[exp] => 1710003600
[sub] => 123
)
Пояснение: токен создается с указанным сроком действия. При каждом запросе к защищенным маршрутам токен передается в заголовке Authorization: Bearer ... и декодируется. В случае невалидности возвращается 401.
Автоматическая генерация документации OpenAPI
Библиотека swagger-php (zircote/swagger-php) позволяет описывать API через аннотации в коде. Установка: composer require zircote/swagger-php.
/**
* @OA\Get(
* path="/api/users/{id}",
* @OA\Parameter(name="id", in="path", required=true, @OA\Schema(type="integer")),
* @OA\Response(response="200", description="OK", @OA\JsonContent(ref="#/components/schemas/User")),
* @OA\Response(response="404", description="Not Found")
* )
*/
function getUser($id) {
// ...
}
После запуска генератора создается файл openapi.json, который можно импортировать в Swagger UI. Пример команды: ./vendor/bin/openapi src/ -o public/openapi.json.
Реализация пагинации и фильтрации
Пагинация обязательна для списков. Пример на чистом PHP с использованием GET-параметров page и per_page.
<?
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = min(100, max(1, (int)($_GET['per_page'] ?? 10)));
$offset = ($page - 1) * $perPage;
$users = $db->query("SELECT * FROM users LIMIT $perPage OFFSET $offset")->fetchAll();
$total = $db->query("SELECT COUNT(*) FROM users")->fetchColumn();
header('Content-Type: application/json');
echo json_encode([
'data' => $users,
'meta' => [
'current_page' => $page,
'per_page' => $perPage,
'total' => $total,
'last_page' => ceil($total / $perPage)
]
]);
?>
{
"data": [{"id":1,"name":"Alice"}, ...],
"meta": {
"current_page": 1,
"per_page": 10,
"total": 25,
"last_page": 3
}
}
Фильтрация: добавьте параметр name__like, реализуйте построитель запросов с безопасной проверкой.
Ограничение частоты запросов (Rate Limiting)
Для защиты от злоупотреблений используйте middleware, например, slim/rate-limiter или собственную реализацию на Redis. Пример простого лимитера с хранением в файле.
<?
$ip = $_SERVER['REMOTE_ADDR'];
$rateFile = sys_get_temp_dir() . '/ratelimit_' . md5($ip) . '.txt';
$maxRequests = 100;
$interval = 3600; // 1 час
$data = @file_get_contents($rateFile);
$records = $data ? unserialize($data) : [];
$now = time();
$records = array_filter($records, fn($t) => $t > $now - $interval);
if (count($records) >= $maxRequests) {
http_response_code(429);
echo json_encode(['error' => 'Too many requests']);
exit;
}
$records[] = $now;
file_put_contents($rateFile, serialize($records));
?>
Результат: при превышении лимита возвращается 429 Too Many Requests. Для production используйте Redis.