Spec-Zone.ru › Phalcon 3

Менеджер событий

Цель этого компонента — перехватывать выполнение большинства других компонентов фреймворка, создавая «точки входа». Эти точки входа позволяют разработчику получать информацию о состоянии, манипулировать данными или изменять поток выполнения во время работы компонента.

Конвенция именования

События 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API