Hyperf 3.0 彻底弃用运行时注解扫描,转向 PHP 8.1+ 原生 Attributes 编译期处理,AnnotationScanner 失效;DI 容器直接通过 ReflectionClass::getAttributes() 获取元数据,不再依赖 Doctrine 或 PHPDoc 解析。

Hyperf 2.2 和 3.0 的注解扫描机制本质不同:2.2 仍依赖反射 + 子进程预扫描,3.0 则彻底放弃运行时反射,转向纯编译期 Attributes 处理,不再需要“扫描”这个动作。
为什么 AnnotationScanner 在 v3.0 里基本失效了
v3.0 不再使用 Doctrine Annotations,也移除了对 PHPDoc 注释的解析逻辑。AnnotationScanner 类虽还存在(为兼容旧扩展),但默认不启用;即使手动调用,也不会识别 #[Value] 这类原生 Attribute——因为它们不是“注解”,而是 PHP 语言级的结构化元数据,由 PHP 解析器在词法/语法阶段就处理完毕。
- PHP 8.1+ 的
#[Attribute]在ReflectionClass::getAttributes()中直接返回,无需正则匹配或 AST 解析 - v3.0 的 DI 容器通过
ReflectionProperty::getAttributes()拿到#[Value]实例后,立即读取其参数(如"app.name"),不缓存中间态 - 旧版基于
Doctrine\Common\Annotations\Reader的整套流程(包括缓存键生成、注释行定位)在 v3.0 中已完全绕过
runtime/container/proxy/ 目录在 v2.2 和 v3.0 中的作用差异
v2.2 的 runtime/container/proxy/ 存的是代理类(如 ProxyAppControllerIndexController.php)和注解元数据缓存(.cache 文件);v3.0 里它只存 AOP 代理类,而注解相关元数据全部下沉到容器定义数组中,由 ConfigProvider 或 DependencyProvider 在启动时静态注册。
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- v2.2 启动时若缺失
runtime/container/proxy/,会触发BetterReflection全量扫描,慢且不可控 - v3.0 即使清空该目录,只要
SCAN_CACHEABLE=true,容器定义仍从config/autoload/下的 PHP 数组加载,不重新提取 Attributes - v3.0 的
proxy/文件不再包含路由、注入等元数据,只负责方法拦截逻辑,体积更小、生成更快
如何验证当前项目走的是 v3.0 的 Attributes 路径
最直接的方式是关掉缓存并观察启动行为:设 SCAN_CACHEABLE=false,然后启动服务,同时用 strace -e trace=stat,openat php bin/hyperf.php start 2>&1 | grep -i 'annotation\|attribute' 查看系统调用。
- v2.2 会大量访问
vendor/doctrine/annotations/和runtime/container/proxy/*.cache - v3.0 几乎不读任何注解相关文件,只加载
config/autoload/*.php和vendor/hyperf/di/src/Container.php - 若看到
ReflectionClass::getAttributes()被频繁调用,说明走的是 Attributes 原生路径;若看到Doctrine\Common\Annotations\*类名,则仍是旧链路
真正容易被忽略的点是:v3.0 的 Attributes 不是“被扫描出来”的,而是被“被 PHP 解析器暴露出来”的。你不能像 v2.x 那样去 hook AnnotationReader 来干预解析过程,所有处理必须发生在反射 API 返回之后——比如在 DefinitionFactory 中根据 ReflectionProperty::getAttributes(#[Value]::class) 的结果动态构建 DI 定义。

















