文档目录
路由构建器

简介

本文件面向 DouPHP 的路由构建器系统,系统性说明以下能力:

  • 基础 RouteEntryBuilder:用于创建单条声明式路由条目(pattern → controller/action + HTTP 方法白名单 + 路由级中间件 DSL)。
  • RouteGroupBuilder:用于按模块与控制器组织一组 actions,支持前缀、子控制器、命名基名覆盖、动词分桶。
  • RouteResourceBuilder:用于 RESTful 资源路由的批量展开(CRUD 标准动作集 + only/except + extras)。
  • JsRouteBuilder:浏览器 route.js 的 PHP 镜像,基于已导出路由清单生成 URL,并处理语言前缀与重写模式。
  • 路由元数据:名称、描述(通过 source 溯源)、HTTP 方法白名单、中间件绑定等属性在 RouteEntry 中统一承载。
  • DSL 设计:链式调用语法、配置选项、组栈叠加、compositeModule 语义。
  • 最佳实践与性能建议:严格动词限定、避免 any()、合理使用 only/except、控制参数约束与中间件开销。

项目结构

路由构建器位于 core/web/routing 下,围绕“构建器 → 累积器 → 条目”的三段式组织:

  • 构建器:RouteEntryBuilder、RouteGroupBuilder、RouteResourceBuilder、JsRouteBuilder
  • 公共能力:RouteMiddlewareDsl(中间件 DSL)、RouteNameResolution(命名解析)
  • 累积器:RouteCollector(收集当前路由文件内的所有条目)
  • 条目:RouteEntry(最终持久化的路由清单项)
graph TB
subgraph "构建器"
A["RouteEntryBuilder"]
B["RouteGroupBuilder"]
C["RouteResourceBuilder"]
D["JsRouteBuilder"]
end
subgraph "公共能力"
E["RouteMiddlewareDsl"]
F["RouteNameResolution"]
end
subgraph "运行时"
G["RouteCollector"]
H["RouteEntry"]
end
A --> E
A --> F
B --> E
B --> F
C --> E
C --> F
A --> G
B --> G
C --> G
G --> H
D --> H

核心组件

  • RouteEntryBuilder:单条声明式路由的 fluent builder,承载 pattern、controller/action、HTTP 方法白名单、路由级中间件 DSL;构造结束或显式 register() 时推入 RouteCollector。
  • RouteGroupBuilder:按 group 维度组织 actions,支持 prefix/sub/name/root,动词方法 get/post/put/patch/delete/any 将 actions 按 HTTP 方法分桶,析构期展开为多条 RouteEntry。
  • RouteResourceBuilder:RESTful 资源路由,默认 CRUD 动作集(不含 show),支持 only/except 过滤与动词 extras;成员动作路径参数可自定义 key 与约束。
  • JsRouteBuilder:基于 payload(routes/rewrite/shell/url/lang)生成完整 URL,支持占位符填充、查询参数合并、前台语言前缀处理。
  • RouteCollector:每个路由文件 include 期间安装一个 collector,收集所有 RouteEntry,并在 include 结束后 flush 到 manifest declared 段。
  • RouteEntry:路由清单条目值对象,承载 name、route_type、pattern、params、module/controller/action/sub、methods、中间件相关字段、source 等。
  • RouteMiddlewareDsl:提供 middleware/withoutMiddleware/skipAllMiddleware/auth/throttle/permission 等 DSL,并以 middlewareFields 输出到每条 RouteEntry。
  • RouteNameResolution:提供 name 前缀叠加与 compositeModule 解析,以及 actionTakesBaseName 判定根动作是否取裸 nameBase。

架构总览

路由构建流程分为“定义阶段”和“注册阶段”:

  • 定义阶段:开发者通过 RouteEntryBuilder/RouteGroupBuilder/RouteResourceBuilder 以 fluent API 声明路由;这些构建器使用 RouteMiddlewareDsl 与 RouteNameResolution 完成中间件与命名解析。
  • 注册阶段:构建器在 __destruct 或显式 register() 时将 RouteEntry 推入 RouteCollector;include 结束后由上层机制 flush 到 manifest。
sequenceDiagram
participant Dev as "开发者代码"
participant GB as "RouteGroupBuilder"
participant RB as "RouteResourceBuilder"
participant EB as "RouteEntryBuilder"
participant RC as "RouteCollector"
participant RE as "RouteEntry"
Dev->>GB : 调用 get/post/... 添加 actions
GB->>RC : push(RouteEntry)
Dev->>RB : resource()->only()/except()/get()/post()/...
RB->>RC : push(RouteEntry)
Dev->>EB : ->name()->params()->sub()->middleware()
EB->>RC : push(RouteEntry)
Note over RC,RE : 各构建器在析构或显式 register 时写入条目

详细组件分析

RouteEntryBuilder(单条声明式路由)

  • 职责:承载一条 pattern → controller/action 映射,支持 params、sub、name、compositeModule、中间件 DSL;析构或 register() 时生成 RouteEntry 并 push 到 RouteCollector。
  • 关键行为:
    • name():尾点表示前缀叠加,否则全量覆盖;结合 RouteNameResolution 与 RouteCollector 的组栈前缀。
    • params():设置占位符正则映射。
    • sub():设置子控制器段。
    • middleware() / withoutMiddleware() / skipAllMiddleware() / auth() / throttle() / permission():通过 RouteMiddlewareDsl 注入中间件元数据。
    • register():幂等注册,组装 fields 并 push RouteEntry。
  • 复杂度:O(1) 注册成本;中间件字段合并 O(k)。
classDiagram
class RouteEntryBuilder {
-collector : RouteCollector
-pattern : string
-controller : string
-action : string
-module : string
-params : array
-sub : string
-methods : string[]
-registered : bool
+name(name) self
+params(params) self
+sub(sub) self
+register() void
+__destruct() void
}
class RouteMiddlewareDsl {
+middleware(aliases) self
+withoutMiddleware(names) self
+skipAllMiddleware() self
+auth(mode) self
+throttle(max, window) self
+permission(node) self
#middlewareFields() array
}
class RouteNameResolution {
#resolveDeclaredName(collector, defaultLocalBase) string
#resolveEmittedModule(collector, cleanModule, sub) string
#actionTakesBaseName(action, rootAction) bool
#qualifyName(prefix, base) string
}
RouteEntryBuilder ..> RouteMiddlewareDsl : "use"
RouteEntryBuilder ..> RouteNameResolution : "use"

RouteGroupBuilder(分组路由)

  • 职责:按 module + controller [+ sub] 组织 actions,支持 prefix/sub/name/root;动词方法将 actions 按 HTTP 方法分桶;析构期展开为多条 RouteEntry。
  • 关键行为:
    • prefix()/sub()/name()/root():设置 URL 前缀、子控制器、命名基名、根动作覆盖。
    • get/post/put/patch/delete/any:追加动词桶;any() 为 permissive 逃生口。
    • flush():校验 prefix 存在,计算 nameBase/emittedModule/middlewareFields,按 bucket→action 顺序展开 RouteEntry。
  • 复杂度:展开 O(n),n 为 actions 总数;中间件字段合并 O(k)。
flowchart TD
Start(["开始"]) --> CheckPrefix{"prefix 是否存在?"}
CheckPrefix --> |否| ThrowErr["抛出逻辑异常"]
CheckPrefix --> |是| Compute["计算 nameBase / emittedModule / middlewareFields"]
Compute --> ForEachBucket{"遍历动词桶"}
ForEachBucket --> ForEachAction{"遍历 actions"}
ForEachAction --> BuildPattern{"是否根动作?"}
BuildPattern --> |是| PatternRoot["pattern = prefix"]
BuildPattern --> |否| PatternSub["pattern = prefix + '/' + action"]
PatternRoot --> NameCalc["根据 action 决定 nameBase 或 nameBase.action"]
PatternSub --> NameCalc
NameCalc --> PushEntry["push RouteEntry"]
PushEntry --> NextAction{"还有 actions?"}
NextAction --> |是| ForEachAction
NextAction --> |否| NextBucket{"还有 buckets?"}
NextBucket --> |是| ForEachBucket
NextBucket --> |否| End(["结束"])

RouteResourceBuilder(RESTful 资源路由)

  • 职责:批量展开 CRUD 标准动作集(index/create/store/edit/update/destroy),show 需经 only() 显式开启;支持 only/except 过滤与动词 extras;成员动作路径参数可自定义 key 与约束。
  • 关键行为:
    • prefix()/sub()/name()/compositeModule():URL 前缀、子控制器、命名基名、复合模块。
    • key(name, pattern):覆盖成员动作路径参数名与约束。
    • only()/except():过滤标准动作集。
    • get/post/put/patch/delete/any:追加 extras 动词桶。
    • register():解析标准动作与 extras,按 resourceMap 生成 pattern/methods/params,push RouteEntry。
  • 复杂度:解析标准动作 O(m),extras 去重 O(e),总体 O(m+e)。
classDiagram
class RouteResourceBuilder {
-collector : RouteCollector
-module : string
-controller : string
-prefix : string
-sub : string
-keyName : string
-keyPattern : string
-onlyList : string[]?
-exceptList : string[]
-extraBuckets : array
-registered : bool
+prefix(p) self
+sub(s) self
+name(n) self
+compositeModule() self
+key(name, pattern) self
+only(actions) self
+except(actions) self
+get(actions) self
+post(actions) self
+put(actions) self
+patch(actions) self
+delete(actions) self
+any(actions) self
+register() void
+__destruct() void
}
class RouteMiddlewareDsl
class RouteNameResolution
RouteResourceBuilder ..> RouteMiddlewareDsl : "use"
RouteResourceBuilder ..> RouteNameResolution : "use"

JsRouteBuilder(前端路由 URL 生成)

  • 职责:基于 payload(routes/rewrite/shell/url/lang)与具名路由名生成完整 URL;支持占位符填充、查询参数合并、前台语言前缀处理。
  • 关键行为:
    • url(payload, name, params, options):查找 routes[name],提取占位符名,填充路径,处理 rewrite/front/base 前缀,合并 query/page。
    • placeholderNames(pattern):从 pattern 提取占位符名列表。
    • applyFrontLanguagePrefix(path, payload):对齐 UrlBuilder 的前台语言前缀策略。
  • 复杂度:O(p) 占位符匹配与填充,p 为占位符数量。
sequenceDiagram
participant FE as "前端代码"
participant JSB as "JsRouteBuilder"
participant PC as "PrettyUrlCompiler"
FE->>JSB : url(payload, name, params, options)
JSB->>JSB : 查找 routes[name]
JSB->>JSB : placeholderNames(pattern)
JSB->>PC : fill(pattern, values)
PC-->>JSB : path
JSB->>JSB : 处理 rewrite/front/base 前缀
JSB->>JSB : 合并 query/page
JSB-->>FE : 返回完整 URL

RouteCollector(路由累积器)

  • 职责:维护当前路由文件的 RouteEntry 队列与组属性栈;提供 push/flush/sourceFor/currentNamePrefix/currentComposite 等方法。
  • 关键行为:
    • pushGroupAttributes/popGroupAttributes:管理嵌套组的 name 前缀与 composite 标记。
    • currentNamePrefix:拼接组栈 name 前缀。
    • currentComposite:任一层 composite 为真即生效。
    • sourceFor:生成 'declared:<rel>:<name>' 源标识。
    • flush:取出全部条目并清空。

RouteEntry(路由条目)

  • 职责:承载路由清单条目字段,提供 acceptsMethod、toRuleArray、toArray 等方法。
  • 关键字段:name、route_type、pattern、params、target、module_fixed、module、controller、action、sub、middleware、methods、without_middleware、middleware_params、skip_all_middleware、is_short_url_aware、is_family、source。
  • 行为:
    • acceptsMethod:空 methods 表示 permissive;HEAD 按 GET 处理。
    • toRuleArray:供 UrlBuilder 兼容视图使用的字段子集。
    • toArray:诊断/缓存序列化用。

依赖关系分析

  • 构建器依赖:
    • RouteEntryBuilder/RouteGroupBuilder/RouteResourceBuilder 均 use RouteMiddlewareDsl 与 RouteNameResolution。
    • 三者最终都 push RouteEntry 到 RouteCollector。
  • 运行时依赖:
    • RouteCollector 管理组属性栈与条目队列。
    • RouteEntry 作为不可变值对象被后续匹配器/工具消费。
  • 外部依赖:
    • JsRouteBuilder 依赖 PrettyUrlCompiler 进行路径填充。
graph LR
RMB["RouteMiddlewareDsl"] --> EB["RouteEntryBuilder"]
RNR["RouteNameResolution"] --> EB
RMB --> GB["RouteGroupBuilder"]
RNR --> GB
RMB --> RB["RouteResourceBuilder"]
RNR --> RB
EB --> RC["RouteCollector"]
GB --> RC
RB --> RC
RC --> RE["RouteEntry"]
JSB["JsRouteBuilder"] --> RE

性能考虑

  • 动词分桶与一次性展开:RouteGroupBuilder/RouteResourceBuilder 在析构期一次性展开为 RouteEntry,减少多次 push 的开销。
  • 中间件字段合并:middlewareFields 一次合并后写入每条 RouteEntry,避免重复计算。
  • 命名解析幂等:RouteNameResolution::qualifyName 防止重复前缀叠加,降低命名冲突与冗余。
  • 参数约束最小化:仅在必要时使用 params 或 key() 约束,避免复杂正则影响匹配性能。
  • 避免 any():permissive 路由会扩大匹配范围,增加歧义与匹配成本,应谨慎使用。

故障排查指南

  • 未找到路由名:JsRouteBuilder.url 在未知 name 时抛出异常,检查 payload.routes 是否包含该 name。
  • 缺少 prefix:RouteGroupBuilder.flush 在未设置 prefix 时抛出逻辑异常,确保 group 调用 prefix()。
  • 方法不匹配:RouteEntry.acceptsMethod 对 HEAD 特殊处理;若请求未命中,检查 methods 白名单是否正确。
  • 中间件未生效:确认 middleware/withoutMiddleware/skipAllMiddleware 是否正确使用;注意 skipAllMiddleware 优先级最高。
  • 命名冲突:检查 name() 与前缀叠加逻辑,避免产生重复前缀;利用 RouteCollector 的组栈前缀进行合理组织。

结论

DouPHP 路由构建器系统通过清晰的构建器分层与公共 DSL,提供了灵活且一致的路由声明方式:

  • RouteEntryBuilder 适合细粒度单条路由。
  • RouteGroupBuilder 适合按模块/控制器组织 actions。
  • RouteResourceBuilder 适合标准 CRUD 场景,配合 only/except 精确控制。
  • JsRouteBuilder 保证前后端 URL 生成一致性。
  • RouteCollector 与 RouteEntry 提供稳定的累积与数据结构契约。
  • RouteMiddlewareDsl 与 RouteNameResolution 提供一致的中间件与命名解析能力。 遵循最佳实践(严格动词、谨慎 any、合理约束、最小化中间件)可获得更优的可维护性与性能。

附录

  • 使用示例(概念性说明):
    • 基础路由:使用 RouteEntryBuilder 的 name/params/sub/middleware 链式配置,最后 register() 或等待析构自动注册。
    • 分组路由:使用 RouteGroupBuilder 的 prefix/sub/name/root 与 get/post/put/patch/delete/any 组织 actions。
    • 资源路由:使用 RouteResourceBuilder 的 only/except/key 与动词 extras 快速生成 CRUD 路由。
    • 前端 URL:使用 JsRouteBuilder.url 基于 payload 与具名路由生成完整 URL,支持语言前缀与查询参数。
  • 扩展指南(高级开发者):
    • 自定义构建器:实现类似 RouteEntryBuilder 的 fluent API,复用 RouteMiddlewareDsl 与 RouteNameResolution,最终 push RouteEntry 到 RouteCollector。
    • 中间件扩展:在 RouteMiddlewareDsl 基础上新增 DSL 方法,并通过 middlewareFields 输出到 RouteEntry。
    • 命名策略:利用 RouteNameResolution 的 qualifyName 与 resolveDeclaredName 实现前缀叠加与覆盖。
添加日期:2026-10-05