Table Of Contents

Previous topic

使用命名空间

Next topic

Request Environment

This Page

事件管理

此组件的目的是通过创建挂钩点拦截框架中大部分组件的执行。这些挂钩点允许开发者获取状态信息,操作数据或改变一个组件在执行过程中的流程。

译者注:挂钩点(hooks point)类似于SVN或GIT中的hook。在使用SVN开发过程中,我们想实现把提交的代码直接部署到演示环境下,那么就需要SVN的hook.

使用示例

在下面的例子中,我们使用EventsManager侦听使用 Phalcon\Db 进行MySQL连接管理过程中产生的事件。首先,我们需要一个侦听器对象,我们创建一个类,它的方法是我们要侦听的事件:

<?php

class MyDbListener
{

    public function afterConnect()
    {

    }

    public function beforeQuery()
    {

    }

    public function afterQuery()
    {

    }

}

这个新的类文件可以更详细一些,因为我们需要使用它。EventsManager 将充当组件与侦听器之间的桥梁,为我们创建的侦听类的方法提供挂钩点:

<?php

$eventsManager = new \Phalcon\Events\Manager();

//Create a database listener
$dbListener = new MyDbListener()

//Listen all the database events
$eventsManager->attach('db', $dbListener);

$connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array(
    "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语句,我们需要使用事件“afterQuery”。第一个参数传递给 事件侦听器,包含正在运行的事件的上下文信息,第二个是连接本身。

<?php

class MyDbListener
{

    protected $_logger;

    public function __construct()
    {
        $this->_logger = new \Phalcon\Logger\Adapter\File("../apps/logs/db.log");
    }

    public function afterQuery($event, $connection)
    {
        $this->_logger->log($connection->getSQLStatement(), \Phalcon\Logger::INFO);
    }

}

作为示例的一部分,我们需要实现 Phalcon\Db\Profiler,以检测SQL语句比预期花费多长时间:

<?php

class MyDbListener
{

    protected $_profiler;

    protected $_logger;

    public function __construct()
    {
        $this->_profiler = new \Phalcon\Db\Profiler();
        $this->_logger = new \Phalcon\Logger\Adapter\File("../apps/logs/db.log");
    }

    public function beforeQuery($event, $connection)
    {
        $this->_profiler->startProfile($connection->getSQLStatement());
    }

    public function afterQuery($event, $connection)
    {
        $this->_logger->log($connection->getSQLStatement(), \Phalcon\Logger::INFO);
        $this->_profiler->stopProfile();
    }

    public function getProfiler()
    {
        return $this->_profiler;
    }

}

可以从监听器获得返回的数据:

<?php

//Send a SQL command to the database server
$connection->query("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";
}

以类似的方式,我们可以注册一个lambda形式的匿名函数来执行任务,而不是一个单独的监听器类(见上面示例):

<?php

//Listen all the database events
$eventManager->attach('db', function($event, $connection) {
    if ($event->getType() == 'afterQuery') {
        echo $connection->getSQLStatement();
    }
});

Creating components that trigger Events

你也可以在应用程序中创建自己的组件,使用EventsManager触发事件。作为结果,事件运行时监听器会作出相应的反应。在下面的例子中,我们创建了一个叫”MyComponent”的组件。这个组件实现了EventsManager aware接口,当它的方法 “someTask” 执行时,监听器会触发相应的两个事件:

<?php

class MyComponent implements \Phalcon\Events\EventsAwareInterface
{

    protected $_eventsManager;

    public function setEventsManager($eventsManager)
    {
        $this->_eventsManager = $eventsManager;
    }

    public function getEventsManager()
    {
        return $this->_eventsManager
    }

    public function someTask()
    {
        $this->_eventsManager->fire("my-component:beforeSomeTask", $this);

        // do some task

        $this->_eventsManager->fire("my-component:afterSomeTask", $this);
    }

}

请注意,这个事件在触发时使用的前辍是 “my-component”,这是一个唯一标志字符,以帮助我们知道事件是由哪个组件产生的。你甚至可以在组件之个创建相同名称的事件。现在,让我们来创建一个监听器监听这个组件:

<?php

class SomeListener
{

    public function beforeSomeTask($event, $myComponent)
    {
        echo "Here, beforeSomeTask\n";
    }

    public function afterSomeTask($event, $myComponent)
    {
        echo "Here, afterSomeTask\n";
    }

}

一个监听器就是一个简单的类文件,它实现了所有组件触发的事件。现在,让我们使他们联合在一起工作:

<?php

//Create an Events Manager
$eventsManager = new Phalcon\Events\Manager();

//Create the MyComponent instance
$myComponent = new MyComponent();

//Bind the eventsManager to the instance
$myComponent->setEventsManager($myComponent);

//Attach the listener to the EventsManager
$eventsManager->attach('my-component', new SomeListener());

//Execute methods in the component
$myComponent->someTask();

“someTask”执行后,监听器中的两个方法也会被执行,下面是输出结果:

Here, beforeSomeTask
Here, afterSomeTask

其他数据也可以通过 “fire” 调用第三个参数进行触发:

<?php

$eventsManager->fire("my-component:afterSomeTask", $this, $extraData);

在监听器中,第三个参数也接收此数据:

<?php

//Receiving the data in the third parameter
$eventManager->attach('my-component', function($event, $component, $data) {
    print_r($data);
});

//Receiving the data from the event context
$eventManager->attach('my-component', function($event, $component) {
    print_r($event->getData());
});

如果监听器只对一个特定类型的事件感兴趣,你可以直接绑定:

<?php

//The handler will only be executed if the event triggered is "beforeSomeTask"
$eventManager->attach('my-component:beforeSomeTask', function($event, $component) {
    //...
});

事件的发布与取消(Event Propagation/Cancelation)

许多监听器可能会添加相同的事件,这意味着,对于同类类型的事件,许多监听器都会被触发(即会有非常多同类型的消息输出)。根据注册到 EventsManager 的顺序,监听器被一一触发。一些事件可以被撤消,表明可以停止一些其他的监听器事件被触发:

<?php

$eventsManager->attach('db', function($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();
    }

    //...

});

在默认情况下,事件是可以被取消的,甚至在框架中的大部分事件都是可以被取消的。你可以使用fire方法传递第四个参数,值为”false”,以达到不可取消的目的:

<?php

$eventsManager->fire("my-component:afterSomeTask", $this, $extraData, false);

实现自定义EventsManager(Implementing your own EventsManager)

The Phalcon\Events\ManagerInterface interface must be implemented to create your own EventsManager replacing the one provided by Phalcon.