Действия API в PHP: от простого switch до фреймворков

Раздел: Веб-разработка -> Разработка API

Действия API в PHP: подходы и реализация

При разработке API на PHP обработка различных действий (actions) - создание, чтение, обновление, удаление ресурсов - является ключевой задачей. Существует несколько подходов для организации этой логики, от простейших до более структурированных. Рассмотрим наиболее эффективное решение и альтернативные варианты.

Наиболее эффективным решением является использование маршрутизатора (router), который сопоставляет HTTP метод и URI с конкретным методом контроллера. Это соответствует RESTful архитектуре и легко расширяется. В качестве примера возьмем простой роутер на чистом PHP без фреймворка.


// index.php
$requestMethod = $_SERVER['REQUEST_METHOD'];
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

$routes = [
    'GET' => [
        '/users' => 'UserController::index',
        '/users/{id}' => 'UserController::show',
    ],
    'POST' => [
        '/users' => 'UserController::store',
    ],
    'PUT' => [
        '/users/{id}' => 'UserController::update',
    ],
    'DELETE' => [
        '/users/{id}' => 'UserController::destroy',
    ],
];

// Функция разбора шаблона
function matchRoute($method, $uri, $routes) {
    if (!isset($routes[$method])) return false;
    foreach ($routes[$method] as $pattern => $action) {
        $pattern = preg_replace('/\{([a-z]+)\}/', '(?P<$1>[0-9]+)', $pattern);
        $pattern = '#^' . $pattern . '$#';
        if (preg_match($pattern, $uri, $matches)) {
            $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
            return ['action' => $action, 'params' => $params];
        }
    }
    return false;
}

$matched = matchRoute($requestMethod, $requestUri, $routes);
if ($matched) {
    list($class, $method) = explode('::', $matched['action']);
    $controller = new $class();
    echo $controller->$method($matched['params']);
} else {
    http_response_code(404);
    echo json_encode(['error' => 'Endpoint not found']);
}
  

Api https php (api на php с https)

Пояснение шагов: определяется HTTP метод и путь, затем в массиве роутов ищется совпадение с использованием регулярных выражений. Для каждого роута можно передать параметры из URI (например, ID пользователя). Контроллер (например, UserController) содержит методы соответствующих действий. Такой подход обеспечивает чистоту кода, легкость добавления новых действий и тестирования.

Какие существуют альтернативные способы определения действий в API?

Вариант 1: Параметр action в строке запроса - один из самых простых, но не RESTful. Пример:


// ?action=create_user&name=John
$action = $_GET['action'] ?? null;
switch ($action) {
    case 'create_user':
        // логика создания пользователя
        break;
    case 'get_user':
        // ...
        break;
    default:
        http_response_code(400);
        echo 'Unknown action';
}
  

Api php action (действия api в php)

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

Типичные ошибки: забывают проверять наличие параметра, не фильтруют входные данные, путают имена действий. Решение: использовать белый список допустимых действий и валидацию ввода.

Как можно организовать действия через классы-обработчики (handlers)?

Вариант 2: Action Handler (Command Pattern) - каждое действие реализуется отдельным классом, реализующим интерфейс HandlerInterface.


interface HandlerInterface {
    public function handle(array $request): array;
}

class CreateUserHandler implements HandlerInterface {
    public function handle(array $request): array {
        // валидация и создание
        return ['status' => 'created', 'id' => 123];
    }
}

// роутер
$handler = new CreateUserHandler();
$response = $handler->handle($_POST);
  

Json api php (php: создание json api)

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

Ошибка: слишком много классов для простых проектов. Решение: использовать для сложных API с большим количеством действий.

Как применить микроплатформу Slim для действий API?

Вариант 3: Slim Framework - легковесный маршрутизатор с поддержкой middleware.


use Slim\Factory\AppFactory;

$app = AppFactory::create();

$app->get('/users', function ($request, $response) {
    $data = ['users' => [['id' => 1, 'name' => 'Alice']]];
    $response->getBody()->write(json_encode($data));
    return $response->withHeader('Content-Type', 'application/json');
});

$app->post('/users', function ($request, $response) {
    $body = $request->getBody()->getContents();
    // обработка
    return $response->withStatus(201);
});

$app->run();
  

Цель: профессиональная разработка с готовыми компонентами (PSR-7, PSR-15). Проблемы: требует установки composer, изучения документации.

Типичная ошибка: неправильная обработка PSR-7 сообщений, что приводит к пустым ответам. Решение: всегда писать тело ответа через $response->getBody()->write().

Выбор подхода зависит от масштаба проекта. Для небольших API можно использовать простой switch, для средних - самописный роутер с контроллерами, для крупных - фреймворк (Slim, Laravel, Symfony). Важно обеспечить единый формат ответов (JSON), обработку ошибок и CORS.

Расширенные примеры реализации действий API

Пример 1: Роутер с поддержкой middleware (аутентификация)

Пример

// Middleware для проверки токена
class AuthMiddleware {
    public function __invoke($request, $handler) {
        $authHeader = $request->getHeaderLine('Authorization');
        if (strpos($authHeader, 'Bearer ') !== 0) {
            $response = new Response(401);
            $response->getBody()->write(json_encode(['error' => 'Unauthorized']));
            return $response->withHeader('Content-Type', 'application/json');
        }
        $token = substr($authHeader, 7);
        // проверка токена...
        $request = $request->withAttribute('user_id', 1);
        return $handler->handle($request);
    }
}

// Применение в Slim
$app->group('/api', function ($group) {
    $group->get('/users', 'UserController:index');
    $group->post('/users', 'UserController:store');
})->add(new AuthMiddleware());
  
Результат: при запросе без токена возвращается 401 Unauthorized; с валидным токеном - нормальный ответ.
  

Пояснение: middleware оборачивает обработку запроса, добавляя проверку аутентификации. Это позволяет отделить общую логику от действий контроллера.

Пример 2: Использование PSR-15 RequestHandler (нативный PHP)

Пример

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\ServerRequestFactory;

class UserHandler implements RequestHandlerInterface {
    public function handle(ServerRequestInterface $request): ResponseInterface {
        $method = $request->getMethod();
        $path = $request->getUri()->getPath();
        if ($method === 'GET' && $path === '/users') {
            $data = getUsers(); // функция из БД
            $response = new Response(200);
            $response->getBody()->write(json_encode($data));
            return $response->withHeader('Content-Type', 'application/json');
        }
        if ($method === 'POST' && $path === '/users') {
            $body = $request->getBody()->getContents();
            $user = json_decode($body, true);
            $id = createUser($user);
            $response = new Response(201);
            $response->getBody()->write(json_encode(['id' => $id]));
            return $response;
        }
        return new Response(404);
    }
}

$request = ServerRequestFactory::fromGlobals();
$handler = new UserHandler();
$response = $handler->handle($request);
// отправка ответа
  
Результат: GET /users возвращает список, POST /users создает нового пользователя. Другие методы или пути дают 404.
  

Пояснение: данный пример демонстрирует полную реализацию на основе стандартов PSR-7/15 без фреймворка. Требуются библиотеки diactoros и http-response. Проблемы: необходимо самостоятельно реализовывать роутинг, обработку ошибок, CORS.

Пример 3: Действия через регулярные выражения и callable

Пример

class Router {
    protected $routes = [];

    public function add($method, $pattern, callable $handler) {
        $this->routes[] = ['method' => $method, 'pattern' => $pattern, 'handler' => $handler];
    }

    public function dispatch($method, $uri) {
        foreach ($this->routes as $route) {
            if ($route['method'] !== $method) continue;
            if (preg_match($route['pattern'], $uri, $matches)) {
                $params = array_slice($matches, 1);
                return call_user_func_array($route['handler'], $params);
            }
        }
        throw new \Exception('Route not found', 404);
    }
}

$router = new Router();
$router->add('GET', '#^/users/(\d+)$#', function($id) {
    return json_encode(['user_id' => $id]);
});
$router->add('POST', '#^/users$#', function() {
    $input = json_decode(file_get_contents('php://input'), true);
    return json_encode(['created' => $input['name']]);
});

try {
    echo $router->dispatch($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
} catch (\Exception $e) {
    http_response_code($e->getCode());
    echo json_encode(['error' => $e->getMessage()]);
}
  
Пример запроса: GET /users/42 -> {"user_id":42}
POST /users с body {"name":"Alice"} -> {"created":"Alice"}
  

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

Пример 4: Группировка действий через именованные контроллеры (на Laravel-стиль)

Пример

class UserController {
    public function index() { /* GET /users */ }
    public function store() { /* POST /users */ }
    public function show($id) { /* GET /users/{id} */ }
    public function update($id) { /* PUT /users/{id} */ }
    public function destroy($id) { /* DELETE /users/{id} */ }
}

// Регистрация через reflection
$controller = new UserController();
$method = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
// ... логика определения метода и параметров
$reflection = new ReflectionMethod($controller, $method);
if ($reflection->getNumberOfRequiredParameters() > 0) {
    $id = getPathVariable($uri); // вытаскиваем id
    echo $controller->$method($id);
} else {
    echo $controller->$method();
}
  
Результат: при обращении к /users/7 методом DELETE вызовется UserController::destroy(7).
  

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

Действия API в PHP - comments

En
Api php action (php)