yii2 源码分析 Component类分析 (二)
转载请注明链接http://www.cnblogs.com/liuwanqiu/p/6739538.html
组件(component),是Yii框架的基类,实现了属性、事件、行为三类功能,它集成自object类
类前面的大部分注释:
.事件在定义的类中唯一名称标示,事件名称区分大小写
.将事件处理程序到附加到事件,是用on
.有三种事件函数的调用方法,匿名函数调用,对象调用,静态方法调用,全局函数调用
.可以在配置中配置组件,使用方式是:
[
'on add' => function ($event) { ... }
]
.当你想在事件中传入数据时可以这样使用:
$post->on('update', function ($event) {
// 数据被这样调用 $event->data
}, $data);
.行为类也是该组件类的子类,用法和事件差不多
class Component extends Object
{
/**
* 以键值对的方式存储事件处理
*/
private $_events = [];
/**
*存储对象的行为。默认为null
*/
private $_behaviors; /**
* 返回组件的属性
* 此方法按照下列顺序检查或执行:
*
* - 属性通过get方法定义,返回get结果
* - 属性是个行为,返回行为的属性值
*
* 不要直接调用此方法,因为它是一个PHP魔术方法
*/
public function __get($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
// 判断get方法是否存在,存在就直接返回
return $this->$getter();
} else {
//确保行为已经绑定
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canGetProperty($name)) {
//如果行为中有这个属性,就返回这个属性
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);
}
} /**
* 设置组件的属性.
* 此方法将按下列顺序检查并相应执行:
*
* - 属性存在,设置该属性并返回
* - 如果 $name 是 'on ex',就会将 ex 事件添加到该对象中
* - 如果 $name 是 'as ex',就会将 ex 行为添加到该对象中
* - 添加对 behaviors 的处理,循环 behaviors,如果其中有相应的属性,就设置它
*/
public function __set($name, $value)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
// 设置该对象的属性,并返回
$this->$setter($value); return;
} elseif (strncmp($name, 'on ', 3) === 0) {
// 字符串截取后是on开头的,执行on方法附件事件,并返回
$this->on(trim(substr($name, 3)), $value); return;
} elseif (strncmp($name, 'as ', 3) === 0) {
// 字符串截取后是as开头的,执行attachBehavior方法附件事件,并返回
$name = trim(substr($name, 3));
//$value这个对象是Behavior类的一个实例,取$value为参数,否则静态调用Yii方法创造一个新的对象。并返回
$this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value)); return;
} else {
//确保行为已经绑定
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canSetProperty($name)) {
//遍历行为,如果行为中有可以设置的属性$name,给该行为类中的属性设置属性值
$behavior->$name = $value; return;
}
}
}
//返回人性化的提示,属性只能读或不存在
if (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
} else {
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
}
} /**
* 检查属性是否已经被定义
* 此方法按照下列顺序检查或执行:
*
* - 定义过的属性返回真
* - 如果是一个行为,返回行为的属性是否被设置
* - 不存在此属性返回false
*/
public function __isset($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
return $this->$getter() !== null;
} else {
// 确定行为已经绑定
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canGetProperty($name)) {
// 如果 behavior 中有 $name 属性,且不为 null,该属性存在 返回 true
return $behavior->$name !== null;
}
}
}
return false;
} /**
* 设置属性为null,即删除属性
* 重写 Object 中的 unset 方法,
* 通过setter定义的属性:设置该属性值为空
* 属性的行为:将属性值设为空
*/
public function __unset($name)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
$this->$setter(null);
return;
} else {
// 确定行为已经绑定
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canSetProperty($name)) {
$behavior->$name = null;
return;
}
}
}
throw new InvalidCallException('Unsetting an unknown or read-only property: ' . get_class($this) . '::' . $name);
} /**
* 调用组件不存在的方法时被隐式调用
* 调用方法名 重写 Object 中的 call 方法,添加对行为的处理,循环 behaviors,
* 如果其中有相应方法,就执行该 behavior 的方法
*/
public function __call($name, $params)
{
// 确定行为已经绑定
$this->ensureBehaviors();
foreach ($this->_behaviors as $object) {
if ($object->hasMethod($name)) {
//行为中存在名为 $name 的方法,就执行它
//call_user_func_array 调用回调函数,并把一个数组参数作为回调函数的参数
return call_user_func_array([$object, $name], $params);
}
}
//方法不存在就抛出异常
throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");
} /**
* 通过克隆现有创建的对象后,此方法会被调用。
* 他将会消除所有的行为。所有的引用属性 仍然会是一个指向原来的变量的引用
*/
public function __clone()
{
$this->_events = [];
$this->_behaviors = null;
} /**
* 返回true或false表示此属性在组件中是否被定义
* - 类拥有一个关联的set或get方法
* (属性名不区分大小写);
* - 类具有指定名称的成员变量 (when `$checkVars` is true);
* - 附加的行为具有给定名称的属性 (when `$checkBehaviors` is true).
*/
public function hasProperty($name, $checkVars = true, $checkBehaviors = true)
{
return $this->canGetProperty($name, $checkVars, $checkBehaviors) || $this->canSetProperty($name, false, $checkBehaviors);
} /**
* 返回一个值,该值指示是否可以读取属性。
* A property can be read if:
*
* - 获取属性
* (属性名称不区分大小写);
* 其它参数和hasProperty方法的参数差不多,就不一一介绍了。。。
*/
public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
{
if (method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name)) {
return true;
} elseif ($checkBehaviors) {
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canGetProperty($name, $checkVars)) {
return true;
}
}
}
return false;
} /**
* 返回一个方法是否可以被设置
*/
public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
{
if (method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name)) {
return true;
} elseif ($checkBehaviors) {
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canSetProperty($name, $checkVars)) {
return true;
}
}
}
return false;
} /**
* 返回一个布尔值,表示是否有此方法
*/
public function hasMethod($name, $checkBehaviors = true)
{
if (method_exists($this, $name)) {
return true;
} elseif ($checkBehaviors) {
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->hasMethod($name)) {
return true;
}
}
}
return false;
} /**
* 返回该组件应表现为的行为列表.
*
* 子类可以重写此方法,以指定它们想要表现的行为.
*
* 此方法的返回值应该是由行为名称索引的行为对象或配置数组,行为配置可以是指定行为类的字符串,也可以是以下结构的数组:
*
* ```php
* 'behaviorName' => [
* 'class' => 'BehaviorClass',
* 'property1' => 'value1',
* 'property2' => 'value2',
* ]
* ```
*
* 新建的行为必须继承行为类 [[Behavior]]. 可以使用名称或匿名连接行为.
* 当用作数组键的名称时, 使用这个名称, 该行为稍后可以检索使用[[getBehavior()]],即添加行为
* 或被分离使用 [[detachBehavior()]],即解除行为. 匿名行为不能检索或分离.
*
* 此方法中声明的行为将自动(按需)连接到组件
*
* @return 返回值为数组的行为配置.
*/
public function behaviors()
{
return [];
} /**
* 判断 _events 中的一个事件是否具有事件处理程序.
* @param string $name 事件名
* @return boolean whether there is any handler attached to the event.
*/
public function hasEventHandlers($name)
{
$this->ensureBehaviors();
// 判断事件是否存在 $name 否则 调用Event类中的的方法判断是否有处理程序
return !empty($this->_events[$name]) || Event::hasHandlers($this, $name);
} /**
*将事件处理程序附加到事件.
*
* 事件处理程序必须是有效的PHP回调. 例如:
*
* ```
* function ($event) { ... } // anonymous function----匿名函数
* [$object, 'handleClick'] // $object->handleClick()---对象方法
* ['Page', 'handleClick'] // Page::handleClick()---静态类方法
* 'handleClick' // global function handleClick()---全局函数
* ```
*
* 事件处理程序必须定义以下签名,
*
* ```
* function ($event)
* ```
*
* `$event`包括与事件关联的参数.
*
* @param string $name 事件名
* @param callable $handler 事件处理函数
* @param $data 当事件触发时,将数据传递给事件处理程序.
* When the event handler is invoked, this data can be accessed via [[Event::data]].
*当事件处理程序被调用时,这些数据可以通过[ [事件::数据] ]来访问
* @param boolean $append是否将新事件处理程序追加到现有的结尾,处理程序列表。如果FALSE,新的处理程序将被插入在现有事件的开始
* @see off()
*/
public function on($name, $handler, $data = null, $append = true)
{
$this->ensureBehaviors();
//$append 判断是否添加到事件(event)的后面,确保_events中有该事件
if ($append || empty($this->_events[$name])) {
//将事件处理程序和参数添加到event数组末尾
$this->_events[$name][] = [$handler, $data];
} else {
//否则 添加到 event 的前面
array_unshift($this->_events[$name], [$handler, $data]);
}
} /**
* 删除事件处理程序.
* [[on()]]的反方法.
* @param string $name 事件名
* @param callable $handler 事件处理程序
* 如果为空,清除所有的事件处理程序
* @return boolean 是否发现并分离的处理程序
* @see on()
*/
public function off($name, $handler = null)
{
$this->ensureBehaviors();
// 相应的事件不存在,返回false
if (empty($this->_events[$name])) {
return false;
}
//没有handler,清除该事件的所有事件处理程序 并返回true
if ($handler === null) {
unset($this->_events[$name]);
return true;
} else {
$removed = false;//删除标记
foreach ($this->_events[$name] as $i => $event) {
if ($event[0] === $handler) {//遍历该事件 判断事件处理程序是否符合
unset($this->_events[$name][$i]);//删除该事件处理程序
$removed = true;
}
}
if ($removed) {
// 如果删除成功,就需要重新构建以下索引,重新赋值
$this->_events[$name] = array_values($this->_events[$name]);
}
return $removed;
}
} /**
* 触发事件.
* 它调用包括事件级处理程序在内的所有附加处理程序
* @param string $name 事件名
* @param Event $event 事件参数. 如果未设置,默认的 [[Event]] 对象将被创建.
*/
public function trigger($name, Event $event = null)
{
//确保行为绑定
$this->ensureBehaviors();
if (!empty($this->_events[$name])) {
// 事件名不为空 构建Event对象,为传入到handler函数中做准备
if ($event === null) {
$event = new Event;
}
if ($event->sender === null) {
$event->sender = $this;
}
$event->handled = false;
$event->name = $name;
// 遍历事件 给事件的data属性赋值
foreach ($this->_events[$name] as $handler) {
$event->data = $handler[1];
// handler的函数中传入了一个Event对象
call_user_func($handler[0], $event);
// 事件是否被处理,如果了处理事件即handled被设置为true时,停止进一步处理
if ($event->handled) {
return;
}
}
}
// 触发类级别的事件处理程序
Event::trigger($this, $name, $event);
} /**
* 获取行为类
* @param string $name t行为名
* @return null|Behavior 行为对象,如果行为不存在为null
*/
public function getBehavior($name)
{
//确保行为绑定
$this->ensureBehaviors();
//_behaviors中的行为类存在,返回行为类名,否则返回空
return isset($this->_behaviors[$name]) ? $this->_behaviors[$name] : null;
} /**
* 获取所有的行为类
* @return Behavior[] list 返回一个所有行为类的数组
*/
public function getBehaviors()
{
$this->ensureBehaviors();
return $this->_behaviors;
} /**
* 内部使用的添加一个行为到该组件。
* 通过提供的配置文件创建一个Behavior对象,通过调用 [[Behavior::attach()]] 方法添加行为到组件.
* @param string $name 行为名
* @param string|array|Behavior $behavior 行为配置. 这可以是以下之一:
*
* - a [[Behavior]] object 一个[[Behavior]]类
* - a string specifying the behavior class 一个字符串形式的指定行为类
* - 一个配置文件数组,通过调用[[Yii::createObject()]] 创建一个行为对象.
*
* @return Behavior the behavior object行为对象
* @see detachBehavior()
*/
public function attachBehavior($name, $behavior)
{
$this->ensureBehaviors();
return $this->attachBehaviorInternal($name, $behavior);//添加行为
} /**
* 将行为列表附加到组件.
* 行为类通过行为名索引,且必须是一个 [[Behavior]] 对象指定的行为类或者一个配置数组
* @param array $behaviors 行为列表
* @see attachBehavior()
*/
public function attachBehaviors($behaviors)
{
$this->ensureBehaviors();
foreach ($behaviors as $name => $behavior) {
$this->attachBehaviorInternal($name, $behavior);
}
} /**
* 从组件解除行为
* 通过[[Behavior::detach()]]解除行为
* @param string $name行为名
* @return null|Behavior 存在返回分离行为 不存在返回null
*/
public function detachBehavior($name)
{
$this->ensureBehaviors();
if (isset($this->_behaviors[$name])) {
//行为存在,解除行为
$behavior = $this->_behaviors[$name];
unset($this->_behaviors[$name]);
//返回分离行为
$behavior->detach();
return $behavior;
} else {
return null;
}
} /**
* 解除所有行为
*/
public function detachBehaviors()
{
$this->ensureBehaviors();
foreach ($this->_behaviors as $name => $behavior) {
$this->detachBehavior($name);
}
} /**
* 确保声明的行为都被添加到组件
*/
public function ensureBehaviors()
{
if ($this->_behaviors === null) {
$this->_behaviors = [];
foreach ($this->behaviors() as $name => $behavior) {
//遍历$this->behaviors()中的behaviors,并添加到$this->_behaviors数组中
$this->attachBehaviorInternal($name, $behavior);
}
}
} /**
* 内部使用的为该对象添加behavior的方法
* @param string|integer $name 如果这是一个整数,则表示该行为是匿名的。否则,该行为是一个命名的和任何现有的同名行为将首先分离
* @param string|array|Behavior $behavior 添加的行为
* @return Behavior the attached behavior.
*/
private function attachBehaviorInternal($name, $behavior)
{
if (!($behavior instanceof Behavior)) {
// $behavior不是Behavior对象,认为是配置,则创建一个$behavior对象
$behavior = Yii::createObject($behavior);
}
if (is_int($name)) {
//行为是整数,绑定到组件
$behavior->attach($this);
$this->_behaviors[] = $behavior;
} else {
if (isset($this->_behaviors[$name])) {
// 如果有同名的行为存在就先解绑掉
$this->_behaviors[$name]->detach();
}
$behavior->attach($this);//重新绑定行为到组件
$this->_behaviors[$name] = $behavior;
}
return $behavior;
}
}
yii2 源码分析 Component类分析 (二)的更多相关文章
- JDK源码之Integer类分析
一 简介 Integer是int基本类型的包装类,同样继承了Number类,实现了Comparable接口,String类中的一些转化方法就使用了Integer类中的一些API,且fianl修饰不可继 ...
- JDK源码之Byte类分析
一 简介 byte,即字节,由8位的二进制组成.在Java中,byte类型的数据是8位带符号的二进制数,以二进制补码表示的整数 取值范围:默认值为0,最小值为-128(-2^7);最大值是127(2^ ...
- JDK源码之Boolean类分析
一 简介 boolean类型的封装类,将基本类型为boolean的值包装在一个对象中,实现序列化接口,和Comparable接口 额外提供了许多便捷方法,比较简单,直接贴代码分析 二 源码分析 //t ...
- [Android FrameWork 6.0源码学习] LayoutInflater 类分析
LayoutInflater是用来解析XML布局文件,然后生成对象的ViewTree的工具类.是这个工具类的存在,才能让我们写起Layout来那么省劲. 我们接下来进去刨析,看看里边的奥秘 //调用i ...
- JDK源码之AbstractStringBuilder类分析
一 概述 二 实现接口 AbstractStringBuilder实现了两个接口: Appendable 概述: Appendable的实现类的对象可以附加字符序列和值. 要追加的字符应该是Unico ...
- Yii2 源码分析 入口文件执行流程
Yii2 源码分析 入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...
- Yii2源码分析(一):入口
写在前面,写这些随笔是记录下自己看Yii2源码的过程,可能会有些流水账,大部分解析放在注释里说明,由于个人水平有限,有不正确的地方还望斧正. web入口文件Index.php // 定义全局的常量,Y ...
- HashMap的源码学习以及性能分析
HashMap的源码学习以及性能分析 一).Map接口的实现类 HashTable.HashMap.LinkedHashMap.TreeMap 二).HashMap和HashTable的区别 1).H ...
- 物联网防火墙himqtt源码之MQTT协议分析
物联网防火墙himqtt源码之MQTT协议分析 himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWall,C语言编写,采用epoll模式支持数十万 ...
随机推荐
- Tomcat软件使用常见问题
Tomcat软件使用常见问题 tomcat软件使用的常见问题 1)闪退问题 原因:tomcat软件是java语言开发的. tomcat软件启动时,会默认到系统的环境变量中查找一个名称叫JAVA_HOM ...
- word:Can't find the word document templant:WordToRqm.doc
问题:打开word文件时弹出提示 Cannot find the Word template:WordToRqm.dot 原因:安装了power designer. 解决:运行regedit.exe ...
- jQuery --- 实现 checkbox 样式的单选框
早就想写点博客了 一直懒着动 最近发现一些写过的东西都不记得了,下决心把自己平时遇到的问题.得到的经验记录下来,希望能大家一点帮助 这是之前写的一个模态框 要求单选 但是 要求radio的默认样式 ...
- Redis进阶实践之七Redis和Lua初步整合使用
一.引言 Redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当 ...
- Linux 下 编译Xerces-c++
按照 doc/html 文件夹中的详细指导编译 Xerces-C++ 共享库. 下面的命令展示了如何用压缩的源文件编译 Xerces-C++ 库. 这里假定在像 /home/ 这样的目录中有 xerc ...
- lvs_nat
nat模式环境模拟 1.实验环境概览 192.168.1.186 192.168.75.131 双网卡 dir 调度器(vip) ipvsadm,双网卡 192.168.1.185 单网卡 rs1 真 ...
- awkOFS问题
awk改变了OFS,$0却没变化一个文件1.txt,内容如下 a b c d e 目的把列变行,输出为: a b c d e 脚本如下: awk 'BEGIN{RS="";FS=& ...
- backupMysql.sh
#!/bin/sh #!/bin/bash function backup() { for i in $* do mysqldump -h$hostip -P$port -u$username -p$ ...
- 【转】对GAMIT/GLOBK的基本认识
1.1 GAMIT/GLOBK软件可从网络上申请下载.该软件功能强大,用途广泛,一般包括精确定位,大气层可降水汽估计和空间电离层变化分析等.后两种用途只需要用到GAMIT模块,精确定位则还需要GL ...
- SQL FOR XML PATH 和 Stuff 用法
sql stuff 用法 1.作用 删除指定长度的字符,并在指定的起点处插入另一组字符. 2.语法 STUFF ( character_expression , start , length ,cha ...