• 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!
  • 相关阅读:
    python学习-(__new__方法和单例模式)
    jQuery弹性展开收缩菜单插件gooey.js
    轻量级Modal模态框插件cta.js
    基于jq流畅度非常好的图片左右切换焦点图
    基于jQuery实现的腾讯互动娱乐网站特效
    基于jQuery和CSS3炫酷图片3D旋转幻灯片特效
    可嵌入图片视频jQuery全屏滑块
    基于jQuery仿迅雷影音官网幻灯片特效
    基于jQuery自适应宽度跟高度可自定义焦点图
    基于jQuery实现汉字转换成拼音代码
  • 原文地址:https://www.cnblogs.com/ling-diary/p/9166575.html
Copyright © 2020-2023  润新知