属性注入后 var_dump 显示 uninitialized 是因 PHP 8.2+ typed property 在未初始化时显式提示该状态,根本原因是 private 属性无法被 ReflectionProperty::setValue() 写入,导致注入静默失败。

为什么属性注入后 var_dump 是 uninitialized
因为 @Inject 属性在对象构造完成前尚未赋值,直接在 __construct 或 __invoke 早期调用 var_dump($this->service) 会看到 uninitialized —— 这不是 bug,是 Hyperf 注入时机的必然表现。
- Hyperf 的属性注入发生在
afterInstantiation阶段,即parent::__construct()执行完毕、但任何业务方法(包括__invoke)被调用之前 - 你在
__construct里访问$this->service,此时注入逻辑根本还没触发,PHP 将该未初始化的 typed property 视为uninitialized - 这个状态只对 PHP 8.2+ 的 typed property 有显式提示;PHP 8.1 及以下可能表现为
null,但本质相同 - 即使类被正确扫描、代理文件存在、
$container->has()返回true,也改变不了这个时序约束
如何验证注入是否真生效
别在构造函数里查,改用可观察的生命周期钩子或业务入口点:
- 加一个
#[PostConstruct]方法,在里面var_dump($this->service)—— 这个回调保证在所有注入完成后执行 - 在 Controller 的 action 方法、Command 的
handle()、Listener 的process()中访问,这些方法均由容器调度,注入已就绪 - 临时在
process()开头加if (!$this->service) { throw new RuntimeException('inject failed'); },比var_dump更能暴露真实问题 - 启动时加
--vvv参数,看日志里有没有Injecting property xxx into class yyy,这是注入逻辑实际运行的证据
中间件里为什么永远拿不到注入值
因为中间件实例是框架手动 new 的,不走 DI 容器流程,@Inject 属性注解完全被忽略。
- 必须把依赖声明在构造函数参数上,并加上
#[Inject]:public function __construct(#[Inject] protected UserService $service) { ... } - 确保该中间件类本身已被容器注册:要么加
#[Middleware]注解并落在scan.paths内,要么在dependencies.php中手动bind() - 运行
php bin/hyperf.php di:dump,搜索中间件类名,确认它出现在输出中且有注入记录 - 不要试图在中间件里写
#[Inject] public UserService $service;—— 这行代码不会产生任何效果
private 属性 + @Inject 报 uninitialized 的真正原因
这不是注入时机问题,而是 PHP 语言限制:ReflectionProperty::setValue() 无法写入 private typed property,Hyperf 在注入阶段直接跳过该属性,导致它始终处于未初始化状态。
- 报
uninitialized是表象,根因是反射失败后注入逻辑静默终止,不是“还没到时间”,是“压根没机会” - 哪怕你把访问延迟到
#[PostConstruct],结果仍是uninitialized - 唯一兼容方案是改用
protected(不推荐封装性)或彻底转向构造函数注入:public function __construct(#[Inject] private UserService $service) { $this->service = $service; } - 注意:构造函数参数上的
private是允许的,PHP 支持私有参数绑定,但属性本身不能是private且带@Inject
代理类生成和注入时机是两件事。前者决定“能不能注入”,后者决定“什么时候能用”。很多 uninitialized 问题,表面像时序错误,实际是代理没生成、类没注册、或属性不可写——先确认这三者,再谈访问时机。











