На сайте Symfony Components про компонент RequestHandler сказано примерно следующее:
Так ли это и что из себя представляет RequestHandler в Symfony 2 я попробую рассмотреть в этом топике.
Если взглянуть на процесс обработки запроса в приложении на базе Symfony 2, а именно рассмотреть метод
Для начала заглянем в
Из этого описания становится ясно, что объект запроса создается просто
Заглянув в Symfony/Components/RequestHandler мы увидим следующий набор файлов:
Ну в общем все почти очевидно. Многое ясно даже не заглядывая внутрь классов. Но все же коротко обо всем. Классы исключений, которые используются при возникновении соответствующих ситуаций, все что в них делается, так это прописывается сообщение и код ответа.
Фактически все, что делает обработчик запроса это используя переданный ему в параметрах диспетчер событий (EventDispatcher) генерирует различные события, тем самым как бы оповещая приложения о различных этапах разбора запроса и формирования ответа. И в зависимости от реакции слушателей (listeners) на эти события завершает работу либо генерирует следующее событие.
Список событий, которые создает обработчик запросов:
То есть события отражают основные этапы разбора запроса и формирования ответа.
Лучше, наверное это все попробовать на примере. Что в общем я сейчас и сделаю. Итак, разденем Symfony до гола, убрав все кроме ядра и навесим свой обработчик на событие
Пример будет на базе sandbox с обновленной версией Symfony, как создавалось окружение можно увидеть вот здесь.
Итак, начнем с конфигов. В файле
Теперь, надо описать в DI контейнере необходимые параметры и сервисы, для этого сначала создадим свое расширение для DI контейнера, создаем директорию
теперь зарегистрируем наше расширение:
Наше расширение будет искать файл
так что надо создать этот файл, с примерно таким содержимым:
В этом файле мы определили сервис, который навешивается на прослушиваение события
Теперь создадим парсер запроса:
Теперь последний штрих, в конфиге
Это всё! Теперь обратившись по любому адресу мы получим ответ:

Совершенно бесполезное приложение получилось. Но на этом примере легко показать как оно работает, подцепляться можно и к другим событиям. Все очень гибко и удобно.
Гибкое микро-ядро для быстрых фреймворков.
Так ли это и что из себя представляет RequestHandler в Symfony 2 я попробую рассмотреть в этом топике.
Если взглянуть на процесс обработки запроса в приложении на базе Symfony 2, а именно рассмотреть метод
run()
класса Symfony\Foundation\Kernel
, то увидим что после формирования и сохранения в кеше либо загрузки из кеша DI контейнера, происходит получение из контейнера объекта обработчика запроса и вызов его метода handle()
в параметрах, которому передается объект запроса, кстати также полученный из DI контейнера. Данный метод должен вернуть объект ответа у которого вызывается метод send()
для отправки ответа. В общем первый удар при обработке запроса принимает на себя RequestHandler, он же возвращает объект ответа, поэтому я решил начать обзор компонентов входящих в состав Symfony 2 именно с него.Для начала заглянем в
Symfony/Foundation/Resources/services.xml
так как именно здесь происходит описание сервиса RequestHandlerService в DI контейнере.<parameter key="request_handler.class">Symfony\Components\RequestHandler\RequestHandler</parameter> <parameter key="request.class">Symfony\components\RequestHandler\Response</parameter> ... <service id="request_handler" class="%request_handler.class%"> <argument type="service" id="event_dispatcher" /> </service> <service id="request" class="%request.class%" />
Из этого описания становится ясно, что объект запроса создается просто
new Request()
, а объект обработчика запроса, как new RequestHandler($dispatcher)
, где $dispatcher
объект класса EventDispatcher, представляющий собой диспетчер событий.Заглянув в Symfony/Components/RequestHandler мы увидим следующий набор файлов:
Exception/
: директория с классами исключений
ForbiddenHttpException.php
: исключение кидаемое при 403 ForbiddenHttpException.php
: базовый класс для исключенийNotFoundHttpException.php
: исключение кидаемое при 404 Not FoundUnauthorizedHttpException.php
: исключение кидаемое при 401 Unauthorized
Request.php
: класс запросаRequestInterface.php
: интерфейс, который должен реализовать объект запросаResponse.php
: класс ответаResponseInterface.php
: интерфейс, который должен реализовать объект ответаRequestHandler.php
: обработчик запроса
Ну в общем все почти очевидно. Многое ясно даже не заглядывая внутрь классов. Но все же коротко обо всем. Классы исключений, которые используются при возникновении соответствующих ситуаций, все что в них делается, так это прописывается сообщение и код ответа.
Request
— класс запроса, который разбирает переменные окружения, параметры и все что связано с запросом. Response
— класс ответа, который содержит в себе тело ответва, формирует заголовки и все связанное с ответом. RequestHandler
— это обработчик запроса. Пожалуй рассмотрим его подробнее.Фактически все, что делает обработчик запроса это используя переданный ему в параметрах диспетчер событий (EventDispatcher) генерирует различные события, тем самым как бы оповещая приложения о различных этапах разбора запроса и формирования ответа. И в зависимости от реакции слушателей (listeners) на эти события завершает работу либо генерирует следующее событие.
Список событий, которые создает обработчик запросов:
core.request
core.load_controller
core.controller
core.view
core.response
core.exception
: в случае возникновения исключений
То есть события отражают основные этапы разбора запроса и формирования ответа.
RequestHandler
проверяя реакцию приложения на события продолжает создавать события либо завершает работу. Соответственно приложение навешивая свои обработчики на те или иные события участвует в формировании ответа. При этом некоторые части приложения могут ничего не знать друг о друге.Лучше, наверное это все попробовать на примере. Что в общем я сейчас и сделаю. Итак, разденем Symfony до гола, убрав все кроме ядра и навесим свой обработчик на событие
core.request
, заодно будет небольшая демонстрация гибкости фреймворка.Пример будет на базе sandbox с обновленной версией Symfony, как создавалось окружение можно увидеть вот здесь.
Итак, начнем с конфигов. В файле
hello/config/config.yml
закоментируем все за исключением строки kernel.config: ~
. Теперь поправим ядро приложения, файл hello/HelloKernel.php
# hello/HelloKernel.php // убираем все бандлы, за исключения ядра и нашего приложения public function registerBundles() { return array( new Symfony\Foundation\Bundle\KernelBundle(), new Application\HelloBundle\Bundle(), ); }
Теперь, надо описать в DI контейнере необходимые параметры и сервисы, для этого сначала создадим свое расширение для DI контейнера, создаем директорию
src/Application/HelloBundle/DependencyInjection
, а в ней файл HelloExtension.php
со следующим содержимым:namespace Application\HelloBundle\DependencyInjection; use Symfony\Components\DependencyInjection\Loader\LoaderExtension, Symfony\Components\DependencyInjection\Loader\XmlFileLoader, Symfony\Components\DependencyInjection\BuilderConfiguration, Symfony\Components\DependencyInjection\Reference, Symfony\Components\DependencyInjection\Definition; class HelloExtension extends LoaderExtension { public function helloLoad($config) { $configuration = new BuilderConfiguration(); $loader = new XmlFileLoader(__DIR__.'/../Resources/config'); $configuration->merge($loader->load('hello.xml')); return $configuration; } public function getAlias() { return 'hello'; } public function getNamespace() { return 'http://poulikov.ru/schema/dic/hello'; } public function getXsdValidationBasePath() { return __DIR__.'/../Resources/config/'; } }
теперь зарегистрируем наше расширение:
# src/Application/HelloBundle/Bundle.php // используем наше расширение use Application\HelloBundle\DependencyInjection\HelloExtension; ... // зарегистрируем расширение в контейнере public function buildContainer(ContainerInterface $container) { Loader::registerExtension(new HelloExtension()); }
Наше расширение будет искать файл
hello.xml
по пути src/Application/HelloBundle/Resources/config/hello.xml
так что надо создать этот файл, с примерно таким содержимым:
<container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd"> <parameters> <parameter key="request_parser.class">Application\HelloBundle\Request\Parser</parameter> </parameters> <services> <service id="request_parser" class="%request_parser.class%"> <annotation name="kernel.listener" event="core.request" method="resolve" /> <argument type="service" id="service_container" /> </service> </services> </container>
В этом файле мы определили сервис, который навешивается на прослушиваение события
core.request
Теперь создадим парсер запроса:
# src/Application/HelloBundle/Request/Parser.php namespace Application\HelloBundle\Request; use Symfony\Components\DependencyInjection\ContainerInterface; use Symfony\Components\EventDispatcher\Event; use Symfony\Components\Routing\RouterInterface; class Parser { protected $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function register() { $this->container->getEventDispatcherService()->connect('core.request', array($this, 'resolve')); } public function resolve(Event $event) { $response = $this->container->getResponseService(); $response->setContent("<div>Hello, World! I'm Symfony 2.0 lite</div>"); $event->setReturnValue($response); $event->setProcessed(true); } }
Теперь последний штрих, в конфиге
hello/config/config.yml
добавим строчку hello.hello: ~
Это всё! Теперь обратившись по любому адресу мы получим ответ:

Совершенно бесполезное приложение получилось. Но на этом примере легко показать как оно работает, подцепляться можно и к другим событиям. Все очень гибко и удобно.