注解是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.通过下面的步骤,注解处理器的工作就全副实现. 注解处理器的工作就是获取所有非法的注解对象,而后进行注册,并未应用获取的注解对象.