经过上一节的注解解析后,swoft将获取到的注解对象解析成了定义对象,完成了对这些原始注解对象的梳理工作.
本小节将梳理获取到的这些定义对象的解析.
解析定义对象的入口方法:
private function parseDefinitions(): void
{
// 实例化解析器,将前面解析好的数据传递进去
$annotationParser = new DefinitionObjParser(
$this->definitions,
$this->objectDefinitions,
$this->classNames,
$this->aliases
);
// 调用解析器的解析方法
// Collect info
$definitionData = $annotationParser->parseDefinitions();
// 将返回的结果保存在容器上
[$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $definitionData;
}
解析器的入口方法:
public function parseDefinitions(): array
{
// 遍历bean配置,得到bean名称和配置(定义)
foreach ($this->definitions as $beanName => $definition) {
// 如果已经通过上一节的注解解析,生成了这个定义对象
if (isset($this->objectDefinitions[$beanName])) {
// 获取这个定义对象
$objectDefinition = $this->objectDefinitions[$beanName];
// 重置这个定义对象
$this->resetObjectDefinition($beanName, $objectDefinition, $definition);
// 继续下一个
continue;
}
// 否则,根据bean配置创建定义对象
$this->createObjectDefinition($beanName, $definition);
}
return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}
重置定义对象:
private function resetObjectDefinition(string $beanName, ObjectDefinition $objDefinition, array $definition): void
{
// 获取bean配置中定义的类名
// Parse class name
$className = $definition['class'] ?? '';
// 获取定义对象的类名
$objClassName = $objDefinition->getClassName();
// 如果配置中定义了类名且与定义对象中的类名不同,抛出异常
if (!empty($className) && $className !== $objClassName) {
throw new InvalidArgumentException('Class for annotations and definitions must be the same Or not to define class');
}
// 更新定义对象
$objDefinition = $this->updateObjectDefinitionByDefinition($objDefinition, $definition);
// 将更新后的定义对象设置回定义对象池
$this->objectDefinitions[$beanName] = $objDefinition;
}
更新定义对象方法:
private function updateObjectDefinitionByDefinition(ObjectDefinition $objDfn, array $definition): ObjectDefinition
{
// 将bean配置解析成构造参数注入对象、属性注入对象数组、选项数组
[$constructInject, $propertyInjects, $option] = $this->parseDefinition($definition);
// 设置构造注入
// Set construct inject
if (!empty($constructInject)) {
$objDfn->setConstructorInjection($constructInject);
}
// 遍历属性注入数组并设置到定义对象中
// Set property inject
foreach ($propertyInjects as $propertyName => $propertyInject) {
// 设置属性注入对象
// 如果已经有这个属性的设置,则回被覆盖
$objDfn->setPropertyInjection($propertyName, $propertyInject);
}
// 类型数组
$scopes = [
Bean::SINGLETON,
Bean::PROTOTYPE,
Bean::REQUEST,
];
// 获取选项中的类型定义
$scope = $option['scope'] ?? '';
// 获取选项中的别名定义
$alias = $option['alias'] ?? '';
// 也就是说不能在这里定义session类型
if (!empty($scope) && !in_array($scope, $scopes, true)) {
throw new InvalidArgumentException('Scope for definition is not undefined');
}
// 设置类型
// Update scope
if (!empty($scope)) {
$objDfn->setScope($scope);
}
// 更新别名
// 感觉这里的代码是有问题的
// 获取对象原始别名和删除别名映射的操作是不是应该放在
// 设置操作的前面?
// 否则这一步操作就等于没有删除前面定义的别名
// 且设置了新的别名
// Update alias
if (!empty($alias)) {
// 设置定义对象的别名
$objDfn->setAlias($alias);
// 获取定义对象别名
$objAlias = $objDfn->getAlias();
unset($this->aliases[$objAlias]);
// 设置新的别名
$this->aliases[$alias] = $objDfn->getName();
}
// 返回已经更新的定义对象
return $objDfn;
}
bean配置解析:
private function parseDefinition(array $definition): array
{
// Remove class key
// 移除类名的定义
unset($definition['class']);
// Parse construct
// 获取构造参数
$constructArgs = $definition[0] ?? [];
if (!is_array($constructArgs)) {
throw new InvalidArgumentException('Construct args for definition must be array');
}
// Parse construct args
$argInjects = [];
// 遍历构造参数
foreach ($constructArgs as $arg) {
// 获取参数的实际值和是否为引用
[$argValue, $argIsRef] = $this->getValueByRef($arg);
// 创建注入的构造参数
$argInjects[] = new ArgsInjection($argValue, $argIsRef);
}
// Set construct inject
$constructInject = null;
如果需要注入的构造参数不为空
if (!empty($argInjects)) {
// 创建__construct的注入方法
$constructInject = new MethodInjection('__construct', $argInjects);
}
// 移除构造参数的定义
// Remove construct definition
unset($definition[0]);
// 解析配置中的__option部分
// Parse definition option
$option = $definition['__option'] ?? [];
if (!is_array($option)) {
throw new InvalidArgumentException('__option for definition must be array');
}
// 移除__option的配置
// Remove `__option`
unset($definition['__option']);
// 解析属性配置
// Parse definition properties
$propertyInjects = [];
// 遍历剩余的所有配置
foreach ($definition as $propertyName => $propertyValue) {
// 如果属性名不是字符串,则报错
if (!is_string($propertyName)) {
throw new InvalidArgumentException('Property key from definition must be string');
}
// 获取属性值和类型
[$proValue, $proIsRef] = $this->getValueByRef($propertyValue);
// 如果属性是个数组,则再次遍历设置
// Parse property for array
if (is_array($proValue)) {
$proValue = $this->parseArrayProperty($proValue);
}
// 创建属性注入对象
$propertyInject = new PropertyInjection($propertyName, $proValue, $proIsRef);
// 保存属性注入对象
$propertyInjects[$propertyName] = $propertyInject;
}
return [$constructInject, $propertyInjects, $option];
}
获取参数的实际值和是否引用:
protected function getValueByRef($value): array
{
if (!is_string($value)) {
return [$value, false];
}
// Reg match
$isRef = preg_match('/^${(.*)}$/', $value, $match);
if ($isRef && isset($match[1])) {
return [$match[1], (bool)$isRef];
}
return [$value, false];
}
创建新的定义对象:
private function createObjectDefinition(string $beanName, array $definition): void
{
// 获取配置中定义的类名,不存在就报错
$className = $definition['class'] ?? '';
if (empty($className)) {
throw new InvalidArgumentException(sprintf('%s key for definition must be defined class', $beanName));
}
// 通过定义对象名称(beanName)和类名实例化新的定义对象
$objDefinition = new ObjectDefinition($beanName, $className);
// 更新定义对象
$objDefinition = $this->updateObjectDefinitionByDefinition($objDefinition, $definition);
// 将新的定义对象名添加到类名映射数组中
// 别名在updateObjectDefinitionByDefinition方法中已经更新
$classNames = $this->classNames[$className] ?? [];
$classNames[] = $beanName;
$this->classNames[$className] = array_unique($classNames);
// 保存定义对象
$this->objectDefinitions[$beanName] = $objDefinition;
}
总结:
本小节的主要功能:
根据bean的配置信息,更新前一小节得到的定义对象,创建前一小节没有创建的定义对象.