PHP项目责任链与中间件

wen PHP项目 2

本文目录导读:

PHP项目责任链与中间件

  1. 核心概念对比
  2. 责任链模式(Chain of Responsibility)
  3. 中间件(Middleware)
  4. 关键区别总结
  5. 实际项目中的选择建议
  6. 性能与维护注意事项

这是一个非常好的技术问题,在 PHP 项目中,责任链模式(Chain of Responsibility)中间件(Middleware) 本质上都是面向切面编程请求预处理的实现方式,但它们在设计理念、实现粒度和典型应用场景上有显著区别。


核心概念对比

维度 责任链模式 中间件
本质 设计模式(GoF) 架构风格(受中间件框架启发)
传递机制 沿着链条逐个处理,直到某个节点处理完或终止 类似“洋葱圈”模型,层层包裹,处理完返回
控制反转 链上节点决定是否继续传递 由中间件调用 $next($request) 触发下一个
典型应用 日志、权限校验、数据过滤 Laravel 路由中间件、PSR-15 中间件
执行顺序 顺序执行,可中断 入栈(前置)→ 核心处理 → 出栈(后置)

责任链模式(Chain of Responsibility)

适用场景

  • 多个处理器按顺序尝试处理同一个请求
  • 每个处理器可独立决定是否处理或传递给下一个
  • 请求的唯一处理者不确定,由运行时决定

经典实现(PHP 示例)

// 抽象处理器
abstract class Handler
{
    protected ?Handler $next = null;
    public function setNext(Handler $handler): Handler
    {
        $this->next = $handler;
        return $handler; // 返回下一个,便于链式调用
    }
    abstract public function handle(Request $request): ?Response;
}
// 具体处理器:日志
class LoggingHandler extends Handler
{
    public function handle(Request $request): ?Response
    {
        // 前置处理
        echo "[LOG] 记录请求: {$request->uri}\n";
        // 传递给下一个(如有)
        if ($this->next) {
            return $this->next->handle($request);
        }
        return null;
    }
}
// 具体处理器:权限检查
class AuthHandler extends Handler
{
    public function handle(Request $request): ?Response
    {
        if (!$request->hasToken()) {
            echo "[AUTH] 未授权,终止链!\n";
            return new Response(403, 'Forbidden'); // 阻断链条
        }
        echo "[AUTH] 用户已验证\n";
        if ($this->next) {
            return $this->next->handle($request);
        }
        return null;
    }
}
// 使用
$request = new Request('/api/data', ['token' => 'abc']);
$chain = new LoggingHandler();
$chain->setNext(new AuthHandler())
      ->setNext(new ValidationHandler());
$response = $chain->handle($request);

特点

  • 每个处理器内部手动调用 $this->next->handle()
  • 可以在任意节点决定返回(中断链条)

中间件(Middleware)

适用场景

  • 请求/响应需要经过一系列预处理和后处理(如 CSRF 保护、Session 启动、CORS 头)
  • 所有中间件都需要执行,顺序重要
  • 需要“双向”处理(进去时做前置,返回时做后置)

洋葱模型(Laravel/PSR-15 风格)

// PSR-15 中间件接口
interface MiddlewareInterface
{
    public function process(
        Request $request,
        RequestHandlerInterface $handler
    ): Response;
}
// 具体中间件
class CorsMiddleware implements MiddlewareInterface
{
    public function process(Request $request, RequestHandlerInterface $handler): Response
    {
        // 前置处理
        $response = $handler->handle($request);  // 调用下一个中间件
        // 后置处理
        $response = $response->withHeader('Access-Control-Allow-Origin', '*');
        return $response;
    }
}
// 简单实现的核心处理器
class CoreHandler implements RequestHandlerInterface
{
    public function handle(Request $request): Response
    {
        // 真正的业务逻辑
        return new Response(200, 'Hello');
    }
}
// 中间件调度器
class MiddlewareDispatcher implements RequestHandlerInterface
{
    private array $middlewareQueue;
    private int $index = 0;
    public function __construct(array $middlewareQueue)
    {
        $this->middlewareQueue = $middlewareQueue;
    }
    public function handle(Request $request): Response
    {
        if ($this->index < count($this->middlewareQueue)) {
            $middleware = $this->middlewareQueue[$this->index];
            $this->index++;
            return $middleware->process($request, $this); // 递归调用
        }
        // 所有中间件处理完毕,执行核心处理
        return new Response(200, 'Final Response');
    }
}

关键区别总结

特性 责任链 中间件
调用方式 手动调用 $next->handle() 回调/闭包 $next($request)
双向性 一般只有前置处理(可做后置但麻烦) 天然支持前后置处理(洋葱圈)
中断机制 任意节点可以返回(不调用 $next 通过异常抛出或直接返回 Response 中断
代码组织 链式结构,顺序由 setNext 决定 管道式结构,顺序由数组/栈决定
框架支持 较少直接提供,需手动实现 Laravel、Slim、Symfony 内置

实际项目中的选择建议

选用责任链模式(手动实现)

  • 当你需要运行时动态组装处理器,且处理器可能选择性跳过
  • 典型的如:验证链(用户输入 → 格式校验 → 业务校验 → 数据库校验)
  • 或者当每个处理器都**可独立成功”,不需要统一的前后处理

选用中间件(推荐框架内置)

  • 现代化 PHP 框架(Laravel、Symfony、Slim)中,始终优先使用中间件
  • 因为框架已经为你实现了:
    • 依赖注入
    • 路由匹配
    • 异常处理包装
    • 调度器(Dispatcher)

混合使用

有时两者可以结合:

Request → AuthMiddleware → LogMiddleware → ChainHandler(责任链) → Controller

性能与维护注意事项

  1. 中间件过多会线性增加请求处理时间,建议控制在 10 个以内
  2. 责任链中不要做同步 IO 操作,每个节点应快速返回
  3. 注意执行顺序
    • 责任链中:handle() 的调用顺序决定
    • 中间件中:通常先注册的后执行(Laravel 中是先注册的先出栈)

项目规模 推荐方式
小项目 / 自建框架 责任链模式(更灵活)
使用 Laravel/Symfony 中间件(框架原生支持)
需要双向处理(前后置) 中间件
需要动态跳过处理器 责任链
两者二选一 选中间件,更符合现代 PHP 生态

抱歉,评论功能暂时关闭!