Разработка открытого API на языке PHP

Раздел: Разработка программного обеспечения -> Разработка API

Основные подходы к разработке открытого 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.

Открытое API на PHP - comments

En
Php open api (php)