文档目录
路由解析器

简介

本技术文档聚焦 DouPHP 的三端路由解析体系,系统阐述 URL 匹配算法、参数提取、路由优先级判断与委托调度机制。重点覆盖:

  • 前台 FrontResolver、后台 AdminResolver、API ApiResolver 的职责与差异
  • DelegatingRouter 如何统一协调三端路由请求
  • 从 URL 到控制器的完整调用链
  • 性能优化策略与调试技巧
  • 面向初学者的概念说明与面向高级开发者的自定义解析器扩展指南

项目结构

DouPHP 将路由能力按“端”拆分,每端拥有独立的 Router(薄壳)与 Resolver(解析器),并通过统一的中间件注册表与分发器协作。核心路径如下:

  • 前台:front/foundation/routing/*
  • 后台:admin/foundation/routing/*
  • API:api/foundation/routing/*
  • 公共路由基础设施:core/web/routing/*
graph TB
subgraph "前台"
FR["FrontResolver"]
FRouter["Front\\Foundation\\Routing\\Router"]
end
subgraph "后台"
AR["AdminResolver"]
ARouter["Admin\\Foundation\\Routing\\Router"]
end
subgraph "API"
R["ApiResolver"]
RRouter["Api\\Foundation\\Routing\\Router"]
end
subgraph "公共"
DR["DelegatingRouter"]
DP["DispatchPlan"]
PPM["PrettyRouteMatcher"]
BDM["BackendDeclaredMatcher"]
DIS["Dispatcher"]
end
FRouter --> FR
ARouter --> AR
RRouter --> R
FR --> PPM
AR --> BDM
R --> BDM
FR --> DP
AR --> DP
R --> DP
DR --> FRouter
DR --> ARouter
DR --> RRouter
DP --> DIS

图示来源

  • front/foundation/routing/Router.php:33-56
  • admin/foundation/routing/Router.php:34-64
  • api/foundation/routing/Router.php:36-68
  • core/web/routing/DelegatingRouter.php:39-67
  • core/web/routing/DispatchPlan.php:33-112
  • front/foundation/routing/PrettyRouteMatcher.php:44-114
  • core/web/routing/BackendDeclaredMatcher.php:38-62

章节来源

  • front/foundation/routing/Router.php:33-56
  • admin/foundation/routing/Router.php:34-64
  • api/foundation/routing/Router.php:36-68
  • core/web/routing/DelegatingRouter.php:39-67

核心组件

  • 三端 Router(薄壳):负责从容器获取 Request,调用对应 Resolver 生成 DispatchPlan,再交由 Dispatcher 执行;未匹配时返回端专属 404 或重定向。
  • 三端 Resolver:实现具体解析逻辑,产出 DispatchPlan,并设置 Request 的路由信息、参数与视图上下文。
  • 匹配器:
    • PrettyRouteMatcher:前台外观 URL 匹配,基于 RouteManifest 的 declared 条目,支持短地址策略与方法消歧。
    • BackendDeclaredMatcher:后台与 API 共用,对 declared 条目进行 specificity 排序与方法过滤。
  • 分发计划 DispatchPlan:不可变值对象,承载控制器 FQCN、方法、参数、中间件列表以及 404/405 状态。
  • 委托调度 DelegatingRouter:统一入口包装,透传 dispatch(),并提供 current() 读取当前路由状态。

章节来源

  • front/foundation/routing/FrontResolver.php:52-106
  • admin/foundation/routing/AdminResolver.php:72-104
  • api/foundation/routing/ApiResolver.php:60-91
  • core/web/routing/DispatchPlan.php:33-112
  • core/web/routing/DelegatingRouter.php:39-109

架构总览

三端路由采用“薄壳 Router + 专用 Resolver + 共享匹配器 + 统一分发”的分层设计。请求进入后:

  1. 端 Router 从容器取出 Request
  2. 调用 Resolver 解析为 DispatchPlan
  3. 根据 DispatchPlan 的状态(404/405/正常)决定响应或继续分发
  4. 通过中央 Dispatcher 在中间件管道中执行控制器
sequenceDiagram
participant C as "客户端"
participant DR as "DelegatingRouter"
participant R as "端 Router"
participant Res as "Resolver"
participant M as "匹配器"
participant DP as "DispatchPlan"
participant D as "Dispatcher"
C->>DR : 发起请求
DR->>R : dispatch()
R->>Res : resolve(request, container)
Res->>M : normalize/match(route, method)
M-->>Res : 命中结果/未命中
Res-->>R : DispatchPlan
alt 404
R-->>C : 端专属 404/重定向
else 405
R-->>C : 405 JSON/Allow 头
else 正常
R->>D : run(plan, container)
D-->>C : Response/null
end

图示来源

  • core/web/routing/DelegatingRouter.php:61-67
  • front/foundation/routing/Router.php:40-56
  • admin/foundation/routing/Router.php:41-64
  • api/foundation/routing/Router.php:43-68
  • front/foundation/routing/FrontResolver.php:52-106
  • admin/foundation/routing/AdminResolver.php:72-104
  • api/foundation/routing/ApiResolver.php:60-91

详细组件分析

前台路由解析器 FrontResolver

职责与流程:

  • 读取已剥离语言前缀的 routeString
  • 使用 PrettyRouteMatcher 规范化匹配,处理首页空串短路
  • 校验控制器类存在性,必要时记录警告日志
  • 组装中间件栈(默认安全栈 + 路由级细化)
  • 设置 Request 的 baseUrl、route/module/action/sub、路由参数与表单目标
  • 产出 DispatchPlan 并交由 Dispatcher 执行

URL 匹配与优先级:

  • 仅依据 RouteManifest 的前台 declared 条目
  • 短地址策略启用时禁止长格式模块前缀,未命中则补回模块前缀重试
  • specificity 排序:占位符少者优先 → 字面段多者优先 → 声明顺序兜底
  • 方法感知:HEAD 按 GET 处理;无方法白名单视为 permissive
flowchart TD
Start(["入口: FrontResolver::resolve"]) --> Read["读取 routeString 与 HTTP 方法"]
Read --> Normalize["PrettyRouteMatcher::normalize"]
Normalize --> Matched{"是否匹配?"}
Matched -- 否 --> LogWarn["记录未匹配日志"] --> NotFound["返回 notFound 计划"]
Matched -- 是 --> Validate["校验控制器类存在"]
Validate --> |不存在| NotFound
Validate --> |存在| MW["组装中间件栈"]
MW --> SetReq["设置 Request 路由信息与参数"]
SetReq --> Plan["构建 DispatchPlan"]
Plan --> End(["返回计划给 Router"])

图示来源

  • front/foundation/routing/FrontResolver.php:52-106
  • front/foundation/routing/PrettyRouteMatcher.php:61-114

章节来源

  • front/foundation/routing/FrontResolver.php:52-106
  • front/foundation/routing/PrettyRouteMatcher.php:61-114

后台路由解析器 AdminResolver

职责与流程:

  • 以 ?route=module[/id[/action]] 形态进入
  • 使用 BackendDeclaredMatcher 按端命名空间 'Admin' 匹配 declared 条目
  • 若命中但方法不被接受,返回 methodNotAllowed 计划
  • 设置 Request 路由信息与参数,装配表单目标(create/edit)
  • 组装中间件栈(安全头、可信代理、认证、权限、CSRF、工作区)
  • 产出 DispatchPlan
sequenceDiagram
participant ARouter as "Admin Router"
participant AR as "AdminResolver"
participant BDM as "BackendDeclaredMatcher"
participant DP as "DispatchPlan"
ARouter->>AR : resolve(request, container)
AR->>BDM : match(routeRaw, 'Admin', method)
alt 未命中
BDM-->>AR : null
AR-->>ARouter : notFound
else 方法不允许
BDM-->>AR : {status : 'method_not_allowed', allow}
AR-->>ARouter : methodNotAllowed
else 命中
BDM-->>AR : {fqcn, action, module, sub, params, entry}
AR->>AR : 设置 Request 路由与参数
AR-->>ARouter : DispatchPlan
end

图示来源

  • admin/foundation/routing/AdminResolver.php:72-104
  • core/web/routing/BackendDeclaredMatcher.php:49-62

章节来源

  • admin/foundation/routing/AdminResolver.php:72-104
  • core/web/routing/BackendDeclaredMatcher.php:49-62

API 路由解析器 ApiResolver

职责与流程:

  • 与后台相同使用 BackendDeclaredMatcher,但端命名空间为 'Api'
  • 未命中返回 notFound(JSON 404);方法不允许返回 methodNotAllowed(JSON 405 + Allow)
  • 设置 Request 路由信息与参数,组装中间件栈(安全头、可信代理、限流、可选用户认证)
  • 产出 DispatchPlan
classDiagram
class ApiResolver {
+resolve(request, container) DispatchPlan
-defaultAliases() string[]
-aliasMap : array
}
class BackendDeclaredMatcher {
+match(routeRaw, end, httpMethod) array|null
}
class DispatchPlan {
+fqcn
+method
+params
+middlewares
+isNotFound() bool
+isMethodNotAllowed() bool
}
ApiResolver --> BackendDeclaredMatcher : "匹配 declared 条目"
ApiResolver --> DispatchPlan : "产出计划"

图示来源

  • api/foundation/routing/ApiResolver.php:60-91
  • core/web/routing/BackendDeclaredMatcher.php:49-62
  • core/web/routing/DispatchPlan.php:33-112

章节来源

  • api/foundation/routing/ApiResolver.php:60-91
  • core/web/routing/BackendDeclaredMatcher.php:49-62

委托调度器 DelegatingRouter

职责:

  • 作为三端 Router 的统一薄包装,不实现解析与分发
  • setDelegate 注入具体端 Router,dispatch() 透传调用
  • current() 从容器中读取 Request 的全量路由状态(module/action/sub/route/lang/is_home)
sequenceDiagram
participant Entry as "入口"
participant DR as "DelegatingRouter"
participant FR as "Front Router"
participant AR as "Admin Router"
participant Rr as "Api Router"
Entry->>DR : setDelegate(任一 Router)
Entry->>DR : dispatch()
alt 前台
DR->>FR : dispatch()
FR-->>DR : Response|null
else 后台
DR->>AR : dispatch()
AR-->>DR : Response|null
else API
DR->>Rr : dispatch()
Rr-->>DR : Response|null
end
DR-->>Entry : Response|null

图示来源

  • core/web/routing/DelegatingRouter.php:39-67
  • core/web/routing/DelegatingRouter.php:79-109

章节来源

  • core/web/routing/DelegatingRouter.php:39-109

分发计划 DispatchPlan

作用:

  • 不可变值对象,承载最终控制器 FQCN、方法、参数、中间件列表
  • 提供 isNotFound()/isMethodNotAllowed() 判定,供各端 Router 渲染端专属错误响应
classDiagram
class DispatchPlan {
+string|null fqcn
+string|null method
+array params
+array middlewares
+bool methodNotAllowed
+string[] allowedMethods
+__construct(fqcn, method, params, middlewares)
+static notFound() DispatchPlan
+static methodNotAllowed(allow) DispatchPlan
+isNotFound() bool
+isMethodNotAllowed() bool
}

图示来源

  • core/web/routing/DispatchPlan.php:33-112

章节来源

  • core/web/routing/DispatchPlan.php:33-112

匹配器与优先级算法

  • PrettyRouteMatcher(前台):
    • 首页空串短路至 IndexController
    • 短地址策略:禁用带模块前缀的长格式;未命中时补回模块前缀重试
    • specificity 排序:占位符少优先 → 字面段多优先 → 声明顺序兜底
    • 方法感知:HEAD 按 GET;无 methods 视为 permissive
  • BackendDeclaredMatcher(后台/API):
    • 按端命名空间过滤 declared 条目
    • specificity 排序与方法过滤;URL 命中但方法全不命中返回 method_not_allowed
    • 空 routeRaw 时 fallback 到 index
flowchart TD
A["收集全部命中规则"] --> B["specificity 排序"]
B --> C{"是否传入 HTTP 方法?"}
C -- 否 --> D["返回首条候选"]
C -- 是 --> E{"首个方法命中?"}
E -- 是 --> F["返回命中"]
E -- 否 --> G{"是否还有候选?"}
G -- 是 --> E
G -- 否 --> H["返回 method_not_allowed"]

图示来源

  • front/foundation/routing/PrettyRouteMatcher.php:117-169
  • core/web/routing/BackendDeclaredMatcher.php:72-126

章节来源

  • front/foundation/routing/PrettyRouteMatcher.php:117-169
  • core/web/routing/BackendDeclaredMatcher.php:72-126

依赖关系分析

  • 三端 Router 依赖各自 Resolver
  • Resolver 依赖匹配器(前台 PrettyRouteMatcher,后台/API BackendDeclaredMatcher)
  • 所有 Resolver 产出 DispatchPlan,并由 Dispatcher 执行
  • DelegatingRouter 统一代理三端 Router 的 dispatch()
  • 中间件通过 MiddlewareRegistry 组合,受路由条目配置影响
graph LR
RFront["Front Router"] --> FR["FrontResolver"]
RAdmin["Admin Router"] --> AR["AdminResolver"]
RApi["Api Router"] --> ARi["ApiResolver"]
FR --> PPM["PrettyRouteMatcher"]
AR --> BDM["BackendDeclaredMatcher"]
ARi --> BDM
FR --> DP["DispatchPlan"]
AR --> DP
ARi --> DP
DP --> DIS["Dispatcher"]
DR["DelegatingRouter"] --> RFront
DR --> RAdmin
DR --> RApi

图示来源

  • front/foundation/routing/Router.php:40-56
  • admin/foundation/routing/Router.php:41-64
  • api/foundation/routing/Router.php:43-68
  • core/web/routing/DelegatingRouter.php:61-67

章节来源

  • front/foundation/routing/Router.php:40-56
  • admin/foundation/routing/Router.php:41-64
  • api/foundation/routing/Router.php:43-68
  • core/web/routing/DelegatingRouter.php:61-67

性能考量

  • 预编译正则:匹配器在加载阶段将 pattern 编译为 PCRE,避免重复编译开销
  • specificity 排序:减少回溯与误匹配,提升命中率与稳定性
  • 短地址策略:减少 URL 长度与解析分支,同时保证可逆
  • 方法感知:尽早过滤不合法方法,降低后续处理成本
  • 中间件按需组合:路由条目可跳过或追加中间件,避免不必要的检查
  • 日志定位:未匹配与控制器缺失记录警告日志,便于快速定位问题

故障排查指南

  • 前台未匹配:
    • 检查 routeString 是否已剥离语言前缀
    • 确认 PrettyRouteMatcher 的 declared 条目是否存在且 pattern 正确
    • 查看日志中的“Front route unmatched”与“Front route dispatch failed”
  • 后台/API 未匹配或 405:
    • 确认 BackendDeclaredMatcher 的端命名空间与 declared 条目
    • 检查 HTTP 方法是否在白名单内;若 URL 命中但方法不匹配,会返回 method_not_allowed
  • 控制器缺失:
    • FrontResolver 会在控制器类不存在时记录警告并返回 notFound
  • 中间件问题:
    • 通过路由条目的 mw_without/mw_append/mw_params 调整中间件行为
    • 确保别名映射正确,缺失类会被 Registry 吞掉跳过

章节来源

  • front/foundation/routing/FrontResolver.php:58-86
  • admin/foundation/routing/AdminResolver.php:76-82
  • api/foundation/routing/ApiResolver.php:64-70

结论

DouPHP 的路由体系以“端隔离、声明式、可组合”为核心思想:

  • 三端 Router 保持薄壳,解析逻辑集中在 Resolver
  • 匹配器统一遵循 specificity 与方法感知,保证一致性与可预测性
  • DispatchPlan 作为不可变契约,简化错误处理与分发流程
  • DelegatingRouter 提供统一入口与当前路由状态读取,便于全局初始化与诊断 该设计既满足初学者理解路由基本概念,也为高级开发者提供了清晰的扩展点(如自定义匹配器、中间件与路由条目)。

附录

  • 自定义解析器开发指南(高级)
    • 实现端 Resolver:遵循 resolve(request, container) 签名,产出 DispatchPlan
    • 复用匹配器:前台使用 PrettyRouteMatcher,后台/API 使用 BackendDeclaredMatcher
    • 中间件组合:通过 MiddlewareRegistry 与别名映射,结合路由条目配置
    • 错误处理:返回 DispatchPlan::notFound() 或 methodNotAllowed(),由各端 Router 渲染端专属响应
    • 测试建议:覆盖未匹配、方法不允许、控制器缺失等边界场景
添加日期:2026-10-05