bin/swoftから、Swoftフレームワークのソースコードを読みます(5)--AnnotationProcessor
注釈はSwoftの特色の一つであり、Swoftプロジェクトのほとんどの業務コードには注釈が欠かせない.AnnotationProcessorプロセッサは、Swoftがビジネスでコンポーネントを使用できるコア依存性である.
まず、AnnotationProcessorのアクセス方法を見てみましょう.
このプロセッサの処理内容は非常に多く、本機環境での実行時間は約1.2秒である.
AnnotationRegister::load実装:
AnnotationResourceの構築方法:
$configの印刷結果:
registerLoaderコード:
初期化完了後にloadメソッドを呼び出すには、次の手順に従います.
loadAnnotation実装:
parseAnnotation実装:
parseOneClassAnnotation実装:
まとめ:
まず、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. , .
, , .