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 |