Вольный перевод официальной документации.
Компонент HttpKernel обеспечивает структурированный процесс, в результате которого объект Request преобразуется в Response, при помощи компонента EventDispatcher.
HttpKernel достаточно гибок, чтобы на его базе создавать полнофункциональные фреймворки (Symfony), микро-фреймвори (Silex) или мощные CMS системы (Drupal).
Установка
Вы можете установить компонент двумя разными способами:
- Через Composer (symphony/http-kernel – проект packagist.org)
- Официальный Git репозиторий (http://github.com/symfony/http-kernel)
Затем, подключить в своем скрипте автозагрузчик – require ‘vendor/autoload.php’, для включения механизма автозагрузки, который предоставляет composer. В противном случае, ваше приложение не сможет воспользоваться классами данного компонента Symfony.
Процесс работы
Каждое взаимодействие через HTTP, начинается с запроса и заканчивается ответом. Ваша задача, как разработчика, создать PHP код, который проанализирует запрос и сгенерирует нужный ответ.
Схематично данный процесс можно описать следующим образом:
- Пользователь набирает в браузере адрес сайта
- Браузер отправляет запрос на сервер
- Symfony предоставляет разработчику объект Request
- Разработчик, на основании объекта Request, подготавливает объект Response
- Сервер посылает ответ браузеру пользователя
- Браузер отображает ответ пользователю
Как правило, для обработки запросов используется какая-то готовая система или фреймворк, в задачи которого входит обеспечение безопасности, маршрутизации и т.д. так чтобы разработчик мог заниматься непосредственно самим проектом, а не отвлекаться на разработку вспомогательных инструментов. Тем более, что они из проекта в проект чаще всего идентичны.
Конкретные реализации таких систем сильно различаются. Компонент HttpKernel предоставляет интерфейс, которые описывает процесс, начиная с запроса, заканчивая ответом. Таким образом, компонент может быть использован, как основа для любого проекта или фреймворка, независимо от архитектуры, которая будет построена поверх него.
namespace Symfony\Component\HttpKernel; use Symfony\Component\HttpFoundation\Request; interface HttpKernelInterface { // ... /** * @return Response A Response instance */ public function handle( Request $request, $type = self::MASTER_REQUEST, $catch = true ); }
Внутри компонента, HttpKernel::handle() – конкретная реализация интерфейса HttpKernelInterface::handle() – определяет метод, который принимает объект Request, а возвращает Response.

Изучение работы данного процесса (последовательности) – ключ к пониманию работы компонента (а значит и фреймворка Symfony или любого другого продукта, построенного на нем).
HttpKernel работает на событиях:
HttpKernel::handle() — генерирует события. Это делает метод и гибким и абстрактным, поскольку вся «работа» фреймворка/проекта построенного на компоненте, на самом деле, выполняется в обработчиках этих событий.
Для лучшего понимания, рассмотрим каждый этап и покажем какие именно события и как реализованы, на примере фреймворка Symfony.
В целом, использование HttpKernel очень простое: включает в себя создание экземпляра EventDispatcher (диспетчер событий), а так же экземпляров ArgumentResolver и ControllerResolver, о которых детально поговорим позже. Для запуска ядра вам потребуется добавить обработчики событий, которые рассмотрим далее:
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; use Symfony\Component\HttpKernel\Controller\ControllerResolver; // создаем объект Request $request = Request::createFromGlobals(); $dispatcher = new EventDispatcher(); // ... добавляем обработчики // создаем controllerResolver и ArgumentResolver $controllerResolver = new ControllerResolver(); $argumentResolver = new ArgumentResolver(); // инициализируем HttpKernel $kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver); // запускаем метод ядра, который на базе Request вернет Response // посредствам генерирования событий и вызова контроллера-обработчика. $response = $kernel->handle($request); // отправляем браузеру заголовки и контент $response->send(); // генерируем событие kernel.terminat $kernel->terminate($request, $response);
Полностью рабочий пример приводится в конце статьи.
Предупреждение! С версии 3.1, конструктор HttpKernel может принимать четвертый аргумент – объект, который должен быть реализацией интерфейса ArgumentResolverInterface. В версии 4.0 этот аргумент станет обязательным.
1) Событие kernel.request
Обычно используется, для добавления информации (заполнения) объекта Request, инициализации подсистем фреймворка, или сразу же для возврата объекта Response, если это необходимо (например когда система безопасности запретила доступ). Возврат Response, по сути, означает завершение работы.
Kernel.request – самое первое событие, которое генерирует HttpKernel::handle(). На него могут быть подписаны различные обработчики (listeners).

Некоторые обработчики событий – такой как система безопасности – могут располагать достаточной информацией, чтобы создать объект Response сразу же. Например, если в ходе проверки выяснится, что пользователь не имеет доступа, обработчик может вернуть объект RedirectResponce для перенаправления на страницу авторизации. Или ошибку 403 – доступ запрещен.
Если на данном этапе обработчиком возвращается объект Response, все промежуточные события пропускаются и процесс переходит сразу же к событию kernel.response.

Остальные обработчики просто инициализируют подсистемы фреймворка, или добавляют в объект Request дополнительную информацию. Например, обработчик может определять и записывать в объект требуемый язык.
Еще один часто используемый обработчик – URI роутер. Он анализирует информацию, содержащуюся в Request, и определяет, какой контроллер должен быть вызван для генерации контента. По сути, объект Request содержит, своего рода, хранилище данных, которое прекрасно подходит для передачи параметров между подсистемами. То есть, если роутер определил, какой должен быть вызван контроллер, он может сохранить эту информацию в данном объекте. Она будет использована в дальнейшем другой подсистемой, такой как ControllerResolver, к примеру.
Когда один из обработчиков события kernel.request возвращает объект Response, выполнение других обработчиков данного события прекращается. Другими словами обработчики с меньшим приоритетом выполнены не будут.
Наиболее важным подписчиком события kernel.request во фреймворке, является RouterListner. Этот класс запускает слой маршрутизации, который возвращает массив с дополнительной информацией на основании запроса, включая имя контроллера (_controller) и все дополнительные параметры из маршрута, например {slug}. Для более подробной информации, смотрите описание компонента Routing
2) Подготовка контроллера (Resolve the Controller)
Допустим, обработчики события kernel.request не вернули Response. Следующий этап компонента HttpKernel – определить и подготовить контроллер. Контроллер — это часть вашего кода, которая создает и заполняет объект Response для конкретной страницы сайта. Единственное требование к нему – возможность быть вызванным (функция, метод или Closure).
Но как вы будете определять, какой именно контроллер следует вызвать, зависит исключительно от вашего проекта. Эту задачу решает «controller resolver» — класс, который реализует интерфейс ControllerResolverInterface. Объект данного класса передается в конструктор HttpKernel при инициализации.

Ваша задача, написать класс, и реализовать два метода: getController() и getArguments(). Вообще-то, реализация по умолчанию уже существует, вы можете ее использовать или изучить ее код, в целях обучения – класс ControllerResolver из первого примера. Код его интерфейса приведен ниже:
namespace Symfony\Component\HttpKernel\Controller; use Symfony\Component\HttpFoundation\Request; interface ControllerResolverInterface { public function getController(Request $request); public function getArguments(Request $request, $controller); }
Предупреждение! Метод getArgument() класса ControllerResolver и соответствующий ему метод, описанный в интерфейсе, объявлен устаревшим, начиная с версии 3.1 и будет удален в версии 4.0. Вы можете использовать ArgumentResolver, который реализует ArgumentResolverInterface, вместо этого.
Внутри метода HttpKernel::handle() сначала вызывается getController() объекта ControllerResolver. В этот метод передается Request и он должен вернуть вызываемую функцию (PHP callable), т.е. контроллер, выбранный на основании информации, содержащейся в Request.
Второй метод – getArguments(), будет вызван после события kernel.controller.
Symfony использует встроенный ControllerResolver. (На самом деле, используется подкласс, отнаследованный от него, который расширяет фнкционал). Этот класс извлекает информацию из объекта Request, которую записал туда роутер (RouterListner).
getController
ControllerResolver ищет ключ _controller, в объекте Request. Помните, говорили о «хранилище» для передачи параметров между подсистемами? Эта информация помогает «вернуть» PHP callable следующим образом:
- Имя контроллера (ключ _controller), записанное в формате AcmeDemoBundle:Default:index, заменяется на строку другого формата (которая содержит полное имя класса и метода), принятого в Symfony – то есть Acme\DemoBundle\Controller\DefaultController::indexAction. Правила этого преобразования как раз и реализованы в подклассе ControllerResolver’a, они специфичны для Symfony.
- Создается объект вашего целевого контроллера, причем никакие параметры в его конструктор не передаются.
- Если ваш контроллер реализует интерфейс ContainerAwareInterface, вызывается метод setContainer(), у объекта (п.2) и в него передается контейнер. Этот шаг тоже реализован в подклассе ControllerResolver (поставляется/используется вместе с Symfony).
3) Событие kernel.controller
Обычно используется для инициализации подсистем или изменений контроллера, перед тем как он будет запущен.
После того, как контроллер был определен и подготовлен, HttpKernel::handle() генерирует событие kernel.controller. Подписчики данного события могут инициализировать части системы, необходимые после того, как некоторые вещи были определены (т.е. имя контроллера, информация о роуте и т.д.), но перед тем как он будет запущен.

Обработчики данного события так же могут полностью поменять контроллер на другой, с помощью вызова setController() объекта FilterControllerEvent, который им передается.
Есть несколько обработчиков события kernel.controller в Symfony и они по большей части касаются сбора информации для профайлера (дебаггера), когда тот активирован.
Одни интересный обработчик подключается из SensioFrameworkExtraBundle, он идет из коробки Standard Edition. Функционал @ParamConverter позволяет передать объект целиком (к примеру, Post объект) вашему контроллеру, вместо скалярных значений (параметра id, что был в роуте). Обработчик ParamConverterListner использует рефлексию (reflection), чтобы «посмотреть» на каждый из параметров метода вашего контроллера, и пытается применить различные подходы, для конвертирования их в объект, который потом сохраняется в атрибутах Request. В следующей секции станет ясно, почему это важно.
4) Извлечение аргументов контроллера (вызываемого метода)
Далее, HttpKernel::handle() вызывает ArgumentResolverInterface::getArguments(). Помните, что контроллер возвращенный методом getController() можно запустить (callable). Цель getArguments() вернуть массив аргументов, которые должны быть переданы методу. Как именно это будет реализовано, полностью зависит от вас, однако, встроенный ArgumentResolver может быть хорошим примером.

В данный момент, у ядра есть PHP callable (ваш контроллер) и массив с аргументами, которые должны быть ему переданы.
Теперь, когда вы знаете что такое вызываемый контроллер (обычно метод ), ArgumentResolver использует reflection (механизм PHP), применительно к объекту вашего класса, и получает имена каждого из аргументов. Потом он перебирает их все, и решает, какое значение должно быть передано для каждого из них следующим образом:
- Если в объекте Request, точнее в «хранилище атрибутов», содержится ключ, соответствующий имени аргумента, используется значение оттуда. Например, имя первого аргумента контроллера – $slug, и в атрибутах объекта Request присутствует slug, то используется его значение. Обычно slug записывается в «хранилище» Request обработчиком RouterListner.
- Если у аргумента указан тип Symfony — Request, тогда в качестве значения аргумента передается объект Request. Если вы расширили класс Request своим подклассом, то передается в качестве аргумента он.
- Когда у контроллера переменное количество аргументов (variadic function) и «хранилище» Request содержит массив для этого аргумента, он будет доступен через variadic.
Перечисленные функции предоставляются резолверами, которые реализуют ArgumentValueResolverInterface. Существует четыре реализации, которые отвечают за стандартное поведение Symfony, но вы можете их кастомизировать. Реализовав указанный интерфейс, и передав объект в ArgumentResolver, вы можете расширить описанный функционал.
5) выполнение контроллера
Следующий шаг прост! HttpKernel::handle() запускает контроллер.

Задача контроллера сформировать ответ для текущего запроса. Это может быть HTML страница, JSON строка или что-то другое. В отличие от других этапов, которые мы рассматривали, данный шаг полностью реализуется разработчиком проекта, для каждой его страницы.
Обычно, контроллер возвращает объект Response. Если это так, работа ядра на этом практически закончена. Следующим шагом будет только событие kernel.response.

Но если контроллер возвращает нечто отличное от Response, тогда ядру еще есть над чем поработать. Оно генерирует kernel.view (ибо конечная цель всегда объект Response).
Контроллер обязательно должен что-то возвращать. Если он вернет null, будет немедленно выброшено исключение.
6) Событие kernel.view
Цель: трансформировать возвращенные контроллером данные в объект Response, если требуется.
Когда контроллер возвращает просто данные, а не объект Response, ядро генерирует еще одно событие – kernel.view. Задача его обработчиков, получить то, что вернул контроллер (например массив данных) и создать из него объект Response.

Данная особенность может быть полезной, если вы планируете использовать слой view. На данном этапе, если ни один из подписчиков события не вернул Response, выбрасывается исключение. Потому что либо контроллер, либо обработчик должен всегда возвращать упомянутый объект.
Если один из подписчиков события вернул Response, цепочка прекращается, так что с более низким приоритетом не будут выполнены.
По умолчанию нет обработчиков данного события. Однако один из бандлов – SensioFrameworkExtraBundle добавляет его. Если ваш контроллер возвращает массив, и вы указали в аннотации @Template перед ним, тогда обработчик генерирует страницу на базе указанного темплейта и возвращает объект Response.
Так же существуцет еще один популярный бандл – FOSRestBndle, который обеспечивает гибкий слой view. Может возвращать различные типы контента (HTML, JSON, XML и т.д.)
7) Событие kernel.response
Обычно используется для модификации объекта Response, прежде чем он будет отправлен пользователю.
Конечная цель ядра (HttpKernel) – создание объекта Response из Request. Он может быть получен в процессе обработки события kernel.request, возвращен непосредственно контроллером, или одним из обработчиков события kernel.view.
Независимо, кто именно создал Response, еще одно событие будет сгенерировано сразу же после этого – kernel.response. Задача обработчиков последнего, модифицировать объект каким-либо образом, — добивать в него дополнительные заголовки, куки или даже изменить его контент (например, добавить JavaScript перед тэгом
[…] на простой пример работы компонента HttpKernel. Как только объект Response был создан, может […]