首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 框架控制器模式演進:從簡單路由到中間件鏈與 Pipeline 設計

     編輯:PHP 相關知識

控制器的本質

Controller(控制器)在 MVC 中承擔「請求協調者」角色:接收請求 → 驗證/預處理 → 調用業務邏輯 → 組裝響應。早期 PHP 框架(CodeIgniter 1.x、ThinkPHP 3.x)採用「URL 直接映射方法」:`/user/list` → `UserController::list()`。隨著業務複雜度提升,權限驗證、日志記錄、限流、參數轉換、異常捕獲等橫切關注點在每個 Action 中重複,導致控制器臃腫、難測試、難復用。現代框架(Laravel、Symfony、Slim、Hyperf)演進出中間件管道、依賴注入、Action 類、資源控制器等模式。

演進四階段

階段 1:硬編碼映射(遠古模式)

<?php
// index.php 入口
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$routes = [
    '/user/list' => ['UserController', 'list'],
    '/user/add'  => ['UserController', 'add'],
];
if (isset($routes[$uri])) {
    [$class, $method] = $routes[$uri];
    (new $class())->$method();
} else {
    http_response_code(404);
    echo 'Not Found';
}

缺陷:路由表膨脹、無參數綁定、無前置/後置鉤子、控制器職責不清。

階段 2:基類控制器 + 前置/後置方法(ThinkPHP 3 / CI 2 風格)

<?php
class Controller {
    protected $request;
    public function __construct() {
        $this->request = Request::instance();
    }
    // 鉤子方法,子類可選重寫
    protected function _initialize() {} // 前置
    protected function _beforeAction($action) { return true; } // 返回 false 阻止
    protected function _afterAction($action) {} // 後置
    protected function _empty() { http_response_code(404); } // 兜底
}

class UserController extends Controller {
    protected function _initialize() {
        // 統一權限檢查
        if (!Session::has('user_id')) $this->redirect('/login');
    }
    protected function _beforeAction($action) {
        if ($action === 'delete' && !$this->request->isPost()) return false;
        return true;
    }
    public function list() { /* ... */ }
    public function add() { /* ... */ }
}

進步:邏輯復用、生命周期鉤子。局限:繼承耦合、無法組合多個橫切關注點(如「權限+限流+日志」需多層繼承或繁瑣調用)。

階段 3:中間件管道 / Pipeline(Laravel / Symfony / PSR-15 風格)

核心思想:請求處理 = 洋蔥模型。每個中間件是一層,包裹下一層,請求向內傳遞,響應向外回溯。

<?php
// PSR-15 標準接口
interface MiddlewareInterface {
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface;
}

// 實現:認證中間件
class AuthMiddleware implements MiddlewareInterface {
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
        if (!$request->getAttribute('user_id')) {
            return new RedirectResponse('/login');
        }
        return $handler->handle($request); // 傳遞給下一層
    }
}

// 實現:日志中間件
class LoggingMiddleware implements MiddlewareInterface {
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
        $start = microtime(true);
        $response = $handler->handle($request);
        $duration = microtime(true) - $start;
        error_log("{$request->getMethod()} {$request->getUri()} -> {$response->getStatusCode()} ({$duration}s)");
        return $response;
    }
}

// Pipeline 構建器(簡化版)
class Pipeline {
    private array $middlewares = [];
    public function pipe(MiddlewareInterface $middleware): self {
        $this->middlewares[] = $middleware;
        return $this;
    }
    public function handle(ServerRequestInterface $request): ResponseInterface {
        $handler = new class implements RequestHandlerInterface {
            public function handle(ServerRequestInterface $request): ResponseInterface {
                // 核心:路由分發 → 執行 Controller Action
                return (new Router())->dispatch($request);
            }
        };
        // 從最後一個中間件向前包裹
        foreach (array_reverse($this->middlewares) as $middleware) {
            $handler = new class($middleware, $handler) implements RequestHandlerInterface {
                public function __construct(private MiddlewareInterface $mw, private RequestHandlerInterface $next) {}
                public function handle(ServerRequestInterface $request): ResponseInterface {
                    return $this->mw->process($request, $this->next);
                }
            };
        }
        return $handler->handle($request);
    }
}

// 使用:組合管道
$app = new Pipeline();
$app->pipe(new LoggingMiddleware())
    ->pipe(new CorsMiddleware())
    ->pipe(new RateLimitMiddleware(100, 60))
    ->pipe(new AuthMiddleware())
    ->pipe(new CsrfMiddleware());

$response = $app->handle(ServerRequest::fromGlobals());
$response->send();

優勢:組合大於繼承、單一職責、可測試、可復用、順序可配置、支持異步(Swoole/Hyperf)。

階段 4:Action 類 / 單一職責控制器(現代最佳實踐)

將每個「用例」拆分為獨立類,控制器僅作為路由入口分發器:

<?php
// 路由定義(屬性路由 / PHP 8 Attribute)
#[Route('/api/users', methods: ['GET'])]
class ListUsersAction {
    public function __construct(
        private UserRepository $repo,
        private UserListPresenter $presenter
    ) {}
    public function __invoke(ServerRequestInterface $request): ResponseInterface {
        $page = (int)($request->getQueryParams()['page'] ?? 1);
        $perPage = (int)($request->getQueryParams()['per_page'] ?? 15);
        $users = $this->repo->paginate($page, $perPage);
        return $this->presenter->present($users);
    }
}

#[Route('/api/users', methods: ['POST'])]
class CreateUserAction {
    public function __construct(
        private UserCreator $creator,
        private UserPresenter $presenter
    ) {}
    public function __invoke(ServerRequestInterface $request): ResponseInterface {
        $data = json_decode($request->getBody()->getContents(), true);
        $user = $this->creator->create($data);
        return $this->presenter->present($user, 201);
    }
}

// 依賴注入容器自動解析構造函數參數
// 路由器根據 HTTP Method + URI 分發到對應 Action 類
// 中間件管道在 Action 之前執行

原文代碼片段解析:閉包鏈模式

原文提供的一種極簡「閉包鏈」控制器骨架:

<?php
function boot(callable $func): void {
    $context = [];
    while ($func = $func($context)) {
        ; // 繼續執行返回的下一個閉包
    }
}

boot(function (&$context) {
    $context[] = 'A'; // 步驟 1
    return function (&$context) {
        $context[] = 'B'; // 步驟 2
        return function (&$context) {
            $context[] = 'C'; // 步驟 3
            return function (&$context) {
                var_dump($context); // 終點:輸出 ['A','B','C']
            };
        };
    };
});

這本質是手動實現的中間件鏈:每個閉包接收上下文、執行邏輯、返回下一個閉包。`boot` 驅動鏈條直到返回 `null`。雖然巧妙,但可讀性差、調試難、異常處理弱、無類型提示,不建議生產使用,僅作為理解「請求管道」概念的教學演示。

現代框架對比表

| 框架 | 控制器風格 | 中間件機制 | 依賴注入 | 異步支持 | |---|---|---|---|---| | **Laravel** | Resource Controller / Invokable Action | 全局/群組/路由中間件、內核管道 | 強大容器、自動綁定、Facades | Octane (Swoole/RoadRunner) | | **Symfony** | Controller 類 / `__invoke` Action | HttpKernel 事件、中間件堆棧 | 標準容器、自動配置 | Mercure / Messenger | | **Hyperf** | Controller / Action 類 | PSR-15 中間件管道、協程上下文 | 基於 PSR-11、註解掃描 | **原生 Swoole 協程** | | **Slim 4** | Invokable Action / 單文件路由 | PSR-15 中間件管道、雙向管道 | PHP-DI 容器 | 可配合 Swoole | | **ThinkPHP 8** | 控制器類 / 操作方法 | 中間件、事件、行為 | 容器、自動注入 | Swoole 支持 |

設計原則總結

  1. 薄控制器:控制器只做「接收請求 → 調用 Service/Action → 返回響應」,業務邏輯下沉到 Service/Domain 層。
  2. 組合大於繼承:用中間件管道組合橫切關注點,避免深層繼承樹。
  3. 依賴注入:構造函數聲明依賴,容器自動裝配,便於單元測試(Mock 依賴)。
  4. 單一職責 Action:一個用例一個類,符合 SRP,易於重構、遷移、微服務化拆分。
  5. 標準化接口:遵循 PSR-7 (Request/Response)、PSR-15 (Middleware)、PSR-11 (Container),框架無關、可移植。

總結

從「URL 映射方法」到「中間件管道 + Action 類」,PHP 控制器模式經歷了去耦合、職責分離、標準化、異步化四次質變。理解這條演進線,能幫助你在維護老項目、選型新框架、設計自有架構時做出清醒決策:**控制器只是 HTTP 邊界的薄適配層,真正的業務價值在 Domain 層**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved