简介
本文件面向 DouPHP 模板函数系统,聚焦以下目标:
- 分类并说明内置过滤器(数据处理、格式化、URL 生成、日期时间等)及其使用方式。
- 详解自定义过滤器的注册机制(定义、参数传递、返回值处理)。
- 解释过滤器的工作原理与典型场景(数据转换、安全过滤、格式化处理)。
- 说明模板中函数调用的语法结构与参数绑定方式。
- 提供丰富的业务场景示例与最佳实践。
- 给出性能优化建议、调试技巧以及异常处理机制。
项目结构
DouPHP 的模板引擎采用“编译式”设计:模板源在首次渲染时经预处理器、词法分析、语法解析、代码生成后输出 PHP 代码,运行期通过渲染上下文执行。核心路径如下:
- 入口与生命周期:DouView
- 运行时能力:RenderContext(变量作用域、循环状态、过滤器分发、子模板包含)
- 过滤器体系:FilterRegistry + StandardFilters
- 编译器管线:DouViewCompiler(Lexer → Parser → CodeGenerator)
- URL 标签:UrlTagCompiler(编译为 route() 调用)
- 预处理器:FrontPrefilter / AdminPrefilter(主题资源绝对化、注释标签还原等)
- URL 门面:Url(封装 UrlGenerator)
graph TB
A["DouView<br/>模板渲染入口"] --> B["DouViewCompiler<br/>编译管线"]
A --> C["RenderContext<br/>运行期上下文"]
C --> D["FilterRegistry<br/>过滤器注册表"]
D --> E["StandardFilters<br/>内置过滤器集合"]
B --> F["Tag 编译器集合<br/>含 UrlTagCompiler"]
A --> G["Prefilter<br/>Front/Admin"]
F --> H["route()<br/>URL 生成"]
H --> I["Url 门面<br/>UrlGenerator"]
核心组件
- DouView:模板渲染主类,负责变量注入、编译缓存、预处理器调度、渲染产物 include。
- RenderContext:运行期唯一载体,承载 $ctx->vars、循环状态、过滤器分发、子模板包含。
- FilterRegistry:名称到可调用对象的映射,供运行期按名分发过滤器。
- StandardFilters:内置过滤器实现集合(字符串处理、转义、日期格式化、计数、大小写等)。
- DouViewCompiler:将模板源编译为 PHP 代码,装配 Tag 编译器(如 url、include、foreach 等)。
- UrlTagCompiler:将 {url ...} 标签编译为 route() 调用,支持 nofilter 开关与自动 HTML 转义。
- Prefilters:前台/后台模板预处理,统一静态资源路径、还原注释中的模板标签。
- Url 门面:对 UrlGenerator 的静态访问封装,供业务侧生成 URL。
架构总览
模板渲染与函数调用流程概览:
- 模板源经预处理器(可选)→ 词法分析 → 语法解析 → 代码生成 → 写入编译缓存 → 运行期 include 执行。
- 运行期通过 RenderContext 访问变量与作用域,并通过 FilterRegistry 分发过滤器。
- URL 标签在编译期转换为 route() 调用,结合全局 Url 门面完成路由解析与 URL 生成。
sequenceDiagram
participant T as "模板源"
participant PV as "DouView"
participant PF as "Prefilter"
participant CV as "DouViewCompiler"
participant CG as "CodeGenerator"
participant RC as "RenderContext"
participant FR as "FilterRegistry"
participant SF as "StandardFilters"
participant UT as "UrlTagCompiler"
participant R as "route()/Url"
T->>PV : fetch(template)
PV->>PF : runPrefilter(source)
PF-->>PV : 预处理后的源
PV->>CV : compile(resource, source)
CV->>CG : generate(ast, texts, resource)
CG-->>PV : 编译产物(php)
PV->>RC : 设置 vars/loops
PV->>PV : include 编译产物
Note over RC,FR : 运行期 {$x|filter : param} 调用
RC->>FR : filter(name, value, ...)
FR->>SF : 调用具体过滤器实现
SF-->>RC : 返回结果
Note over UT,R : {url link=...} 编译为 route() 调用
详细组件分析
过滤器系统与内置函数
- 工作原理
- 模板表达式中可使用管道语法 {$value|filter:arg1:arg2},由 RenderContext::filter 按名从 FilterRegistry 查找 callable,并以剩余参数调用。
- 标准过滤器集中在 StandardFilters,构造时由 DouView 自动注册。
- 内置过滤器分类与用途
- 数据处理:truncate、replace、cat、strip、spacify、wordwrap、indent、capitalize、lower、upper、count_* 系列。
- 安全过滤:escape(html/htmlall/url/urlpathinfo/javascript/mail 等)、strip_tags。
- 格式化:nl2br、string_format、date_format(strftime 语义,兼容 PHP 8.1+)。
- 默认值:default(空值回退)。
- 扩展点
- 通过 FilterRegistry::register 注册自定义过滤器,形如 function($value, $arg1, ...)。
- 未注册的过滤器名会原样返回输入值,避免中断渲染。
classDiagram
class FilterRegistry {
+register(name, callable) void
+has(name) bool
+get(name) callable|null
}
class StandardFilters {
+registerInto(registry) void
+truncate(...)
+escape(...)
+dateFormat(...)
+defaultValue(...)
+...其他过滤器...
}
class RenderContext {
+filter(name, value, ...) mixed
+set(name, value) void
+loopProp(var, property) mixed
+includeTemplate(file, vars, assignVar) void
}
RenderContext --> FilterRegistry : "按名分发"
FilterRegistry --> StandardFilters : "内置实现"
URL 生成函数与标签
- 标签语法
- {url link="module.action" params=[] options=[] page=N nofilter=true}
- 编译为 route(link, params, options) 调用;支持 inline 属性合并到 params。
- 安全与转义
- 当开启全局自动 HTML 转义且未指定 nofilter 时,URL 输出会被 htmlspecialchars 包裹。
- 底层实现
- 通过 Url 门面(封装 UrlGenerator)或 helper route() 生成站点 URL。
flowchart TD
Start(["{url ...}"]) --> Parse["解析属性<br/>link/params/options/page/nofilter"]
Parse --> Build["构建 options 与 params"]
Build --> Call["调用 route(link, params, options)"]
Call --> Escape{"是否启用自动转义<br/>且未 nofilter?"}
Escape --> |是| Html["htmlspecialchars 包裹"]
Escape --> |否| Raw["直接输出"]
Html --> End(["输出 URL"])
Raw --> End
日期时间函数
- 内置 date_format
- 支持 strftime 风格占位符,兼容 Windows 与 PHP 8.1+(内部映射至 date)。
- 输入支持多种形态:时间戳、YYYYMMDDHHmmss、strtotime 可解析字符串;为空时可回退默认日期或当前时间。
- 使用建议
- 在模板中通过 {$timestamp|date_format:'%Y-%m-%d %H:%M'} 进行本地化展示。
- 注意时区配置与服务器环境差异。
预处理器(Prefilter)
- 前台 FrontPrefilter
- 将主题相对路径 images/css/js 替换为主题绝对路径;移除 meta 头;还原注释中的模板标签。
- 后台 AdminPrefilter
- 将 css/js/images 指向 admin/view 下的绝对路径;修复深路径下裸链接前缀;还原注释标签。
- 使用方式
- 通过 DouView::registerPrefilter 注册回调,或在框架初始化阶段挂载。
自定义函数注册机制
- 注册位置
- 在应用启动阶段,向 FilterRegistry 注册自定义过滤器(名称 → callable)。
- 参数与返回值
- 第一个参数为待处理值,后续参数为冒号分隔的额外参数;返回处理后的值。
- 错误处理
- 若未找到对应过滤器,RenderContext::filter 会返回原始值,不会抛出异常。
- 示例模式
- 参考 StandardFilters::registerInto 的注册方式,集中管理过滤器映射。
函数调用语法与参数绑定
- 表达式内管道语法
- {$var|filter:arg1:arg2} 等价于调用 RenderContext::filter('filter', $var, 'arg1', 'arg2')。
- 标签级函数
- {url ...} 在编译期转换为 route() 调用,支持 nofilter 控制转义。
- 变量与循环属性
- 通过 $ctx->set 写入变量;{$item@iteration/index/total/first/last/show} 读取循环状态。
依赖关系分析
- 松耦合设计
- DouView 不直接实现过滤器逻辑,仅持有 FilterRegistry;StandardFilters 以静态方法提供实现。
- 编译器与运行期职责分离:DouViewCompiler 只负责生成 PHP;运行期由 RenderContext 驱动。
- 外部集成
- URL 生成依赖 Url 门面(UrlGenerator),保证与业务路由一致。
- 预处理器与模板源强相关,但通过接口 PrefilterContext 解耦。
graph LR
DV["DouView"] --> RC["RenderContext"]
RC --> FR["FilterRegistry"]
FR --> SF["StandardFilters"]
DV --> DC["DouViewCompiler"]
DC --> UT["UrlTagCompiler"]
UT --> U["Url(route)"]
DV --> PF["Prefilter(Front/Admin)"]
性能考虑
- 编译缓存
- 使用 CompileCache 基于 COMPILE_REVISION 与 VERSION 失效策略,减少重复编译。
- 预处理器开销
- 正则替换在前台/后台预处理器中较频繁,建议仅在必要时启用,并尽量复用匹配模式。
- 过滤器链
- 避免在模板中堆叠过多过滤器;复杂处理建议在控制器或服务层完成。
- URL 生成
- 批量生成 URL 时,优先使用 route() 或 Url 门面提供的缓存预热能力(如 warmupUrlCache)。
- 自动转义
- 全局 escapeHtml 会增加每次输出的编码成本,可按需关闭并在关键处手动转义。
故障排查指南
- 常见错误
- 模板路径非法:resolveTemplatePath 会拒绝包含 '..'、空字节、非白名单扩展名的路径。
- 递归包含:RenderContext::includeTemplate 限制最大深度,防止栈溢出。
- 未注册过滤器:RenderContext::filter 找不到时会返回原值,检查名称拼写与注册时机。
- URL 标签缺少 link:UrlTagCompiler 会在缺失必填属性时报错。
- 调试技巧
- 临时关闭编译缓存(force_compile)观察编译产物。
- 在预处理器中打印或记录源内容变化,定位路径替换问题。
- 使用 nofilter 排除自动转义干扰,确认 URL 输出是否符合预期。
- 日志与异常
- 包含深度超限会抛出 RuntimeException;可在上层捕获并记录上下文信息。
结论
DouPHP 模板函数系统通过清晰的编译与运行期分层、可扩展的过滤器注册机制、安全的 URL 生成与灵活的预处理器,提供了高效、安全且易用的模板能力。开发者应优先使用内置过滤器与 URL 标签,按需扩展自定义过滤器,并结合编译缓存与预处理器优化性能与兼容性。
附录:常用内置过滤器与用法示例
- 数据处理
- truncate:截断长文本并追加省略号(UTF-8 安全)。
- replace/cat/strip/spacify/wordwrap/indent/capitalize/lower/upper:字符串拼接、清洗、换行、缩进、大小写等。
- count_*:统计字符、单词、段落、句子数量。
- 安全过滤
- escape:多类型转义(html/url/javascript/mail/hex 等)。
- strip_tags:去除 HTML 标签(可选择保留空格)。
- 格式化
- nl2br:换行转 <br/>。
- string_format:sprintf 格式化。
- date_format:strftime 风格日期格式化,兼容多平台。
- 默认值
- default:空值回退到默认值。
- URL 生成
- {url link="module.action" params=[] options=[] page=N nofilter=true}:生成站点 URL,支持无过滤输出。
- 示例(描述性)
- 商品标题截断:{$product.title|truncate:20:'...'}
- 用户评论转义:{$comment.content|escape:'html'}
- 日期显示:{$order.create_time|date_format:'%Y年%m月%d日'}
- 拼接链接:{$base_url|cat:'/detail?id='|cat:$id}
- URL 生成:{url link="product.detail" params=["id"=>$id]}