关联阅读
十一、逐行阅读Yii2.0.43源码_Yii框架文件Component.php(1)
十二、逐行阅读Yii2.0.43源码_Yii框架文件Component.php(2)
十三、逐行阅读Yii2.0.43源码_Yii框架文件Component.php(3)
十四、逐行阅读Yii2.0.43源码_Yii框架文件Component.php(4)
目录
Container类继承Component类, Component类继承BaseObject类,Component类实现*property*,*event*,*behavior*特性的基类
一.、3个属性
1.$_events 事件数组
2.$_eventWildcards 通配符事件数组
3.$_behaviors 行为数组
/**
* 事件处理函数数组
* 格式 [event name => handlers]
*/
private $_events = [];
/**
* 通配符事件处理函数数组
* 格式 [event name wildcard => handlers]
*/
private $_eventWildcards = [];
/**
* 行为数组,默认为 null
* 格式 [behavior name => behavior]
*/
private $_behaviors;
二、6个魔术方法
1.__get魔术方法,返回组件属性值,执行逻辑分为以下四步:
- 第一步:是否有getXXX方法,有则调用获取结果返回;
- 第二步:遍历行为数组,查找XXX属性,有则返回;
- 第三步:是否有setXXX方法,有则抛出InvalidCallException异常
- 第四步:以上三步均未得到执行,抛出UnknownPropertyException异常
/**
* 返回组件属性值
* 主要分为四步执行:XXX属性名称
* 第一步:是否有getXXX方法,有则调用获取结果返回;
* 第二步:遍历行为数组,查找XXX属性,有则返回;
* 第三步:是否有setXXX方法,有则抛出InvalidCallException异常
* 第四步:以上三步均未得到执行,抛出UnknownPropertyException异常
*/
public function __get($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
// read property, e.g. getName()
return $this->$getter();
}
// behavior property
$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);
}
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
}
2.__set魔术方法,设置组件属性,执行逻辑分为以下六步:
- 第一步:是否有setXXX方法,有则调用返回;
- 第二步:判断XXX的前3个字符是否是‘on ’,是则调用on方法绑定事件
- 第三步:判断XXX的前3个字符是否是‘as ’,是则调用attachBehavior方法设置属性
- 第四步:遍历行为数组,查找XXX属性,并设置
- 第五步:是否有getXXX方法,有则抛出InvalidCallException异常
- 第六步:以上五步均未得到执行,抛出UnknownPropertyException异常
/**
* 设置组件属性值
* 主要分为六步执行:XXX属性名称
* 第一步:是否有setXXX方法,有则调用返回;
* 第二步:判断XXX的前3个字符是否是‘on ’,是则调用on方法绑定事件
* 第三步:判断XXX的前3个字符是否是‘as ’,是则调用attachBehavior方法设置属性
* 第四步:遍历行为数组,查找XXX属性,并设置
* 第五步:是否有getXXX方法,有则抛出InvalidCallException异常
* 第六步:以上五步均未得到执行,抛出UnknownPropertyException异常
*/
public function __set($name, $value)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
// 设置属性
$this->$setter($value);
return;
} elseif (strncmp($name, 'on ', 3) === 0) {
// 绑定事件
$this->on(trim(substr($name, 3)), $value);
return;
} elseif (strncmp($name, 'as ', 3) === 0) {
// 设置行为
$name = trim(substr($name, 3));
$this->attachBehavior($name, $value instanceof Behavior ? $value : Yii::createObject($value));
return;
}
// behavior property
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canSetProperty($name)) {
$behavior->$name = $value;
return;
}
}
if (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
}
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
}
3.__isset魔术方法,检查属性是否定义,执行逻辑分为以下三步:
- 第一步:检查是否有getXXX方法,有则获取值与null比较
- 第二步:遍历行为数组,获取属性值与null比较
- 第三步:返回false
/**
* 检查属性是否设置了
* 主要分为三步执行:XXX属性名称
* 第一步:检查是否有getXXX方法,有则获取值与null比较
* 第二步:遍历行为数组,获取属性值与null比较
* 第三步:返回false
*/
public function __isset($name)
{
//1. 判断getXXX方法
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
return $this->$getter() !== null;
}
//2. 遍历行为
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canGetProperty($name)) {
return $behavior->$name !== null;
}
}
//3. 直接返回false
return false;
}
4.__unset魔术方法,设置组件属性为null,执行逻辑分为以下三步:
- 第一步:检查是否有setXXX方法,有则设置null
- 第二步:遍历行为数组,设置属性为null
- 第三步:抛出InvalidCallException异常
/**
* 设置属性为null
* 主要分为三步执行:XXX属性名称
* 第一步:检查是否有setXXX方法,有则设置null
* 第二步:遍历行为数组,设置属性为null
* 第三步:抛出InvalidCallException异常
*/
public function __unset($name)
{
//1.setXXX方法
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
$this->$setter(null);
return;
}
//2.遍历行为数组
$this->ensureBehaviors();
foreach ($this->_behaviors as $behavior) {
if ($behavior->canSetProperty($name)) {
$behavior->$name = null;
return;
}
}
//3.抛出异常
throw new InvalidCallException('Unsetting an unknown or read-only property: ' . get_class($this) . '::' . $name);
}
5.__call魔术方法,遍历行为,调用方法
- 第一步:遍历行为,判断是否存在$name方法,有则调用返回
- 第二步:抛出UnknownMethodException异常
/**
* 调用类中不存在方法
* 第一步:遍历行为,判断是否存在$name方法,有则调用返回
* 第二步:抛出UnknownMethodException异常
*/
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()");
}
6.__clone魔术方法,移除clone出来的实例的所有行为
/**
* 通过clone一个已存在的对象时,移除其所有行为
*/
public function __clone()
{
$this->_events = [];
$this->_eventWildcards = [];
$this->_behaviors = null;
}
总结:
1.阅读了3个属性
- $_events 事件数组
- $_eventWildcards 通配符事件数组
- $_behaviors 行为数组
2.阅读了6个魔术方法
- __get 获取组件属性
- __set 设置组件属性
- __isset 检查属性是否定义
- __unset设置属性为null
- __call 遍历行为,调用方法
- __clone 克隆实例,并移除新实例中的所有行为