bin/swoftから、Swoftフレームワークのソースコードを読みます(5)--AnnotationProcessor

16597 ワード

注釈は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.       ,             .
                       ,      ,           .