• 老刘 Yii2 源码学习笔记之 Component 类


     类图关系

     属性与方法

    class Component extends BaseObject
    {
        
        private $_events = [];
        private $_eventWildcards = [];
        private $_behaviors;
    
        
        public function __get($name)
        public function __set($name, $value)
        public function __isset($name)
        public function __unset($name)
        public function __call($name, $params)
        public function __clone()
    
        
        public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
        public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
        public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
        public function hasMethod($name, $checkBehaviors = true)
        public function behaviors()
        public function hasEventHandlers($name)
            
        public function on($name, $handler, $data = null, $append = true)
        public function off($name, $handler = null)
        public function trigger($name, Event $event = null)
        public function getBehavior($name)
        public function getBehaviors()
        public function attachBehavior($name, $behavior)
        public function attachBehaviors($behaviors)
        public function detachBehavior($name)
        public function detachBehaviors()
        public function ensureBehaviors()
        private function attachBehaviorInternal($name, $behavior)
    }
    

    除了className() 方法,BaseObject 父类的方法已经全部重写,因为 BaseObject 只是一个单独的基类,Component 类与 Event 和 Behavior 有更复杂的关联。

     事件

     事件的代码可以先看 on 方法。[$handler, $data] 数组 0 是 handler  2 是 data 参数 

        /**
         * @param $name 事件名称
         * @param $handler 处理回调
         * @param null $data 参数
         * @param bool $append 是否追加
         */
        public function on($name, $handler, $data = null, $append = true)
        {
            //初始化行为
            $this->ensureBehaviors();
    
            if (strpos($name, '*') !== false) {
                if ($append || empty($this->_eventWildcards[$name])) {
                    $this->_eventWildcards[$name][] = [$handler, $data];
                } else {
                    array_unshift($this->_eventWildcards[$name], [$handler, $data]);
                }
                return;
            }
            // 如果已经存在事件名称追加
            if ($append || empty($this->_events[$name])) {
                $this->_events[$name][] = [$handler, $data];
            } else {
                array_unshift($this->_events[$name], [$handler, $data]);
            }
        }

     事件的执行,下面的代码可以看到 如果 $event 为 NULL, 会 new Event, 并设置 sender 属性为当前 类, $event->data 为 on 的data 参数,  方法的最后一行, Event::trigger($this, $name, $event)  会执行类级别的事件

     public function trigger($name, Event $event = null)
        {
            $this->ensureBehaviors();
    
            $eventHandlers = [];
            foreach ($this->_eventWildcards as $wildcard => $handlers) {
                if (StringHelper::matchWildcard($wildcard, $name)) {
                    $eventHandlers = array_merge($eventHandlers, $handlers);
                }
            }
    
            if (!empty($this->_events[$name])) {
                $eventHandlers = array_merge($eventHandlers, $this->_events[$name]);
            }
    
            if (!empty($eventHandlers)) {
                if ($event === null) {
                    $event = new Event();
                }
                if ($event->sender === null) {
                    $event->sender = $this;
                }
                $event->handled = false;
                $event->name = $name;
                foreach ($eventHandlers as $handler) {
                    $event->data = $handler[1];
                    call_user_func($handler[0], $event);
                    // stop further handling if the event is handled
                    if ($event->handled) {
                        return;
                    }
                }
            }
    
            // invoke class-level attached handlers
            Event::trigger($this, $name, $event);
        }

    可以举个例子, 可以看一下,全部都执行了,可以把 $event 打印一下看一下data

    namespace appevents;
    use yiiaseEvent;
    
    class MyEvent extends Event {
        public $message;
    
        function __construct($message)
        {
            parent::__construct();
            $this->message = $message;
        }
    }
    
    
    class SiteController extends Controller
    {
        const EVENT_TEST = 'test';
    
        /**
         * {@inheritdoc}
         */
        function init()
        {
            parent::init(); // TODO: Change the autogenerated stub
            $this->on(self::EVENT_TEST, [new appmodelsEventTest(), 'add'],  ['a', 'b']);
            Event::on(SiteController::className(), self::EVENT_TEST,  function () { echo 'class level event';});
        }
    public function actionTest() { $event = new MyEvent('implements'); $this->trigger(self::EVENT_TEST, $event); echo 'done'; } }

      行为

     使用行为(behavior)可以在不修改现有类的情况下,对类的功能进行扩充。 通过将行为绑定到一个类,可以使类具有行为本身所定义的属性和方法,就好像类本来就有这些属性和方法一样。 而且不需要写一个新的类去继承或包含现有类。把行为注入到类中。举个例子

    class MyClass extends yiiaseComponent
    {
        // 空的
    }
    
    // Step 2: 定义一个行为类,他将绑定到MyClass上
    class MyBehavior extends yiiaseBehavior
    {
        // 行为的一个属性
        public $property1 = 'This is property in MyBehavior.';
    
        // 行为的一个方法
        public function method1()
        {
            return 'Method in MyBehavior is called.';
        }
    }
    
    $myClass = new MyClass();
    $myBehavior = new MyBehavior();
    
    // Step 3: 将行为绑定到类上
    $myClass->attachBehavior('myBehavior', $myBehavior);
    
    // Step 4: 访问行为中的属性和方法,就和访问类自身的属性和方法一样
    echo $myClass->property1;
    echo $myClass->method1();

    以上是怎么做到呢, 可以看 Component  里的魔术方法 set、get、call ,对行为对象的属性和方法进行了注入。

    public function __get($name)
    {
        //其他省略
        // behavior property
        $this->ensureBehaviors();
        foreach ($this->_behaviors as $behavior) {
            if ($behavior->canGetProperty($name)) {
                return $behavior->$name;
            }
        }
        //其他省略
    }
    
    public function __set($name, $value)
    {
        //其他省略
        // behavior property 
        $this->ensureBehaviors();
        foreach ($this->_behaviors as $behavior) {
            if ($behavior->canSetProperty($name)) {
                $behavior->$name = $value;
                return;
            }
        }
        //其他省略
    }
    
    public function __call($name, $params)
    {
        $this->ensureBehaviors();
        foreach ($this->_behaviors as $object) {
            if ($object->hasMethod($name)) {
                return call_user_func_array([$object, $name], $params);
            }
        }
        throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");
    }
    

     Behavior 类中可以绑定事件, 如下面的代码,继承 Behavior并设置 events 属性. 

    public function attach($owner)
        {
            $this->owner = $owner;
            foreach ($this->events() as $event => $handler) {
                $owner->on($event, is_string($handler) ? [$this, $handler] : $handler);
            }
        }

    举个具体的例子,下面的代码

    namespace appcomponent;
    
    use Yii;
    use yiiaseController;
    use yiiaseBehavior;
    
    class MyBehavior extends Behavior
    {
        public $param;
    
        public function events()
        {
            return [
                Controller::EVENT_BEFORE_ACTION => 'handlerBeforeAction'
            ];
        }
    
        public function handlerBeforeAction()
        {
            echo '由行为注册的组件事件, 执行 beforeAction <br>';
        }
    
        public function behaviorMethod()
        {
            echo '行为中的定义的方法';
        }
    }
    
    
    namespace appcontrollers;
    
    use Yii;
    use yiiwebController;
    use appcomponentMyBehavior;
    
    
    
    /**
     * Class  CurdController
     * @package appcontrollers
     */
    class BehaviorController extends Controller
    {
        public function behaviors()
        {
            return [
                'access' => [
                    'class' => MyBehavior::className(),
                    'param' => 'behavior param'
                ]
            ];
        }
    
    
        public function actionIndex()
        {
            echo '行为中的属性: '.$this->param.'<br>';
            $this->behaviorMethod();
            echo '<br>';
        }
    }

    执行结果为:

    由行为注册的组件事件, 执行 beforeAction 
    行为中的属性: behavior param
    行为中的定义的方法
    

    行为设计灵活,  遵循设计原则对修改关闭,对扩展开放. 行为与 traits 区别可以参考文档核心概念. 行为的添加删除具体可以阅读类里面的详细内容.

     属性

    属性的设置可以参考魔术方法 set 和 get, 非常方便的像调用属性一样调用方法.

  • 相关阅读:
    阿里HBase高可用8年“抗战”回忆录
    Service Mesh 初体验
    阿里云HBase推出普惠性高可用服务,独家支持用户的自建、混合云环境集群
    Ververica Platform-阿里巴巴全新Flink企业版揭秘
    深度 | 带领国产数据库走向世界,POLARDB底层逻辑是什么?
    AI加持的阿里云飞天大数据平台技术揭秘
    Nacos 常见问题及解决方法
    数据上云,应该选择全量抽取还是增量抽取?
    一文带你了解 Flink Forward 柏林站全部重点内容
    Oracle数据库中序列(SEQUENCE)的用法详解
  • 原文地址:https://www.cnblogs.com/liuzhang/p/8857544.html
Copyright © 2020-2023  润新知