> Как устроен жизненный цикл запроса в Symfony (PHP)

Уровень: senior · Роль: backend · Язык: PHP · Категория: Технические вопросы

Компании: Travelata

Стек: PHP

> Пример ответа

Короткий ответ

Жизненный цикл запроса в Symfony - это последовательность этапов от входа в public/index.php до отправки ответа клиенту. Ключевые шаги: загрузка ядра (Kernel), создание Request из глобальных переменных, обработка через HttpKernel::handle(), диспетчеризация событий (kernel.request, kernel.controller, kernel.response), разрешение контроллера, вызов action, формирование Response и его отправка. Вся логика построена вокруг event dispatcher и middleware-подобных listener'ов, что делает процесс расширяемым.

Подробное объяснение

Жизненный цикл начинается с фронт-контроллера public/index.php. Он загружает автозагрузчик Composer, создает экземпляр ядра (Kernel) в нужном окружении (dev, prod, test) и вызывает метод handle().

Внутри HttpKernel::handle() происходит следующее:

  1. Создание Request - из суперглобальных массивов ($_GET, $_POST, $_SERVER, $_COOKIE, $_FILES) формируется объект Symfony\Component\HttpFoundation\Request. Это происходит в Request::createFromGlobals().

  2. Диспетчеризация события kernel.request - здесь работают listener'ы, которые могут модифицировать запрос, создавать sub-request, или даже сразу вернуть Response (например, при кэшировании).

  3. Разрешение контроллера - через ControllerResolver определяется, какой класс и метод будут вызваны. Это происходит на основе маршрутизации (RouterListener), которая сопоставляет URL с маршрутом.

  4. Диспетчеризация события kernel.controller - позволяет изменить контроллер или его аргументы (например, для внедрения зависимостей через ArgumentResolver).

  5. Вызов контроллера - action возвращает либо Response, либо другие данные (например, массив для сериализации), которые затем конвертируются в Response.

  6. Диспетчеризация события kernel.response - финальная модификация ответа: добавление заголовков, сжатие, кэширование.

  7. Диспетчеризация события kernel.finish_request - завершение обработки, очистка ресурсов.

  8. Отправка ответа - Response::send() выводит заголовки и тело.

  9. Диспетчеризация события kernel.terminate - после отправки ответа выполняются "тяжелые" операции (например, отправка email), которые не должны задерживать ответ клиенту.

Важно: Symfony использует паттерн "front controller" и event-driven архитектуру. Каждый этап можно перехватить и модифицировать через event subscriber/listener. Также есть понятие "kernel.request" как единая точка входа для middleware-подобной логики.

На практике

Для senior-позиции важно понимать не только последовательность, но и практические аспекты:

  • Производительность: в dev окружении кэш конфигурации и маршрутов отключен, поэтому каждый запрос проходит полный цикл компиляции. В prod используется скомпилированный контейнер и кэш маршрутов.
  • Sub-requests: kernel.sub_request используется для вложенных запросов (например, при рендеринге вложенных шаблонов через render()).
  • Обработка исключений: kernel.exception - событие, которое перехватывает исключения и преобразует их в Response (например, 404, 500).
  • Кэширование HTTP: через HttpCache (reverse proxy) можно кэшировать ответы на уровне ядра, не затрагивая логику контроллера.
  • Микровремя: kernel.terminate позволяет выполнять фоновые задачи после отправки ответа, что критично для API с высоким RPS.

На практике часто нужно добавлять свои listener'ы на kernel.request для аутентификации, CORS, локализации. Или на kernel.response для добавления заголовков безопасности.

Пример кода

PHP
// public/index.php
use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};
PHP
// Пример listener'а на kernel.request
use Symfony\Component\HttpKernel\Event\RequestEvent;
class LocaleListener
{
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
$locale = $request->query->get('_locale', 'en');
$request->setLocale($locale);
}
}
PHP
// Регистрация listener'а в services.yaml
services:
App\EventListener\LocaleListener:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

Как отвечать на собеседовании

Начните с краткого описания потока: фронт-контроллер → Kernel → HttpKernel → события → контроллер → Response. Затем углубитесь в детали:

  • Подчеркните event-driven архитектуру и назовите ключевые события (kernel.request, kernel.controller, kernel.response, kernel.exception, kernel.terminate).
  • Объясните, как работает ControllerResolver и ArgumentResolver.
  • Упомяните разницу между handle() и terminate().
  • Приведите пример практического использования - например, listener для CORS или аутентификации.
  • Если спросят про производительность, расскажите про HttpCache и компиляцию контейнера.

Не углубляйтесь в реализацию каждого listener'а - покажите понимание архитектуры и умение расширять систему.

Что проверяет интервьюер

  • Понимание event-driven архитектуры и умение работать с event dispatcher.
  • Знание внутреннего устройства HttpKernel и Kernel.
  • Умение объяснить, как Symfony обрабатывает запрос от начала до конца.
  • Понимание практических аспектов: sub-requests, исключения, кэширование.
  • Способность проектировать расширения через listener'ы и subscriber'ы.

Типичные ошибки

  • Путаница между kernel.request и kernel.controller - первый срабатывает до разрешения контроллера, второй - после.
  • Утверждение, что kernel.terminate выполняется до отправки ответа - на самом деле после.
  • Игнорирование kernel.exception - без него исключения не превращаются в HTTP-ответы.
  • Непонимание разницы между Request и Response из HttpFoundation и HTTP-сообщениями PSR-7.
  • Забывают про kernel.finish_request - это событие срабатывает для каждого sub-request, а kernel.terminate - только для главного.
  • Попытка использовать kernel.request для изменения маршрута - это делается в RouterListener, который уже подписан на это событие.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью