05 Менеджер событий
Менеджер событий
Цель этого компонента - перехватить выполнение большинства других компонентов структуры, создав «точки прерываний». Эти точки прерываний позволяют разработчику получать информацию о состоянии, манипулировать данными или изменять поток выполнения в процессе работы компонента.
Соглашение об именовании
События Phalcon используют пространства имен, чтобы избежать коллизий имен. Каждый компонент в Phalcon занимает другое пространство имен событий, и вы можете создавать свои собственные, как сочтете нужным. Имена событий форматируются как component:event
. Например, поскольку Phalcon\Db занимает пространство имен db
, полное имя события afterQuery
- db:afterQuery
.
При присоединении прослушивателей событий к менеджеру событий можно использовать component
для перехвата всех событий из этого компонента (например. db
, чтобы поймать все события Phalcon\Db) или component:event
для конкретного события (например. db:afterQuery
).
Пример использования
В следующем примере мы будем использовать EventManager для прослушивания события afterQuery
, созданного в соединении MySQL, управляемого Phalcon\Db:
<?php use Phalcon\Events\Event; use Phalcon\Events\Manager as EventsManager; use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter; $eventsManager = new EventsManager(); $eventsManager->attach( 'db:afterQuery', function (Event $event, $connection) { echo $connection->getSQLStatement(); } ); $connection = new DbAdapter( [ 'host' => 'localhost', 'username' => 'root', 'password' => 'secret', 'dbname' => 'invo', ] ); // Назначить eventManager экземпляру адаптера db $connection->setEventsManager($eventsManager); // Отправить команду SQL на сервер базы данных $connection->query( 'SELECT * FROM products p WHERE p.status = 1' );
Теперь каждый раз, когда запрос выполняется, оператор SQL будет выводиться. Первый параметр, передаваемый лямбда-функции, содержит контекстную информацию о запущенном событии, второй-источник события (в данном случае само соединение). Можно также указать третий параметр, который будет содержать произвольные данные, относящиеся к событию.
Вместо использования лямбда-функций можно использовать классы прослушивателя событий. Прослушиватели событий также позволяют прослушивать несколько событий. В этом примере мы реализуем Phalcon\Db\Profiler для обнаружения операторов SQL, выполнение которых занимает больше времени, чем ожидалось:
<?php use Phalcon\Db\Profiler; use Phalcon\Events\Event; use Phalcon\Logger; use Phalcon\Logger\Adapter\File; class MyDbListener { protected $profiler; protected $logger; /** * Создает профилировщик и запускает ведение журнала */ public function __construct() { $this->profiler = new Profiler(); $this->logger = new Logger('../apps/logs/db.log'); } /** * Это выполняется, если событие 'beforeQuery' */ public function beforeQuery(Event $event, $connection) { $this->profiler->startProfile( $connection->getSQLStatement() ); } /** * Это выполняется, если событие 'afterQuery' */ public function afterQuery(Event $event, $connection) { $this->logger->log( $connection->getSQLStatement(), Logger::INFO ); $this->profiler->stopProfile(); } public function getProfiler() { return $this->profiler; } }
Прикрепление прослушивателя событий к менеджеру событий так же просто, как:
<?php // Создание прослушивателя базы данных $dbListener = new MyDbListener(); // Прослушивание всех событий базы данных $eventsManager->attach( 'db', $dbListener );
Полученные данные профиля можно получить из прослушивателя:
<?php // Отправка команды SQL на сервер баз данных $connection->execute( 'SELECT * FROM products p WHERE p.status = 1' ); foreach ($dbListener->getProfiler()->getProfiles() as $profile) { echo 'SQL Statement: ', $profile->getSQLStatement(), '\n'; echo 'Start Time: ', $profile->getInitialTime(), '\n'; echo 'Final Time: ', $profile->getFinalTime(), '\n'; echo 'Total Elapsed Time: ', $profile->getTotalElapsedSeconds(), '\n'; }
Создание компонентов, запускающих события
В приложении можно создавать компоненты, которые инициируют события в EventsManager. Как следствие, могут существовать прослушиватели, реагирующие на эти события при генерации.В следующем примере мы создаем компонент MyComponent
. Этот компонент поддерживает EventsManager (реализует Phalcon\Events\EventsAwareInterface); при выполнении метода someTask()
он инициирует два события для любого прослушивателя в EventsManager:
<?php use Phalcon\Events\EventsAwareInterface; use Phalcon\Events\ManagerInterface; class MyComponent implements EventsAwareInterface { protected $eventsManager; public function setEventsManager(ManagerInterface $eventsManager) { $this->eventsManager = $eventsManager; } public function getEventsManager() { return $this->eventsManager; } public function someTask() { $this->eventsManager->fire('my-component:beforeSomeTask', $this); // Do some task echo 'Here, someTask\n'; $this->eventsManager->fire('my-component:afterSomeTask', $this); } }
Обратите внимание, что в этом примере используется пространство имен событий my-component
. Теперь нам нужно создать прослушиватель событий для этого компонента:
<?php use Phalcon\Events\Event; class SomeListener { public function beforeSomeTask(Event $event, $myComponent) { echo "Здесь, beforeSomeTask\n"; } public function afterSomeTask(Event $event, $myComponent) { echo "Здесь, afterSomeTask\n"; } }
Теперь давайте заставим все работать вместе:
<?php use Phalcon\Events\Manager as EventsManager; // Создание менеджера событий $eventsManager = new EventsManager(); // Создание экземпляра MyComponent $myComponent = new MyComponent(); // Привязка eventsManager к экземпляру $myComponent->setEventsManager($eventsManager); // Присоединить слушателя к EventsManager $eventsManager->attach( 'my-component', new SomeListener() ); // Выполнение методов в компоненте $myComponent->someTask();
Как someTask()
выполняется, два метода в слушателе будут выполнены, производя следующий вывод:
Here, beforeSomeTask Here, someTask Here, afterSomeTask
Дополнительные данные также могут передаваться при срабатывании события с помощью третьего параметра fire()
:
<?php $eventsManager->fire('my-component:afterSomeTask', $this, $extraData);
В прослушиватель третий параметр также получает эти данные:
<?php use Phalcon\Events\Event; // Получение данных по третьему параметру $eventsManager->attach( 'my-component', function (Event $event, $component, $data) { print_r($data); } ); // Получение данных из контекста события $eventsManager->attach( 'my-component', function (Event $event, $component) { print_r($event->getData()); } );
Использование сервисов DI
Расширяя Phalcon\Mvc\User\Plugin, вы можете получить доступ к службам из DI, как и в контроллере:
<?php use Phalcon\Events\Event; use Phalcon\Mvc\User\Plugin; class SomeListener extends Plugin { public function beforeSomeTask(Event $event, $myComponent) { echo 'Здесь, beforeSomeTask\n'; $this->logger->debug( 'beforeSomeTask has been triggered' ); } public function afterSomeTask(Event $event, $myComponent) { echo 'Здесь, afterSomeTask\n'; $this->logger->debug( 'afterSomeTask has been triggered' ); } }
Распространение/аннулирование события
Многие слушатели могут быть добавлены к тому же менеджеру событий. Это означает, что для одного и того же типа событий многие слушатели могут быть уведомлены. Слушатели уведомляются в том порядке, в котором они были зарегистрированы в EventManager. Некоторые события отменены, что указывает на то, что они могут быть остановлены, чтобы другие слушатели не были уведомлены о событии:
<?php use Phalcon\Events\Event; $eventsManager->attach( 'db', function (Event $event, $connection) { // We stop the event if it is cancelable if ($event->isCancelable()) { // Остановить событие, поэтому другие слушатели не будут уведомлены об этом $event->stop(); } // ... } );
По умолчанию события отменены - даже большинство событий, созданных фреймворком, являются отменными. Вы можете запустить событие без отмены, передав false
в четвертом параметре fire()
:
<?php $eventsManager->fire('my-component:afterSomeTask', $this, $extraData, false);
Приоритеты слушателя
При подключении слушателей вы можете установить определенный приоритет. С помощью этой функции вы можете подключить слушателей, указывающих порядок их вызова:
<?php $eventsManager->enablePriorities(true); $eventsManager->attach('db', new DbListener(), 150); // Больше приоритета $eventsManager->attach('db', new DbListener(), 100); // Обычный приоритет $eventsManager->attach('db', new DbListener(), 50); // Менее приоритетный
Сбор ответов
Менеджер событий может собирать каждый ответ, возвращаемый каждым уведомленным слушателем. В этом примере объясняется, как это работает:
<?php use Phalcon\Events\Manager as EventsManager; $eventsManager = new EventsManager(); // Настройте диспетчер событий для сбора ответов $eventsManager->collectResponses(true); // Прикрепите слушателя $eventsManager->attach( 'custom:custom', function () { return 'first response'; } ); // Прикрепите слушателя $eventsManager->attach( 'custom:custom', function () { return 'second response'; } ); // Выполнить событие $eventsManager->fire('custom:custom', null); // Получить все собранные ответы print_r($eventsManager->getResponses());
Приведенный выше пример производит:
Array ( [0] => first response [1] => second response )
Реализация собственного EventsManager
Интерфейс Phalcon\Events\ManagerInterface должен быть реализован, чтобы создать собственный EventManager, заменяющий тот, который предоставлен Phalcon.
Список событий
События, доступные в Phalcon:
Компонент | Событие |
---|---|
ACL | acl:afterCheckAccess |
ACL | acl:beforeCheckAccess |
Application | application:afterHandleRequest |
Application | application:afterStartModule |
Application | application:beforeHandleRequest |
Application | application:beforeSendResponse |
Application | application:beforeStartModule |
Application | application:boot |
Application | application:viewRender |
CLI | dispatch:beforeException |
Collection | afterCreate |
Collection | afterSave |
Collection | afterUpdate |
Collection | afterValidation |
Collection | afterValidationOnCreate |
Collection | afterValidationOnUpdate |
Collection | beforeCreate |
Collection | beforeSave |
Collection | beforeUpdate |
Collection | beforeValidation |
Collection | beforeValidationOnCreate |
Collection | beforeValidationOnUpdate |
Collection | notDeleted |
Collection | notSave |
Collection | notSaved |
Collection | onValidationFails |
Collection | validation |
Collection Manager | collectionManager:afterInitialize |
Console | console:afterHandleTask |
Console | console:afterStartModule |
Console | console:beforeHandleTask |
Console | console:beforeStartModule |
Db | db:afterQuery |
Db | db:beforeQuery |
Db | db:beginTransaction |
Db | db:createSavepoint |
Db | db:commitTransaction |
Db | db:releaseSavepoint |
Db | db:rollbackTransaction |
Db | db:rollbackSavepoint |
Dispatcher | dispatch:afterExecuteRoute |
Dispatcher | dispatch:afterDispatch |
Dispatcher | dispatch:afterDispatchLoop |
Dispatcher | dispatch:afterInitialize |
Dispatcher | dispatch:beforeException |
Dispatcher | dispatch:beforeExecuteRoute |
Dispatcher | dispatch:beforeDispatch |
Dispatcher | dispatch:beforeDispatchLoop |
Dispatcher | dispatch:beforeForward |
Dispatcher | dispatch:beforeNotFoundAction |
Loader | loader:afterCheckClass |
Loader | loader:beforeCheckClass |
Loader | loader:beforeCheckPath |
Loader | loader:pathFound |
Micro | micro:afterHandleRoute |
Micro | micro:afterExecuteRoute |
Micro | micro:beforeExecuteRoute |
Micro | micro:beforeHandleRoute |
Micro | micro:beforeNotFound |
Middleware | afterBinding |
Middleware | afterExecuteRoute |
Middleware | afterHandleRoute |
Middleware | beforeExecuteRoute |
Middleware | beforeHandleRoute |
Middleware | beforeNotFound |
Model | afterCreate |
Model | afterDelete |
Model | afterSave |
Model | afterUpdate |
Model | afterValidation |
Model | afterValidationOnCreate |
Model | afterValidationOnUpdate |
Model | beforeDelete |
Model | notDeleted |
Model | beforeCreate |
Model | beforeDelete |
Model | beforeSave |
Model | beforeUpdate |
Model | beforeValidation |
Model | beforeValidationOnCreate |
Model | beforeValidationOnUpdate |
Model | notSave |
Model | notSaved |
Model | onValidationFails |
Models Manager | modelsManager:afterInitialize |
Request | request:afterAuthorizationResolve |
Request | request:beforeAuthorizationResolve |
Router | router:beforeCheckRoutes |
Router | router:beforeCheckRoute |
Router | router:matchedRoute |
Router | router:notMatchedRoute |
Router | router:afterCheckRoutes |
Router | router:beforeMount |
View | view:afterRender |
View | view:afterRenderView |
View | view:beforeRender |
View | view:beforeRenderView |
View | view:notFoundView |
Volt | compileFilter |
Volt | compileFunction |
Volt | compileStatement |
Volt | resolveExpression |