Swoft 框架源码阅读(二): AnnotationProcess 做了那些事儿

上回内容速读:

注解注册

书接上文,”swoft/framework/src/Processor/AnnotationProcessor.php” 的handle方法完成了注解的注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public function handle(): bool
{
if (!$this->application->beforeAnnotation()) {
CLog::warning('Stop annotation processor by beforeAnnotation return false');
return false;
}

$app = $this->application;

// Find AutoLoader classes. Parse and collect annotations.
AnnotationRegister::load([
'inPhar' => \IN_PHAR,
'basePath' => $app->getBasePath(),
'notifyHandler' => [$this, 'notifyHandler'],
'disabledAutoLoaders' => $app->getDisabledAutoLoaders(),
'disabledPsr4Prefixes' => $app->getDisabledPsr4Prefixes(),
]);

$stats = AnnotationRegister::getClassStats();

CLog::info(
'Annotations is scanned(autoloader %d, annotation %d, parser %d)',
$stats['autoloader'],
$stats['annotation'],
$stats['parser']
);

return $this->application->afterAnnotation();
}

重点在 AnnotationRegister::load上,实际上该方法运行的是 swoft/annotation/src/Resource/AnnotationResource.php文件中的 load 方法. 我们分析下面函数体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public function load(): void
{
$prefixDirsPsr4 = $this->classLoader->getPrefixesPsr4();

foreach ($prefixDirsPsr4 as $ns => $paths) {
// Only scan namespaces
if ($this->onlyNamespaces && !in_array($ns, $this->onlyNamespaces, true)) {
$this->notify('excludeNs', $ns);
continue;
}

// It is excluded psr4 prefix
if ($this->isExcludedPsr4Prefix($ns)) {
AnnotationRegister::registerExcludeNs($ns);
$this->notify('excludeNs', $ns);
continue;
}

// Find package/component loader class
foreach ($paths as $path) {
$loaderFile = $this->getAnnotationClassLoaderFile($path);
if (!file_exists($loaderFile)) {
$this->notify('noLoaderFile', $this->clearBasePath($path), $loaderFile);
continue;
}

$loaderClass = $this->getAnnotationLoaderClassName($ns);
if (!class_exists($loaderClass)) {
$this->notify('noLoaderClass', $loaderClass);
continue;
}

$loaderObject = new $loaderClass();
if (!$loaderObject instanceof LoaderInterface) {
$this->notify('invalidLoader', $loaderFile);
continue;
}

$this->notify('findLoaderClass', $this->clearBasePath($loaderFile));

// If is disable, will skip scan annotation classes
if (!isset($this->disabledAutoLoaders[$loaderClass])) {
AnnotationRegister::registerAutoLoaderFile($loaderFile);
$this->notify('addLoaderClass', $loaderClass);
$this->loadAnnotation($loaderObject);
}

// Storage auto loader to register
AnnotationRegister::addAutoLoader($ns, $loaderObject);
}
}
}

由于每个依赖包中的src文件夹下都有一个Autoloader.php文件 都实现了一个 名为getPrefixDirs的方法:

1
2
3
4
5
6
public function getPrefixDirs(): array
{
return [
__NAMESPACE__ => __DIR__,
];
}

$this->classLoader->getPrefixesPsr4()搜集了可以加载的组件所在命名空间和文件地址,并返回一个关联数组.

所以$prefixDirsPsr4 则是由命名空间做key,文件所在路径为值的关联数组

然后,通过循环,检测出需要遍历的文件夹,过滤掉不合法地址,然后通过registerAutoLoaderFile(),addAutoLoader() 将所有需要注册的文件路径放入 AnnotationRegister 对象中的相应成员变量中.这里就不一一展示了.

ps:如果对上面加在机制不太熟的,就需要补一补 composer 相关知识了

然后程序调用了 loadAnnotation(),传入的则是一个 Autoloader对象,以便迭代.

迭代注册

loadAnnotation() 中,函数体内进行双重for循环,迭代所有需要注册的文件,并做条件判断与筛选,过滤掉不需要注册的类,最终执行 parseAnnotation() 方法.传入命名空间及类名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

private function parseAnnotation(string $namespace, string $className): void
{
// Annotation reader
$reflectionClass = new ReflectionClass($className);

// Fix ignore abstract
if ($reflectionClass->isAbstract()) {
return;
}
$oneClassAnnotation = $this->parseOneClassAnnotation($reflectionClass);

if (!empty($oneClassAnnotation)) {
AnnotationRegister::registerAnnotation($namespace, $className, $oneClassAnnotation);
}
}

该方法使用反射类,获取类的反射. 然后对其做解析操作,并把成功的结果集,注册进 AnnotationRegister 的静态成员中.

那么 parseOneClassAnnotation()是如何解析的呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
private function parseOneClassAnnotation(ReflectionClass $reflectionClass): array
{
// Annotation reader
$reader = new AnnotationReader();
$className = $reflectionClass->getName();

$oneClassAnnotation = [];
$classAnnotations = $reader->getClassAnnotations($reflectionClass);

// Register annotation parser
foreach ($classAnnotations as $classAnnotation) {
if ($classAnnotation instanceof AnnotationParser) {
$this->registerParser($className, $classAnnotation);

return [];
}
}

// Class annotation
if (!empty($classAnnotations)) {
$oneClassAnnotation['annotation'] = $classAnnotations;
$oneClassAnnotation['reflection'] = $reflectionClass;
}

// Property annotation
$reflectionProperties = $reflectionClass->getProperties();
foreach ($reflectionProperties as $reflectionProperty) {
$propertyName = $reflectionProperty->getName();
$propertyAnnotations = $reader->getPropertyAnnotations($reflectionProperty);

if (!empty($propertyAnnotations)) {
$oneClassAnnotation['properties'][$propertyName]['annotation'] = $propertyAnnotations;
$oneClassAnnotation['properties'][$propertyName]['reflection'] = $reflectionProperty;
}
}

// Method annotation
$reflectionMethods = $reflectionClass->getMethods();
foreach ($reflectionMethods as $reflectionMethod) {
$methodName = $reflectionMethod->getName();
$methodAnnotations = $reader->getMethodAnnotations($reflectionMethod);

if (!empty($methodAnnotations)) {
$oneClassAnnotation['methods'][$methodName]['annotation'] = $methodAnnotations;
$oneClassAnnotation['methods'][$methodName]['reflection'] = $reflectionMethod;
}
}

$parentReflectionClass = $reflectionClass->getParentClass();
if ($parentReflectionClass !== false) {
$parentClassAnnotation = $this->parseOneClassAnnotation($parentReflectionClass);
if (!empty($parentClassAnnotation)) {
$oneClassAnnotation['parent'] = $parentClassAnnotation;
}
}

return $oneClassAnnotation;
}

它使用了 DoctrineAnnotationReader 类,对该类中的所有注释进行读取.

在此之前,我们先要明白,在 swoft 注解有三种形式

  • 对类本身的注解
  • 对类中方法的注解
  • 对类中成员的注解

所以接下来的代码可以分为 3 块. 从注释中就可以看出来, 就是对以上 3 类的解析.然后用放进 $oneClassAnnotation中,以关联数组的形式.

$oneClassAnnotation 则形成了一个对该类拥有的所有注解的一个简易描述.形式为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[
// 这里是对父类的解析,可以看作是一个分形结构
'parent' => [
...
],
// 对类的注解,及它的反射
'annotation' => [
new ClassAnnotation(),
new ClassAnnotation(),
new ClassAnnotation(),
]
'reflection' => new ReflectionClass(),
// 该类中对成员变量的注解,及它的反射
'properties' => [
'propertyName' => [
'annotation' => [
new PropertyAnnotation(),
new PropertyAnnotation(),
new PropertyAnnotation(),
]
'reflection' => new ReflectionProperty(),
]
],
// 该类中对方法的注解,及它的反射
'methods' => [
'methodName' => [
'annotation' => [
new MethodAnnotation(),
new MethodAnnotation(),
new MethodAnnotation(),
]
'reflection' => new ReflectionFunctionAbstract
]
]

然后返回,然后parseAnnotation() 方法中的AnnotationRegister::registerAnnotation 会将它收集值该类静态成员$annotation,成为一个数组集合,方便后面的调用。

总结

至此 AnnotationProcessor 所做的主要工作也就结束了.自己看了的朋友大概也发现了,这个 process 只仅仅做了一个收集的工作,并未有完成实质性的操作.

确实,此处只是做了一个记录,所有注入操作还未真正开始。下一篇将会讲到 BeanProcessor. 这个概念也是swoft的一个核心亮点概念,并且后面会相关联到swoft 中的一个 event 组件。