惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Blog of Author Tim Ferriss
Microsoft Azure Blog
Microsoft Azure Blog
S
SegmentFault 最新的问题
Schneier on Security
Schneier on Security
W
WeLiveSecurity
Webroot Blog
Webroot Blog
T
Threatpost
量子位
大猫的无限游戏
大猫的无限游戏
C
Cisco Blogs
腾讯CDC
N
News | PayPal Newsroom
T
Troy Hunt's Blog
T
Tailwind CSS Blog
Latest news
Latest news
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Register - Security
The Register - Security
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Engineering at Meta
Engineering at Meta
SecWiki News
SecWiki News
MyScale Blog
MyScale Blog
GbyAI
GbyAI
Application and Cybersecurity Blog
Application and Cybersecurity Blog
A
Arctic Wolf
The GitHub Blog
The GitHub Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
有赞技术团队
有赞技术团队
NISL@THU
NISL@THU
L
LINUX DO - 最新话题
雷峰网
雷峰网
P
Privacy International News Feed
Spread Privacy
Spread Privacy
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
月光博客
月光博客
V
V2EX
H
Help Net Security
博客园 - 三生石上(FineUI控件)
U
Unit 42
B
Blog
PCI Perspectives
PCI Perspectives
G
GRAHAM CLULEY
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
Last Week in AI
Last Week in AI

流动

对口型视频合成方案对比:Wav2Lip、VideoReTalking 与 MuseTalk 2026 音色克隆方案对比:IndexTTS-2、CosyVoice、GPT-SoVITS、Fish Speech、VoxCPM 部署与实测 AI Agent折腾记(OpenClaw / Hermes Agent) 我的2025年 大连之行 回家收麦 六一儿童节爬长城 Golang database/sql 数据库断线自动重连机制解析 Golang默认Http Client导致的cannot assign requested address错误 清明踏春,爬山看海 购入小牛G400T电动车 北京的三月飞雪 wrenAI本地LLM模型部署 天津一日游 2024年终总结 停止使用staticfile.org服务 使用 ImageMagick 自动添加水印,保护图片版权 如何注册一个.sol域名 奥森公园半日游 昌平42公里骑行绿道打卡 十月一日爬慕田峪长城 当Hugo遇上AVIF,优化图片加载 博客被恶意镜像 Github Pages 部署流程解析 搭建个人锻炼页面 你好 Follow 中秋爬山 Google Adsense的审核之旅 让你的IPFS站点持久在线:接入Filebase的Names(IPNS)服务 一次简短的青岛之行 解决 "undeclared name: any (requires version go1.18 or later)" 编译错误 搭建自托管IPFS Gateway服务,替代Cloudflare的IPFS Gateway 302跳转的跨域问题(CORS) GORM增加sqlcommenter特性 源码分析:GORM是如何生成sql的 工银亚洲网银密码重置 加速Cloudflare访问 2023年终总结 2023年12月北京暴雪记录 使用Hugo实现响应式和优化的图片 加速Google Analytics 使用Google Indexing API加速博客收录 在Netlify上部署Twikoo评论系统 利用Github Actions定时抓取微博 北大口腔牙周刮治记录 故乡回忆之旅 解决Golang使用go get安装包后找不到可执行文件的问题 修正Hugo的JSON Feed格式 我的学车之路 将博客部署到星际文件系统(IPFS) 新冠疫情后的第一个春节 第一次清理键盘 2022年终总结 去掉Cloudflare烦人的email-decode.min.js请求 累计布局偏移修复方案改进 —— 自动生成图片宽高 优化博客的累计布局偏移(CLS)问题 将博客部署到Cloudflare Pages 奥林匹克公园向日葵之旅 记第二次洗牙 记录2022年海淀幼升小 Golang解析json的一个问题 疫情下的生活 整理下博客的一些调整 疫情下的五一假期 自己动手,更换thinkpad x1硬盘 二刷百望山 带娃游颐和园 博客架构说明 难得的清明假期 十一年的等待,终于拿到了liudon.com域名 被隔离的一周 mysql中字符串和整型自动转换的问题 一次惊心动魄的Mysql更新操作 如何在北京公积金网站上修改婚姻状况 PHP7.2编译安装后没有php.ini文件的问题 检测网站支持的SSL/TLS协议版本 记一次难忘的手术经历 十一假期经历 Swoft 框架运行分析(五) —— ConsoleProcessor模块分析 Swoft 框架运行分析(四) —— EventProcessor模块分析 一个git submodule update引发的问题 一个Curl的耗时长的问题 Swoft 框架运行分析(三) —— BeanProcessor模块分析 Swoft 框架运行分析(一) BCMath 与 科学计数 Flink Could Not Resolve Resourcemanager Address 解决Sublime Text安装包时"There Are No Packages Available for Installation"的报错 关于本站 2019,新开始
Swoft 框架运行分析(二) —— AnnotationProcessor模块分析
Liudon · 2019-08-29 · via 流动

上一篇介绍了,SwoftApplication里定义了6个Processor对象。

protected function processors(): array
    {
        return [
            new EnvProcessor($this),
            new ConfigProcessor($this),
            new AnnotationProcessor($this),
            new BeanProcessor($this),
            new EventProcessor($this),
            new ConsoleProcessor($this),
        ];
    }

所有的Processor实现都在framework\src\Processor目录下。

  1. EnvProcessor,运行环境检查。

  2. ConfigProcessor,配置相关。

  3. AnnotationProcessor,注解相关。

  4. BeanProcessor,Bean相关。

  5. EventProcessor,事件相关。

  6. ConsoleProcessor,命令行输入相关。

今天先讲一下AnnotationProcessor这个模块的实现。

<?php declare(strict_types=1);

namespace Swoft\Processor;

use Exception;
use Swoft\Annotation\AnnotationRegister;
use Swoft\Log\Helper\CLog;

/**
 * Annotation processor
 * @since 2.0
 */
class AnnotationProcessor extends Processor
{
    /**
     * Handle annotation
     *
     * @return bool
     * @throws Exception
     */
    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();
    }

    /**
     * @param string $type
     * @param string $target
     * @see \Swoft\Annotation\Resource\AnnotationResource::load()
     */
    public function notifyHandler(string $type, $target): void
    {
        switch ($type) {
            case 'excludeNs':
                CLog::debug('Exclude namespace %s', $target);
                break;
            case 'noLoaderFile':
                CLog::debug('No autoloader on %s', $target);
                break;
            case 'noLoaderClass':
                CLog::debug('Autoloader class not exist %s', $target);
                break;
            case 'findLoaderClass':
                CLog::debug('Find autoloader %s', $target);
                break;
            case 'addLoaderClass':
                CLog::debug('Parse autoloader %s', $target);
                break;
            case 'noExistClass':
                CLog::debug('Skip interface or trait %s', $target);
                break;
        }
    }
}

核心逻辑调用AnnotationRegister类的load方法,定义如下。

/**
    * Load annotation class
    *
    * @param array $config
    *
    * @throws AnnotationException
    * @throws ReflectionException
    */
public static function load(array $config = []): void
{
    $resource = new AnnotationResource($config);
    $resource->load();
}

这里又调用了AnnotationResource类的load方法,定义如下。

/**
    * Load annotation resource by find ClassLoader
    *
    * @throws AnnotationException
    * @throws ReflectionException
    */
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);
        }
    }
}

通过getPrefixesPsr4方法获取所有自动加载的命名空间和目录,遍历目录下的AutoLoader.php文件。

通过registerAutoLoaderFile注册自动加载文件到AnnotationRegister对象上。

然后调用了loadAnnotation方法,传入的是一个autoload对象。

/**
    * Load annotations from an component loader config.
    *
    * @param LoaderInterface $loader
    *
    * @throws AnnotationException
    * @throws ReflectionException
    */
private function loadAnnotation(LoaderInterface $loader): void
{
    $nsPaths = $loader->getPrefixDirs();

    foreach ($nsPaths as $ns => $path) {
        $iterator = DirectoryHelper::recursiveIterator($path);

        /* @var SplFileInfo $splFileInfo */
        foreach ($iterator as $splFileInfo) {
            $filePath = $splFileInfo->getPathname();
            // $splFileInfo->isDir();
            if (is_dir($filePath)) {
                continue;
            }

            $fileName  = $splFileInfo->getFilename();
            $extension = $splFileInfo->getExtension();

            if ($this->loaderClassSuffix !== $extension || strpos($fileName, '.') === 0) {
                continue;
            }

            // It is exclude filename
            if (isset($this->excludedFilenames[$fileName])) {
                AnnotationRegister::registerExcludeFilename($fileName);
                continue;
            }

            $suffix    = sprintf('.%s', $this->loaderClassSuffix);
            $pathName  = str_replace([$path, '/', $suffix], ['', '\\', ''], $filePath);
            $className = sprintf('%s%s', $ns, $pathName);

            // Fix repeat included file bug
            $autoload = in_array($filePath, $this->includedFiles, true);

            // Will filtering: interfaces and traits
            if (!class_exists($className, !$autoload)) {
                $this->notify('noExistClass', $className);
                continue;
            }

            // Parse annotation
            $this->parseAnnotation($ns, $className);
        }
    }
}

通过getPrefixDirs获取当前命名空间的目录,然后通过recursiveIterator遍历目录下的文件。

排除目录和非.php结尾的文件,最后会调用parseAnnotation方法。

/**
    * Parser annotation
    *
    * @param string $namespace
    * @param string $className
    *
    * @throws AnnotationException
    * @throws ReflectionException
    */
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);
    }
}

这里调用了parseOneClassAnnotation方法。

/**
    * Parse an class annotation
    *
    * @param ReflectionClass $reflectionClass
    *
    * @return array
    * @throws AnnotationException
    * @throws ReflectionException
    */
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;
}

这里就是解析注解了,可以看到分别有类注解、属性注解和方法注解三类。

这里注意这一段代码。

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

        return [];
    }
}

遍历注解类,如果注解属于AnnotationParser实例,这里调用registerParser进行注册。

/**
    * @param string $annotationClass
    * @param string $parserClassName
    */
public static function registerParser(string $annotationClass, string $parserClassName): void
{
    self::$classStats['parser']++;
    self::$parsers[$annotationClass] = $parserClassName;
}

回到上一个方法,解析完后,又调用了AnnotationRegister类的registerAnnotation方法进行注册。

/**
    * @param string $loadNamespace
    * @param string $className
    * @param array  $classAnnotation
    */
public static function registerAnnotation(string $loadNamespace, string $className, array $classAnnotation): void
{
    self::$classStats['annotation']++;
    self::$annotations[$loadNamespace][$className] = $classAnnotation;
}

至此,整个AnnotationProcessor加载完毕,这里AnnotationRegister类里会有annotationsparsers两个属性,这个信息在后面的BeanProcessor里还会用到。