zoukankan      html  css  js  c++  java
  • Yii2.0源码阅读-behavior的实现原理

    Yii2.0中的一个思想就是组件化的思想,所以、大多数的类都直接或间接的继承自yiiaseComponent,而组件的三大功能:属性、事件、行为。

    行为的目的是为了方便的扩展一个类的功能,而不需要直接去修改这个类,同时行为中也附带了事件的实现。

    1、整体的结构

    • Controller和模型ActiveRecord都继承自yiiaseComponent
    • 而Component继承自yiiaseObject
    • Object中和Component中都实现了魔术方法__get和__set以及__call
    • 所以,在控制器和模型中都可以使用属性这个特性,可以很方便的控制属性的可读、可写

    2、行为

    可以无须改变类继承关系即可增强一个已有的组件类功能,当行为附加到组件之后,它将“注入”它的方法和属性到组件,然后可以像访问组件内定义的方法和属性一样访问它们

    3、行为如何做到“注入”?

    注入一个行为的方式:

    • 自定义一个行为类MyBehavior、继承自yiiaseBehavior
    • 实例化一个组件类,组件类继承自yiiaseComponent
    • 然后调用attachBehavior将行为附加到组件类
    • 组件类可以直接使用MyBehavior中的属性和方法

    attach注入做了什么?

    • 在所有组件的父类Component中有个$_behaviors
    • 保存了所有注入到当前组件的行为,见Component中的attachBehaviorInternal方法
    • 同时行为类中有个$owner,保存了当前的组件对象(用于事件绑定)

    注入完成,怎么生效的(以model为例)?

    • 见yiiaseBaseActiveRecord的魔术方法
    • 如果当前对象的attributes中没有找到对应的属性,调用parent::__get($name),位于Component
    • Component::__get($name)中,判断当前对象中是否存在getter方法,没有则遍历$_behaviors,如果behavior中有匹配的getter方法,则返回值

    注:如果同一个组件类中注入了不同的behavior,而不同的behavior有相同的属性,那么使用组件类实例访问属性的时候,返回的 是先attach的behavior,因为在getter中是foreach循环$_behaviors,存在则return,循环终止

    同理:

    • 设置属性的值是在__set魔术方法中做了$_behaviors的遍历
    • 调用行为中的方法是在__call魔术方法中做了$_behaviors的遍历

    不手动attachBehavior,怎么做?

    • 只需要在具体的组件类中定义一个behaviors方法
    • 方法中返回行为类的配置
    • 在魔术方法__get、__set、__call中,调用相应的方法前都执行了ensureBehaviors
    • ensureBehaviors做的工作就是获取behaviors()方法中的配置数组,然后attach到当前组件类

    行为中的事件?

    • 可以在行为类中定义一个events方法,返回事件的配置数组
    • 在attach行为的时候,会获取events中的配置,执行on事件绑定

    所以在事件绑定方法on和事件触发方法trigger方法中,都首先调用了$this->ensureBehaviors()方法,来保证行为中的事件也已经调用绑定成功了

    4、简单的示例代码

    //Yii中的祖先类
    class Object
    {
        //魔术方法,实现getter
        public function __get($name)
        {
            $getter = 'get'.$name;
            if(method_exists($this, $getter)){
                return $this->$getter();
            }
        }  
        //判断是否有对应的getter方法 || 直接有相应的属性
        public function canGetProperty($name, $checkVars = true)
        {
            return method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name);
        }
    }
    //行为基类
    class Behavior extends Object
    {
        //行为注册到的那个组件类的引用
        public $owner;
        
        //行为附加操作
        public function attach($owner)
        {
           $this->owner = $owner;
           //事件的处理
           foreach($this->events() as $event => $handler){
                //$this->owner->on($event, $handler);
           }
    
        }
        //behavior中可以定义events,attach中会进行事件的绑定
        public function events()
        {
            return [];
        }
    }
    //组件基类
    class Component extends Object
    {
        public $_behaviors = [];
    
        //__get 
        public function __get($name)
        {
    
            $getter = 'get'.$name;
            if(method_exists($this, $getter)){
                return $this->$getter();
            }else{
                $this->ensureBehaviors();
                foreach($this->_behaviors as $behavior){
                    return $behavior->$name;
                }      
            }
    
        }
        //读取相应组件中的behaviors配置,调用attachBehaviorInternal,将当前组件行为保存到$_behaviors
        public function ensureBehaviors()
        {
            if($this->_behaviors === null){
                $this->_behaviors = [];
                foreach($this->behaviors() as $name => $behavior){
                    $this->attachBehaviorInternal($name, $behavior);
                }
            }
        }
        //将当前组件对象保存传递给行为类中了owner,在组件类中保存所有的行为
        public function attachBehaviorInternal($name, $behavior)
        {
            if(!($behavior instanceof Behavior)){
                //根据配置创建behavior对象
            } 
            if(is_int($name)){
                $behavior->attach($this);
                $this->_behaviors[] = $behavior;
            }else{
                $behavior->attach($this);
                $this->_behaviors[$name] = $behavior;
            }
            return $behavior;
        }
    }
    //test 测试要使用的行为的类必须继承Component
    class TestModel extends Component
    {
        public $test = 'test';
    
    }
    class MyBehavior extends Behavior
    {
        public $mybehavior = 'get my behavior success';
        public function getMybehavior()
        {
            return $this->mybehavior;
        }
    }
    
    $model = new TestModel();
    $myBehavior = new MyBehavior();
    $model->attachBehaviorInternal('myBehavior', $myBehavior);
    
    echo $model->mybehavior;
    //输出
    //get my behavior success
    
    

    原文链接http://www.cnblogs.com/skyfynn/p/8921058.html

  • 相关阅读:
    微信公众平台开发介绍(一)
    C#使用iTextSharp操作PDF文件
    使用NPOI读取Excel文件
    jquery写的树状列表插件-alvintree
    分享一个图片上传插件(TP5.0)
    TP5.0实现无限极回复功能
    php静态缓存简单制作
    LinQ to SQL用法详解
    php简单实现socket通信
    简单分析JavaScript中的面向对象
  • 原文地址:https://www.cnblogs.com/skyfynn/p/8921058.html
Copyright © 2011-2022 走看看