Компонент HttpKernel

Дата обновления перевода 2023-01-19

Компонент HttpKernel

Компонент HttpKernel предоставляет структурный процесс для преобразоваия Request в Response используя компонент EventDispatcher. Он достаточно гибкий, чтобы создавать полный фреймворк (Symfony), микро-фреймворк (Silex) или продвинутую CMS-систему (Drupal).

Установка

1
$ composer require symfony/http-kernel

Note

Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно подключить файл vendor/autoload.php в вашем коде для включения механизма автозагрузки классов, предоставляемых Composer. Детальнее читайте в этой статье.

Рабочий процесс запроса

See also

Эта статья объясняет как использовать функции HttpKernel как независимого компонента в любом приложении PHP. В приложениях Symfony уже всё настроено и готово к использованию. Прочитайте статьи Контроллер и События и слушатели событий для понимания как использовать эти функции при создании контроллеров и определения событий в приложениях Symfony.

Каждое веб-взаимодействие HTTP начинается с запроса и заканчивается ответом. Ваша работа, как разработчика, - создать PHP-код, который читает информацию запроса (например, URL) и создаёт и возвращает ответ (например, страницу HTML или строку JSON). Вот упрощённый обзор рабочего процесса запроса в приложениях Symfony:

  1. Пользователь запрашивает источник в браузере;
  2. Браузер отправляет запрос серверу;
  3. Symfony даёт приложению объект Запрос;
  4. Приложение генерирует объект Ответ, используя данные объекта Запрос;
  5. Сервер отправляет запрос обратно браузеру;
  6. Браузер отображает источник пользователю.

Обычно, какой-то фреймворк или система строятся для обработки всех повторяющихся задач (например, маршрутизации, безопасности и т.д.), чтобы разработчик мог с лёгкостью построить каждую страницу приложения. То как именно эти системы строятся, очень отличается. Компонент HttpKernel предоставляет интерфейс, который формализует процесс начала запроса и создание соответствующего ответа. Компонент должен быть сердцем любого приложения фреймворка, независимо от того, насколько изменена архитектура этой системы:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace Symfony\Component\HttpKernel;

use Symfony\Component\HttpFoundation\Request;

interface HttpKernelInterface
{
    // ...

    /**
     * @return Response A Response instance
     */
    public function handle(
        Request $request,
        int $type = self::MAIN_REQUEST,
        bool $catch = true
    );
}

Внутренне, метод HttpKernel::handle() - твёрдая реализация HttpKernelInterface::handle() - определяет рабочий процесс, который начинается с Request и заканчивается Response.

Точные детали этого рабочего процесса - ключ к помниманию того, как работает ядро (и фреймворк Symfony или любая другая библиотека, которая использует ядро).

HttpKernel: Управляемое событиями

Метод HttpKernel::handle() работает внутренне, запуская события. Это делает метод как гибким, так и немного абстрактным, так как вся "работа" фреймворка / приложения, построенная с помощью HttpKernel на самом деле производится слушателями событий.

Чтобы помочь объяснить этот процесс, данный документ рассматривает каждый шаг процесса и объясняет, как работает одна особенная реализация HttpKernel - фреймворк Symfony.

Изначально, использование HttpKernel очень просто и требует создания event dispatcher и controller and argument resolver (объясняется ниже). Чтобы завершить ваше работающее ядро, вы добавите больше слушателей событий к событиям, которые обсуждаются ниже:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\HttpKernel\HttpKernel;

// создать объект Request
$request = Request::createFromGlobals();

$dispatcher = new EventDispatcher();
// ... добавить какие-то слушатели событий

// создать ваш контроллер и разрешитель аргументов
$controllerResolver = new ControllerResolver();
$argumentResolver = new ArgumentResolver();

// инстанциировать ядро
$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);

// на самом деле запустить ядро, которое превращает запрос в ответ,
// запуская события, вызывая контроллер и возвращая ответ
$response = $kernel->handle($request);

// отправить загловки и отразить содержание
$response->send();

// запустить событие kernel.terminate
$kernel->terminate($request, $response);

См. "", чтобы увидеть более конкретную релизацию.

Чтобы узнать общую информацию о добавлении слушателей к событиям ниже, см. .

Caution

Начиная с 3.1, HttpKernel принимает четвёртый аргумент, который должен быть экземпляром ArgumentResolverInterface. В 4.0 этот аргумент станет обязательным.

See also

Существует ряд отличных туториалов об использовании компонента HttpKernel и других компонентов Symfony для создания вашего собственного фреймворка. См. Вступление.

1) Событие kernel.request

Типичные цели: Добавлять больше информации в Request, инициализировать части системы, или возвращать Response, если это возможно (например, слой безопасности, отказывающий в доступе).

Информационная таблица событий ядра

Первое событие, которое запускается внутре HttpKernel::handle - это kernel.request, которое может иметь множество разных слушателей.

Слушатели этого события могут сильно отличаться. Некоторые слушатели - например, слушатель безопасности - могут иметь достаточно информации, чтобы создать объект Response незамедлительно. Например, если слушатель безопасности определил, что пользователь не имеет доступа, этот слушатель может вернуть RedirectResponse к странице входа или ответ Отказ в доступе 403.

Если Response возвращается на этом этапе, то процесс переходит напрямую к событию kernel.response .

Другие слушатели просто инициализируют что-то или добавляют больше информации в запрос. Например, слушатель может определить и установить локаль в объекте Request.

Другой распространённый слушатель - маршрутизатор. Слушатель маршрутизатора может обрабатывать Request и определять контроллер, который должен быть отображён (см. следующий раздел). На самом деле, объект Request имеет набор "атрибутов", которая является идеальным местом для хранения дополнительных данных о запросе,относящихся к запросу. Это означает, что если ваш слушатель маршрутизатора каким-то образом определяет контроллер, он может хранить его в атрибутах Request (что может быть использовано вашим разрешителем контроллера).

В конце-концов, цель слушателя kernel.request - либо создать и вернуть Response напрямую, либо добавить информацию к Request (например, установить локаль или установить какую-то другую информацию в атрибутах Request).

Note

При установке ответа для события kernel.request, распространение останавливается. Это означает, что слушатели с более низким приоритетом, не будут выполняться.

Наиболее важный слушатель kernel.request в фреймворке Symfony - это класс RouterListener. Этот класс выполняет слой маршрутизации, который возвращает массив информации о соответствующем запросе, включая _controller и любые заполнители, которые находятся в схеме маршрута (например, {slug}). См. документацию маршрутизации.

Этот массив информации хранится в Request массива объекта attributes. Добавление информации о маршрутизации сюда пока ничего не делает, но используется при разрешении контроллера.

2) Разрешение контроллера

Предполагая, что ни один слушатель kernel.request не смог создать Response, следующий шаг в HttpKernel - определить и подготовить (т.е. разрешить) контроллер. Контроллер - это часть кода конечного приложения, которая отвечает за создание и возвращение Response для конкретной страницы. Единственное требование - чтобы PHP был вызываемым, т.е. функцией, методом, объектом или Closure.

Но то как вы определяете конкретный контроллер для запроса, зависит полностью от вашего приложения. Это работа "разрешителя контроллера" - класса, реализующего ControllerResolverInterface, и являющегося одним из аргументов конструктора HttpKernel.

Ваша работа заключается в создании класса, реализующего интерфейс, и заполнении его метода: getController(). На самом деле, одна реализация по умолчанию уже существует, и вы можете использовать её напрямую или научиться у: ControllerResolver. Эта реализация объясняется больше в сноске ниже:

1
2
3
4
5
6
7
8
namespace Symfony\Component\HttpKernel\Controller;

use Symfony\Component\HttpFoundation\Request;

interface ControllerResolverInterface
{
    public function getController(Request $request);
}

Внутренне, метод HttpKernel::handle() вначале вызывает getController() в разрешителе контроллера. Этот метод передаётся в Request и отвечает за определение и возвращение PHP-вызываемого (контроллера), основанного на информации запроса.

Фреймворк Symfony использует встроенный класс ControllerResolver (на самом деле, он использует подкласс с некоторым дополнительным функционалом, описанным ниже). Этот класс использует информацию, которая была размещена в свойстве attributesобъекта Request во время RouterListener.

getController

ControllerResolver ищет ключ _controller в свойстве атрибутов объекта Request (вспомните, что эта информация обычно размещается в Request через RouterListener). Эта строка затем преобрразуется в PHP вызываемое, путём выполнения следуюзего:

a) Если ключ _controller не следует рекомендованному формату пространства
имён PHP (например, App\Controller\DefaultController::index),т то его формат преобразуется в него. Например, формат наследования FooBundle:Default:index будет изменён на Acme\FooBundle\Controller\DefaultController::indexAction. Это преобразование относится к подклассу ControllerResolver, используемому фреймворком Symfony.
b) Новый экхемпляр вашего класса контроллера инстанциируется без аргументов
конструктора.
c) Если контролер реализует ContainerAwareInterface,
setContainer() вызывается в объекте контроллера и ему передаётся контейнер. Этот шаг также относится к подклассу ControllerResolver, используемому фреймворком Symfony.

3) Событие kernel.controller

Типичные цели: Инициализировать что-то или изменить контроллер прямо перед его выполнением.

Информационная таблица событий ядра

После того, как было определено вызываемое контроллера, HttpKernel::handle() запускает событие kernel.controller. Слушатели этого события могут инициализировать какую-то часть системы, которую нужно инициализировать после определения некоторых вещей (например, контроллера, информации маршрутизации), но перед выполнением контроллера. Чтобы увидеть примеры, смотрите раздел Symfony ниже.

Ещё один типичный случай применения этого события - извлекать атрибуты из контроллера, используя метод getAttributes(). См. раздел Symfony ниже, чтобы увидеть примеры.

6.2

Метод ControllerEvent::getAttributes() был представлен в Symfony 6.2.

Слушатели этого события могут также изменять вызываемое контроллера полностью, вызвав ControllerEvent::setController в объекте события, который передаётся слушателям этого события.

Интересный слушатель kernel.controller в фреймворке Symfony - CacheAttributeListener. Этот класс получает атрибут конфигурации #[Cache] из контроллера и исполььзует его для конфигурации HTTP кеширования ответа.

Существует несколько других небольших слушателей события kernel.controller в фреймворке Symfony, которые работают со сбором данных профилировщика, когда он включен.

4) Получение аргументов контроллера

Далее, HttpKernel::handle() вызывает ArgumentResolverInterface::getArguments(). Помните, что контроллер, возвращённый в getController() - вызываемое. Цель getArguments() 0 вернуть массив аргументов, который должен быть передан этому конроллеру. То, как именно это будет сделано, зависит только от вас, хотя встроенный ArgumentResolver является хорошим примером.

На этом этапе, ядро имеет PHP-вызываемое (контроллер) и массив аргументов, который должен быть передан при выполении этого вызываемого.

Теперь, когда вы точно знаете, что такое вызываемое контроллера (обычно метод внутри объекта контроллера), ArgumentResolver использует reflection в вызываемом, чтобы вернуть массив имён каждого из аргументов. Потом он итерирует каждый из этих аргументов и использует следующие фокусы, чтобы определить, какое значение стоит передать каждому аргументу:

a) Если сумка атрибутов Request содержит ключ, соответствующий имени аргумента,
то используется это значение. Например, если первый аршумент контроллера - $slug и в сумке attributes Request есть ключ slug, то используется это значение (и обычно это значение походит из RouterListener).
b) Если аргумент контроллера типизировван объектом Symfony
Request, то Request передаётся в качестве значения. Если у вас есть пользовательский класс Request, то он будет внедрён, если вы расширите Request Symfony.
c) Если функция или метод аргумента - вариативные (с переменным количеством
аргументов), и сумка attributes Request содеаржит массив для этого аргумента, то они все будут досупны через вариативный аргумент.

Этот функционал предоставлен разрешителями, реализующими ArgumentValueResolverInterface. Существует четыре реализации, которые предоставляют поведение Symfony по умолчанию, но основное здесь - настраиваемость. Реализуя ArgumentValueResolverInterface самостоятельно, и передавая это в ArgumentResolver, вы можете расширить этот функционал.

5) Вызов контроллера

Следущий шаг HttpKernel::handle() - выполнение контроллера.

Работа контроллера - построить ответ для заданного источника. Это может быть HTML страница, строка JSON или что-либо другое. В отличие от любой другой части процесса до этого времени, этот шаг реализуется "конечным-разработчиком" для каждой страницы, которая строится.

Обычно, контроллер возвращает объект Response. Если это так, то работа ядра уже почти закончена! В этом случае, следующий шаг - событие kernel.response .

Но если контроллер возвращает что-либо, кроме Response, то ядру предстоит ещё немного работы - kernel.view (так как конечная цель всегда - сгенерировать объект Response).

Note

Контроллер должен вернуть что-то. Если контроллер возвращает null, то незамедлительно будет вызвано исключение.

6) Событие kernel.view

Типичные цели: Преобразовать возвратное значение не-Response из контроллера в Response

Информационная таблица событий ядра

Если контроллер не возвращает объект Response, то ядро запскает другое событие - kernel.view. Работа слушателя этого события - вернуть значение контроллера (например, массив данных или объект), чтобы создать Response.

Это может быть полезно, если вы хотите исползовать слой "просмотра": вместо возвращения Response из контроллера, вы возвращаете даные, которые представляют страницу. Слушатель этого события потом может использовать эти данные, чтобы созать Response в правильном формате (например, HTML, JSON, и др.).

На этом этапе, если ни один слушатель не установил в событии ответ, вызывается исключение: либо контроллер, либо один из слушателей просмотра должен всегда возвращать Response.

Note

При установке ответа для события kernel.view, распространение останавливается. Это означает, что слушатели с более низким приоритетом не будут выполнены.

Внутри фреймворка Symfony не существует слушателя по умолчанию для события kernel.view. Однако, SensioFrameworkExtraBundle добавляет слушателя к событи. Если ваш контроллер взвращает массив, и вы помещаете аннотацию @Template над контроллером, тогда этот слушатель отображает шаблон, передаёт массив, который вы вернули из вашего контроллера в этот шаблон, а потом создаёт Response, содержащий возвращённое содержимое из этого шаблона.

Кроме того, популярный общественный пакет FOSRestBundle реализует слушателя этого события, который должен предоставить вам обширный слой просмотра, способный использовать единственный контроллер для возвращения множества различных по типу содержания ответов (например, HTML, JSON, XML, и др.).

7) Событие kernel.response

Типичные цели: Изменить объект Response прямо перед его отправкой

Информационная таблица событий ядра

Конечной целью ядра является преобразование Request в Response. Response может быть создан во время события kernel.request , возвращён из контроллера , или возвращён одним из слушателей события kernel.view .

Независимо от того, кто создаёт Response, другое событие - kernel.response, запускается сразу после этого. Типичный слушатель этого события изменит объект Response каким-то образом, например, изменит заголовки, добавит cookie, или даже изменит содержимое самого Response (например, внедрив JavaScript до конца тега </body> HTML-ответа).

После того, как запущено это событие, финальный объект Response возвращается из handle(). В наиболее частых случаях применения, после этого вы вызываете метод send(), который отправляет заголовки и отображает содержимое Response.

Существует несколько мелких слущателей внутри фреймворка Symfony, и большинство из них каким-то образом меняют ответ. Например, WebDebugToolbarListener внедряет некоторый JavaScript внизу вашей страницы в окружении dev, что вызывает отображение панели инструментов веб-отладки. Другой слушатель - ContextListener, сериализует информацию текущего пользователя в сессию, чтобы её можно было загрузить повторно при следующем запросе.

8) Событие kernel.terminate

Типичные цели: Выполнить какие-то "тяжёлые" действия после отправки ответа пользователю

Информационная таблица событий ядра

Финальным событием процесса HttpKernel является kernel.terminate и оно уникально, так как происходит после метода HttpKernel::handle(), и после того, как ответ отправлен пользователю. Вспомните из примеров выше, в таком случае код, использующий ядро, заканчивается так:

1
2
3
4
5
// отправляет заголовки и отражает содержимое
$response->send();

// запускает событие kernel.terminate
$kernel->terminate($request, $response);

Как вы видите, вызывав $kernel->terminate после отправки ответа, вы запустите событие kernel.terminate, где вы можете вполнить некоторые действия, которые вы откладывали, чтобы вернуть ответ клиенту максимально быстро (например, отправка электронных писем).

Caution

Внутренне, HttpKernel использует PHP функцию fastcgi_finish_request. Это означает, что в этот момент, только API сервер PHP FPM может отправлять ответ клиенту, в то время как процесс PHP сервера всё ещё выполняет какие-то задачи. Со всему другими API сервера, слушатели kernel.terminate всё равно выполняются, но ответ не отправляется клиенту, пока они все не будут выполнены.

Note

Использование события kernel.terminate необязательно, и должно быть вызывано только если ваше ядро реализует TerminableInterface.

Обработка исключений: событие kernel.exception

Типичные цели: Обработать какое-то исключение и создать подходящий Response для возврата исключению

Информационная таблица событий ядра

Если в какой-то момент внутри HttpKernel::handle() вызывается исключение, то вызывается другое событие - kernel.exception. Внутренне, тело функции handle() обёрнуто в блок "попробуй поймай". Когда вызывается исключение, запускается событие kernel.exception , чтобы ваша система могла как-то ответить на исключение.

Каждому слушателю этого события передаётся объект ExceptionEvent, который вы можете использовать для доступа к изначальному исключению через метод getThrowable(). Типичный слушатель этого события проверит наличие определённого типа исключений и создаст соответствующую ошибку Response.

Например, чтобы сгенерировать страницу 404, вы можете вызвать специальный тип исключения, а потом добавить слушатель этого события, который выглядит как исключение, и создаёт и возвращает 404 Response. На самом деле, компонент HttpKernel поставляется с ErrorListener, который, если вы решите его использовать, сделает это и многое другое по умолчаню (см. сноску ниже, чтобы узнать больше).

Note

При установке ответа для события kernel.exception, распространение останавливается. Это означает, что слушатели с более низким приоритетом не будут выполнены.

Существует для основных слушателя kernel.exception при использовании фреймворка Symfony.

ErrorListener в HttpKernel

Первый поставляется базово в компоненте HttpKernel и называется ErrorListener. Слушатель имеет несколько целей:

  1. Вызванное исключение преобразуется в объект FlattenException, который содержит информацию о запросе, которую можно отобразить и сериализовать (упорядочить).
  2. Если исходное исключение реализует HttpExceptionInterface, то в исключении вызываются getStatusCode() и getHeaders(), которые используются для наполнения загловков и статус-кодов объекта FlattenException. Идея заключается в том, что они используются в следущем шаге при создании финального ответа. Если вы хотите установить пользовательские HTTP-заголовки, вы всегда можете использовать метод setHeaders() в исключениях, пошедших от класса HttpException.
  3. Если исходное исключеие реализует RequestExceptionInterface, то статус-код объекта FlattenException наполняется 400 и не изменяются никакие другие заголовки.
  4. Контроллер выполняется и ему передаётся упрощённое исключение. Точный контроллер для отображения передаётся в виде аргумента конструктора этому слушателю. Этот контроллер вернёт финальный Response для этой страницы ошибки.

ExceptionListener в компоненте Security

Другой важный слушатель - ExceptionListener. Цель этого слушателя - обработать исключения безопасности, и, когда это правильно, помочь пользователю пройти аутентификацию (например, перенаправить на страницу входа).

Создание слушателя событий

Как вы видели, вы можете создавать и присоединять слушателей событий к любым событиям, запущенным во время цикла HttpKernel::handle(). Обычно слушатель - это PHP-класс с выполняющимся методом, но он может быть чем угодно. Чтобы узнать больше о создании и присоединении слушателей событий, см. Компонент EventDispatcher.

Имя каждого события "ядра" определяется в виде константы в классе KernelEvents. Кроме того, каждому слушателю событий передаётся один аргумент, который является подклассом KernelEvent. Этот объект содержит информацию о текущем состоянии системы, и каждое событие имеет свой собственный объект события:

??? ????????? KernelEvents ????????, ?????????? ?????????
kernel.request KernelEvents::REQUEST GetResponseEvent
kernel.controller KernelEvents::CONTROLLER FilterControllerEvent
kernel.controller_arguments KernelEvents::CONTROLLER_ARGUMENTS ControllerArgumentsEvent
kernel.view KernelEvents::VIEW GetResponseForControllerResultEvent
kernel.response KernelEvents::RESPONSE FilterResponseEvent
kernel.finish_request KernelEvents::FINISH_REQUEST FinishRequestEvent
kernel.terminate KernelEvents::TERMINATE PostResponseEvent
kernel.exception KernelEvents::EXCEPTION GetResponseForExceptionEvent

Полный рабочий пример

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\HttpKernel\EventListener\RouterListener;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$routes = new RouteCollection();
$routes->add('hello', new Route('/hello/{name}', [
    '_controller' => function (Request $request) {
        return new Response(
            sprintf("Hello %s", $request->get('name'))
        );
    }]
));

$request = Request::createFromGlobals();

$matcher = new UrlMatcher($routes, new RequestContext());

$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack()));

$controllerResolver = new ControllerResolver();
$argumentResolver = new ArgumentResolver();

$kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);

$response = $kernel->handle($request);
$response->send();

$kernel->terminate($request, $response);

Подзапросы

В дополенение в "главному" запросу, который отправляется в HttpKernel::handle(), вы можете также отправить так называемы "подзапрос". Подзапрос выглядит и ведёт себя так же, как любой другой запрос, но бычно слушит для отображения одной маленькой части страницы вместо целой. Вы чаще всего будете делать подзапросы из вашего контроллера (или, возможно, из шаблона, который отображается вашим контроллером).

Чтобы выполнить подзапрос, используйте HttpKernel::handle(), но измените второй аргумент следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

// ...

// создать какой-то другой запрос вручную, как это необходимо
$request = new Request();
// например, установить its _controller вручную
$request->attributes->set('_controller', '...');

$response = $kernel->handle($request, HttpKernelInterface::SUB_REQUEST);
// сделать что-то с ответом

Это создаёт ещё один полный цикл запрос-ответ, где новый Request преобразуется в Response. Единственное отличие внутренне в том, что некоторые слушатели (например, безопасности) могут действовать только в главном запросе. Каждому слушателю передаётся некоторый подкласс KernelEvent, метод isMainRequest() которого может быть использован для проверки, является ли текущий запрос "главным" или "под-" запросом.

Например, слушатель, которому нужно действовать только по главному запросу, может выглядеть так:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\HttpKernel\Event\RequestEvent;
// ...

public function onKernelRequest(RequestEvent $event)
{
    if (!$event->isMainRequest()) {
        return;
    }

    // ...
}

Расположение источников

Компонент HttpKernel отвечает за механизм пакета, используемый в приложениях Symfony. Ключевая функция пакетов заключается в том, что они позволяют переопределять любой источник, используемый приложением (файлы конфигурации, шаблоны, контроллеры, файлы переводов и др.).

Этот механизм переопределения работает, так как на источники ссылаются не по их физическому пути, а по логическому. Например, на файл services.xml, хранящийся в каталоге Resources/config/ пакета по имени FooBundle ссылаются так: @FooBundle/Resources/config/services.xml. Этот логический путь будет работать, когда приложение переопределит этот файл, и даже если вы измените каталог FooBundle.

Компонент HttpKernel предоставляет метод под названием locateResource(), который может быть использован для преобразования логических путей в физические:

1
$path = $kernel->locateResource('@FooBundle/Resources/config/services.xml');