Менеджер событий
Цель этого компонента — перехватывать выполнение большинства других компонентов фреймворка, создавая «точки входа». Эти точки входа позволяют разработчику получать информацию о состоянии, манипулировать данными или изменять поток выполнения во время работы компонента.
Конвенция именования
События Phalcon используют пространства имён для предотвращения коллизий имён. Каждый компонент в Phalcon занимает разное пространство имён событий, и вы можете создавать свои собственные по своему усмотрению. Имена событий имеют формат «компонент:событие». Например, так как Phalcon\Db занимает пространство имён «db», полное имя события «afterQuery» — «db:afterQuery».
При подключении обработчиков событий к менеджеру событий вы можете использовать «компонент», чтобы поймать все события от этого компонента (например, «db», чтобы поймать все события Phalcon\Db), или «компонент:событие», чтобы нацелиться на конкретное событие (например, «db:afterQuery»).
Пример использования
В следующем примере мы будем использовать EventsManager для прослушивания события «afterQuery», генерируемого подключением к MySQL, управляемым Phalcon\Db:
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",
]
);
// Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);
// Send a SQL command to the database server
$connection->query(
"SELECT * FROM products p WHERE p.status = 1"
);
Теперь каждый раз, когда выполняется запрос, SQL-запрос будет выводиться на экран. Первый параметр, переданный в лямбда-функцию, содержит контекстную информацию о выполняемом событии, второй — источник события (в данном случае: само соединение). Также может быть указан третий параметр, который будет содержать произвольные данные, специфичные для события.
Вы должны явно установить Менеджер событий для компонента, используя методsetEventsManager(), чтобы этот компонент мог генерировать события. Вы можете создать новый экземпляр Менеджера событий для каждого компонента или установить один и тот же Менеджер событий для нескольких компонентов, так как конвенция именования предотвратит конфликты.
Вместо использования лямбда-функций, вы можете использовать классы обработчиков событий. Обработчики событий также позволяют прослушивать несколько событий. В этом примере мы будем использовать Phalcon\Db\Profiler для определения SQL-запросов, которые выполняются дольше, чем ожидалось:
use Phalcon\Db\Profiler;
use Phalcon\Events\Event;
use Phalcon\Logger;
use Phalcon\Logger\Adapter\File;
class MyDbListener
{
protected $_profiler;
protected $_logger;
/**
* Creates the profiler and starts the logging
*/
public function __construct()
{
$this->_profiler = new Profiler();
$this->_logger = new Logger("../apps/logs/db.log");
}
/**
* This is executed if the event triggered is 'beforeQuery'
*/
public function beforeQuery(Event $event, $connection)
{
$this->_profiler->startProfile(
$connection->getSQLStatement()
);
}
/**
* This is executed if the event triggered is 'afterQuery'
*/
public function afterQuery(Event $event, $connection)
{
$this->_logger->log(
$connection->getSQLStatement(),
Logger::INFO
);
$this->_profiler->stopProfile();
}
public function getProfiler()
{
return $this->_profiler;
}
}
Подключение обработчика событий к менеджеру событий так же просто, как:
// Create a database listener
$dbListener = new MyDbListener();
// Listen all the database events
$eventsManager->attach(
"db",
$dbListener
);
Получить результирующие профилирующие данные можно от обработчика:
// Send a SQL command to the database server
$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:
use Phalcon\Events\ManagerInterface;
use Phalcon\Events\EventsAwareInterface;
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». Теперь нам нужно создать обработчик событий для этого компонента:
use Phalcon\Events\Event;
class SomeListener
{
public function beforeSomeTask(Event $event, $myComponent)
{
echo "Here, beforeSomeTask\n";
}
public function afterSomeTask(Event $event, $myComponent)
{
echo "Here, afterSomeTask\n";
}
}
Теперь давайте заставим всё работать вместе:
use Phalcon\Events\Manager as EventsManager;
// Create an Events Manager
$eventsManager = new EventsManager();
// Create the MyComponent instance
$myComponent = new MyComponent();
// Bind the eventsManager to the instance
$myComponent->setEventsManager($eventsManager);
// Attach the listener to the EventsManager
$eventsManager->attach(
"my-component",
new SomeListener()
);
// Execute methods in the component
$myComponent->someTask();
Когда выполняется someTask(), будут выполнены два метода в обработчике, что приведёт к следующему выводу:
Here, beforeSomeTask Here, someTask Here, afterSomeTask
Дополнительные данные также могут быть переданы при генерировании события с помощью третьего параметра fire():
$eventsManager->fire("my-component:afterSomeTask", $this, $extraData);
В обработчике третий параметр также получает эти данные:
use Phalcon\Events\Event;
// Receiving the data in the third parameter
$eventsManager->attach(
"my-component",
function (Event $event, $component, $data) {
print_r($data);
}
);
// Receiving the data from the event context
$eventsManager->attach(
"my-component",
function (Event $event, $component) {
print_r($event->getData());
}
);
Использование сервисов из DI
Расширяя Phalcon\Mvc\User\Plugin, вы можете получить доступ к сервисам из DI, как и в контроллере:
use Phalcon\Events\Event;
use Phalcon\Mvc\User\Plugin;
class SomeListener extends Plugin
{
public function beforeSomeTask(Event $event, $myComponent)
{
echo "Here, beforeSomeTask\n";
$this->logger->debug(
"beforeSomeTask has been triggered";
);
}
public function afterSomeTask(Event $event, $myComponent)
{
echo "Here, afterSomeTask\n";
$this->logger->debug(
"afterSomeTask has been triggered";
);
}
}
Распространение/отмена событий
Многие обработчики могут быть добавлены к одному и тому же менеджеру событий. Это означает, что для одного и того же типа события может быть уведомлено множество обработчиков. Обработчики уведомляются в порядке их регистрации в EventsManager. Некоторые события могут быть отменены, что указывает на то, что они могут быть остановлены, предотвращая уведомление других обработчиков о событии:
use Phalcon\Events\Event;
$eventsManager->attach(
"db",
function (Event $event, $connection) {
// We stop the event if it is cancelable
if ($event->isCancelable()) {
// Stop the event, so other listeners will not be notified about this
$event->stop();
}
// ...
}
);
По умолчанию события могут быть отменены — большинство событий, генерируемых фреймворком, являются отменяемыми. Вы можете сгенерировать неотменяемое событие, передав false в четвёртом параметре fire():
$eventsManager->fire("my-component:afterSomeTask", $this, $extraData, false);
Приоритеты обработчиков
При подключении обработчиков вы можете установить определённый приоритет. С помощью этой функции вы можете подключить обработчики, указав порядок, в котором они должны быть вызваны:
$eventsManager->enablePriorities(true);
$eventsManager->attach("db", new DbListener(), 150); // More priority
$eventsManager->attach("db", new DbListener(), 100); // Normal priority
$eventsManager->attach("db", new DbListener(), 50); // Less priority
Сбор ответов
Менеджер событий может собирать каждый ответ, возвращаемый каждым уведомлённым обработчиком. Этот пример показывает, как это работает:
use Phalcon\Events\Manager as EventsManager;
$eventsManager = new EventsManager();
// Set up the events manager to collect responses
$eventsManager->collectResponses(true);
// Attach a listener
$eventsManager->attach(
"custom:custom",
function () {
return "first response";
}
);
// Attach a listener
$eventsManager->attach(
"custom:custom",
function () {
return "second response";
}
);
// Fire the event
$eventsManager->fire("custom:custom", null);
// Get all the collected responses
print_r($eventsManager->getResponses());
Вышеприведённый пример выводит:
Array ( [0] => first response [1] => second response )
Реализация собственного EventsManager
Интерфейс Phalcon\Events\ManagerInterface должен быть реализован для создания собственного EventsManager, заменяющего предоставленный Phalcon.
© 2011–2017 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/latest/reference/events.html