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

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

1、整体的结构

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

2、行为

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

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

注入一个行为的方式:

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

attach注入做了什么?

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

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

  • 见yii\base\BaseActiveRecord的魔术方法
  • 如果当前对象的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

Yii2.0源码阅读-behavior的实现原理的更多相关文章

  1. Yii2.0源码阅读-一次请求的完整过程

    Yii2.0框架源码阅读,从请求发起,到结束的运行步骤 其实最初阅读是从yii\web\UrlManager这个类开始看起,不断的寻找这个类中方法的调用者,最终回到了yii\web\Applicati ...

  2. Yii2.0源码阅读-从路由到控制器

    之前的文章弄清了一次请求的开始到结束.主要讲了Yii Applicaton实例的创建.初始化,UrlManager如何返回Yii中的路由信息,到runAction,最后将Response发送给客户端. ...

  3. Yii2.0源码阅读-视图(View)渲染过程

    之前的文章我们根据源码的分析,弄清了Yii如何处理一次请求,以及根据解析的路由如何调用控制器中的action,那接下来好奇的可能就是,我在控制器action中执行了return $this->r ...

  4. Yii2.0源码阅读-PHP如何与redis通信?

    PHP与Redis可以通过socket进行通信,前提是PHP需要实现Redis的协议 RESP协议描述: 字符串 \r\n : 表示一个正确的状态信息,具体信息是'+'后面的字符(Simple Str ...

  5. Yii2.0源码分析之——控制器文件分析(Controller.php)创建动作、执行动作

    在Yii中,当请求一个Url的时候,首先在application中获取request信息,然后由request通过urlManager解析出route,再在Module中根据route来创建contr ...

  6. Vue2.0源码阅读笔记(四):nextTick

      在阅读 nextTick 的源码之前,要先弄明白 JS 执行环境运行机制,介绍 JS 执行环境的事件循环机制的文章很多,大部分都阐述的比较笼统,甚至有些文章说的是错误的,以下为个人理解,如有错误, ...

  7. Vue2.0源码阅读笔记--生命周期

    一.Vue2.0的生命周期 Vue2.0的整个生命周期有八个:分别是 1.beforeCreate,2.created,3.beforeMount,4.mounted,5.beforeUpdate,6 ...

  8. Vue2.0源码阅读笔记--双向绑定实现原理

    上一篇 文章 了解了Vue.js的生命周期.这篇分析Observe Data过程,了解Vue.js的双向数据绑定实现原理. 一.实现双向绑定的做法 前端MVVM最令人激动的就是双向绑定机制了,实现双向 ...

  9. Vue2.0源码阅读笔记(二):响应式原理

      Vue是数据驱动的框架,在修改数据时,视图会进行更新.数据响应式系统使得状态管理变的简单直接,在开发过程中减少与DOM元素的接触.而深入学习其中的原理十分有必要,能够回避一些常见的问题,使开发变的 ...

随机推荐

  1. 不高兴的小名 nyoj

    不高兴的小明 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述    小明又出问题了.妈妈认为聪明的小明应该更加用功学习而变的更加厉害,所以小明除了上学之外,还要参加妈 ...

  2. vue 保留两位小数 不能直接用toFixed(2) ?

    用vue做项目的时候多多少少都会遇到这个问题 刚开始我是用toFixed()这个方法来写的  效果是有的 但是控制台一直是红红的围绕着我 突然想到 vue和jquery混搭 的 问题 于是乎 看了一下 ...

  3. Python 简单聊天室

    #coding=utf-8 from socket import * from threading import Thread import time udpSocket = socket(AF_IN ...

  4. linux的脚本应用for循环答应变量

    #!/bin/bash for var in A B C ; do echo "var is $var" done

  5. C# 使用 GDI+ 给图片添加文字,并使文字自适应矩形区域

    需求 需求是要做一个编辑文字的页面.用户在网页端写文字,文字区域是个矩形框,用户可以通过下方的拖动条调节文字大小. 如下图: 提交数据的时候前端传文字区域的左上角和右下角定位给后台.因为前端的字体大小 ...

  6. android 运行时异常捕获

    1,将运行时异常捕获并存到手机SD卡上 可以直接使用logcat 命令Runtime.getRuntime().exec("logcat -f "+ file.getAbsolut ...

  7. python入门(11)条件判断和循环

    python入门(11)条件判断和循环 条件判断 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. 比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现: ag ...

  8. Spring Security入门(3-2)Spring Security对接用户的权限系统

    源文链接,多谢作者的分享: http://www.360doc.com/content/14/0727/16/18637323_397445724.shtml 1.原生的spring-security ...

  9. LSTM主要思想和网络结构

    在你阅读这篇文章时候,你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义.我们不会将所有的东西都全部丢弃,然后用空白的大脑进行思考.我们的思想拥有持久性. 相关信息和当前预测位置之间的间 ...

  10. leetcode算法:Two Sum II - Input array is sorted

    Given an array of integers that is already sorted in ascending order, find two numbers such that the ...