Hyperf 3.0 彻底移除 Doctrine Annotations 支持,注解扫描器仅识别 PHP 8 Attributes,旧版 @Controller 等注解因未被解析而失效,导致路由注册失败、404 或重复注册报错。
Hyperf 3.0 默认不再使用 Doctrine 注解解析器,而是原生支持 PHP 8 Attributes(即
),旧版
、
等 Doctrine 风格注解在未做适配时会直接报错或被忽略。
为什么旧注解在 Hyperf 3.0 中失效
Hyperf 3.0 彻底移除了对
的依赖,启动时不会加载
实例;即使你手动 require 它,框架内部的注解扫描逻辑(如
)也只识别 PHP 8 Attributes 类型的类,不处理
返回的
对象。
常见错误现象包括:
—— 实际是注解未被识别,导致路由注册失败,后续又因配置残留触发重复注册校验
控制器方法响应 404,但日志里看不到路由注册记录
运行
生成的代码含
,但旧注解写法仍留在代码中未清理
如何启用 Doctrine 注解兼容层(不推荐长期使用)
如果你有大量遗留代码暂无法重写,Hyperf 官方提供了一个临时兼容包:
。它不是“升级 AnnotationReader”,而是桥接层,把 Doctrine 注解转换为 Attributes 对象供框架消费。
操作步骤如下:
执行
在
中添加配置项:
(确保扫描开启)
确认你的 Doctrine 注解类(如
)已正确声明
和
,且命名空间未被误删
启动前清空注解缓存:
⚠️ 注意:
仅支持 Doctrine 注解语法,不支持混合写法(比如一个类里同时写
和
),且不保证与未来 Hyperf 小版本完全兼容。
真正推荐的迁移路径:用 PHP 8 Attributes 替代全部 Doctrine 注解
Hyperf 3.0 的注解系统本质是「Attributes + 自定义 Attribute 类 + 扫描器扩展」。迁移不是简单替换符号,而是要理解 Attribute 类的定义方式和生命周期绑定点。
关键差异点:
Hyperf 3.1.66
本页面提供企业级 PHP 协程框架 Hyperf 3.1.66 版本的官方源码下载与完整更新日志。重点解析 v3.1.66 版本中新增的 gRPC 多客户端负载均衡支持、Pool 连接池全量刷新、Guzzle 持久化 Cookie 以及数据库 JSON 包含键查询等核心优化特性。
下载
→
:参数必须显式命名,不支持位置参数
→
自定义注解类必须继承
,并指定
等标志
旧版通过
获取注解的方式失效,应改用
或框架提供的
(注意:这是 Hyperf 自研的 Attributes 读取器,非 Doctrine)
示例迁移:
}
容易被忽略的细节:Attribute 类的自动加载与反射可见性
PHP 8 Attributes 默认只在
时解析,如果 Attribute 类本身未被自动加载(比如放在
下但未配置 PSR-4),
会返回空数组,且无任何警告。
务必检查以下几点:
所有自定义 Attribute 类所在的命名空间是否已正确注册到
的
Attribute 类的构造函数参数必须有类型声明(如
),否则运行时报
若使用
这类带验证逻辑的 Attribute,需确保其
方法在
扫描阶段可被调用 —— 意味着不能依赖尚未初始化的容器服务
最稳妥的做法是:所有 Attribute 类保持无状态、无依赖、仅作元数据容器,业务逻辑收归到对应的 Aspect 或 Listener 中处理。
#[Attribute]@Controller@GetMappingdoctrine/annotationsAnnotationReaderHyperf\Di\Annotation\ScannerDoctrine\Common\Annotations\ReaderDoctrine\Common\Annotations\AnnotationController annotation cannot be repeatedphp bin/hyperf.php gen:controller#[Controller]hyperf/annotation-compatcomposer require hyperf/annotation-compatconfig/autoload/annotations.php'scan' => ['enable' => true, 'ignore_annotations' => []]Hyperf\HttpServer\Annotation\Controller@Annotation@Targetrm -rf runtime/containerhyperf/annotation-compat@Controller#[Controller]@Controller("user")#[Controller(prefix: "user")]@GetMapping(path="/list", name="user.list")#[GetMapping(path: "/list", name: "user.list")]AttributeAttribute::TARGET_CLASS | Attribute::TARGET_METHODAnnotationReader::getClassAnnotations()ReflectionClass::getAttributes()Hyperf\Di\Annotation\AnnotationReader// 旧写法(v2.x)
/**
* @Controller(prefix="user")
*/
class UserController
{
/**
* @GetMapping(path="/list")
*/
public function list() { }
}
// 新写法(v3.0+)
[Controller(prefix: "user")]
class UserController
{
[GetMapping(path: "/list")]
public function list() { }
__constructapp/AnnotationgetAttributes()composer.jsonautoload.psr-4public function __construct(public string $prefix = '')ReflectionException: Internal error: Failed to retrieve the default value#[Required]isValid()Hyperf\Di\Annotation\Scanner