组件(component),是Yii框架的基类,实现了属性、事件、行为三类功能,如果需要事件和行为的功能,需要继承该类,不需要可直接继承Object类;

 namespace yii\base;

 use Yii;

 /**
  * Component is the base class that implements the *property*, *event* and *behavior* features.
  * 组件,是Yii框架的基类,实现了属性、事件、行为三类功能
  * Component provides the *event* and *behavior* features, in addition to the *property* feature which is implemented in
  * its parent class [[Object]].
  * 组件提供了事件和行为功能,通过附加到父类[[Object]]的属性功能中
  * Event is a way to "inject" custom code into existing code at certain places. For example, a comment object can trigger
  * an "add" event when the user adds a comment. We can write custom code and attach it to this event so that when the event
  * is triggered (i.e. comment will be added), our custom code will be executed.
  * 事件,可以在特定的时点,触发执行预先设定的一段代码,例如一个评论对象能够在用户添加评论的时候触发一个add事件,我们能够自定义一些代码添加到
  * 这个事件中,在添加评论的时候会触发事件,执行自定义的代码
  * An event is identified by a name that should be unique within the class it is defined at. Event names are *case-sensitive*.
  * 事件的名称在类中是唯一的,区分大小写
  * One or multiple PHP callbacks, called *event handlers*, can be attached to an event. You can call [[trigger()]] to
   raise an event. When an event is raised, the event handlers will be invoked automatically in the order they were
   attached.
  * 一个或者多个事件处理程序嫩巩固被添加到一个事件,能够通过调用[[trigger()]]触发事件,事件处理程序将会按照添加的先后顺序依次执行
  * To attach an event handler to an event, call [[on()]]:
  * 添加事件是通过on()方法添加
  * ```php
  * $post->on('update', function ($event) { //给update方法添加一个匿名函数的时间,该事件的作用是发送邮件信息
  *     // send email notification
  * });
  * ```
  *
  * In the above, an anonymous function is attached to the "update" event of the post. You may attach
  * the following types of event handlers:
  * 事件处理程序有以下几种形式
  * - anonymous function: `function ($event) { ... }`匿名函数
  * - object method: `[$object, 'handleAdd']` 对象的方法 以数组的方式传入
  * - static class method: `['Page', 'handleAdd']` 静态方法  以数组的方式传入
  * - global function: `'handleAdd'` 全局函数 不带参数和括号,只有函数名
  *
  * The signature of an event handler should be like the following:
  *
  * ```php
  * function foo($event) 事件处理程序的格式
  * ```
  *
  * where `$event` is an [[Event]] object which includes parameters associated with the event.
  *
  * You can also attach a handler to an event when configuring a component with a configuration array.
  * The syntax is like the following:
  * 可以通过配置文件来配置事件,格式如下
  * ```php
  * [
  *     'on add' => function ($event) { ... }  给add 添加一个事件
  * ]
  * ```
  *
  * where `on add` stands for attaching an event to the `add` event.
  *
  * Sometimes, you may want to associate extra data with an event handler when you attach it to an event
  * and then access it when the handler is invoked. You may do so by
  * on()方法的第三个参数可以给事件处理程序传入参数
  * ```php
  * $post->on('update', function ($event) {
  *     // the data can be accessed via $event->data
  * }, $data);
  * ```
  *
  * A behavior is an instance of [[Behavior]] or its child class. A component can be attached with one or multiple
  * behaviors. When a behavior is attached to a component, its public properties and methods can be accessed via the
  * component directly, as if the component owns those properties and methods.
  * 行为可以在不修改现有类的情况下,对类的功能进行扩充。 通过将行为绑定到一个类,可以使类具有行为本身所定义的属性和方法,
  * 就好像类本来就有这些属性和方法一样。 而且不需要写一个新的类去继承或包含现有类。
  * To attach a behavior to a component, declare it in [[behaviors()]], or explicitly call [[attachBehavior]]. Behaviors
  * declared in [[behaviors()]] are automatically attached to the corresponding component.
  *
  * One can also attach a behavior to a component when configuring it with a configuration array. The syntax is like the
  * following:
  * 配置行为的数组格式
  * ```php
  * [
  *     'as tree' => [
  *         'class' => 'Tree',
  *     ],
  * ]
  * ```
  *
  * where `as tree` stands for attaching a behavior named `tree`, and the array will be passed to [[\Yii::createObject()]]
  * to create the behavior object.
  *
  * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
 class Component extends Object
 {
     /**
      * @var array 当前对象的已绑定的事件数组 (事件名 => 处理程序)
      */
     private $_events = [];
     /**
      * @var Behavior[]|null 当前对象的已绑定的行为数组 (行为名 => 行为类). 初始化之前为空.
      */
     private $_behaviors;

     /**
      * Returns the value of a component property.
      * This method will check in the following order and act accordingly:
      *
      *  - a property defined by a getter: return the getter result
      *  - a property of a behavior: return the behavior property value
      *
      * 重写 Object 中的 getter 方法,添加对 behaviors 的处理
      * 如果当前组件的属性中没有该属性,则遍历 behaviors,如果其中有相应的属性,返回behaviors中的属性
      *
      * Do not call this method directly as it is a PHP magic method that
      * will be implicitly called when executing `$value = $component->property;`.
      * @param string $name 属性名
      * @return mixed 属性值或者行为中的属性值
      * @throws 属性未定义
      * @throws 属性只读
      * @see __set()
      */
     public function __get($name)
     {
         $getter = 'get' . $name;
         if (method_exists($this, $getter)) {
             // 构造getter方法,如果getter方法存在的话,调用getter方法,返回属性值
             return $this->$getter();
         } else {
             // behavior property
             $this->ensureBehaviors();//调用该方法确保行为已绑定
             foreach ($this->_behaviors as $behavior) {
                 if ($behavior->canGetProperty($name)) {
            //调用父类的方法,判断检查对象或类是否能够获取 $name 属性,如果第二个参数为 true(默认是true),则不局限于是否有 getter
                 // 如果 behavior 中含有该属性,就返回 behavior 中的这个属性
                     return $behavior->$name;
                 }
             }
         }
         if (method_exists($this, 'set' . $name)) {//跟父类相同,判断属性是否只写
             throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);
         } else {//抛出异常
             throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
         }
     }

     /**
      * Sets the value of a component property.
      * This method will check in the following order and act accordingly:
      *
      *  - a property defined by a setter: set the property value
      *  - an event in the format of "on xyz": attach the handler to the event "xyz"
      *  - a behavior in the format of "as xyz": attach the behavior named as "xyz"
      *  - a property of a behavior: set the behavior property value
      **
      * 重写 Object 中的 setter 方法
      * 如果 $name 是 'on xyz',就会将 xyz 事件添加到该对象中
      * 如果 $name 是 'as xyz',就会将 xyz 行为添加到该对象中
      * 添加对 behaviors 的处理,循环 behaviors,如果其中有相应的属性,就设置它
      *
      * Do not call this method directly as it is a PHP magic method that
      * will be implicitly called when executing `$component->property = $value;`.
      * @param string $name 属性名或者事件名
      * @param mixed $value the 属性值
      * @throws UnknownPropertyException if the property is not defined
      * @throws InvalidCallException if the property is read-only.
      * @see __get()
      */
     public function __set($name, $value)
     {
         $setter = 'set' . $name;//在属性名前面加set构建setter方法
         if (method_exists($this, $setter)) {//如果setter方法存在
             // 调用该方法设置属性值
             $this->$setter($value);

             return;
         } elseif (strncmp($name, 'on ', 3) === 0) {//如果方法不存在,且$name以on+空格开始
             // on event: attach event handler
             $this->on(trim(substr($name, 3)), $value);//调用on方法将事件处理程序附加到事件

             return;
         } elseif (strncmp($name, 'as ', 3) === 0) {//否则,如果$name以as+空格开始
             // as behavior: attach behavior
             $name = trim(substr($name, 3));//截取as后面的字符
             $this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value));//用attachBehavior添加一个行为到组件

             return;
         } else {//否则
             // behavior property
             $this->ensureBehaviors();
             foreach ($this->_behaviors as $behavior) {
                 if ($behavior->canSetProperty($name)) {//遍历行为(behaviors),如果行为中有可以设置的属性
                     $behavior->$name = $value;//给该行为类中的属性设置属性值

                     return;
                 }
             }
         }
         if (method_exists($this, 'get' . $name)) {//如果该属性的get方法存在,表名该方法只读,抛出异常
             throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
         } else {//否则抛出异常,未知的属性
             throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
         }
     }

     /**
      * Checks if a property is set, i.e. defined and not null.
      * This method will check in the following order and act accordingly:
      *
      *  - a property defined by a setter: return whether the property is set
      *  - a property of a behavior: return whether the property is set
      *  - return `false` for non existing properties
      *
      *  重写 Object 中的 isset 方法,添加对 behaviors 的处理,
      *  如果component中不存在该属性,遍历 behaviors,如果其中有相应的属性,就认为有
      *
      * Do not call this method directly as it is a PHP magic method that
      * will be implicitly called when executing `isset($component->property)`.
      * @param string $name 属性名或事件名
      * @return boolean whether the named property is set
      * @see http://php.net/manual/en/function.isset.php
      */
     public function __isset($name)
     {
         $getter = 'get' . $name;
         if (method_exists($this, $getter)) {
             // 判断是否有getter方法,且是否有返回值,有 getter 方法且获取的值不为 null,才认为该属性存在
             return $this->$getter() !== null;
         } else {
             // behavior property
             $this->ensureBehaviors();//确定行为已经绑定
             foreach ($this->_behaviors as $behavior) {//遍历 behaviors
                 if ($behavior->canGetProperty($name)) {//如果行为(behaviors)中有相应的属性,就认为有
                     return $behavior->$name !== null;
                 }
             }
         }
         return false;
     }

     /**
      * Sets a component property to be null.
      * This method will check in the following order and act accordingly:
      *
      *  - a property defined by a setter: set the property value to be null
      *  - a property of a behavior: set the property value to be null
      *
      * 重写 Object 中的 unset 方法,添加对 behaviors 的处理
      * 如果component中不存在该属性,遍历 behaviors,如果其中有相应的属性,设置为空
      *
      * Do not call this method directly as it is a PHP magic method that
      * will be implicitly called when executing `unset($component->property)`.
      * @param string $name 属性名
      * @throws InvalidCallException if the property is read only.
      * @see http://php.net/manual/en/function.unset.php
      */
     public function __unset($name)
     {
         $setter = 'set' . $name;
         if (method_exists($this, $setter)) {
             // 如果setter方法存在,通过 setter 方法,将它设置为 null
             $this->$setter(null);
             return;
         } else {
             // behavior property
             $this->ensureBehaviors();
             foreach ($this->_behaviors as $behavior) {//否则遍历 behaviors
                 if ($behavior->canSetProperty($name)) {//如果行为中有相应的属性,设置为空
                     $behavior->$name = null;
                     return;
                 }
             }
         }
         //抛出异常
         throw new InvalidCallException('Unsetting an unknown or read-only property: ' . get_class($this) . '::' . $name);
     }

     /**
      * Calls the named method which is not a class method.
      *
      * This method will check if any attached behavior has
      * the named method and will execute it if available.
      *
      * 重写 Object 中的 call 方法,添加对 behaviors 的处理
      * 遍历 behaviors,如果其中有相应方法,就执行该 behavior 的方法
      *
      * Do not call this method directly as it is a PHP magic method that
      * will be implicitly called when an unknown method is being invoked.
      * @param string $name 方法名
      * @param array $params 参数
      * @return mixed 方法执行后的返回值
      * @throws UnknownMethodException when calling unknown method
      */
     public function __call($name, $params)
     {
         $this->ensureBehaviors();//确认行为已经绑定
         foreach ($this->_behaviors as $object) {
             if ($object->hasMethod($name)) {//调用父类的方法,判断该行为中是否有$name方法
                 return call_user_func_array([$object, $name], $params);//如果行为中有该方法,则执行该方法
             }
         }
         throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");
     }

     /**
      * This method is called after the object is created by cloning an existing one.
      * It removes all behaviors because they are attached to the old object.
      *
      * 在对象克隆clone的时候调用这个方法,将其 _events 和 _behaviors 设置为空
      * 当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。
      * 所有的引用属性 仍然会是一个指向原来的变量的引用,所以没必要浪费内存
      *
      */
     public function __clone()
     {
         // 对象复制时,将它的 _events 设置为空数组,将 _behaviors 设置为 null
         $this->_events = [];
         $this->_behaviors = null;
     }

     /**
      * Returns a value indicating whether a property is defined for this component.
      * A property is defined if:
      *
      * - the class has a getter or setter method associated with the specified name
      *   (in this case, property name is case-insensitive);
      * - the class has a member variable with the specified name (when `$checkVars` is true);
      * - an attached behavior has a property of the given name (when `$checkBehaviors` is true).
      *
      * 与 Object 中的方法类似,只是添加了是否检测 behavior 的参数
      *
      * @param string $name the property name
      * @param boolean $checkVars whether to treat member variables as properties
      * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
      * @return boolean whether the property is defined
      * @see canGetProperty()
      * @see canSetProperty()
      */
     public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
     {
         // $checkVars 参数,用来设置是否局限于是否有 getter/setter
         //$checkBehaviors参数,用来设置是否检测behavior
         return $this->canGetProperty($name, $checkVars, $checkBehaviors) || $this->canSetProperty($name, false, $checkBehaviors);
     }

     /**
      * Returns a value indicating whether a property can be read.
      * A property can be read if:
      *
      * - the class has a getter method associated with the specified name
      *   (in this case, property name is case-insensitive);
      * - the class has a member variable with the specified name (when `$checkVars` is true);
      * - an attached behavior has a readable property of the given name (when `$checkBehaviors` is true).
      *
      * 检查对象或类是否能够获取 $name 属性,增加了对behavior的检查
      *
      * @param string $name the property name
      * @param boolean $checkVars whether to treat member variables as properties
      * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
      * @return boolean whether the property can be read
      * @see canSetProperty()
      */
     public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
     {     // $checkVars 参数,用来设置是否局限于是否有 getter/setter
         if (method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name)) {
             return true;
         } elseif ($checkBehaviors) {
            //$checkBehaviors参数,用来设置是否检测behavior
             $this->ensureBehaviors();
             foreach ($this->_behaviors as $behavior) {
                 if ($behavior->canGetProperty($name, $checkVars)) {
                     return true;
                 }
             }
         }
         return false;
     }

     /**
      * Returns a value indicating whether a property can be set.
      * A property can be written if:
      *
      * - the class has a setter method associated with the specified name
      *   (in this case, property name is case-insensitive);
      * - the class has a member variable with the specified name (when `$checkVars` is true);
      * - an attached behavior has a writable property of the given name (when `$checkBehaviors` is true).
      *
      * 检查对象或类是否能够设置 $name 属性,增加了对behavior的检查
      *
      * @param string $name the property name
      * @param boolean $checkVars whether to treat member variables as properties
      * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component
      * @return boolean whether the property can be written
      * @see canGetProperty()
      */
     public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
     {
         // $checkVars 参数,用来设置是否局限于是否有 getter/setter
         if (method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name)) {
             return true;
         } elseif ($checkBehaviors) {//$checkBehaviors参数,用来设置是否检测behavior
             $this->ensureBehaviors();
             foreach ($this->_behaviors as $behavior) {
                 if ($behavior->canSetProperty($name, $checkVars)) {//调用父类方法判断
                     return true;
                 }
             }
         }
         return false;
     }

Yii源码阅读笔记(六)的更多相关文章

  1. Yii源码阅读笔记(一)

    今天开始阅读yii2的源码,想深入了解一下yii框架的工作原理,同时学习一下优秀的编码规范和风格.在此记录一下阅读中的小心得. 每个框架都有一个入口文件,首先从入口文件开始,yii2的入口文件位于we ...

  2. Yii源码阅读笔记(二十六)

    Application 类中设置路径的方法和调用ServiceLocator(服务定位器)加载运行时的组件的方法注释: /** * Handles the specified request. * 处 ...

  3. Yii源码阅读笔记(十六)

    Model类,集中整个应用的数据和业务逻辑—— /** * Generates a user friendly attribute label based on the give attribute ...

  4. Yii源码阅读笔记(八)

    前面阅读了Yii2的两个基本类Object和Component,了解了Yii的三个重要概念属性.事件.行为,下面开始阅读Event类,Event类是所有事件类的基类: <?php /** * @ ...

  5. Yii源码阅读笔记(三)

    接着上次的继续阅读BaseYii.php vendor/yiisoft/yii2/BaseYii.php—— public static function getRootAlias($alias)// ...

  6. Yii源码阅读笔记(二)

    接下来阅读BaseYii.php vendor/yiisoft/yii2/BaseYii.php—— namespace yii; use yii\base\InvalidConfigExceptio ...

  7. Yii源码阅读笔记(三十五)

    Container,用于动态地创建.注入依赖单元,映射依赖关系等功能,减少了许多代码量,降低代码耦合程度,提高项目的可维护性. namespace yii\di; use ReflectionClas ...

  8. Yii源码阅读笔记(三十四)

    Instance类, 表示依赖注入容器或服务定位器中对某一个对象的引用 namespace yii\di; use Yii; use yii\base\InvalidConfigException; ...

  9. Yii源码阅读笔记(三十三)

    ServiceLocator,服务定位类,用于yii2中的依赖注入,通过以ID为索引的方式缓存服务或则组件的实例来定位服务或者组件: namespace yii\di; use Yii; use Cl ...

随机推荐

  1. repo sync下载脚本

    #!/bin/sh echo "======start repo sync======" repo sync while [ $? -ne 0 ]do echo "=== ...

  2. UVa 11991:Easy Problem from Rujia Liu?(STL练习,map+vector)

    Easy Problem from Rujia Liu? Though Rujia Liu usually sets hard problems for contests (for example, ...

  3. Xamarin.Android开发实践(九)

    Xamarin.Android之ActionBar与菜单 一.选项卡 如今很多应用都会使用碎片以便在同一个活动中能够显示多个不同的视图.在 Android 3.0 以上的版本中,我们已经可以使用Act ...

  4. 利用OData轻易实现串流数据的可视化

    OData(开放数据协议,Open Data Protocol)一直是我喜欢一种的标准(OASIS 标准),它基于RESTful协议提供了一种强大的查询和编辑数据的访问接口.虽然是微软推出的,不过在诞 ...

  5. C编译: 动态连接库 (.so文件)

    转自:http://www.cnblogs.com/vamei/archive/2013/04/04/2998850.html 在“纸上谈兵: 算法与数据结构”中,我在每一篇都会有一个C程序,用于实现 ...

  6. C++的那些事:数据与类型

    一.前言 最近在看C++Primer第5版,先前已经看过第4版,但是发现第5版在整个知识布局与个别知识的讲解上跟第4版差别还是挺大的,尤其是新增了C++11的内容,正如孟岩老师在第5版前言中所讲:“现 ...

  7. SQL Server连接数据库失败,可能的问题!

    SQL Server Configuration Manager中启动服务 SQL Server外围应用配置器中,打开远程IP连接属性 别的应该没什么问题了!

  8. 【spring 区别】ClassXmlAplicationContext和FileSystemXmlApplicationContext的区别

    今天写一个简单的spring使用例子,遇到这个问题: 项目结构如下: 代码如下: package com.it.sxd; import java.nio.file.FileSystem; import ...

  9. LoadRunner11支持的浏览器小结

    LoadRunner11录制脚本时不能打开IE浏览器,解决方案有以下几个步骤: l  LoadRunner11支持的浏览器版本最高是ie9,把浏览器版本换成ie9; l  打开IE选项----高级—去 ...

  10. 国家与城市的sql

    --省表 create table tb_province ( pID int NOT NULL PRIMARY KEY, pName ) ) --省 ,'北京市') ,'天津市') ,'上海市') ...