Symfony 7.2 请求生命周期由 Fiber 和协程调度器接管,HttpKernel::handle() 变为协程入口包装器,执行链转向 RequestContext 绑定与 ScopedRuntime 隔离,事件分发、服务作用域、资源管理全面协程化,预加载与全局状态成主要兼容陷阱。

Symfony 7.2 的请求生命周期不再由传统 HTTP 内核线性驱动,而是被虚拟线程(Fiber)和协程调度器接管——这意味着你写的控制器、事件监听器、中间件,实际运行在用户态轻量执行流中,而非 OS 线程或 FPM 进程里。
HTTP Kernel 不再是入口点,而是调度桥接器
在 Symfony 7.2 中,HttpKernel::handle() 本身已被重写为协程友好的入口包装器。它不直接执行逻辑,而是将请求注入调度上下文,并触发 Fiber 启动。真正的执行链从 RequestContext 绑定开始,经由 ScopedRuntime 分配隔离栈空间,再进入服务容器解析。
- 传统调用链:
index.php → HttpKernel::handle() → Controller → Response - 7.2 新链:
index.php → Swoole event loop → go() → Fiber::start() → RequestContext::bind() → Container::get() - 若未启用协程运行时(如未调用
Swoole\Runtime::enableCoroutine()),HttpKernel会退回到兼容模式,但所有虚拟线程特性(如线程本地存储、安全上下文传播)将不可用 -
Kernel::boot()仍需显式调用,但它现在预加载的是「协程感知型」服务定义,例如标记public(false)的服务会被内联编译,避免运行时反射
事件分发器(EventDispatcher)已深度协程化
7.2 的 EventDispatcher 不再同步逐个调用监听器,而是自动识别异步监听器(返回 Promise 或 Generator 的),并交由协程调度器并发执行。关键变化在于:监听器的执行上下文与发起请求的 Fiber 强绑定,$event->getRequest()->attributes 和安全令牌都来自当前虚拟线程的 ScopedStorage。
- 阻塞式监听器(如调用
sleep(1)或file_get_contents())会导致整个Fiber挂起,但不会阻塞其他虚拟线程——前提是已启用runtime_hook_flags: all - 监听器中访问
$_SESSION或$_SERVER会被自动代理到当前线程上下文封装层,原始超全局变量不可直接读写 - 自定义事件类必须实现
ScopedEventInterface才能参与上下文传播,否则其属性无法跨Fiber安全传递
服务容器作用域规则彻底重构
7.2 容器默认禁用 singleton 作用域(即 shared: true),所有服务默认为 prototype。这不是配置项变更,而是运行时强制策略:每个虚拟线程首次请求某服务时,都会创建新实例;销毁该 Fiber 时自动调用 __destruct() 并清理关联资源。
- 若强行注册
shared: true服务,容器启动时会抛出LogicException,提示 “Shared services are not allowed in virtual thread mode” - 静态属性(
static $cache = [];)在任何服务类中均被禁止,ContainerBuilder在编译阶段扫描并报错 - 数据库连接、缓存客户端等“共享资源”需通过连接池中间件(如
Doctrine\Common\Pool)提供,不能直接注入单例客户端 -
request作用域已废弃,改用fiber作用域——它不是配置字符串,而是一个运行时类型标识,由Fiber::getCurrentId()隐式绑定
开发环境下的调试陷阱:OPcache + 预加载 + Fiber 冲突
7.2 开发模式启用 opcache.preload 和 bin/console cache:warmup --no-optional-warmers 后,部分类(尤其是 Controller 和 EventListener)可能被提前加载进主进程内存,导致 Fiber 中的实例状态被意外共享。这不是 bug,而是预加载机制与协程内存模型的天然张力。
- 现象:修改控制器方法后刷新页面无变化,或日志中出现跨请求的
$this->counter++累加 - 原因:预加载的类在主 Fiber 外初始化,其静态/常量/闭包绑定未随虚拟线程隔离
- 临时解决:开发时禁用预加载(注释
opcache.preload配置),或在services.php中对敏感类添加->disablePreload() - 根本规避:所有可变状态必须放在实例属性(
private int $count = 0;),且确保该类未被class_exists()提前触发加载
真正难处理的不是 Fiber 启动或事件分发,而是旧代码中那些隐式依赖全局状态的逻辑——比如一个从 $_ENV 读取配置的工具类,或基于 microtime(true) 实现的简易限流器,在虚拟线程下会因上下文缺失而失效。这些地方不会报错,但行为不可预测。


















