?关联阅读?
六、逐行阅读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)
目录
1. isNulledParam方法
?2. getDependencies方法,返回类的依赖项
3. validateDependencies方法,检查配置参数
4. mergeDependencies方法
5. resolveDependencies方法,解析依赖
6. build方法,创建对象的核心方法
7. mergeParams方法,合并参数
1. isNulledParam方法
- 可选参数 返回true
- 可空参数返回 true
- 其他返回false
/**
* 判断参数是 可选参数或者可空的类型参数
*/
private function isNulledParam($param)
{
//1. 可选参数
// public function foo($str = 'optional') {
// ...可选参数...
// }
//2. >=7.1.0 且指定为可空的类型参数
// public funciton tool(?int $number) {
// ...可空的类型参数...
// }
return $param->isOptional()
|| (PHP_VERSION_ID >= 70100 && $param->getType()->allowsNull());
}
?2. getDependencies方法,返回类的依赖项
- 第一步:判断是否解析过,如果是直接返回,否则解析类依赖
- 第二步:获取当前类的反射类实例,取得类构造函数的参数列表
- 第三步:分析参数,解析参数
- 第四步:存储解析结果并返回
/**
* 返回指定类的依赖项。
*/
protected function getDependencies($class)
{
//判断 $_reflections中是否存在指定类
if (isset($this->_reflections[$class])) {
return [$this->_reflections[$class], $this->_dependencies[$class]];
}
$dependencies = [];
try {
//反射类实例,可以获取指定类的信息
$reflection = new ReflectionClass($class);
} catch (\ReflectionException $e) {
throw new NotInstantiableException(
$class,
'Failed to instantiate component or class "' . $class . '".',
0,
$e
);
}
//获取类$class的构造函数
//如果$class没有构造函数,返回NULL
$constructor = $reflection->getConstructor();
if ($constructor !== null) {
//$class构造函数存在,解析参数
// $constructor->getParameters()返回一个ReflectionParameter实例数组
foreach ($constructor->getParameters() as $param) {
//$param->isVariadic()返回是否是可变参数
//是返回true, 否则返回false
// php版本 >= 5.6.0
if (PHP_VERSION_ID >= 50600 && $param->isVariadic()) {
break;
}
// php版本 >= 8.0.0
if (PHP_VERSION_ID >= 80000) {
//getType方法中,如果参数指定了类型,则返回一个 ReflectionType的实例
//否则,返回NULL
// ReflectionNamedType 继承 ReflectionType
$c = $param->getType();
$isClass = false;
if ($c instanceof ReflectionNamedType) {
//isBuiltin方法返回,当前类型是否是内建
// 是返回true, 否则返回false
//内建类型:int,float,string, bool, array,object...
$isClass = !$c->isBuiltin();
}
} else {
try {
// 返回ReflectionClass实例
$c = $param->getClass();
} catch (ReflectionException $e) {
//不是可选参数和可空类型的参数
if (!$this->isNulledParam($param)) {
//进入到这里,说明指定的类不存在
$notInstantiableClass = null;
if (PHP_VERSION_ID >= 70000) {
$type = $param->getType();
//命名参数
if ($type instanceof ReflectionNamedType) {
//参数类型名称
$notInstantiableClass = $type->getName();
}
}
//抛出异常
throw new NotInstantiableException(
$notInstantiableClass,
$notInstantiableClass === null ? 'Can not instantiate unknown class.' : null
);
} else {
$c = null;
}
}
$isClass = $c !== null;
}
//获取类名称
$className = $isClass ? $c->getName() : null;
//$dependencies存储格式, xxx是Instance实例
// [
// 'name' => xxx
// 'addr' => 'CHINA',
// 'sex' => null
// ]
if ($className !== null) {
$dependencies[$param->getName()] = Instance::of($className, $this->isNulledParam($param));
} else {
$dependencies[$param->getName()] = $param->isDefaultValueAvailable()
? $param->getDefaultValue()
: null;
}
}
}
//存储 类名称 --> 类反射实例的映射
$this->_reflections[$class] = $reflection;
//存储 类名称 --> 类构造函数参数数组的映射
$this->_dependencies[$class] = $dependencies;
return [$reflection, $dependencies];
}
3. validateDependencies方法,检查配置参数
- 参数数组键是字符串类型 或者 整型 类型
- 同一个数组,两个类型的键不能同时存在
/**
* @param array $parameters
* @throws InvalidConfigException
*/
private function validateDependencies($parameters)
{
$hasStringParameter = false;
$hasIntParameter = false;
//这个foreach遍历,检查参数的 索引类型
//如果同一个数组中同时存在 字符串类型键和整型键,
//将抛出参数异常
foreach ($parameters as $index => $parameter) {
if (is_string($index)) {
$hasStringParameter = true;
if ($hasIntParameter) {
break;
}
} else {
$hasIntParameter = true;
if ($hasStringParameter) {
break;
}
}
}
if ($hasIntParameter && $hasStringParameter) {
throw new InvalidConfigException(
'Dependencies indexed by name and by position in the same array are not allowed.'
);
}
}
4. mergeDependencies方法
/**
* 用$b中值赋值给$a
*/
private function mergeDependencies($a, $b)
{
foreach ($b as $index => $dependency) {
$a[$index] = $dependency;
}
return $a;
}
5. resolveDependencies方法,解析依赖
/**
* 解析依赖
*/
protected function resolveDependencies($dependencies, $reflection = null)
{
foreach ($dependencies as $index => $dependency) {
if ($dependency instanceof Instance) {
if ($dependency->id !== null) {// 类名称
//从容器中获取类实例
$dependencies[$index] = $dependency->get($this);
} elseif ($reflection !== null) {
//$dependency->id
$name = $reflection->getConstructor()->getParameters()[$index]->getName();
$class = $reflection->getName();
throw new InvalidConfigException("Missing required parameter \"$name\" when instantiating \"$class\".");
}
} elseif ($this->_resolveArrays && is_array($dependency)) {
//递归解析
$dependencies[$index] = $this->resolveDependencies($dependency, $reflection);
}
}
return $dependencies;
}
6. build方法,创建对象的核心方法
/**
* 创建指定类的实例
* $class 类名称
* $params 构造参数
* $config
*/
protected function build($class, $params, $config)
{
/* @var $reflection ReflectionClass */
//$reflection ReflectionClass实例
//$dependencies 一个数组, 存储了$class构造函数参数信息
list($reflection, $dependencies) = $this->getDependencies($class);
$addDependencies = [];
if (isset($config['__construct()'])) {
$addDependencies = $config['__construct()'];
unset($config['__construct()']);
}
foreach ($params as $index => $param) {
$addDependencies[$index] = $param;
}
// 数组参数规范检查,
$this->validateDependencies($addDependencies);
if ($addDependencies && is_int(key($addDependencies))) {
//数组键是 int 类型,则转换$dependencies成为 数值数组
$dependencies = array_values($dependencies);
//按照顺序赋值
$dependencies = $this->mergeDependencies($dependencies, $addDependencies);
} else {
//按照索引赋值
$dependencies = $this->mergeDependencies($dependencies, $addDependencies);
$dependencies = array_values($dependencies);
}
$dependencies = $this->resolveDependencies($dependencies, $reflection);
//执行到这里,
//$class的构造函数的参数对应的$dependencies已经被传递值了
//检查类是否可实例化
if (!$reflection->isInstantiable()) {
throw new NotInstantiableException($reflection->name);
}
//无多余配置项,直接返回类实例
if (empty($config)) {
return $reflection->newInstanceArgs($dependencies);
}
//解析$config,替换$config中的配置
$config = $this->resolveDependencies($config);
if (!empty($dependencies) && $reflection->implementsInterface('yii\base\Configurable')) {
// set $config as the last parameter (existing one will be overwritten)
$dependencies[count($dependencies) - 1] = $config;
return $reflection->newInstanceArgs($dependencies);
}
//给类挂载属性
$object = $reflection->newInstanceArgs($dependencies);
foreach ($config as $name => $value) {
$object->$name = $value;
}
return $object;
}
7. mergeParams方法,合并参数
/**
* 合并参数
*/
protected function mergeParams($class, $params)
{
if (empty($this->_params[$class])) {
return $params;
} elseif (empty($params)) {
return $this->_params[$class];
}
$ps = $this->_params[$class];
foreach ($params as $index => $value) {
$ps[$index] = $value;
}
return $ps;
}
总结:
阅读了7个方法:
- isNulledParam 判断参数
- getDependencies 获取依赖
- validateDependencies 检查依赖数组
- mergeDependencies 合并依赖
- resolveDependencies 解析依赖
- build 创建实例的核心方法
- mergeParams 合并参数
|