关联阅读
六、逐行阅读Yii2.0.43源码_Yii框架文件Container.php(1)
七、逐行阅读Yii2.0.43源码_Yii框架文件Container.php(2)
八、逐行阅读Yii2.0.43源码_Yii框架文件Container.php(3)
九、逐行阅读Yii2.0.43源码_Yii框架文件Container.php(4)
十、逐行阅读Yii2.0.43源码_Yii框架文件Container.php(5)
目录
一、属性
学习了6个属性
$_singletons:
保存了单例对象数组
$_definitions
当创建对象的时候,根据其中的值,类型不同,选择不同的创建方式
$_params
构造函数参数
$_reflections
存储ReflectionClass实例
$_dependencies
存储依赖数据
$_resolveArrays
是否要尝试解析$_dependencies中的元素
/**
* 保存了单例对象数组
*/
private $_singletons = [];
/**
* 对象定义数组
* 当创建对象的时候,根据其中的值,类型不同
* 选择不同的创建方式
* $_definitions看上去类似下面这样
* $_definitions = [
* 'yii\db\Connection' => ['yii\db\Connection', 'connect'],
* 'app\product\Order' => $order,//一个order实例
* 'app\admin' => function(){return new Admin()},
* 'role' => ['class' => 'app/Role', ...],
* ....
* ]
*/
private $_definitions = [];
/**
* 构造函数参数
*/
private $_params = [];
/**
* 存储ReflectionClass实例
*/
private $_reflections = [];
/**
* 存储依赖数据
*/
private $_dependencies = [];
/**
* 是否要尝试解析$_dependencies中的元素
*/
private $_resolveArrays = false;
二、方法
- get 返回一个对象实例
1)BaseYii::createObject方法实质调用的是这个方法
2)BaseYii::createObject创建一个对象实例:
- 基于 类名称
- 基于配置数组
- 基于php调用
/**
* 返回一个对象实例
* BaseYii::createObject方法实质调用的是这个方法
* BaseYii::createObject创建一个对象实例:
* 1. 基于 类名称
* 2. 基于配置数组
* 3. 基于php调用
*
* $class 类实例,名称或者别名
* $params 构造函数所需的参数
* $config 用来初始化对象的属性
*/
public function get($class, $params = [], $config = [])
{
if ($class instanceof Instance) {
$class = $class->id;
}
//1. 单例数组中存储有,直接返回
if (isset($this->_singletons[$class])) {
// singleton
return $this->_singletons[$class];
} elseif (!isset($this->_definitions[$class])) {
//如果$_singletons没有且也没有定义,创建返回
//这里创建的对象不是单例,每一次都会是一个新的实例
return $this->build($class, $params, $config);
}
$definition = $this->_definitions[$class];
if (is_callable($definition, true)) {
//这里is_callable函数的第二个参数是true,意味着,
//$definition只要符合是一个php调用的格式就可以了
//,不一定真的可以调用,
// $definition 可以是一个字符串,匿名函数,
//两个元素的给关联的数组,第一个元素是对象或者字符串,
//第二个元素是字符串
//解析参数
$params = $this->resolveDependencies($this->mergeParams($class, $params));
//如果可以调用,应当返回一个实例
$object = call_user_func($definition, $this, $params, $config);
} elseif (is_array($definition)) {
$concrete = $definition['class'];
unset($definition['class']);
$config = array_merge($definition, $config);
$params = $this->mergeParams($class, $params);
if ($concrete === $class) {
$object = $this->build($class, $params, $config);
} else {
//递归调用
$object = $this->get($concrete, $params, $config);
}
} elseif (is_object($definition)) {
return $this->_singletons[$class] = $definition;
} else {
throw new InvalidConfigException('Unexpected object definition type: ' . gettype($definition));
}
if (array_key_exists($class, $this->_singletons)) {
// 存在则赋值新值,
//这里注意 isset 和 array_key_exists的区别
//这个方法上面代码判断使用 isset($this->_singletons[$class])
//也就是说当 $this->_singletons[$class] 为null时,
// isset($this->_singletons[$class])返回false,而
// array_key_exists($class, $this->_singletons)返回true,
//这里会重新赋值
$this->_singletons[$class] = $object;
}
return $object;
}
总结:
1. 阅读了Container.php中6个属性
2.重点阅读了get方法,整个方法主要分为7个分支步骤完成:
- 第一步:判断 $_singletons数组中是否存在对应实例,存在立即返回
- 第二步:判断$_definitions数组中是不是存在,不存在,就创建一个实例返回
- 第三步:如果$_definitions数组中存在,则取出对应数据,如果值是一个php调用格式,则尝试执行call_user_func,尝试获取一个实例
- 第四步:如果是一个配置数组,则取出其中的class键对应的数据与当前$class值比较,完全相同,则创建一个实例
- 第五步:如果不同,递归调用get,重新走这7个步骤,获得一个实例
- 第六步:如果是一个实例,则赋值给单例数组,并返回
- 第七步:以上都不符合,抛出异常
扩展学习
1. isset和array_key_exists的区别
- isset是语言构造器,不是一个函数,array_key_exists是函数
- isset和array_key_exists都可以用来检测数组中是否存在对应键,不同的是,当数组中某个键对应的值为NULL时, isset 返回 false, 而array_key_exists返回true
- isset可以用来检测多维数组,而array_key_exists检测一维数组
- 执行效率上isset优于array_key_exists
<?php
$arr = [
'aa' => 'a',
'bb' => '',
'cc' => false,
'dd' => NULL,
'ee' => 0,
];
var_dump(isset($arr['aa']));// true
var_dump(isset($arr['bb']));// true
var_dump(isset($arr['cc']));// true
var_dump(isset($arr['dd']));// false
var_dump(isset($arr['ee']));// true
var_dump(array_key_exists('aa', $arr));// true
var_dump(array_key_exists('bb', $arr));// true
var_dump(array_key_exists('cc', $arr));// true
var_dump(array_key_exists('dd', $arr));// true
var_dump(array_key_exists('ee', $arr));// true
2. is_callable函数 验证变量的内容是否可以作为函数调用
is_callable有三个参数,重点研究,第二个参数的作用
当第二个参数为true时,只检查参数1是否符合php调用的格式,满足即可返回true
当第二个参数为false时,同时检查参数1是否符合php调用的格式和是否真的可以调用,同时满足返回true
<?php
//注意看一下区别
var_dump(is_callable('aa', true));// true
var_dump(is_callable(function(){}, true)); //true
var_dump(is_callable(['aa', 'bb'], true)); // true
var_dump(is_callable(['aa' => 'bb'], true));// false
var_dump(is_callable('aa', false));// false
var_dump(is_callable(function(){}, false)); //true
var_dump(is_callable(['aa', 'bb'], false)); // false
var_dump(is_callable(['aa' => 'bb'], false));// false