中间件用于拦截并修改http请求/响应流,必须实现__invoke()方法,按管道链式执行;事件监听器响应应用内部事件广播,方法名任意,通过事件调度器触发,不干预请求流程。

中间件和事件监听器是 Symfony 中两个不同层级的机制,不能混用。中间件(Middleware)处理的是 HTTP 请求/响应生命周期,而事件监听器(EventListener)响应的是应用内部广播的事件信号——它们目的不同、触发时机不同、注册方式也不同。
中间件:拦截请求与响应流
中间件在请求进入控制器前、响应返回客户端前执行,属于“管道式”处理链。它不依赖事件调度器,而是由 Kernel::handle() 显式调用。
- 中间件必须实现
__invoke(Request $request, RequestHandlerInterface $handler): Response方法 - 注册在
config/packages/framework.yaml的framework.http_method_override同级位置,或通过kernel.middleware标签手动配置服务 - 典型用途:权限预检、CORS 头设置、请求体解包、日志记录(按请求粒度)
- 示例:一个记录请求耗时的中间件
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class TimingMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$start = microtime(true);
$response = $handler->handle($request);
$duration = (microtime(true) - $start) * 1000;
$response->headers->set('X-Response-Time-Ms', (int) $duration);
return $response;
}
}
事件监听器:响应业务或框架广播的事件
监听器不介入请求流程本身,而是在某个动作发生后被动接收通知,比如用户注册完成、邮件即将发出、数据库实体即将保存等。
- 监听器类方法可任意命名(如
onUserRegistered),只要参数类型匹配事件对象即可 - 注册方式包括:自动发现(启用
autoconfigure: true)、#[AsEventListener]属性(PHP 8.2+)、YAML service tag(kernel.event_listener) - 典型用途:发欢迎邮件、更新搜索索引、清理缓存、审计日志
- 注意:Doctrine 的
prePersist等不是中间件,是 ORM 生命周期事件,需通过 Doctrine 监听器(#[AsDoctrineListener])响应
别把事件监听器当“中间件替代品”
有人试图用 kernel.request 监听器做权限校验,这虽能工作但违背设计原则:
-
kernel.request在路由解析前触发,拿不到 controller/action 信息,无法做细粒度授权 - 它不阻断后续流程,无法像中间件那样直接返回
Response终止请求 - 安全逻辑应放在防火墙(Security)或自定义中间件中,而非事件系统
什么时候该选哪个?
判断依据很简单:
- 需要修改、终止、包装 HTTP 请求或响应 → 用中间件
- 需要在某个业务动作(如“订单已支付”、“邮件已发送”)之后执行副作用 → 用事件监听器
- 需要在数据库操作前后调整实体状态(如自动设时间戳)→ 用Doctrine 监听器,不是通用事件监听器











