乐趣区

关于php:从binswoft开始阅读Swoft框架源码五AnnotationProcessor

注解是 Swoft 的特色之一,Swoft 我的项目中简直所有的业务代码中都离不开注解.
AnnotationProcessor 处理器就是 Swoft 能在业务中应用组件的外围依赖.

先看 AnnotationProcessor 的入口办法:

public function handle(): bool
{
     // 注解加载前回调
     // 零碎未做解决 间接返回的 true
     if (!$this->application->beforeAnnotation()) {CLog::warning('Stop annotation processor by beforeAnnotation return false');
         return false; 
     }
     
     // 获取以后利用实例, 这个实例是在利用初始化处理器时传递给
     // 每个处理器的
     $app = $this->application;
     
     // 找到 AutoLoader 类. 解析和采集注解.
     // Find AutoLoader classes. Parse and collect annotations.
     AnnotationRegister::load([
         'inPhar' => IN_PHAR,
         'basePath' => $app->getBasePath(),
         'notifyHandler' => [$this, 'notifyHandle'],
         // TODO force load framework components: bean, error, event, aop
         'disabledAutoLoaders' => $app->getDisabledAutoLoaders(),
         'excludedPsr4Prefixes' => $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();}

这一处理器解决的内容十分多, 在我本机环境下执行工夫约 1.2 秒.

AnnotationRegister::load 实现:

public static function load(array $config = []): void
{
     // 初始化和配置注解资源
     $resource = new AnnotationResource($config);
     
     // 执行注解的加载办法
     $resource->load();}

AnnotationResource 的构造方法:

public function __construct(array $config = [])
{
     // 设置默认不蕴含的 PSR4 前缀
     // 这里有 'Psr\\','Monolog\\','PHPUnit\\','Symfony\\'
     // Init $excludedPsr4Prefixes
     $this->excludedPsr4Prefixes = self::DEFAULT_EXCLUDED_PSR4_PREFIXES;
     
     // 将传递进来的 config 设置到以后对象上
     // 后附 $config 的打印后果
     // 打印后果可见: 未额定设置生效的 loader 和不引入的 psr4 前缀
     // 设置了以后是否在 phar 环境, 我的项目根目录和告诉回调函数
     // Can set property by array
     ObjectHelper::init($this, $config);
     
     // 向 Doctrine 组件注册了一个查看类是否存在的回调
     $this->registerLoader();
     
     // 获取 composer 的 classLoader
     $this->classLoader = ComposerHelper::getClassLoader();
     
     // 获取已加载的文件
     $this->includedFiles = get_included_files();}

$config 的打印后果:

array(5) {
  // 以后不是在 phar 中执行 所以此处为 false
  ["inPhar"]=>
  bool(false)
  // 我的项目根目录
  ["basePath"]=>
  string(54) "/Volumes/Samsung_T5/hmqr/phpProgram/fulian/AuthService"
  // 告诉的回调函数, 是 AnnotationProcessor 处理器上的 notifyHandle 办法
  ["notifyHandler"]=>
  array(2) {[0]=>
    object(Swoft\Processor\AnnotationProcessor)#10 (1) {["application":protected]=>
      object(App\Application)#3 (12) {["basePath":protected]=>
        string(54) "/Volumes/Samsung_T5/hmqr/phpProgram/fulian/AuthService"
        ["envFile":protected]=>
        string(10) "@base/.env"
        ["beanFile":protected]=>
        string(13) "@app/bean.php"
        ["appPath":protected]=>
        string(9) "@base/app"
        ["configPath":protected]=>
        string(12) "@base/config"
        ["runtimePath":protected]=>
        string(13) "@base/runtime"
        ["resourcePath":protected]=>
        string(14) "@base/resource"
        ["processor":"Swoft\SwoftApplication":private]=>
        object(Swoft\Processor\ApplicationProcessor)#14 (2) {["processors":"Swoft\Processor\ApplicationProcessor":private]=>
          array(6) {[0]=>
            object(Swoft\Processor\EnvProcessor)#8 (1) {["application":protected]=>
              *RECURSION*
            }
            [1]=>
            object(Swoft\Processor\ConfigProcessor)#9 (1) {["application":protected]=>
              *RECURSION*
            }
            [2]=>
            *RECURSION*
            [3]=>
            object(Swoft\Processor\BeanProcessor)#11 (1) {["application":protected]=>
              *RECURSION*
            }
            [4]=>
            object(Swoft\Processor\EventProcessor)#12 (1) {["application":protected]=>
              *RECURSION*
            }
            [5]=>
            object(Swoft\Processor\ConsoleProcessor)#13 (1) {["application":protected]=>
              *RECURSION*
            }
          }
          ["application":protected]=>
          *RECURSION*
        }
        ["startConsole":protected]=>
        bool(true)
        ["disabledProcessors":"Swoft\SwoftApplication":private]=>
        array(0) { }
        ["disabledAutoLoaders":"Swoft\SwoftApplication":private]=>
        array(0) { }
        ["disabledPsr4Prefixes":"Swoft\SwoftApplication":private]=>
        array(0) {}}
    }
    [1]=>
    string(12) "notifyHandle"
  }
  // 有效的 AutoLoaders
  ["disabledAutoLoaders"]=>
  array(0) { }
  // 不引入的 Psr4 前缀
  ["excludedPsr4Prefixes"]=>
  array(0) {}}

registerLoader 代码:

private function registerLoader(): void
{
     // 此处看到 swoft 的注解依赖了 Doctrine 框架的注解性能
     // 这里给 Doctrine\Common\Annotations\AnnotationRegistry
     // 注册了回调函数, 回调的性能是依据传递进来的类返回该类是否存在
     // 这是 Doctrine 框架的用法, 如有疑难, 可先尝试应用 Doctrine
     // 所以为什么不间接 return class_exists($class);
     /** @noinspection PhpDeprecationInspection */
     AnnotationRegistry::registerLoader(function (string $class) {if (class_exists($class)) {return true;}
         return false;
     });
}

初始化实现后调用 load 办法:

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::addExcludeNamespace($ns);
             $this->notify('excludeNs', $ns);
             continue; 
         }
         
         // 遍历命名空间的源码根目录, 寻找 loader 的类文件
         // 未经设置的话, 此处寻找的是源码包目录下的
         // AutoLoader.php 文件
         // Find package/component loader class
         foreach ($paths as $path) {
         
             // 拼接 loader 文件的全门路
             $loaderFile = $this->getAnnotationClassLoaderFile($path);
             
             // 如果包目录内不存在 AutoLoader.php 文件
             if (!file_exists($loaderFile)) {$this->notify('noLoaderFile', $this->clearBasePath($path), $loaderFile);
                 continue; 
             }
             
             // 拼接命名空间和 loader 的类名
             $loaderClass = $this->getAnnotationLoaderClassName($ns);
             
             // 如果类不存在
             // 发送告诉, 跳过以后主动加载类
             if (!class_exists($loaderClass)) {$this->notify('noLoaderClass', $loaderClass);
                 continue; 
             }
             
             $isEnabled = true;
             // 实例化主动加载类
             $autoLoader = new $loaderClass();
             
             // 如果类不是 LoaderInterface 的实现者
             // 跳过以后加载
             if (!$autoLoader instanceof LoaderInterface) {$this->notify('invalidLoader', $loaderFile);
                 continue; 
             }
             
             // 告诉查找 loader 胜利音讯
             $this->notify('findLoaderClass', $this->clearBasePath($loaderFile));
             
             // 如果以后 loader 不是一个可用的 loader
             // 则告诉并批改 enabled 状态
             // If is disable, will skip scan annotation classes
             if (isset($this->disabledAutoLoaders[$loaderClass]) || !$autoLoader->isEnable()) {
                 // 批改 enabled 状态
                 $isEnabled = false;
                 // 告诉音讯
                 $this->notify('disabledLoader', $loaderFile);
             } else {
                 // 注册 loaderFile
                 AnnotationRegister::addAutoLoaderFile($loaderFile);
                 // 告诉增加 loaderFile 胜利音讯
                 $this->notify('addLoaderClass', $loaderClass);
                 
                 // 获取并收集类 bean
                 // Scan and collect class bean s
                 $this->loadAnnotation($autoLoader);
             }
             // Storage autoLoader instance to register
             // 注册 loader
             AnnotationRegister::addAutoLoader($ns, $autoLoader, $isEnabled);
         }
     }
 }

loadAnnotation 实现:

private function loadAnnotation(LoaderInterface $loader): void
{
     // 获取命名空间和对应根目录的映射关系
     $nsPaths = $loader->getPrefixDirs();
     
     // 遍历失去命名空间和目录
     foreach ($nsPaths as $ns => $path) {
         // 调用 spl 的 RecursiveIteratorIterator 迭代器
         // 获取指定目录内的所有文件
         $iterator = DirectoryHelper::recursiveIterator($path);
         
         // 遍历迭代器
         /* @var SplFileInfo $splFileInfo */
         foreach ($iterator as $splFileInfo) {
             // 获取以后文件全门路
             $filePath = $splFileInfo->getPathname();
             // $splFileInfo->isDir();
             
             // 如果是目录, 则跳过, 因为迭代器是以
             // RecursiveIteratorIterator::LEAVES_ONLY
             // 模式关上的, 所以不会存在目录的状况
             // 然而会返回. 和.. 文件
             if (is_dir($filePath)) {continue;}
             
             // 获取文件的文件名, 不蕴含门路
             $fileName = $splFileInfo->getFilename();
             
             // 获取文件的后缀名
             $extension = $splFileInfo->getExtension();
             
             // 如果后缀和定义的 loader 后缀不同
             // 或者文件名中不蕴含. 则跳过
             if ($this->loaderClassSuffix !== $extension || strpos($fileName, '.') === 0) {continue;}
             
             // 如果是不引入的文件
             // 则告诉并跳过
             // It is exclude filename
             if (isset($this->excludedFilenames[$fileName])) {AnnotationRegister::addExcludeFilename($fileName);
                 continue; 
             }
             
             // 拼接 loader 带. 的后缀
             $suffix = sprintf('.%s', $this->loaderClassSuffix);
             
             // 获取文件所在目录
             $pathName = str_replace([$path, '/', $suffix], ['','', ''], $filePath);
             
             // 获取类的全名(蕴含命名空间)
             $className = sprintf('%s%s', $ns, $pathName);
             // autoload 标识以后文件是否曾经存在于
             // 曾经引入的文件汇合内
             // Fix repeat included file bug
             $autoload = in_array($filePath, $this->includedFiles, true);
             
             // 如果文件曾经被引入过, 类依然不存在, 则告诉并跳过
             // 如果文件未被引入过, 则调用类的__autoload 引入文件后, 再判断类是否存在, 如果依然不存在, 则告诉并跳过
             // Will filtering: interfaces and traits
             if (!class_exists($className, !$autoload)) {$this->notify('noExistClass', $className);
                 continue;
             }
             
             // 解析注解
             // Parse annotation
             $this->parseAnnotation($ns, $className);
         }
     }
 }

parseAnnotation 实现:

private function parseAnnotation(string $namespace, string $className): void
{
     // 创立反射类
     // Doctine 须要用到
     // Annotation reader
     $reflectionClass = new ReflectionClass($className);
     
     // 如果是一个 abstract 类, 则不持续解析注解
     // Fix ignore abstract
     if ($reflectionClass->isAbstract()) {return;}
     
     
     $oneClassAnnotation = $this->parseOneClassAnnotation($reflectionClass);
     if (!empty($oneClassAnnotation)) {AnnotationRegister::registerAnnotation($namespace, $className, $oneClassAnnotation);
     }
}

parseOneClassAnnotation 实现:

private function parseOneClassAnnotation(ReflectionClass $reflectionClass): array
 {
     // 实例化 Doctrine 的 reader
     // Annotation reader
     $reader = new AnnotationReader();
     
     // 获取类名
     $className = $reflectionClass->getName();
     
     // 返回后果
     $oneClassAnnotation = [];
     
     // 获取类的注解
     $classAnnotations = $reader->getClassAnnotations($reflectionClass);
     
     // 遍历类的注解
     // Register annotation parser 
     foreach ($classAnnotations as $classAnnotation) {
         // 如果是 AnnotationParser 的实例
         if ($classAnnotation instanceof AnnotationParser)  {
             // 将此注解对象注册到注解 parser
             $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;
 }

总结:

1.swoft 的注解底层依赖于 Doctrine 的 annotations 包.
2. 注解处理器的工作流程:
    (1).run()调用 AnnotationProsser 的 handle 办法.
    (2).handle()调用 Swoft\Annotation\AnnotationRegister::load 办法.
    (3). 第 (2) 步的 load 办法调用 Swoft\Annotation\Resource\AnnotationResource 的 load 办法.
    (4). 第 (3) 步的 load 办法通过 composer 的 autoloader 获取到所有的命名空间和源码目录的映射.
        而后遍历获取目录内的 AutoLoader 类, 而后调用 loadAnnotation 传入 autoloader 实例.
        同时将这个命名空间下的 autoloader 注册到 Swoft\Annotation\AnnotationRegister.
    (5).loadAnnotation 依据传进来的 loader 获取到其对应目录内的所有文件.
        而后遍历这些文件找到非法的库类文件, 同时将目录内未加载的类加载.
        而后将命名空间和类名传给 parseAnnotation 办法解决.
    (6).parseAnnotation 首先创立传进来的类的反射对象, 而后调用 parseOneClassAnnotation 办法.
        parseOneClassAnnotation 办法调用 Doctrine 的 reader 办法.
        获取以后类的类注解、属性注解、办法注解.
        而后递归调用本身, 获取父类的注解对象.
    (7).parseAnnotation 调用 Swoft\Annotation\AnnotationRegister::registerAnnotation 办法, 将这个类的注解进行注册.
3. 通过下面的步骤, 注解处理器的工作就全副实现.
  注解处理器的工作就是获取所有非法的注解对象, 而后进行注册, 并未应用获取的注解对象.
退出移动版