Yii2事件初探

Yii2事件原理解析介绍了Yii2中事件分为三个等级:

(1)类级别事件,

(2)实例级别事件,

(3)全局事件。

下面写一个demo简单展示类与实例级别事件的绑定与触发的实现原理,加深理解。

1.demo类结构:

(1)Event类代表事件基类,存储了事件名称,触发者,处理状态等基本信息,同时提供一个静态私有变量$_events用于存储类级别的事件绑定情况,静态公有on方法用于绑定类级别事件,静态公有trigger方法用于触发类级别事件;

(2)Component类代表组件基类,这里只实现了与事件绑定与触发相关的部分属性与方法,私有成员变量$_events用于记录实例对象绑定的事件信息,on方法用于绑定实例级别事件,trigger方法用于触发实例级别事件;

(3)类A继承自Component,定义了两个事件handler,类B继承自A,定义了一个事件handler。

demo主要目的是理解以下内容:

(1)实例级别事件的实例隔离特性;

(2)实例事件与类级别事件的触发顺序;

(3)基类事件与子类事件的相互影响。

2.demo源码

(1)基础类实现

class Event{
    public $name;
    public $sender;
    public $handled;
    private static $_events = [];
    
    public static function on($class,$name,$handler,$data=[],$append=true){
        $class = ltrim($class,'\\');
        if($append || empty(self::_events[$name][$class])){//注意这里从事件出发找到对应类的handlers
            self::$_events[$name][$class][] = [$handler,$data];
        }else{
            array_unshift(self::$_events[$name][$class][],[$handler,$data]);
        }
    }
    public static function trigger($class,$name,$event=null){
        $class = is_object($class) ? get_class($class) : ltrim($class,'\\');
        echo 'class event trigger:',$class,' event:',$name,' ';
        
        if(empty($event)){
            $event = new Event();
        }
        $event->name = $name;
        $event->sender = empty($event->sender) ? $class : $event->sender;
        
        //方法1)yii源码中使用spl的class_parents与class_implements找到类的所有父类与实现的接口
//         $classes = array_merge(
//             [$class],
//             class_parents($class),
//             class_implements($class)
//             );
        //方法2)这里使用反射机制实现同样功能
        $classes = [$class];
        $reflection = new \ReflectionClass($class);
        $classes = array_merge($classes,$reflection->getInterfaceNames());
        while($parent = $reflection->getParentClass()){
            $parentName = $parent->getName();
            $classes[] = $parentName;
            $classes = array_merge($classes,$parent->getInterfaceNames());
            $reflection = $parent;
        }
        
        foreach($classes as $c){
            $handlers = self::$_events[$name][$c];
            if(is_array($handlers)){
                foreach($handlers as $handler){
                    call_user_func($handler[0],$event);
                    if($event->handled){
                        return;
                    }
                }
            }else{
                echo $name,' has no handlers binded to class ',$c,'!<br>';
            }
        } 
    }
}
class Component{
    private $_events;
    public function on($event,$handler,$data=null,$append=true){
        $this->_events[$event][] = [$handler,$data];
    }
    public function trigger($name,$event=null){
        if(!isset($event)){
            $event = new Event();
        }
        if(empty($event->sender)){
            $event->sender = $this;
        }
        $event->name = $name;
        
        //输出事件触发者
        echo 'object event trigger:',$event->sender->name,', event:',$event->name,' ';
        
        $handlers = $this->_events[$name];
        if(is_array($handlers)){
            foreach($handlers as $handler){
                call_user_func($handler[0],$event);
                if($event->handled){
                    return;
                }
            }
        }else{
            echo 'no handler binded to object ',$event->sender->name,'<br>';
        }
        
        Event::trigger($this,$name,$event);
    }
}
class A extends Component{
    public $name;
    public function __construct($name){
        $this->name = $name;
    }
    public function handlerA($event){
        echo 'handler: handlerA!<br>';
    }
    public function handlerC($event){
        echo 'handler: handlerC!<br>';
    }
}
class B extends A{
    public function handlerB($event){
        echo 'handler: handlerB!<br>';
    }
}

(2)验证我的问题

//给类A绑定一个事件EVENT_A
Event::on('A','EVENT_A',['A','handlerC']);

//给A的实例a绑定同一事件EVENT_A并触发
$a = new A('a');
$a->on('EVENT_A',[$a,'handlerA']);
$a->trigger('EVENT_A',new Event());
//A的实例c触发未绑定的实例事件EVENT_A
$c = new A('c');
$c->trigger('EVENT_A');

//给类B的实例b绑定另一个事件EVENT_B并触发EVENT_A与EVENT_B
$b = new B('b');
$b->on('EVENT_B',[$b,'handlerB']);
$b->trigger('EVENT_A');
$b->trigger('EVENT_B');

(3)演示结果

//当类与实例绑定了同一事件时,实例级别的handlers先执行,然后再执行类级别的handlers
object event trigger:a, event:EVENT_A handler: handlerA! class event trigger:A event:EVENT_A handler: handlerC! EVENT_A has no handlers binded to class Component!
//实例a与实例c均为A的实例,但c未绑定实例事件,所以实例事件存在实例隔离特性 object event trigger:c, event:EVENT_A no handler binded to object c class event trigger:A event:EVENT_A handler: handlerC! EVENT_A has no handlers binded to class Component!
//实例b是A的子类B的实例,虽然b与B均未绑定事件EVENT_A,但A存在对该事件的类级别处理,所以基类的事件绑定会影响子类 object event trigger:b, event:EVENT_A no handler binded to object b class event trigger:B event:EVENT_A EVENT_A has no handlers binded to class B! handler: handlerC! EVENT_A has no handlers binded to class Component!
//子类B的类级别事件不会影响基类 object event trigger:b, event:EVENT_B handler: handlerB! class event trigger:B event:EVENT_B EVENT_B has no handlers binded to class B! EVENT_B has no handlers binded to class A! EVENT_B has no handlers binded to class Component!

转载于:https://www.cnblogs.com/ling-diary/p/9166575.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值