文档目录
路由条目构建器

简介

本技术文档围绕 DouPHP 的“路由条目构建器”展开,聚焦 RouteEntryBuilder 类及其在声明式路由体系中的职责。内容涵盖:

  • 如何创建单个路由条目、设置路由属性与配置选项
  • 路由条目的生命周期管理(从创建到注册)
  • HTTP 方法限制、参数验证、默认值等高级用法
  • 与控制器方法的绑定机制与参数传递方式
  • 调试与测试建议
  • 面向初学者的简明示例与面向高级开发者的扩展指南

项目结构

DouPHP 的路由系统采用“构建期声明 + 运行时匹配”的分层设计:

  • 构建期:通过 Route 门面提供的 fluent API 在 route/*.php 中声明路由,生成 RouteEntry 并累积到清单
  • 运行期:由 PrettyRouteMatcher 等匹配器根据清单进行 URL 解析与方法分发
graph TB
A["Route 门面<br/>提供 get/post/put/patch/delete/match/any"] --> B["RouteEntryBuilder<br/>单条路由链式构建"]
B --> C["RouteCollector<br/>累积当前文件的条目"]
C --> D["RouteEntry<br/>不可变清单条目"]
E["RouteManifestBuilder<br/>收集所有来源的条目"] --> F["路由清单<br/>供匹配器消费"]
B --> D
C --> F

核心组件

  • RouteEntryBuilder:单条声明式路由的链式构建器,承载 pattern → controller/action 映射、HTTP 方法白名单、路由级中间件 DSL,并在构造结束或显式 register() 时推入 RouteCollector
  • Route:声明式路由的静态门面,提供 get/post/put/patch/delete/match/any/group/resource 等入口,内部统一调用 makeEntry 创建 RouteEntryBuilder
  • RouteEntry:不可变的路由清单条目,包含 name、pattern、params、module、controller、action、sub、methods、中间件元数据、source 等字段
  • RouteCollector:每个路由文件 include 期间的累积器,负责 push/flush 与组属性栈管理
  • RouteMiddlewareDsl:被多个 builder 复用的中间件 DSL,支持追加、豁免、参数覆盖、跳过全部中间件
  • RouteNameResolution:被多个 builder 复用的名称解析与 compositeModule 逻辑
  • RouteManifestBuilder:批量生成路由清单,汇总 declared 风格规则与系统内置端点

架构总览

构建期流程概览:

  • RouteManifestBuilder 在每个 route/*.php 执行前安装 RouteCollector
  • 路由文件通过 Route::get/post/... 等 fluent API 创建 RouteEntryBuilder
  • RouteEntryBuilder 在 __destruct 或 register() 时生成 RouteEntry 并 push 到 RouteCollector
  • RouteManifestBuilder 最终 flush 得到 RouteEntry[] 清单,供运行期匹配器使用
sequenceDiagram
participant MB as "RouteManifestBuilder"
participant RC as "RouteCollector"
participant R as "Route 门面"
participant B as "RouteEntryBuilder"
participant E as "RouteEntry"
MB->>RC : 安装 collector
MB->>R : include 路由文件
R->>B : makeEntry(...)
B->>B : 链式设置 params/sub/name/middleware
B-->>RC : __destruct/register -> push(E)
MB->>RC : flush() 获取条目列表

详细组件分析

RouteEntryBuilder:单条路由条目构建器

  • 职责:封装单条路由的 pattern、controller、action、module、name、params、sub、methods 以及中间件 DSL;在析构或显式 register() 时生成 RouteEntry 并推入 RouteCollector
  • 关键能力:
    • 链式设置:name(params)、sub、middleware/withoutMiddleware/skipAllMiddleware/auth/throttle/permission
    • 自动注册:__destruct 确保未显式 register 时仍会推入 collector
    • 名称解析:结合 RouteCollector 的组属性栈,计算最终 name 与 module(compositeModule)
  • 典型调用路径:Route::get/post/... -> makeEntry -> new RouteEntryBuilder -> 链式配置 -> register
classDiagram
class RouteEntryBuilder {
-collector : RouteCollector
-pattern : string
-controller : string
-action : string
-module : string
-params : array
-sub : string|null
-methods : string[]
-registered : bool
+register() void
+__destruct() void
+name(name) self
+params(params) self
+sub(sub) self
+middleware(aliases) self
+withoutMiddleware(names) self
+skipAllMiddleware() self
+auth(mode) self
+throttle(max, window) self
+permission(node) self
}
class RouteCollector {
+push(entry) void
+flush() RouteEntry[]
+currentNamePrefix() string
+currentComposite() bool
}
class RouteEntry {
+name
+route_type
+pattern
+params
+module
+controller
+action
+sub
+methods
+middleware
+without_middleware
+middleware_params
+skip_all_middleware
+is_short_url_aware
+is_family
+source
}
RouteEntryBuilder --> RouteCollector : "push(RouteEntry)"
RouteEntryBuilder --> RouteEntry : "构造并写入"

Route:声明式路由门面

  • 职责:暴露 get/post/put/patch/delete/match/any/group/resource/column/simple/page 等入口;内部统一调用 makeEntry 创建 RouteEntryBuilder
  • 重要语义:
    • 严格动词:get/post/put/patch/delete 分别限定 HTTP 方法
    • match:显式指定 methods 白名单
    • any:permissive 逃生口(空 methods),需谨慎使用
    • group/resource:批量展开多条 declared 条目
    • column/simple/page:按风格规则展开 meta 模板为具体条目

RouteEntry:不可变清单条目

  • 职责:承载一条路由的完整字段,包括 name、pattern、params、module、controller、action、sub、methods、中间件元数据、source 等
  • 关键行为:
    • acceptsMethod:判断是否接受某 HTTP 方法(HEAD 按 GET 处理;空 methods = permissive)
    • endNamespace:根据 controller FQCN 推导所属端(Front/Admin/Api)
    • toRuleArray/toArray:用于视图兼容与诊断序列化

RouteCollector:构建期累积器

  • 职责:维护当前路由文件的条目集合与组属性栈;提供 push/flush 与 sourceFor 拼装
  • 关键点:
    • currentNamePrefix/currentComposite:支持嵌套组的 name 前缀叠加与 compositeModule 标记
    • sourceFor:生成 'declared:&lt;rel>:&lt;name>' 形式的 source 标识

RouteMiddlewareDsl:路由级中间件 DSL

  • 职责:提供 middleware/withoutMiddleware/skipAllMiddleware/auth/throttle/permission 等链式方法,将中间件细化配置写入 RouteEntry
  • 输出字段:middleware、without_middleware、middleware_params、skip_all_middleware

RouteNameResolution:名称解析与复合模块

  • 职责:解析最终路由名(叠加组栈前缀与单条 name)、emit 的 module 字段(compositeModule 且 sub 非空时取复合名)
  • 工具方法:actionTakesBaseName、qualifyName

依赖关系分析

  • RouteEntryBuilder 依赖:
    • RouteCollector:push 条目、读取组属性栈
    • RouteMiddlewareDsl:中间件 DSL
    • RouteNameResolution:名称解析与 compositeModule
    • RouteEntry:构造不可变条目
  • Route 门面依赖:
    • RouteEntryBuilder:创建单条条目
    • RouteResourceBuilder/RouteGroupBuilder:批量展开(与本主题相关但非核心)
  • RouteManifestBuilder:
    • 汇总 declared、system_reserved、风格规则条目,产出清单
graph LR
R["Route 门面"] --> B["RouteEntryBuilder"]
B --> C["RouteCollector"]
B --> M["RouteMiddlewareDsl"]
B --> N["RouteNameResolution"]
B --> E["RouteEntry"]
MB["RouteManifestBuilder"] --> C
MB --> E

性能与行为特性

  • 构建期开销:每条路由条目在 __destruct 或 register() 时生成 RouteEntry 并 push,整体线性复杂度 O(n)
  • 方法匹配:RouteEntry::acceptsMethod 对 HEAD 特殊处理(视为 GET),空 methods 表示 permissive
  • 名称解析:qualifyName 幂等拼接前缀与基名,避免重复前缀
  • 中间件 DSL:仅影响 RouteEntry 的中间件元数据,不改变匹配顺序

故障排查指南

常见问题与建议:

  • 未在构建期安装 collector:调用 Route::collector() 时会抛出 LogicException,需确保在 RouteManifestBuilder include 周期内使用
  • 误用 any():permissive 路由应谨慎使用,仅在确实需要多动词同 URL 时使用
  • 中间件冲突:skipAllMiddleware 优先级最高,若与其他中间件配置混用需注意
  • 名称前缀错误:注意 name('api.') 尾点表示前缀,无尾点表示全量覆盖;group 栈可叠加前缀
  • 复合模块:compositeModule 仅在 sub 非空时生效,否则 emit 的 module 不变

结论

RouteEntryBuilder 是 DouPHP 声明式路由的核心构件之一,负责将单条路由的 pattern、控制器动作、HTTP 方法与中间件配置组装为不可变的 RouteEntry,并通过 RouteCollector 纳入路由清单。配合 Route 门面与 RouteManifestBuilder,实现了从声明到清单生成的完整链路。对于初学者,可从简单的 get/post 声明入手;对于高级开发者,可通过中间件 DSL 与名称解析机制实现细粒度的权限、限流与命名策略。

附录:使用示例与最佳实践

基础示例:声明一条 GET 路由

  • 在 front/route 目录下新建或编辑路由文件,使用 Route::get 声明一条路由
  • 参考示例文件:routes_js.php

高级用法:HTTP 方法限制与参数验证

  • 使用 Route::match 显式指定 methods 白名单
  • 使用 params 设置占位符正则约束(如 id 必须为数字)
  • 使用 sub 设置子控制器段

中间件配置:权限、限流与豁免

  • 使用 permission 设置权限节点
  • 使用 throttle 设置限流配额
  • 使用 withoutMiddleware 豁免默认栈中的中间件
  • 使用 skipAllMiddleware 跳过全部中间件

名称解析与复合模块

  • 使用 name('api.') 设置前缀,或 name('api.book') 全量覆盖
  • 使用 compositeModule 在 sub 非空时 emit 复合 module

调试与测试

  • 检查 RouteEntry::acceptsMethod 是否正确接受目标 HTTP 方法
  • 检查 RouteEntry::endNamespace 是否正确推导所属端
  • 使用 RouteManifestBuilder 产出的清单进行断言
添加日期:2026-10-05