简介
本技术文档面向DouPHP的URL生成子系统,围绕以下目标展开:
- 基于路由名称生成URL、参数绑定、查询字符串处理机制
- ShortUrlPolicy短地址模块策略(路径前缀省略、分类段替换等)
- PrettyUrlCompiler美化URL编译(pattern迷你语言、双向可逆)
- 完整示例覆盖静态页面、动态参数、分页、多语言、小程序
- 性能优化与缓存策略
- 版本管理与向后兼容建议
项目结构
URL生成子系统位于 core/web/routing 下,由四个核心类协作完成:
- UrlGenerator:对外统一入口,封装调用面,负责声明式路由命中、意图解析、后端URL生成
- UrlBuilder:具体路径构建器,按 kind 分发到 list/detail/category/class/action 等分支,并组合 ROOT_URL、语言前缀、分页
- ShortUrlPolicy:短地址模块开关与路径工具(是否启用、前缀判断、前缀移除/补回)
- PrettyUrlCompiler:pattern 迷你语言的编译器与填充器,保证入站匹配与出站生成一致
graph TB
A["业务代码<br/>route()/Url::xxx"] --> B["UrlGenerator<br/>统一入口"]
B --> C["UrlBuilder<br/>路径构建"]
C --> D["PrettyUrlCompiler<br/>pattern→路径"]
C --> E["ShortUrlPolicy<br/>短地址策略"]
B --> F["Admin/API端<br/>buildBackendUrl"]
C --> G["composeFullUrl<br/>ROOT_URL+语言+分页"]
核心组件
- UrlGenerator:对外暴露 url()、urlMini()、getSlugPath()、warmupUrlCache();内部委托 UrlBuilder;对声明式条目优先走 RouteManifest 的 pattern 直接填充;对后台/API端输出绝对地址。
- UrlBuilder:实现 buildFullUrl()、rewriteUrlMiniprogram()、warmupUrlCache()、getSlugPath();按 intent['kind'] 分发到不同路径构建方法;统一处理分页 /oN、语言前缀、伪静态开关。
- ShortUrlPolicy:提供 isShort()、isPrefixedRoute()、prefixRoute()、stripModulePrefix() 等工具;支持 article→news 别名。
- PrettyUrlCompiler:compileToRegex() 与 fill() 双向可逆;支持 {name}、{name:regex}、[optional] 语法;进程级正则缓存。
架构总览
URL生成流程分为“声明式命中”和“意图解析”两条主线:
- 声明式命中:在浏览器端非小程序场景,先尝试通过 RouteManifest 获取具名 entry;若命中且为 declared 类型,则直接用 PrettyUrlCompiler 填充 pattern,并追加未消费参数为 query。
- 意图解析:将点分路由键 + 具名参数解析为结构化 intent(list/detail/category/class/action2/action3),再由 UrlBuilder 构建路径片段,最后 composeFullUrl 拼接 ROOT_URL、语言前缀、分页。
sequenceDiagram
participant Caller as "调用方"
participant Gen as "UrlGenerator"
participant Manifest as "RouteManifest"
participant Builder as "UrlBuilder"
participant Compiler as "PrettyUrlCompiler"
participant Policy as "ShortUrlPolicy"
Caller->>Gen : url(route, params, options)
alt 非小程序且存在具名entry
Gen->>Manifest : getEntryByName(route)
alt 命中declared
Gen->>Compiler : fill(pattern, values)
Compiler-->>Gen : path
Gen->>Gen : 追加未消费参数为query
Gen-->>Caller : URL
else 未命中
Gen->>Gen : parseRouteToIntent()
Gen->>Builder : buildFullUrl(intent, page)
Builder->>Policy : 短地址策略判断
Builder->>Compiler : fill(pattern, values)
Builder-->>Gen : path
Gen-->>Caller : URL
end
else 小程序或后台/API
Gen->>Builder : buildMiniProgramUrl/buildBackendUrl
Builder-->>Gen : URL
Gen-->>Caller : URL
end
详细组件分析
UrlGenerator:统一入口与意图解析
- 对外方法:
- url(route, params, options):主入口,支持声明式条目直出、意图解析、后端绝对URL、查询串合并
- urlMini(module, value, tabbar):小程序 pages 路径
- getSlugPath(module, id, mode):slug 片段
- warmupUrlCache(module, rows):列表预热
- 关键逻辑:
- 声明式条目优先:非小程序时先查 RouteManifest,若命中 declared 类型,直接 fill pattern 并追加未消费参数为 query
- 意图解析:parseRouteToIntent 将 module.action 转为 kind/module/seg2/seg3/id/slug/category_id/class 等结构化字段
- 后端URL:admin/api端以绝对地址输出,开启伪静态时 BASE<path>,关闭时 BASE index.php?route=<path>
flowchart TD
Start(["进入 url()"]) --> CheckDeclared{"是否存在具名entry?"}
CheckDeclared --> |是| FillPattern["fill pattern 并追加未消费参数"]
FillPattern --> Return1["返回URL"]
CheckDeclared --> |否| ParseIntent["parseRouteToIntent()"]
ParseIntent --> BuildPath["UrlBuilder.buildFullUrl()"]
BuildPath --> AppendQuery["追加options.query与未消费参数"]
AppendQuery --> Return2["返回URL"]
UrlBuilder:路径构建与完整URL组装
- 主要职责:
- buildFullUrl(intent, page):根据 kind 选择对应路径构建方法,再 composeFullUrl 拼接 ROOT_URL、语言前缀、分页
- rewriteUrlMiniprogram(module, value, tabbar):小程序 pages 路径生成
- warmupUrlCache(module, rows):批量预热 slug、created_at、category_id 等字段缓存
- getSlugPath(module, id, mode):slug 片段,短地址模式下返回纯 slug 或顶级祖先别名
- 路径构建分支:
- list:page/column/其它模块根
- detail:page 单页、single 模块、column 栏目详情
- category:仅 column 模块的分类页
- class:module/class/{class}
- action2/action3:动作路径拼接
classDiagram
class UrlBuilder {
+buildFullUrl(intent, page) string
+rewriteUrlMiniprogram(module, value, tabbar) string
+warmupUrlCache(module, rows) void
+getSlugPath(module, id, mode) string
-buildPrettyPath(intent) string
-composeFullUrl(path, rewriteMode, page) string
-applyLanguagePrefix(path) string
-applyPagination(url, page, rewriteMode) string
}
class PrettyUrlCompiler {
+fill(pattern, values) string
+compileToRegex(pattern, params) string
}
class ShortUrlPolicy {
+isShort(baseModule) bool
+stripModulePrefix(path, urlModule, baseModule) string
+prefixRoute(route) string
}
UrlBuilder --> PrettyUrlCompiler : "填充pattern"
UrlBuilder --> ShortUrlPolicy : "短地址策略"
PrettyUrlCompiler:pattern 迷你语言编译器
- 功能:
- compileToRegex(pattern, params):将 pattern 编译为带具名捕获组的 PCRE,进程级缓存
- fill(pattern, values):将 pattern 中的占位符替换为值,支持可选段 [...]
- 语法:
- {name}:默认子模式 [^/]+,可由 $params[name] 覆盖
- {name:regex}:内联正则优先于 $params[name]
- [ ... ]:可选段,fill 时任一占位符非空才渲染;compile 时编译为 (?:...)?
flowchart TD
Start(["fill(pattern, values)"]) --> Scan["扫描字符"]
Scan --> IsBracket{"是否'['"}
IsBracket --> |是| Optional["递归处理可选段"]
IsBracket --> |否| IsPlaceholder{"是否'{'"}
IsPlaceholder --> |是| Replace["提取name/regex并替换"]
IsPlaceholder --> |否| Append["追加字面量"]
Optional --> Next["继续扫描"]
Replace --> Next
Append --> Next
Next --> End(["返回规范化路径"])
ShortUrlPolicy:短地址模块策略
- 功能:
- enabled():判断短地址功能是否启用
- isShort(baseModule):判断数据库模块是否为当前短地址模块
- isPrefixedRoute(route):检测路径是否带有短地址模块前缀(避免长短URL并存)
- prefixRoute(route):为解析端补回模块前缀重试
- stripModulePrefix(path, urlModule, baseModule):移除生成路径开头的模块前缀
- 特殊处理:article 模块映射为 news 别名
依赖关系分析
- UrlGenerator 依赖:
- RouteManifest:获取具名 entry 的 pattern 与 params
- UrlBuilder:实际路径构建
- PrettyUrlCompiler:pattern 填充
- ShortUrlPolicy:短地址策略
- UrlBuilder 依赖:
- DB:读取 slug、created_at、category_id 等字段进行缓存预热
- Config:读取 site.rewrite、site.short_url_module 等配置
- Naming:模块名转换(如 article → news)
- Util:查询串规范化
- 小程序侧:
- miniprogram/company/utils/route.ts:前端命名路由取址,与后端保持一致
- miniprogram/company/config/site.ts:mp_url、rewrite_enable 等运行配置
graph LR
Gen["UrlGenerator"] --> BM["UrlBuilder"]
Gen --> PM["PrettyUrlCompiler"]
Gen --> SP["ShortUrlPolicy"]
BM --> DB["DB"]
BM --> CFG["Config"]
BM --> NAM["Naming"]
BM --> UT["Util"]
MP["小程序route.ts"] --> CFG
性能与缓存
- 进程级缓存:
- PrettyUrlCompiler::$regexCache:编译后的正则缓存,避免重复编译
- UrlBuilder::$urlFieldCache:字段缓存(slug、created_at、category_id 等)
- UrlBuilder::$urlCategoryCache:分类行缓存(表名 => [id => ['slug', 'parent_id']])
- 列表预热:
- warmupUrlCache(module, rows):批量预热 slug、created_at、category_id,减少逐条查库
- 查询串规范化:
- appendQuery() 使用 Util::normalizeQueryString 确保首个 & 转 ?,避免重复分隔符
优化建议:
- 列表页务必调用 warmupUrlCache(),减少详情URL生成的数据库访问
- 合理设置 config/route.php 的 pattern,避免复杂正则导致匹配开销
- 短地址模块启用后,注意路径前缀一致性,避免长短URL并存
多语言与SEO
- 多语言:
- applyLanguagePrefix() 根据当前语言状态添加语言前缀(rewrite_open 模式或 lang=pack 查询串)
- 小程序侧通过 mp_url 与 rewrite_enable 控制基础路径与伪静态
- SEO:
- PrettyUrlCompiler 支持 {year}/{month} 归档型规则,便于搜索引擎抓取
- 短地址模块可省略模块名段,提升URL可读性
- 分类页支持 category_slug 别名,增强语义化
短链接策略
- 短地址模块:
- 通过 site.short_url_module 与 features.<short> 控制启用
- 启用后,该模块的URL省略模块名段(如 item/fenleiyi → fenleiyi)
- 分类段取顶级祖先别名(短地址家族)或自身别名(普通模块)
- 冲突处理:
- isPrefixedRoute() 检测带前缀的路径,避免长短URL并存
- stripModulePrefix() 移除生成路径的前缀,确保输出一致
- 存储管理:
- 通过 getSlugPath() 与 warmupUrlCache() 读取 slug、category_id 等字段,结合数据库表结构管理
URL版本管理与兼容性
- 版本管理:
- 通过 config/route.php 的 pattern 定义URL形态,升级时可调整 pattern 而不影响业务代码
- 声明式路由(RouteManifest)提供单一真相源,前后端共享同一份规则
- 向后兼容:
- 关闭伪静态时自动回退 index.php?route=<path> 形态,确保旧环境兼容
- 小程序侧通过 rewrite_enable 控制路径形态,与后端保持一致
- 短地址模块启用后,解析端可补回前缀重试,避免历史链接失效
使用示例
- 静态页面:
- route('page.show', ['slug' => 'agreement'])
- 动态参数:
- route('article.show', ['id' => 123])
- route('product.category', ['category_id' => 5])
- 多语言支持:
- 自动根据当前语言状态添加语言前缀或 lang=pack 查询串
- 小程序:
- 使用 miniprogram/company/utils/route.ts 的 route(name, params) 生成pages路径
- 通过 site.ts 的 mp_url 与 rewrite_enable 控制基础路径与伪静态
故障排查
- 常见问题:
- URL生成失败:检查 route 键是否正确,params 是否包含必需字段(id、category_id、slug等)
- 短地址冲突:确认 short_url_module 配置与 features.<short> 开关一致
- 伪静态问题:检查 site.rewrite 配置与服务器重写规则
- 调试建议:
- 使用 Url::getSlugPath() 检查slug片段是否正确
- 使用 Url::warmupUrlCache() 预热列表页数据,减少数据库压力
- 检查 PrettyUrlCompiler 的 pattern 是否符合语法规则
结论
DouPHP的URL生成系统通过UrlGenerator、UrlBuilder、ShortUrlPolicy、PrettyUrlCompiler四大组件协同工作,实现了:
- 基于路由名称的URL生成与参数绑定
- 短地址模块的策略化处理
- 美化URL的pattern编译与填充
- 多语言、分页、SEO优化
- 高性能缓存与列表预热
- 版本管理与向后兼容
开发者可通过route()助手函数快速生成URL,高级用户可自定义pattern与策略以满足特定需求。