文档目录
路由管理

简介

本技术文档面向 DouPHP 的路由管理系统,围绕以下目标展开:

  • 深入解释 RouteCollector 的路由收集机制(自动发现、手动注册、动态加载)
  • 详细说明 RouteManifest 的路由清单管理(元数据、依赖关系、版本控制思路)
  • 记录 RouteTableExporter 的路由表导出能力(性能分析、调试输出、部署优化)
  • 解释 ModuleRegistry 的模块路由注册机制(模块化组织与管理)
  • 深入分析 BackendDeclaredMatcher 的后端声明匹配器(匹配算法与性能优化)
  • 提供最佳实践(命名规范、组织结构、维护策略)与调试排错方法
  • 面向初学者讲解基础概念,为高级开发者提供大规模应用的路由架构设计指南

项目结构

DouPHP 的路由系统位于 core/web/routing 下,采用“声明式 + 配置式”混合模式:

  • 声明式:三端(front/admin/api)route/*.php 通过 fluent API 或返回数组注册路由
  • 配置式:config/route.php 定义 page/column/simple 风格规则
  • 构建期:RouteManifestBuilder 统一扫描并生成 RouteEntry[] 清单
  • 运行期:各端 Matcher 基于清单进行 URL 匹配与分发
graph TB
A["前端/后台/API 入口"] --> B["RouteManifestBuilder<br/>扫描三端 route 文件夹"]
B --> C["RouteManifest<br/>进程级缓存条目"]
C --> D["BackendDeclaredMatcher / PrettyRouteMatcher<br/>URL 匹配"]
C --> E["RouteTableExporter<br/>导出命名路由表"]
B --> F["RouteCollector<br/>fluent API 累积器"]
B --> G["RouteRules<br/>读取 config/route.php"]
G --> C

核心组件

  • RouteCollector:在 include 路由文件期间累积 fluent API 生成的 RouteEntry,支持组属性栈与 source 追踪
  • RouteManifestBuilder:聚合 home、系统内置 declared、风格规则、三端 declared 条目,产出完整清单
  • RouteManifest:不可变可枚举的数据源,提供按类型过滤、具名反查、分组视图与进程级缓存
  • RouteTableExporter:按端导出 name→pattern 映射,供小程序等外部消费
  • ModuleRegistry:模块分类单一来源,提供固定前台模块、保留首段、栏目/单表判定等
  • BackendDeclaredMatcher:后端(Admin/Api)declared 条目的匹配器,实现 specificity 排序与 405 处理
  • RouteResourceBuilder / RouteEntryBuilder:fluent API 的具体实现,负责资源与动作的展开与入队

架构总览

整体流程分为“构建期”和“运行期”两个阶段:

  • 构建期:RouteManifestBuilder 扫描三端 route 目录,执行每个路由文件的 include,将 fluent API 累积的条目与 return 数组合并;同时读取 config/route.php 的风格规则,生成 meta 模板条目;最终产出 RouteEntry[] 清单并缓存
  • 运行期:Matcher 从清单中筛选 declared 条目,编译为正则进行匹配;若命中则返回控制器与方法;未命中时根据 HTTP 方法返回 405;空路径回落到 index
sequenceDiagram
participant R as "请求"
participant M as "BackendDeclaredMatcher"
participant L as "RouteManifest"
participant P as "PrettyUrlCompiler"
participant C as "控制器"
R->>M : match(routeRaw, end, httpMethod)
M->>L : getEntriesByType('declared')
loop 遍历候选
M->>P : compileToRegex(pattern, params)
P-->>M : regex
M->>M : 收集命中候选
end
M->>M : specificity 排序
alt 有命中且方法允许
M-->>R : {status : 'hit', fqcn, action, params}
R->>C : 调用控制器方法
else 方法不允许
M-->>R : {status : 'method_not_allowed', allow : [...]}
else 无命中
M-->>R : null (404)
end

详细组件分析

RouteCollector:路由收集机制

  • 作用:在每个路由文件 include 期间安装 collector,fluent API 产生的 RouteEntry 被有序推入;include 结束后 flush 取出并入 manifest
  • 关键能力:
    • 组属性栈:pushGroupAttributes/popGroupAttributes 维护 name 前缀与 compositeModule 标志
    • source 追踪:sourceFor(name) 生成 'declared:&lt;rel>:&lt;name>' 用于定位来源
    • 相对路径推导:deriveRelPath 计算相对于 ROOT_PATH 的正斜杠路径,便于诊断
  • 自动发现与动态加载:由 RouteManifestBuilder 扫描三端 route 目录并 include 文件,collector 在 include 期间工作
  • 手动注册:路由文件中通过 Route::get/post/resource/group 等 fluent API 注册
classDiagram
class RouteCollector {
-string file
-string relPath
-RouteEntry[] entries
-array groupStack
+__construct(file)
+pushGroupAttributes(attrs)
+popGroupAttributes()
+currentNamePrefix() string
+currentComposite() bool
+relPath() string
+sourceFor(name) string
+push(entry)
+flush() RouteEntry[]
}

RouteManifest:路由清单管理

  • 作用:承载 home/static/system_reserved/family/page/column/column_short/simple/declared 的物化清单,提供进程级缓存
  • 关键能力:
    • getEntries/getEntriesByType:按顺序或类型获取条目
    • getRuleGroups:供 UrlBuilder 出站生成使用(不含家族/具名条目)
    • getEntryByName:具名反查(如 user_center.member)
    • clearCache/isBuilt:测试与热更场景下的缓存控制
  • 元数据与依赖:
    • 条目字段契约见 RouteEntry 类注释;构建期依赖 ModuleRegistry 与 RouteRules
    • 版本控制建议:通过 source 字段记录来源文件与名称,配合 devtools 工具在启动期断言唯一性
classDiagram
class RouteManifest {
-static array $entries
-static array $nameIndex
-static array $ruleGroups
+clearCache()
+getEntries() RouteEntry[]
+getEntriesByType(type) RouteEntry[]
+getRuleGroups() array
+getEntryByName(name) RouteEntry?
+isBuilt() bool
}

RouteTableExporter:路由表导出

  • 作用:按端导出 name→pattern 映射,供小程序等外部消费;仅取 declared 条目,剥去 api. 前缀
  • 性能与调试:
    • 只遍历 declared 条目,避免无关 meta 模板开销
    • 可按端过滤,减少不必要的前缀处理
  • 部署优化:
    • 可在构建期生成静态路由表文件,减少运行时查找成本
    • 结合缓存策略(如文件缓存)提升小程序侧解析速度
flowchart TD
Start(["开始"]) --> Filter["遍历 RouteManifest::getEntries()"]
Filter --> CheckType{"route_type == 'declared'?"}
CheckType --> |否| Filter
CheckType --> |是| CheckEnd{"endNamespace == 指定端?"}
CheckEnd --> |否| Filter
CheckEnd --> |是| CheckName{"name 非空?"}
CheckName --> |否| Filter
CheckName --> |是| Normalize["去除 api. 前缀"]
Normalize --> Table["写入 name => pattern 表"]
Table --> Sort["按键升序排序"]
Sort --> End(["结束"])

ModuleRegistry:模块路由注册机制

  • 作用:模块分类单一来源,提供固定前台模块、系统保留首段、栏目/单表判定、父模块启用检查
  • 与路由的关系:
    • 风格规则条目(page/column/simple)在匹配时需经 ModuleRegistry 准入校验
    • 短地址模块(column_short)与 column 族互斥选用
  • 配置驱动:所有判定来自 Config 与命名规范(Naming),保证三端一致
classDiagram
class ModuleRegistry {
+fixedFrontModules() array
+systemReservedFirstSegments() array
+isColumn(module) bool
+isSingle(module) bool
+isFixedFront(module) bool
+isSystemReserved(segment) bool
+isParentModuleEnabled(parent) bool
}

BackendDeclaredMatcher:后端声明匹配器

  • 作用:对 Admin/Api 端的 declared 条目逐条编译为 PCRE,进行 URL 匹配与 HTTP 方法过滤
  • 匹配算法:
    • 收集全部命中条目(不在首条即返回)
    • specificity 排序:占位符少者优先 → 字面段多者优先 → 声明顺序兜底
    • 若传入 HTTP 方法:首个 method 命中即 HIT;全不命中返回 405(含 allow 列表)
    • 未传 HTTP 方法(permissive):返回排序后首条
    • 空路径回落到 index
  • 性能优化:
    • 仅在需要时编译正则
    • 通过 placeholderCount/literalSegmentCount 快速评估具体性
    • 使用 usort 稳定排序,避免重复计算
flowchart TD
S(["match(routeRaw, end, httpMethod)"]) --> T["trim 路径"]
T --> M1["matchRouteRaw(routeRaw, end, httpMethod)"]
M1 --> C1["遍历 declared 条目并按端过滤"]
C1 --> C2["编译正则并匹配"]
C2 --> C3{"命中?"}
C3 --> |否| C1
C3 --> |是| C4["收集候选(含 placeholders/literals/order)"]
C4 --> U["usort 按 specificity 排序"]
U --> H{"httpMethod 为空?"}
H --> |是| R1["返回首条 hit"]
H --> |否| V["遍历候选找允许的方法"]
V --> A{"找到允许方法?"}
A --> |是| R2["返回 hit"]
A --> |否| R3["返回 405 with allow"]

RouteResourceBuilder:资源路由展开

  • 作用:由 Route::resource() 创建,按 RESTful 标准动作集展开为多条 RouteEntry
  • 默认动作集:index/create/store/edit/update/destroy(show 需 only() 显式开启)
  • 扩展能力:prefix/sub/only/except/动词方法 extras,最终 push 到 collector
  • 命名规则:index → nameBase;其余 → nameBase.action;nameBase 受 sub 影响
classDiagram
class RouteResourceBuilder {
-RouteCollector collector
-string module
-string controller
-string prefix
-string sub
-string keyName
-string keyPattern
-array onlyList
-array exceptList
-array extraBuckets
-bool registered
+prefix(prefix) self
+sub(sub) self
+only(list) self
+except(list) self
+get(actions) self
+post(actions) self
+put(actions) self
+patch(actions) self
+delete(actions) self
+any(actions) self
}

示例:后台路由注册

  • admin/route/index.php:演示 name('admin.') 与 group/resource 的使用
  • admin/route/ai.php:演示 resource 与 group 的组合,prefix/sub/only/动词 extras 的链式调用

依赖关系分析

  • RouteManifestBuilder 依赖:
    • ModuleRegistry:模块分类与启用判定
    • RouteRules:读取 config/route.php 风格规则
    • RouteCollector:收集 fluent API 产物
    • RouteEntry:条目值对象
  • BackendDeclaredMatcher 依赖:
    • RouteManifest:获取 declared 条目
    • PrettyUrlCompiler:编译 pattern 为正则
  • RouteTableExporter 依赖:
    • RouteManifest:遍历条目并过滤
  • 配置文件:
    • config/route.php:定义 page/column/simple 风格规则,影响匹配顺序与模块准入
graph LR
RB["RouteManifestBuilder"] --> MR["ModuleRegistry"]
RB --> RR["RouteRules"]
RB --> RC["RouteCollector"]
RB --> RE["RouteEntry"]
BM["BackendDeclaredMatcher"] --> RM["RouteManifest"]
BM --> PC["PrettyUrlCompiler"]
TE["RouteTableExporter"] --> RM
CFG["config/route.php"] --> RR

性能考量

  • 构建期优化:
    • 进程级缓存:RouteManifest 首次构建后缓存 entries/nameIndex/ruleGroups,避免重复扫描
    • 按需构建:getRuleGroups 懒加载,仅当 UrlBuilder 需要时构建
  • 运行期优化:
    • 匹配器仅遍历 declared 条目,减少无关 meta 模板开销
    • specificity 排序确保高优先级规则优先命中,降低平均匹配成本
    • 正则编译仅在匹配时进行,避免预编译全部 pattern
  • 导出优化:
    • RouteTableExporter 仅处理 declared 条目,并按端过滤,减少不必要的前缀处理
    • 可结合文件缓存或构建期生成静态表,进一步降低运行时开销

故障排除指南

  • 常见问题:
    • 404 未命中:检查 routeRaw 是否为空(空路径会回落到 index);确认 declared 条目是否包含该端且 controller 非空
    • 405 方法不允许:确认 entry->acceptsMethod(httpMethod);查看 allow 列表以了解支持的方法
    • 具名路由失败:确认 name 唯一性(devtools 启动期断言);检查 getEntryByName 是否能查到对应条目
  • 调试建议:
    • 使用 permissive 调用(httpMethod=null)查看排序后首条候选,辅助定位 specificity 问题
    • 检查 source 字段定位路由来源文件与名称
    • 利用 RouteManifest::clearCache() 在配置热更后重置缓存
  • 工具与脚本:
    • devtools/url-route-scan.php:可用于扫描路由使用情况
    • devtools/route-list.php:启动期断言 name 唯一性

结论

DouPHP 的路由系统通过“声明式 + 配置式”的混合模式,实现了灵活、可扩展且高性能的路由管理:

  • RouteCollector 提供 fluent API 的副作用累积,便于模块化注册
  • RouteManifestBuilder 统一聚合多源条目,保证匹配顺序与一致性
  • RouteManifest 作为不可变数据源,提供高效查询与缓存
  • RouteTableExporter 支持外部消费,便于前后端协同
  • ModuleRegistry 提供统一的模块分类与准入判定
  • BackendDeclaredMatcher 实现精确的 specificity 排序与 405 处理

最佳实践包括:

  • 命名规范:使用点分命名(module.action),保持唯一性与可读性
  • 组织结构:按端划分 route 目录,资源路由优先使用 resource,复杂场景使用 group
  • 维护策略:通过 source 字段追踪来源,结合 devtools 工具进行诊断与验证

附录

  • 风格规则参考:config/route.php 定义了 page/column/simple 的多套风格,便于不同站点形态的快速适配
  • 示例路由:admin/route/index.php 与 admin/route/ai.php 展示了常见用法
添加日期:2026-10-05