Составные части¶
Похоже, что вы хотите понять, как работает Symfony2 и как его расширить. Это радует! Этот раздел подробно объясняет внутренности Symfony2.
Note
Чтение этого раздела необходимо, только если вы хотите понять, как работает Symfony2 за кулисами или если хотите расширять Symfony2.
Обзор¶
Код Symfony2 сделан из нескольких независимых слоёв. Каждый следующий слой надстраивается на предыдущем.
Tip
Автозагрузка не управляется непосредственно фреймворком; она выполняется
независимо с помощью класса
Symfony\Component\ClassLoader\UniversalClassLoader
и файла src/autoload.php
. За дополнительной информацией обращайтесь к
разделу, посвящённому этой теме.
Компонент HttpFoundation
¶
На самом глубоком уровне находится компонент :namespace:`Symfony\\Component\\HttpFoundation`. HttpFoundation предоставляет основные объекты, необходимые для работы с HTTP. Это объектно-ориентированная абстракция некоторых встроенных PHP функций и переменных:
- Класс
Symfony\Component\HttpFoundation\Request
абстрагирует основные глобальные переменные в PHP, такие как$_GET
,$_POST
,$_COOKIE
,$_FILES
и$_SERVER
; - Класс
Symfony\Component\HttpFoundation\Response
абстрагирует некоторые PHP функции типаheader()
,setcookie()
иecho
; - Класс
Symfony\Component\HttpFoundation\Session
иSymfony\Component\HttpFoundation\SessionStorage\SessionStorageInterface
абстрагируют функцииsession_*()
для управления сессией.
Компонент HttpKernel
¶
Поверх HttpFoundation располагается компонент :namespace:`Symfony\\Component\\HttpKernel`. HttpKernel управляет динамической частью HTTP; это тонкая обёртка поверх классов Request и Response, которая приводит способы обработки запросов к стандарту. Компонент также предоставляет точки для расширений и инструменты, делающие его идеальной стартовой площадкой для создания Web фреймворка без лишних проблем.
Также, дополнительно, он добавляет настраиваемость и расширяемость благодаря компоненту Dependency Injection и мощной системе пакетов (Bundles).
See also
Узнайте больше о компоненте HttpKernel. Узнайте больше о Dependency Injection и Пакетах.
Пакет FrameworkBundle
¶
:namespace:`Symfony\\Bundle\\FrameworkBundle` это пакет, связывающий основные компоненты и библиотеки вместе, что создаёт лёгкий и быстрый MVC фреймворк. Он поставляется с правильной первоначальной конфигурацией и соглашениями для облегчения изучения.
Ядро (Kernel)¶
Класс Symfony\Component\HttpKernel\HttpKernel
- это центральный
класс в Symfony2 и он в ответе за обработку клиентских запросов. Его главная
цель - “превратить” объект Symfony\Component\HttpFoundation\Request
в объект Symfony\Component\HttpFoundation\Response
.
Каждый Symfony2 Kernel наследует
Symfony\Component\HttpKernel\HttpKernelInterface
:
function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
Контроллеры (Controllers)¶
При преобразования запроса в ответ, Kernel полагается на “Controller”. Контроллер может быть любой валидной PHP-сущностью, которую можно вызвать тем или иным образом.
Ядро делегирует право выбора запустить тот или иной контроллер классу,
реализующему интерфейс
Symfony\Component\HttpKernel\Controller\ControllerResolverInterface
:
public function getController(Request $request);
public function getArguments(Request $request, $controller);
Метод
:method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface::getController`
возвращает контроллер (PHP callable - функцию, метод, замыкание...), ассоциированный
с данным запросом. Каноническая реализация
(Symfony\Component\HttpKernel\Controller\ControllerResolver
)
ищет атрибут запроса _controller
, который хранит наименование контроллера
(строку “class::method”, например Bundle\BlogBundle\PostController:indexAction
).
Tip
Реализация по умолчанию использует
Symfony\Bundle\FrameworkBundle\EventListener\RouterListener
для определения атрибута _controller
из запроса (see Событие kernel.request).
Метод :method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface::getArguments` возвращает массив аргументов для передачи их в контроллер. Реализация по умолчанию автоматически определяет аргументы, основываясь на атрибутах запроса.
Обработка запросов¶
Метод handle()
принимает Request
и всегда возвращает Response
.
При конвертации объекта Request
, handle()
полагается на Resolver и
упорядоченную цепь нотификаций о событиях (Event notifications, см. следующую
секцию для более подробной информации о каждом событии из этой цепи):
- Перед тем как что-либо делать, срабатывает нотификация о событии
kernel.request
– если один из слушателей (listeners) возвращает объектResponse
, процесс сразу переходит к шагу 8; - Вызывается Resolver для определения Контроллера, который необходимо выполнить;
- Слушатели события
kernel.controller
теперь могут манипулировать методом Контроллера (изменить, обернуть...); - Kernel проверяет, что Контроллер представляет собой валидный PHP callable;
- Для определения аргументов Контроллера вызывается Resolver;
- Kernel выполняет Контроллер;
- Если Контроллер не возвращает объект
Response
, слушатели событияkernel.view
могут конвертировать данные, которые вернул Контроллер в объектResponse
; - Слушатели события
kernel.response
могут манипулировать объектомResponse
( контент и заголовки); - Возвращается Ответ.
Если во время этого процесса возникает исключительная ситуация, срабатывает
событие kernel.exception
и его слушатели получают возможность конвертировать
исключение (Exception) в Ответ. Если это удаётся, событие уведомляется, если нет,
исключение вызывается повторно.
Если вы не хотите, чтобы возникали исключения (для вложенных запросов, к примеру),
отключите событие kernel.exception
передав false
в качестве третьего аргумента
метода handle()
.
Внутренние Запросы¶
В любой момент во время обработки запроса (назовём его ‘мастер’), может быть обработан
подзапрос. Вы можете передать тип запроса в метод handle()
его вторым
параметром:
HttpKernelInterface::MASTER_REQUEST
;HttpKernelInterface::SUB_REQUEST
.
Тип также передаётся во все события, и их слушатели могут действовать в соответствии с переданным типом (некоторые действия могут соответствовать только мастер-запросу).
События¶
Каждое событие, создаваемое в Kernel, это дочерний класс
Symfony\Component\HttpKernel\Event\KernelEvent
. Это означает, что
каждое событие имеет доступ к одной и той же базовой информации:
getRequestType()
- возвращает тип запроса (HttpKernelInterface::MASTER_REQUEST
илиHttpKernelInterface::SUB_REQUEST
);getKernel()
- возвращает экземпляр Kernel, обрабатывающий этот запрос;getRequest()
- возвращает объектRequest
, соответствующий обрабатываемому запросу;
getRequestType()
¶
Метод getRequestType()
позволяет слушателям узнавать тип запроса.
Например, если слушатель должен быть активен только для мастер-запроса,
добавьте следующий код в начало вашего “слушающего” метода:
<?php
use Symfony\Component\HttpKernel\HttpKernelInterface;
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
// немедленно возвращаемся
return;
}
Tip
Если вы ещё не знакомы с Диспетчером Событий Symfony2 (Event Dispatcher), прочитайте сначала секцию События.
Событие kernel.request
¶
Класс события: Symfony\Component\HttpKernel\Event\GetResponseEvent
Цель этого события - либо незамедлительно вернуть объект Response
,
или же подготовить переменные, чтобы можно было вызвать контроллер после
события. Любой слушатель может вернуть объект Response
при помощи
метода события setResponse()
. В этом случае, все остальные слушатели
не будут вызываться.
Это событие используется в FrameworkBundle
для заполнения атрибута _controller
в объекте Request
при помощи класса
Symfony\Bundle\FrameworkBundle\EventListener\RouterListener
. RequestListener
использует объект, реализующий интерфейс Symfony\Component\Routing\RouterInterface
для согласования объекта Request
и определения наименования Контроллера (которое хранится
в атрибуте _controller
объекта Request
).
Событие kernel.controller
¶
Класс события: Symfony\Component\HttpKernel\Event\FilterControllerEvent
Это событие не используется в FrameworkBundle
, но оно может быть точкой входа,
используемой для модификации исполняемого контроллера:
<?php
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
// ...
// the controller can be changed to any PHP callable
$event->setController($controller);
}
Событие kernel.view
¶
Класс события: Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent
Это событие не используется в FrameworkBundle
, но оно может быть использовано
для реализации подсистемы view. Это событие вызывается только если Контроллер
не возвращает объект Response
. Назначение этого события - разрешить конвертацию
возвращаемых значений в объект Response
.
Значение, возвращаемое Контроллером доступно при помощи метода getControllerResult
:
<?php
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\Response;
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$val = $event->getReturnValue();
$response = new Response();
// код получения объекта Response из полученного значения
$event->setResponse($response);
}
Событие kernel.response
¶
Класс события: Symfony\Component\HttpKernel\Event\FilterResponseEvent
Назначение этого события - позволить другим системам модифицировать или
заменять объект Response
после его создания:
<?php
public function onKernelResponse(FilterResponseEvent $event)
{
$response = $event->getResponse();
// .. modify the response object
}
FrameworkBundle
регистрирует несколько слушателей:
Symfony\Component\HttpKernel\EventListener\ProfilerListener
: собирает данные для текущего запроса;Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener
: внедряет Web Debug Toolbar;Symfony\Component\HttpKernel\EventListener\ResponseListener
: устанавливаетContent-Type
ответа, основываясь на формате запроса;Symfony\Component\HttpKernel\EventListener\EsiListener
: добавляет заголовокSurrogate-Control
, в случае если ответ необходимо парсить на предмет наличия ESI тагов.
Событие kernel.exception
¶
Класс события: Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent
FrameworkBundle
регистрирует Symfony\Component\HttpKernel\EventListener\ExceptionListener
,
который перенаправляет Request
в указанные Контроллер (определяется
значением параметра exception_listener.controller
, указывается в нотации class::method
).
Слушатель этого события может создавать объект Response
, создавать новый объект
Exception
или же ничего не делать:
<?php
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
$response = new Response();
// Настраиваем объект Response, основываясь на перехваченном исключении
$event->setResponse($response);
// как вариант - вы можете создать новое исключение
// $exception = new \Exception('Some special exception');
// $event->setException($exception);
}
Диспетчер событий (Event Dispatcher)¶
Объектно-ориентированный код прошёл длинный путь по обеспечению расширяемости кода. Путём создания узкоспециализированных классов, ваш код становится более гибким и разработчик может расширять его при помощи дочерних классов, чтобы изменять их поведение. Но что, если требуется использовать его изменения совместно с другими разработчиками, которые также создают свои дочерние классы? Здесь использование наследования уже не столь удобно.
Рассмотрим реальный пример, в котором вам нужно создать систему плагинов для вашего проекта. Плагин должен иметь возможность добавлять методы или же делать что-то до или после выполнения некоторого метода, не пересекаясь с прочими плагинами. Эту задачу непросто решить при помощи одиночного наследования, да и множественное наследование (если бы оно было возможно в PHP) имеет свои недостатки.
Диспетчер событий Symfony2 реализует шаблон проектирования Observer простым и эффективным способом, позволяя создавать, например, что-то вроде системы плагинов, которую упоминали выше, и делая ваш проект действительно расширяемым.
Рассмотрим ещё один простой пример из Symfony2 HttpKernel component.
Когда создаётся объект Response
, было бы здорово позволить другим
системам проекта модифицировать его (например, добавить заголовки для
кэширования) перед последующим использованием. Для того, чтобы достичь этого,
ядро Symfony2 создаёт событие - kernel.response
. Вот как это работает:
- Слушатель (listener, PHP объект) сообщает центральному диспетчеру, что
он собирается слушать (ожидать) событие
kernel.response
; - В какой-то момент ядро Symfony2 просит объект диспетчера отправить событие
kernel.response
, и вместе с ним - объектResponse
; - Диспетчер уведомляет (фактически вызывает метод) всех слушателей события
kernel.response
, позволяя каждому из них выполнить модификацию объектаResponse
.
События¶
Когда сообщение отправлено, оно идентифицируется по уникальному имени
(например, kernel.response
), которое могут ожидать некоторое число
слушателей. Также создаётся экземпляр класса
Symfony\Component\EventDispatcher\Event
, который затем передаётся
всем слушателям. Как вы увидите чуть позже, объект Event
часто содержит
данные о направляемом событии.
Соглашения по именованию¶
Уникальным именем для события может быть любая строка, но желательно следование нескольким простым правилам:
- Допустимые символы: буквы в нижнем регистре, цифры, точка (
.
), подчерк (_
); - Добавляйте префикс пространства имён с точкой на конце (например,
kernel.
); - Оканчивайте имя глаголом, который обозначает действие (например,
request
).
Вот пара примеров хороших имён для событий:
kernel.response
form.pre_set_data
Объекты событий¶
Когда диспетчер уведомляет слушателей, он передаёт им объект Event
. Базовый
класс Event
очень прост: он содержит метод для прекращения воспроизведения
(event propagation) и ничего более.
Зачастую, необходимо передавать в объекте Event
также данные о событии,
чтобы слушатели могли их обработать тем или иным образом. В случае события
kernel.response
, объект Event
, передаваемый каждому слушателю, фактически
имеет тип Symfony\Component\HttpKernel\Event\FilterResponseEvent
,
дочерний по отношению к Event
класс. Этот класс содержит методы, такие
как getResponse
и setResponse
, позволяющие слушателям получать и даже
заменять объект Response
.
Мораль этой истории в следующем: при создании слушателя некоторого события,
объект Event
, который будет передан этому слушателю, может быть
специализированным дочерним классом и иметь дополнительные методы для получения
данных события и их обработки.
Диспетчер¶
Диспетчер - это центральный объект системы обработки событий. Как правило, создаётся единственный диспетчер, который обслуживает реестр слушателей. Когда событие поступает к диспетчеру - он уведомляет всех слушателей, подписанных на это событие.
<?php
use Symfony\Component\EventDispatcher\EventDispatcher;
$dispatcher = new EventDispatcher();
Подключаем Слушателей¶
Для того, чтобы отреагировать на некое существующее событие, вам необходимо
подключить слушателя к диспетчеру, чтобы последний имел возможность сообщить
о появлении нужного события. Вызов метода диспетчера addListener()
ассоциирует
любую исполнимую функцию/метод с событием:
<?php
$listener = new AcmeListener();
$dispatcher->addListener('foo.action', array($listener, 'onFooAction'));
Метод addListener()
получает три аргумента:
- Наименование события, которое слушатель будет ожидать;
- Некий объект (функцию, в общем же случае PHP callable), который будет вызван при наступлении события;
- Опциональный приоритет (чем больше - тем более важный), который определяет
очерёдность вызова слушателей (по умолчанию
0
). Если два слушателя имеют одинаковый приоритет, они выполняются в порядке их добавления.
Note
PHP callable - это переменная, которая может быть использована в функции
call_user_func()
и возвращает true
при проверке с помощью функции
is_callable()
. Это может быть, в том числе, и экземпляр замыкания (\Closure
),
строка с именем функции или массив, представляющий собой метод объекта или же
метод класса.
Ранее вы уже видели как PHP объект может быть зарегистрирован в качестве слушателя. Вы также можете регистрировать Замыкания (Closures) в качестве слушателей:
<?php
use Symfony\Component\EventDispatcher\Event;
$dispatcher->addListener('foo.action', function (Event $event) {
// этот код будет вызван при обработке события foo.action
});
Когда слушатель зарегистрирован диспетчером, он ожидает наступления события.
В примере выше, когда появляется событие foo.action
, диспетчер вызывает
метод AcmeListener::onFooAction
и передаёт объекту Event
один аргумент:
<?php
use Symfony\Component\EventDispatcher\Event;
class AcmeListener
{
// ...
public function onFooAction(Event $event)
{
// do something
}
}
Tip
Если вы используете Symfony2 MVC framework, слушатели могут быть зарегистрированы при помощи конфигурации. В качестве бонуса, объект слушателя будет создан лишь когда будет нужен.
Во многих случаях, слушателю передаётся специализированный дочерний класс Event
.
Это даёт слушателю доступ к информации о событии. Сверяйтесь с документацией или
реализацией каждого конкретного события для определения какой именно экземпляр
Symfony\Component\EventDispatcher\Event
будет передан. Например, событие
kernel.event
передаёт экземпляр класса Symfony\Component\HttpKernel\Event\FilterResponseEvent
:
<?php
use Symfony\Component\HttpKernel\Event\FilterResponseEvent
public function onKernelResponse(FilterResponseEvent $event)
{
$response = $event->getResponse();
$request = $event->getRequest();
// ...
}
Создание и обработка события¶
В дополнение к регистрации слушателей для уже существующих событий, вы можете создавать и вызывать свои собственные события. Это может быть удобно при создании сторонних библиотек и если вы хотите чтобы различные компоненты вашей системы были гибкими и независимыми.
Статический класс Events
¶
Предположим, вы хотите создать новое событие - store.order
- которое
создаётся всякий раз, когда в вашем приложении создаётся заказ. Для того,
чтобы поддерживать порядок в приложении, начнём с создания класса StoreEvents
,
который будет определять ваше событие:
<?php
namespace Acme\StoreBundle;
final class StoreEvents
{
/**
* Событие store.order создаётся всякий раз, когда в системе создаётся заказ.
*
* Слушатель получит экземпляр Acme\StoreBundle\Event\FilterOrderEvent
*
* @var string
*/
const onStoreOrder = 'store.order';
}
Отметим также, что этот класс по сути свой ничего не делает. Назначение
класса StoreEvents
- централизация данных о событии. Слушателям этого события
будет передаваться специализированный класс FilterOrderEvent
.
Создание объекта события¶
Позднее, когда вы будете отправлять это событие, вы создадите экземпляр
класса Event
и передадите этот экземпляр всем слушателям события. Если
вы не хотите передавать никакой дополнительной информации слушателям, вы
можете использовать класс Symfony\Component\EventDispatcher\Event
.
В большинстве же случаев, вы будете передавать информацию о событии слушателям.
Для этого необходимо создать новый класс, который будет наследоваться от
класса Symfony\Component\EventDispatcher\Event
.
В этом примере, каждый слушатель будет должен получить доступ к некоторому объекту
Order
. Создадим класс Event
, который реализует такое поведение:
<?php
namespace Acme\StoreBundle\Event;
use Symfony\Component\EventDispatcher\Event;
use Acme\StoreBundle\Order;
class FilterOrderEvent extends Event
{
protected $order;
public function __construct(Order $order)
{
$this->order = $order;
}
public function getOrder()
{
return $this->order;
}
}
Каждый слушатель теперь имеет доступ к объекту Order
при помощи метода
getOrder
.
Отправка события¶
Метод :method:`Symfony\\Component\\EventDispatcher\\EventDispatcher::dispatch`
уведомляет всех слушателей о событии. Он принимает два аргумента:
наименование события для отправки и экземпляр Event
для передачи
каждому слушателю этого события:
<?php
use Acme\StoreBundle\StoreEvents;
use Acme\StoreBundle\Order;
use Acme\StoreBundle\Event\FilterOrderEvent;
// заказ как-то создаётся или получается
$order = new Order();
// ...
// создаём FilterOrderEvent и его отправка
$event = new FilterOrderEvent($order);
$dispatcher->dispatch(StoreEvents::onStoreOrder, $event);
Объект FilterOrderEvent
создаётся и передаётся в метод dispatch
.
Теперь, любой слушатель события store.order
будет получать FilterOrderEvent
и соответственно иметь доступ к объекту Order
при помощи метода getOrder
:
<?php
// какой-то слушатель, подписанный на событие store.order методом onStoreOrder
use Acme\StoreBundle\Event\FilterOrderEvent;
public function onStoreOrder(FilterOrderEvent $event)
{
$order = $event->getOrder();
// далее выполняются какие-то действия с заказом
}
Внутри объекта Диспетчера событий¶
Если вы взглянете на класс EventDispatcher
, вы увидите, что этот класс
работает не как Singleton (нет статического метода getInstance()
). Это
сделано преднамеренно, так как вам, возможно, потребуется иметь несколько
конкурирующих диспетчеров в рамках одного запроса. Но это также означает, что
вам нужен способ для передачи диспетчеру объектов, которые нужно подключить или
которые надо уведомить о событии.
Общепринятой практикой является внедрение объекта диспетчера в ваши объекты, т.е. внедрение зависимости.
Вы можете использовать внедрение в конструктор:
class Foo
{
protected $dispatcher = null;
public function __construct(EventDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
}
Или же внедрение через метод (setter injection):
class Foo
{
protected $dispatcher = null;
public function setEventDispatcher(EventDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
}
Выбор того или иного метода - это дело вкуса. Многие предпочитают метод с конструктором, так как объекты полностью инициализируются во время создания. Но когда у вас имеется длинный список зависимостей, использовать метод-сеттер это тоже вариант, особенно для опциональных зависимостей.
Tip
Если вы используете внедрение зависимости как мы делали в двух примерах выше,
вы можете использовать Symfony2 Dependency Injection component для
того чтобы управлять внедрением службы event_dispatcher
для этих
объектов.
# src/Acme/HelloBundle/Resources/config/services.yml services: foo_service: class: Acme/HelloBundle/Foo/FooService arguments: [@event_dispatcher]
Подписка на события¶
Типичный способ ожидать возникновение события - зарегистрировать слушателя события при помощи диспетчера. Этот слушатель может слушать одно или несколько событий и уведомляется каждый раз при отправке нужного события.
Альтернативным способом для ожидания событий - использование подписчика события.
Подписчик - это PHP класс, который имеет возможность сообщить диспетчеру
на какие события он подписывается. Подписчик должен реализовывать
интерфейс Symfony\Component\EventDispatcher\EventSubscriberInterface
,
который требует наличие одного статического метода getSubscribedEvents
.
Рассмотрим пример подписчика, который подписывается на события
kernel.response
и store.order
:
<?php
namespace Acme\StoreBundle\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
class StoreSubscriber implements EventSubscriberInterface
{
static public function getSubscribedEvents()
{
return array(
'kernel.response' => 'onKernelResponse',
'store.order' => 'onStoreOrder',
);
}
public function onKernelResponse(FilterResponseEvent $event)
{
// ...
}
public function onStoreOrder(FilterOrderEvent $event)
{
// ...
}
}
Этот класс похож на класс слушателя, за исключением того, что он сам может сообщить диспетчеру, на какие именно события он подписывается (будет слушать). Для регистрации подписчика в диспетчере необходимо использовать метод :method:`Symfony\\Component\\EventDispatcher\\EventDispatcher::addSubscriber`:
<?php
use Acme\StoreBundle\Event\StoreSubscriber;
$subscriber = new StoreSubscriber();
$dispatcher->addSubscriber($subscriber);
Диспетчер автоматически зарегистрирует подписчика для каждого события,
возвращаемого методом getSubscribedEvents
. Этот метод возвращает массив,
индексами которого служат наименования событий, а значениями служат либо
наименования методов, которые будут вызваны, либо массивы с именем метода и
его приоритетом при обработке события.
Tip
Если вы используете Symfony2 MVC framework, подписчики можно регистрировать при помощи конфигурации. В качестве приятного бонуса, экземпляр подписчика будет создан лишь когда будет нужен.
Прекращение обработки событий¶
В некоторых случаях, один из слушателей может затребовать прекращение обработки события другими слушателями. Другими словами, слушатель должен иметь возможность сообщить диспетчеру, что он должен остановить обработку события всеми оставшимися слушателями (не уведомлять их о событии). Этого можно достигнуть внутри слушателя при помощи метода :method:`Symfony\\Component\\EventDispatcher\\Event::stopPropagation`:
<?php
use Acme\StoreBundle\Event\FilterOrderEvent;
public function onStoreOrder(FilterOrderEvent $event)
{
// ...
$event->stopPropagation();
}
Теперь, все слушатели store.order
, которые ещё не были уведомлены о событии,
уведомляться уже не будут.
Профайлер¶
Профайлер Symfony2, если он активирован, собирает полезную информацию о каждом запросе, выполненном к вашему приложение и сохраняет его для последующего анализа. Использование профайлера в девелоперском окружении поможет вам в отладке кода и увеличении быстродействия; используйте его в продуктовой среде для обнаружения проблем “по факту”.
Вам вряд ли придётся часто взаимодействовать с профайлером непосредственно, так как Symfony2 предоставляет визуализатор по типу Web Debug Toolbar и Web Profiler. Если вы используете Symfony2 Standard Edition, профайлер, дебаг-панель и веб-профайлер уже настроены и подключены.
Note
Профайлер собирает информацию обо всех запросах (простые запросы, перенаправления, исключения, Ajax запросы, ESI запросы; а также о всех HTTP методах и обо всех форматах). Это означает, что для одного URL вы можете иметь много профилированных данных (по одному на каждую пару запрос/ответ).
Визуализация данных профайлера¶
Использование Web Debug Toolbar¶
В dev окружении web debug toolbar расположен в низу каждой страницы. Он отображает обобщённые данные профайлера и предоставляет доступ к полезной информации, когда что-либо работает не так как ожидалось.
Если обобщённых данных не хватает, вы можете кликнуть на ссылку с токеном ( строка из 13 случайных символов) и перейти на страницу Web Profiler.
Note
Если токен не кликается, это означает, что маршруты профайлера не зарегистрированы (см. ниже информацию о конфигурировании).
Анализ данных в Web Profiler¶
Web Profiler - это инструмент визуализации данных профилирования, который вы можете использовать в разработке для отладки вашего кода и увеличения его быстродействия; но его также можно использовать для отслеживания проблем в продуктовой среде. Он предоставляет всю информацию, собранную профайлером, в своём веб-интерфейсе.
Доступ к данным профайлера¶
Вам не обязательно использовать визуализатор для доступа к данным
профайлера. Как же вам получить доступ к информации профайлера
для некоторого запроса по факту его выполнения? Когда профайлер сохраняет
данные о запросе, он также ассоциирует с ними некоторый токен; этот
токен доступен в заголовке ответа X-Debug-Token
:
$profile = $container->get('profiler')->loadProfileFromResponse($response);
$profile = $container->get('profiler')->loadProfile($token);
Tip
Когда профайлер активирован, но нет web debug toolbar, или же когда
вы хотите получить токен для Ajax запроса, используйте, например,
Firebug для того, чтобы получить заголовок X-Debug-Token
.
Используйте метод find()
, для получения доступа к токенам по какому-либо
критерию:
// получить 10 последних токенов
$tokens = $container->get('profiler')->find('', '', 10);
// получить последние 10 токенов для всех URL, содержащих /admin/
$tokens = $container->get('profiler')->find('', '/admin/', 10);
// получить последние 10 токенов для локальных запросов
$tokens = $container->get('profiler')->find('127.0.0.1', '', 10);
Если вы хотите манипулировать данными профайлера на другой машине, используйте
методы export()
и import()
:
// в prod окружении
$profile = $container->get('profiler')->loadProfile($token);
$data = $profiler->export($profile);
// в dev окружении
$profiler->import($data);
Конфигурирование¶
Конфигурация по умолчанию содержит разумные настройки профайлера, дебаг-панели (web debug toolbar) и веб-профайлера (web profiler). Ниже приведён пример конфигурации для dev окружения:
Если only-exceptions
имеет значение true
, профайлер собирает данные
только при возникновении исключений.
Если intercept-redirects
имеет значение true
, профайлер перехватывает
перенаправления и предоставляет вам возможность наблюдать собранные данные
перед перенаправлением.
Если verbose
имеет значение true
, Web Debug Toolbar отображает большое
количество данных. Если присвоить verbose
значение false
, вторичная
информация не будет отображаться.
Если вы активировали web profiler, вам также необходимо подключить его маршруты:
Так как профайлер выполняет дополнительную работу для каждого запроса, вы,
возможно, захотите активировать его в продуктовой среде лишь в некоторых случаях.
Опция only-exceptions
устанавливает лимит профилирования в 500 страниц, но
что, если вы захотите получить информацию, когда IP клиента имеет некоторое
определённое значение или если запрашивается строго определённая часть сайта?
Вы можете использовать request matcher:
Читайте в книге рецептов¶
- /cookbook/testing/profiling
- /cookbook/profiler/data_collector
- /cookbook/event_dispatcher/class_extension
- /cookbook/event_dispatcher/method_behavior