PHP的Yii框架中行为的定义与绑定方法讲解
作者:goldensun 发布时间:2023-07-21 20:42:33
定义行为
要定义行为,通过继承 yii\base\Behavior 或其子类来建立一个类。如:
namespace app\components;
use yii\base\Behavior;
class MyBehavior extends Behavior
{
public $prop1;
private $_prop2;
public function getProp2()
{
return $this->_prop2;
}
public function setProp2($value)
{
$this->_prop2 = $value;
}
public function foo()
{
// ...
}
}
以上代码定义了行为类 app\components\MyBehavior 并为要附加行为的组件提供了两个属性 prop1 、 prop2 和一个方法 foo()。注意属性 prop2 是通过 getter getProp2() 和 setter setProp2() 定义的。能这样用是因为 yii\base\Object 是 yii\base\Behavior 的祖先类,此祖先类支持用 getter 和 setter 方法定义属性
提示:在行为内部可以通过 yii\base\Behavior::owner 属性访问行为已附加的组件。
静态方法绑定行为
静态绑定行为,只需要重载 yii\base\Component::behaviors() 就可以了。 这个方法用于描述类所具有的行为。如何描述呢? 使用配置来描述,可以是Behavior类名,也可以是Behavior类的配置数组:
namespace app\models;
use yii\db\ActiveRecord;
use app\Components\MyBehavior;
class User extends ActiveRecord
{
public function behaviors()
{
return [
// 匿名的行为,仅直接给出行为的类名称
MyBehavior::className(),
// 名为myBehavior2的行为,也是仅给出行为的类名称
'myBehavior2' => MyBehavior::className(),
// 匿名行为,给出了MyBehavior类的配置数组
[
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop3' => 'value3',
],
// 名为myBehavior4的行为,也是给出了MyBehavior类的配置数组
'myBehavior4' => [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop3' => 'value3',
]
];
}
}
还有一个静态的绑定办法,就是通过配置文件来绑定:
[
'as myBehavior2' => MyBehavior::className(),
'as myBehavior3' => [
'class' => MyBehavior::className(),
'prop1' => 'value1',
'prop3' => 'value3',
],
]
动态方法绑定行为
动态绑定行为,需要调用 yii\base\Compoent::attachBehaviors():
$Component->attachBehaviors([
'myBehavior1' => new MyBehavior, // 这是一个命名行为
MyBehavior::className(), // 这是一个匿名行为
]);
这个方法接受一个数组参数,参数的含义与上面静态绑定行为是一样一样的。
在上面的这些例子中,以数组的键作为行为的命名,而对于没有提供键名的行为,就是匿名行为。
对于命名的行为,可以调用 yii\base\Component::getBehavior() 来取得这个绑定好的行为:
$behavior = $Component->getBehavior('myBehavior2');
对于匿名的行为,则没有办法直接引用了。但是,可以获取所有的绑定好的行为:
$behaviors = $Component->getBehaviors();
绑定的内部原理
只是重载一个 yii\base\Component::behaviors() 就可以这么神奇地使用行为了? 这只是冰山的一角,实际上关系到绑定的过程,有关的方面有:
yii\base\Component::behaviors()
yii\base\Component::ensureBehaviors()
yii\base\Component::attachBehaviorInternal()
yii\base\Behavior::attach()
4个方法中,Behavior只占其一,更多的代码,是在Component中完成的。
yii\base\Component::behaviors() 上面讲静态方法绑定行为时已经提到了,就是返回一个数组用于描述行为。 那么 yii\base\Component::ensuerBehaviors() 呢?
这个方法会在Component的诸多地方调用 __get() __set() __isset() __unset() __call() canGetProperty() hasMethod() hasEventHandlers() on() off() 等用到,看到这么多是不是头疼?一点都不复杂,一句话,只要涉及到类的属性、方法、事件这个函数都会被调用到。
这么众星拱月,被诸多凡人所需要的 ensureBehaviors() 究竟是何许人也? 就像名字所表明的,他的作用在于“ensure” 。其实只是确保 behaviors() 中所描述的行为已经进行了绑定而已:
public function ensureBehaviors()
{
// 为null表示尚未绑定
// 多说一句,为空数组表示没有绑定任何行为
if ($this->_behaviors === null) {
$this->_behaviors = [];
// 遍历 $this->behaviors() 返回的数组,并绑定
foreach ($this->behaviors() as $name => $behavior) {
$this->attachBehaviorInternal($name, $behavior);
}
}
}
这个方法主要是对子类用的, yii\base\Compoent 没有任何预先注入的行为,所以,这个调用没有用。 但是对于子类,你可能重载了 yii\base\Compoent::behaviros() 来预先注入一些行为。 那么,这个函数会将这些行为先注入进来。
从上面的代码中,自然就看到了接下来要说的第三个东东, yii\base\Component\attachBehaviorInternal():
private function attachBehaviorInternal($name, $behavior)
{
// 不是 Behavior 实例,说是只是类名、配置数组,那么就创建出来吧
if (!($behavior instanceof 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;
}
首先要注意到,这是一个private成员。其实在Yii中,所有后缀为 *Internal 的方法,都是私有的。 这个方法干了这么几件事:
如果 $behavior 参数并非是一个 Behavior 实例,就以之为参数,用 Yii::createObject() 创建出来。
如果以匿名行为的形式绑定行为,那么直接将行为附加在这个类上。
如果是命名行为,先看看是否有同名的行为已经绑定在这个类上,如果有,用后来的行为取代之前的行为。
在 yii\base\Component::attachBehaviorInternal() 中, 以 $this 为参数调用了 yii\base\Behavior::attach() 。 从而,引出了跟绑定相关的最后一个家伙 yii\base\Behavior::attach() , 这也是前面我们讲行为的要素时没讲完的。先看看代码:
public function attach($owner)
{
$this->owner = $owner;
foreach ($this->events() as $event => $handler) {
$owner->on($event, is_string($handler) ? [$this, $handler] :
$handler);
}
}
上面的代码干了两件事:
设置好行为的 $owner ,使得行为可以访问、操作所依附的对象
遍历行为中的 events() 返回的数组,将准备响应的事件,通过所依附类的 on() 绑定到类上
总结
说了这么多,关于绑定,做个小结:
绑定的动作从Component发起;
静态绑定通过重载 yii\base\Componet::behaviors() 实现;
动态绑定通过调用 yii\base\Component::attachBehaviors() 实现;
行为还可以通过为 Component 配置 as 配置项进行绑定;
行为有匿名行为和命名行为之分,区别在于绑定时是否给出命名。 命名行为可以通过其命名进行标识,从而有针对性地进行解除等操作;
绑定过程中,后绑定的行为会取代已经绑定的同名行为;
绑定的意义有两点,一是为行为设置 $owner 。二是将行为中拟响应的事件的handler绑定到类中去。


猜你喜欢
- 本文实例讲述了Python集中化管理平台Ansible介绍与YAML。分享给大家供大家参考,具体如下:一 中文社区http://ansibl
- asp之家注:长文章分页算是asp编程中一个比较经典单位问题,怎么分页,什么时候分页.方法挺多,有的是人为的加入分页标志,有的是程序自动加分
- 爬虫:一段自动抓取互联网信息的程序,从互联网上抓取对于我们有价值的信息,一般来说,Python爬虫程序很多时候都要使用(飞猪IP)代理的IP
- 本文实例介绍了javascript实现九宫格相加数值相等的对应方法,分享给大家供大家参考,具体内容如下实现思路:1、每个格子输入的数值必须为
- A.截取从字符串左边开始N个字符 Declare @S1 varchar(100) Select @S1='http://www.x
- 在以前的日志中讲了怎么制作验证码,这篇就讲讲怎么给验证码加上起干扰效果的杂点。 其实很简单,首先做一个
- 在进行excel文件读取的时候,我自己设置了部分直接从公式获取单元格的值但是用之前的读取方法进行读取的时候,返回值为空import osim
- PYTHON Pandas批量读取csv文件到DATAFRAME首先使用glob.glob获得文件路径。然后定义一个列表,读取文件后再使用c
- MySQL 8 带来了全新的体验,比如支持 NoSQL、JSON 等,拥有比 MySQL 5.7 两倍以上的性能提升。本文讲解如何在 Win
- 上篇关于Go模板库应用 的文章最后我们留下一个问题,页面模板是通过 CDN 引用的 BootStrap 的 css , js 文件。到目前位
- 一、前言1.1 关于描述性统计分析概括地来说,描述性统计分析就是在收集到的数据的基础上,运用制表和分类,图形以及计算概括性数据来描述数据特征
- 目录1.准备工作2. 开始2.1 生成控件2.2 定义输入和计算函数2.3 绑定键盘事件2.4 循环3.全部代码4. 结束语做一个计算器,这
- 目录一、IPO模型 二、基本输入 - input()函数1、函数格式2、参数说明3、实例演示(1)接收字符串数据(2)接收整型数据
- echarts legend点击事件首先,明确本篇文章的重点,主要有三个:1. 给legend添加点击事件2. 禁用legend点击事件的默
- Java开发者对于面向对象编程思维与命令行编程思维的协调程度,取决于他们如下几种能力的水平:技巧(任何人都可以编写命令行形式的代码)教条(有
- 前言大家好,说起动态条形图,之前推荐过两个 Python 库,比如Bar Chart Race、Pandas_Alive,都可以实现。今天就
- 先看看 allure 命令的帮助文档cmd 敲allure -hallure 命令的语法格式allure [options] [comman
- 1、安装Django在命令行模式下使用pip工具来安装Django,pip工具的安装方法见此篇pip install django2、确认是
- MySQL内部复制功能是建立在两个或两个以上服务器之间,通过设定它们之间的主-从关系来实现的。其中一个作为主服务器,其它的作为从服务器。本节
- 1,复制简介 简单的说,复制是获取一个或多个数据库的过程,它系统的针对出入不同数据库的数据,提供基于规则的拷贝机制。 复制分为三种角色, 1