2
概念理解:在某一个事件(trigger)发生的时候,触发预先设定(on)的代码,这是代码解耦的一种方式。

事件按照级别分为三类

1. 实例级别

绑定事件、触发事件的类继承的是Component,只在当前示例中运行,不与类的其他实例发生关系,也不与其他类、其他实例发生关系。

代码示例:

<?php

namespace common\components;


use yii\base\Component;

class EventTest extends Component
{
    const EVENT_SAY_HELLO = 'say_hello';
    
    public function sayHello($event)
    {
        echo $event->data;
        $event->handled = true;
    }
    
    public function sayWorld($event)
    {
        echo $event->data;
    }
}

1.绑定事件:

$event = new EventTest();

$event->on(EventTest::EVENT_SAY_HELLO, [$event, 'sayHello'], 'Hello!');

$event->on(EventTest::EVENT_SAY_HELLO, [$event, 'sayWorld'], 'World!');

2.解除事件:

$event->off(EventTest::EVENT_SAY_HELLO, [$event, 'sayHello']);

3.触发事件:

$event->trigger( EventTest::EVENT_SAY_HELLO);

2. 类级别

绑定事件、触发事件的类继承的是Event,类级别事件会被类自身、类的实例、后代类、后代类实例的同一事件所触发,影响范围更大

代码示例:

<?php

namespace common\components;


use yii\base\Event;

class EventTest extends Event
{
    const EVENT_SAY_HELLO = 'say_hello';
    
    public function sayHello($event)
    {
        echo $event->data;
    }
    
    public function sayWorld($event)
    {
        echo $event->data;
    }
}

1.绑定事件:

$event = new EventTest();

$event->on('\common\components\EventTest', EventTest::EVENT_SAY_HELLO, [$event, 'sayHello'], 'Hello!');

$event->on('\common\components\EventTest', EventTest::EVENT_SAY_HELLO, [$event, 'sayWorld'], 'World!');

2.解除事件:

$event->off('\common\components\EventTest', EventTest::EVENT_SAY_HELLO, [$event, 'sayWorld']);
        

3.触发事件:

$event->trigger('\common\components\EventTest', EventTest::EVENT_SAY_HELLO);

类级别参数比实例级别多一个参数:类名
注意:
解除事件如果不传 handle,则会解除该事件下的所有 handle。
在其中一个 handle 里面加上 $event->handled = true 也会阻止后续 handle 的触发,如上代码。

3. 全局级别

利用Application实例在整个应用的生命周期中全局可访问的特性,来实现这个全局事件的。在任意需要的时候,都可以触发全局事件,也可以在任意必要的时候绑定,或解除一个事件。

代码示例:

1.绑定事件:
$event = new EventTest();

Yii::$app->on('global_event', [$event, 'globalEvent'], 'global event');

2.触发事件:
Yii::$app->trigger('global_event');

全局级别实际上也是一个特殊的实例。[注意所谓全局其实作用范围也只是在当前请求的生命周期有效,而非当前会话(简单的说,打开浏览器,访问服务器,直到浏览器关闭,称为一次会话)]

原理

实例级别通过维护Component类的属性 $_events 数组,触发事件时通过[事件名称]取到[当前实例]的handle数据,再通过函数call_user_func触发handle

类级别通过维护Event类的属性 $_events 数组,触发事件时通过[类名和事件名称]取到[当前类以及父类]的handle数据,再通过函数call_user_func触发handle

Component 的 $_event 数据结构示例如下

Array
(
    [say_hello] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [0] => common\components\EventTest Object
                                (
                                    [_events:yii\base\Component:private] => Array
 *RECURSION*
                                    [_eventWildcards:yii\base\Component:private] => Array
                                        (
                                        )

                                    [_behaviors:yii\base\Component:private] => Array
                                        (
                                        )

                                )

                            [1] => say_hello
                        )

                    [1] => Hello!
                )

        )

)

小结

以上总结参考了深入理解Yii2.0,其实以前就看过,但是也只是局限于看过,没有自己跑demo调试、查看源代码,然后就误以为自己明白了,其实过俩天什么都不记得了。所以现在通过写博客来加深自己的理解,这篇博客其实主要讲的还是事件的使用,具体原理一带而过,如果有小伙伴一知半解,可能查看一下源代码[yii\base\Component yii\base\Event]你就明白了。由于水平有限,欢迎小伙伴交流和指正。


胖哥初来乍到
12 声望1 粉丝

爱自己 爱生活 爱你所爱