文档目录
路由解析器

简介

本文件为 DouPHP 框架的路由解析器提供全面文档,覆盖前台、后台、API 三个应用的路由实现差异与共性;解释路由匹配算法、URL 模式解析、参数提取机制;说明路由优先级处理、动态路由生成、路由缓存策略;阐述模块化路由设计(分组、命名空间映射、控制器绑定);并给出性能优化技巧、调试方法与故障排除指南,重点说明路由系统与模块系统的集成方式。

项目结构

  • 前端应用:入口 Router 薄壳调用 FrontResolver,基于声明式规则进行外观 URL 匹配,产出分发计划后交由中央 Dispatcher 执行。
  • 后台应用:入口 Router 薄壳调用 AdminResolver,基于后端声明式匹配器按 admin/route/*.php 的 declared 条目匹配,产出分发计划后进入中间件管道执行。
  • API 应用:入口 Router 薄壳调用 ApiResolver,同样使用后端声明式匹配器按 api/route/*.php 的 declared 条目匹配,未命中返回 JSON 404/405。
  • 公共基础设施:Dispatcher 负责在中间件管道中懒实例化控制器并调用方法;DispatchPlan 作为不可变值对象承载最终决策结果;BackendDeclaredMatcher 为后台/API 端通用匹配器;PrettyRouteMatcher 为前台外观 URL 匹配器;config/route.php 提供前台风格化规则配置。
graph TB
subgraph "前台"
FR["FrontResolver"]
PRM["PrettyRouteMatcher"]
end
subgraph "后台"
AR["AdminResolver"]
BDM["BackendDeclaredMatcher"]
end
subgraph "API"
AAR["ApiResolver"]
BDM2["BackendDeclaredMatcher"]
end
D["Dispatcher"]
DP["DispatchPlan"]
FR --> PRM
AR --> BDM
AAR --> BDM2
FR --> D
AR --> D
AAR --> D
D --> DP

核心组件

  • 前端路由薄壳 Router:读取 Request 中的路由字符串,委托 FrontResolver 解析,未命中渲染 page_wrong,命中则交给 Dispatcher 执行。
  • 后台路由薄壳 Router:委托 AdminResolver 解析,区分 405 与 404,分别重定向到后台首页或携带错误提示。
  • API 路由薄壳 Router:委托 ApiResolver 解析,统一以 JSON 形式返回 NOT_FOUND 或 Method Not Allowed。
  • 前端解析器 FrontResolver:通过 PrettyRouteMatcher 将外观 URL 标准化为模块/动作/子段/控制器/参数,组装中间件链,设置 Request 路由信息,产出 DispatchPlan。
  • 后台解析器 AdminResolver:通过 BackendDeclaredMatcher 按 admin/route/*.php 的 declared 条目匹配,产出 DispatchPlan。
  • API 解析器 ApiResolver:通过 BackendDeclaredMatcher 按 api/route/*.php 的 declared 条目匹配,产出 DispatchPlan。
  • 中央分发器 Dispatcher:在中间件管道中懒实例化控制器并调用方法,将路由参数注入 Request。
  • 分发计划 DispatchPlan:不可变值对象,承载 fqcn/method/params/middlewares 以及 404/405 状态。
  • 匹配器:
    • PrettyRouteMatcher:前台外观 URL 匹配,支持短地址策略、方法感知消歧、具体性排序。
    • BackendDeclaredMatcher:后台/API 端声明式匹配,支持 specificity 排序与方法白名单过滤。

架构总览

三个应用的入口 Router 均遵循“薄壳”模式:仅做请求上下文获取、委托 Resolver 解析、根据 DispatchPlan 的状态决定响应或继续执行。Resolver 负责将 URL 与声明式规则匹配,产出包含控制器 FQCN、方法名、路径参数与中间件链的分发计划。Dispatcher 在中间件管道中执行控制器方法,并将路由参数注入 Request。

sequenceDiagram
participant Client as "客户端"
participant AppRouter as "应用Router"
participant Resolver as "Resolver"
participant Matcher as "匹配器"
participant Plan as "DispatchPlan"
participant Pipe as "中间件管道"
participant Controller as "控制器"
Client->>AppRouter : 发起请求
AppRouter->>Resolver : resolve(request, container)
Resolver->>Matcher : normalize/match(route, method)
Matcher-->>Resolver : 命中结果(模块/动作/参数/控制器)
Resolver-->>AppRouter : DispatchPlan
alt 未匹配
AppRouter-->>Client : 端专属404/重定向
else 方法不允许
AppRouter-->>Client : 405 + Allow头
else 匹配成功
AppRouter->>Pipe : run(plan, container)
Pipe->>Controller : 实例化并调用方法
Controller-->>Pipe : 返回值/Response
Pipe-->>AppRouter : 执行结果
AppRouter-->>Client : 响应
end

详细组件分析

前台路由系统

  • 入口 Router:从容器获取 Request,调用 FrontResolver::resolve,若未命中返回 message()->respond('page_wrong', HOME_URL),命中则交由 Dispatcher 执行。
  • FrontResolver:
    • 通过 PrettyRouteMatcher::normalize 将已去语言前缀的路由字符串标准化为模块/动作/子段/控制器/参数。
    • 对空串短路返回 IndexController。
    • 装配表单目标(create/edit 时自动设置 form_action/form_method)。
    • 加载主题扩展(ThemeExtensionLoader)。
    • 组装中间件链(默认栈含安全头、可信代理、限流、可选用户认证、CSRF),并通过 MiddlewareRegistry 组合。
    • 设置 Request 基础 URL、路由段、路由参数,合并输入,分配视图变量 cur。
    • 产出 DispatchPlan 并交由 Dispatcher 执行。
  • PrettyRouteMatcher:
    • 仅消费 RouteManifest 中前台命名空间的 declared 条目。
    • 支持短地址策略:启用后禁止带模块前缀的长格式;未命中时尝试补回模块前缀重试。
    • 匹配算法:收集全部命中规则 → specificity 排序(占位符少优先、字面段多优先、声明顺序兜底)→ 方法感知(HEAD 按 GET 处理)→ 首个方法命中即返回。
    • 构建结果:显式字段优先(module/target/action/sub/controller/name),具名捕获组作为 params,附带 mw_* 路由级中间件细化。
flowchart TD
Start(["前台请求"]) --> GetRoute["读取路由字符串"]
GetRoute --> Normalize["PrettyRouteMatcher::normalize"]
Normalize --> Matched{"是否命中?"}
Matched -- 否 --> NotFound["返回 notFound()"]
Matched -- 是 --> BuildPlan["组装中间件链<br/>设置Request路由信息<br/>产出DispatchPlan"]
BuildPlan --> Dispatch["Dispatcher::run"]
Dispatch --> End(["响应"])
NotFound --> End

后台路由系统

  • 入口 Router:委托 AdminResolver 解析,区分 405 与 404,分别重定向到后台首页并携带错误提示。
  • AdminResolver:
    • 通过 BackendDeclaredMatcher::match 按 admin/route/*.php 的 declared 条目匹配。
    • 校验会员模块可用性(Module::assertUserAvailable)。
    • 设置 Request 基础 URL、路由段、路由参数,合并输入,分配视图变量 cur。
    • 组装中间件链(默认栈含安全头、可信代理、认证、权限、CSRF、工作区),通过 MiddlewareRegistry::compose 叠加路由级细化。
    • 产出 DispatchPlan。
  • BackendDeclaredMatcher(后台/API 共用):
    • 遍历 RouteManifest 中对应端命名空间的 declared 条目,编译 PCRE 正则匹配。
    • specificity 排序与方法白名单过滤,返回命中或 405(含允许方法集合)。
    • 空路径回落 index。
sequenceDiagram
participant AdminRouter as "后台Router"
participant AdminResolver as "AdminResolver"
participant Matcher as "BackendDeclaredMatcher"
participant Plan as "DispatchPlan"
participant Pipe as "中间件管道"
participant Controller as "控制器"
AdminRouter->>AdminResolver : resolve(request, container)
AdminResolver->>Matcher : match(routeRaw, 'Admin', method)
Matcher-->>AdminResolver : hit/status=method_not_allowed/null
AdminResolver-->>AdminRouter : DispatchPlan
alt 405
AdminRouter-->>AdminRouter : 重定向首页+Allow头
else 404
AdminRouter-->>AdminRouter : 重定向首页+错误提示
else 命中
AdminRouter->>Pipe : run(plan, container)
Pipe->>Controller : 实例化并调用方法
Controller-->>Pipe : 返回值/Response
Pipe-->>AdminRouter : 执行结果
AdminRouter-->>AdminRouter : 返回响应
end

API 路由系统

  • 入口 Router:委托 ApiResolver 解析,未命中返回 ApiResponse::error 404,方法不允许返回 405 并携带 allow 列表。
  • ApiResolver:
    • 通过 BackendDeclaredMatcher::match 按 api/route/*.php 的 declared 条目匹配。
    • 校验会员模块可用性。
    • 设置 Request 基础 URL、路由段、路由参数,合并输入。
    • 组装中间件链(默认栈含安全头、可信代理、限流、可选用户认证),通过 MiddlewareRegistry::compose 叠加路由级细化。
    • 产出 DispatchPlan。
sequenceDiagram
participant ApiRouter as "API Router"
participant ApiResolver as "ApiResolver"
participant Matcher as "BackendDeclaredMatcher"
participant Plan as "DispatchPlan"
participant Pipe as "中间件管道"
participant Controller as "控制器"
ApiRouter->>ApiResolver : resolve(request, container)
ApiResolver->>Matcher : match(routeRaw, 'Api', method)
Matcher-->>ApiResolver : hit/status=method_not_allowed/null
ApiResolver-->>ApiRouter : DispatchPlan
alt 405
ApiRouter-->>ApiRouter : 返回405 JSON
else 404
ApiRouter-->>ApiRouter : 返回404 JSON
else 命中
ApiRouter->>Pipe : run(plan, container)
Pipe->>Controller : 实例化并调用方法
Controller-->>Pipe : 返回值/Response
Pipe-->>ApiRouter : 执行结果
ApiRouter-->>ApiRouter : 返回JSON响应
end

路由匹配算法与优先级

  • 前台(PrettyRouteMatcher):
    • 收集全部命中规则 → specificity 排序(占位符数量少优先、字面段数量多优先、声明顺序兜底)→ 方法感知(HEAD 按 GET 处理)→ 首个方法命中即返回。
    • 短地址策略:启用后禁止带模块前缀的长格式;未命中时尝试补回模块前缀重试。
  • 后台/API(BackendDeclaredMatcher):
    • 收集全部命中条目 → specificity 排序 → 方法白名单过滤 → 首个方法命中即返回;全不命中返回 405(含允许方法集合)。
    • 空路径回落 index。
flowchart TD
S["开始匹配"] --> Collect["收集所有命中规则/条目"]
Collect --> Sort["specificity排序<br/>占位符少→字面段多→声明顺序"]
Sort --> MethodCheck{"是否传入HTTP方法?"}
MethodCheck -- 否 --> First["返回排序首条"]
MethodCheck -- 是 --> Iterate["按序检查方法白名单"]
Iterate --> Hit{"找到方法命中?"}
Hit -- 是 --> ReturnHit["返回命中"]
Hit -- 否 --> Return405["返回405(含allow列表)"]
First --> End["结束"]
ReturnHit --> End
Return405 --> End

URL 模式解析与参数提取

  • 前台风格化规则:config/route.php 定义 PAGE/COLUMN/SIMPLE 等风格,pattern 支持 {param}、{param:regex}、[/optional] 语法;target 可指定目标文件名模板;short_rules 用于短地址模块。
  • 前台匹配:PrettyUrlCompiler 将 pattern 编译为 PCRE 正则;具名捕获组(除 module/action/sub_action)作为路由参数带出。
  • 后台/API 匹配:BackendDeclaredMatcher 对 declared 条目编译正则,抽取具名捕获组作为 params。

路由优先级处理

  • specificity 排序确保更具体的规则优先匹配(如 article/featured 胜过 article/{id})。
  • 方法白名单进一步消歧(如 update PUT vs destroy DELETE 共用 prefix/{id})。
  • 声明顺序兜底保证确定性。

动态路由生成

  • 前台:StyleRuleExpander 将 column/simple/page 等声明按当前选中风格展开为具体 pattern;UrlBuilder 与 PrettyUrlCompiler 共用同一引擎,保证双向可逆。
  • 后台/API:Route::name/group/prefix/post/get 等 DSL 生成 declared 条目,经 RouteManifest 汇总供匹配器消费。

路由缓存策略

  • 匹配器在构造期加载并预编译规则(前台 PrettyRouteMatcher::__construct 加载 routePatterns;后台/API 匹配器在匹配时按需编译)。
  • 建议在生产环境启用 OPcache 与 PHP-FPM 常驻进程以减少重复编译开销。
  • 前台短地址策略与风格化规则在启动时确定,避免运行时频繁切换导致缓存失效。

模块化路由设计

  • 分组:Route::group 用于组织相关路由,支持 name 前缀与 prefix 路径前缀。
  • 命名空间映射:declared 条目携带 endNamespace(Front/Admin/Api),匹配器按端隔离消费。
  • 控制器绑定:declared 条目明确 controller FQCN,Resolver 直接委派给 Dispatcher 实例化并调用。

路由系统与模块系统集成

  • 模块可用性闸:各 Resolver 在解析后调用 Module::assertUserAvailable,当 features.user 关闭时阻止访问 user 衍生模块,避免容器反射到未安装模块类。
  • 主题扩展:FrontResolver 在控制器执行前加载主题包 inc/..from_theme.php,基于已确定的 routeModule/routeAction。
  • 视图变量:Resolver 设置 View::assign('cur', module),便于模板层感知当前模块。

依赖关系分析

  • 应用 Router 依赖各自 Resolver。
  • Resolver 依赖匹配器(前台 PrettyRouteMatcher,后台/API BackendDeclaredMatcher)。
  • 匹配器依赖 RouteManifest 与 PrettyUrlCompiler。
  • Resolver 依赖 MiddlewareRegistry 组装中间件链。
  • Dispatcher 依赖 MiddlewarePipeline 与 Container。
  • DispatchPlan 作为跨组件传递的不可变值对象。
graph LR
R_F["Front Router"] --> FR["FrontResolver"]
R_A["Admin Router"] --> AR["AdminResolver"]
R_API["Api Router"] --> AAR["ApiResolver"]
FR --> PRM["PrettyRouteMatcher"]
AR --> BDM["BackendDeclaredMatcher"]
AAR --> BDM
PRM --> RM["RouteManifest"]
BDM --> RM
FR --> MW["MiddlewareRegistry"]
AR --> MW
AAR --> MW
FR --> D["Dispatcher"]
AR --> D
AAR --> D
D --> DP["DispatchPlan"]

性能考虑

  • 匹配器预编译:前台在构造期加载并编译规则;后台/API 在匹配时按需编译,建议开启 OPcache。
  • specificity 排序减少回溯:通过占位符与字面段计数优化匹配效率。
  • 短地址策略:启用后避免长格式冗余匹配,降低正则复杂度。
  • 中间件链最小化:通过 mw_without/mw_append/mw_params 精细控制,避免不必要的中间件执行。
  • 路由缓存:生产环境启用 PHP-FPM 常驻进程与 OPcache,减少启动与编译开销。

故障排除指南

  • 前台未命中:FrontResolver 记录警告日志(channel=route),输出 page_wrong;检查 PrettyRouteMatcher 的 declared 条目与短地址策略。
  • 后台/API 未命中:返回 404 JSON 或重定向首页;检查 BackendDeclaredMatcher 的 declared 条目与方法白名单。
  • 方法不允许:405 响应携带 allow 列表;确认路由声明的 methods 与实际请求方法一致。
  • 模块不可用:features.user 关闭时访问 user 模块会抛出 DomainException;检查配置与模块安装状态。
  • 中间件问题:通过 mw_skip_all/mw_without 调整默认栈;确认别名映射正确。

结论

DouPHP 的路由系统采用“薄壳 Router + 声明式 Resolver + 共享匹配器 + 中央 Dispatcher”的架构,前台侧重外观 URL 与风格化规则,后台/API 侧重声明式资源路由与严格的方法白名单。通过 specificity 排序、短地址策略、中间件分层与模块可用性闸,实现了高内聚、低耦合、可扩展的路由体系。配合 OPcache 与路由预编译,可在生产环境获得稳定高效的性能表现。

附录

  • 前台风格化规则示例:config/route.php 定义了 PAGE/COLUMN/SIMPLE 等风格的 pattern、params、target 与 short_rules。
  • 后台/API 路由声明示例:admin/route/index.php 与 api/route/index.php 展示了 Route::name/group/prefix/post/get 的使用。
添加日期:2026-10-05