简介
本手册面向需要在 DouPHP 模板中处理数据的开发者,系统讲解模板“过滤器(修饰器)”和“函数(表达式能力)”的概念、工作原理与使用方法。内容涵盖:
- 过滤器的概念、注册机制、参数传递与错误处理
- 内置过滤器的使用示例(日期格式化、字符串处理、数值计算等)
- 模板表达式语言特性与高级用法(变量访问、算术、三元、条件、内建变量)
- 性能优化建议与最佳实践
项目结构
DouPHP 的模板引擎位于 core/web/template 下,采用“编译式 + 运行期上下文”的设计:
- 编译期:Prefilter → Lexer → Parser → CodeGenerator,产出 PHP 代码并缓存
- 运行期:通过 RenderContext 提供变量作用域、循环状态、过滤器分发、子模板包含等能力
- 过滤器:以 FilterRegistry 为中心,StandardFilters 提供常用内置过滤器
- 表达式:ExprParser/ExprEmitter 将模板值表达式与 if 条件 lowering 为 $ctx 可调用的 PHP 片段
graph TB
A["模板源"] --> B["前置过滤器<br/>runPrefilter()"]
B --> C["词法切分<br/>Lexer::tokenize()"]
C --> D["语法解析<br/>Parser/TagCompiler"]
D --> E["代码生成<br/>CodeGenerator"]
E --> F["编译产物缓存"]
F --> G["运行时执行<br/>include 编译产物"]
G --> H["渲染上下文<br/>RenderContext"]
H --> I["过滤器分发<br/>filter(name, value, ...)"]
I --> J["标准过滤器集合<br/>StandardFilters"]
图表来源
- DouView.php:138-156
- Lexer.php:21-48
- RenderContext.php:87-104
- StandardFilters.php:23-63
章节来源
- DouView.php:24-82
- RenderContext.php:21-54
- FilterRegistry.php:21-63
- StandardFilters.php:23-63
核心组件
- 模板引擎主类:负责变量赋值、预处理器、编译与渲染生命周期管理
- 渲染上下文:承载变量作用域、循环状态、过滤器分发、子模板包含
- 过滤器注册表:名称到可调用对象的映射,运行期按名分发
- 标准过滤器:字符串、日期、计数、大小写转换、替换、换行等常用处理
- 表达式引擎:将模板中的值表达式与条件表达式转换为可执行的 PHP 片段
- 标签编译器:如 EchoTagCompiler 负责输出变量的转义策略与三元表达式
章节来源
- DouView.php:78-180
- RenderContext.php:56-104
- FilterRegistry.php:28-63
- StandardFilters.php:36-63
- ExprParser.php:24-66
- EchoTagCompiler.php:26-74
架构总览
下图展示了从模板到输出的关键路径:模板源经预处理器进入词法与语法阶段,生成 PHP 代码;运行期通过 RenderContext 访问变量、调用过滤器、执行循环与包含。
sequenceDiagram
participant T as "模板"
participant V as "DouView"
participant L as "Lexer"
participant P as "Parser/TagCompiler"
participant C as "CodeGenerator"
participant R as "RenderContext"
participant F as "FilterRegistry"
participant S as "StandardFilters"
T->>V : fetch()/display()
V->>V : compileSource()
V->>V : runPrefilter()
V->>L : tokenize()
L-->>P : TokenStream
P->>C : 生成PHP片段
C-->>V : 编译产物
V->>R : include 编译产物
R->>F : filter(name, value, ...)
F->>S : 调用具体过滤器
S-->>R : 处理后结果
R-->>T : 输出HTML
图表来源
- DouView.php:164-230
- Lexer.php:21-48
- RenderContext.php:87-104
- StandardFilters.php:36-63
详细组件分析
过滤器系统与内置过滤器
- 过滤器注册:StandardFilters::registerInto(FilterRegistry) 将内置过滤器名映射到静态方法
- 运行时分发:RenderContext::filter(name, value, ...) 根据名称查找 callable 并调用
- 内置过滤器类别:
- 字符串:truncate、escape、nl2br、strip_tags、indent、string_format、strip、capitalize、cat、lower、upper、replace、spacify、wordwrap
- 统计:count_characters、count_paragraphs、count_sentences、count_words
- 日期:date_format(支持 strftime 语义,PHP 8.1+ 走 date 映射)
- 默认值:default(空值回退)
classDiagram
class FilterRegistry {
+register(name, callable) void
+has(name) bool
+get(name) callable?
}
class StandardFilters {
+registerInto(registry) void
+truncate(...) string
+escape(...) string
+dateFormat(...) string?
+defaultValue(...) mixed
+... 其他过滤器 ...
}
class RenderContext {
+filter(name, value, ...) mixed
}
FilterRegistry <.. RenderContext : "按名分发"
StandardFilters --> FilterRegistry : "注册"
图表来源
- FilterRegistry.php:28-63
- StandardFilters.php:23-63
- RenderContext.php:87-104
章节来源
- StandardFilters.php:36-63
- RenderContext.php:87-104
表达式语言与高级用法
- 值表达式:支持变量路径、字符串、数字、算术运算、修饰器链(|modifier:arg1:arg2)
- 条件表达式:{if}/{elseif} 支持比较运算符、逻辑运算符、字面量保留词
- 三元表达式:{$a ? x : y},支持 nofilter 标记与 escape 修饰器识别
- 内建变量:$smarty.now / $smarty.template / $smarty.version / $smarty.ldelim / $smarty.rdelim / $smarty.foreach.NAME.*
- 安全与限制:if 语句禁止函数调用与变量函数调用;白名单 token 控制
flowchart TD
Start(["表达式入口"]) --> Parse["解析值/条件"]
Parse --> Mods{"是否含修饰器链?"}
Mods -- 是 --> Chain["构建修饰器节点链"]
Mods -- 否 --> Emit["直接发射节点"]
Chain --> Emit
Emit --> Ternary{"是否三元?"}
Ternary -- 是 --> TernaryCompile["compileTernary()"]
Ternary -- 否 --> IfCond{"是否if条件?"}
IfCond -- 是 --> CondCompile["compileCondition()"]
IfCond -- 否 --> Done(["完成"])
TernaryCompile --> Done
CondCompile --> Done
图表来源
- ExprParser.php:105-163
- ExprParser.php:235-329
- ExprParser.php:331-367
- BuiltinVarResolver.php:67-134
章节来源
- ExprParser.php:24-66
- ExprParser.php:105-163
- ExprParser.php:235-329
- ExprParser.php:331-367
- BuiltinVarResolver.php:67-134
输出与转义策略
- EchoTagCompiler 负责变量/对象/数字输出与三元表达式输出
- 全局自动 HTML 转义:当开启且未显式使用 nofilter 或 |escape:html 时,对输出进行 htmlspecialchars
- 三元表达式:由 ExprParser::compileTernary 生成纯表达式,转义策略交由调用方决定
sequenceDiagram
participant Tag as "EchoTagCompiler"
participant Expr as "ExprParser"
participant Ctx as "RenderContext"
participant Reg as "FilterRegistry"
participant Std as "StandardFilters"
Tag->>Expr : parseVarProps()/compileTernary()
Expr-->>Tag : 返回表达式与nofilter标志
alt 需要转义
Tag->>Tag : 包裹htmlspecialchars()
end
Tag->>Ctx : echo(表达式)
Note over Tag,Ctx : 若表达式含修饰器链,运行期由Ctx.filter分发
Ctx->>Reg : get(name)
Reg->>Std : 调用具体过滤器
Std-->>Ctx : 处理后结果
图表来源
- EchoTagCompiler.php:36-74
- ExprParser.php:331-367
- RenderContext.php:87-104
- FilterRegistry.php:28-63
- StandardFilters.php:36-63
章节来源
- EchoTagCompiler.php:26-74
- ExprParser.php:331-367
自定义过滤器开发指南
- 注册方式:在应用初始化阶段调用 FilterRegistry::register('your_filter', callable)
- 签名约定:callable($value, $arg1, $arg2, ...) 返回处理后的值
- 参数传递:模板中使用 {$var|your_filter:arg1:arg2},运行期由 RenderContext::filter 按顺序传入
- 错误处理:建议在过滤器内部做输入校验与异常抛出;模板层可通过 nofilter 避免不必要的转义
- 最佳实践:保持过滤器幂等、无副作用、快速返回;复杂逻辑建议封装为服务并在过滤器中调用
flowchart TD
Dev["编写自定义过滤器函数"] --> Reg["注册到FilterRegistry"]
Reg --> Use["模板中使用: {$var|your_filter:args}"]
Use --> Run["运行期: RenderContext::filter()"]
Run --> Call["调用注册的callable"]
Call --> Return["返回处理结果"]
图表来源
- FilterRegistry.php:28-63
- RenderContext.php:87-104
章节来源
- FilterRegistry.php:28-63
- RenderContext.php:87-104
常用过滤器使用示例
- 日期格式化:使用 date_format 过滤器,支持 strftime 格式;PHP 8.1+ 自动映射到 date()
- 字符串处理:使用 truncate、strip、replace、spacify、wordwrap、capitalize、lower、upper
- 数值计算:在表达式中进行算术运算(+ - * / %),或使用 string_format 格式化数值
- 默认值:使用 default 过滤器为可能为空的数据提供回退值
提示:以上示例均基于 StandardFilters 提供的能力,可在模板中组合使用。
章节来源
- StandardFilters.php:65-565
模板中可用的内置函数与能力
- 表达式能力:变量访问、数组/对象属性访问、算术运算、比较与逻辑、三元表达式
- 条件语句:{if}/{elseif}/{else},支持比较运算符与逻辑运算符
- 内建变量:$smarty.now、$smarty.template、$smarty.version、$smarty.ldelim、$smarty.rdelim、$smarty.foreach.NAME.*
- 循环属性:{foreach} 支持 @iteration/@index/@total/@first/@last/@show 等属性
注意:模板侧不直接暴露“函数调用”,而是通过表达式与过滤器组合实现数据处理。
章节来源
- ExprParser.php:235-329
- BuiltinVarResolver.php:67-134
依赖关系分析
- DouView 依赖 FilterRegistry 与 StandardFilters,负责生命周期管理与编译缓存
- RenderContext 依赖 FilterRegistry,提供运行期过滤器分发与循环状态管理
- 表达式引擎(ExprParser/ExprEmitter/BuiltinVarResolver)被标签编译器与条件编译使用
- 标签编译器(如 EchoTagCompiler)依赖表达式引擎进行值与三元表达式处理
graph LR
DV["DouView"] --> FR["FilterRegistry"]
DV --> RC["RenderContext"]
RC --> FR
FR --> SF["StandardFilters"]
EC["EchoTagCompiler"] --> EP["ExprParser"]
EP --> EE["ExprEmitter"]
EP --> BVR["BuiltinVarResolver"]
图表来源
- DouView.php:78-82
- RenderContext.php:46-54
- EchoTagCompiler.php:36-74
- ExprParser.php:24-66
章节来源
- DouView.php:78-82
- RenderContext.php:46-54
- EchoTagCompiler.php:36-74
- ExprParser.php:24-66
性能考虑
- 编译缓存:DouView 使用 CompileCache 按版本头与修订号失效,减少重复编译开销
- 零正则优化:表达式引擎与词法器尽量使用字符串操作替代 preg_*,提升解析性能
- 过滤器选择:优先使用内置过滤器;自定义过滤器应避免重 IO 与复杂计算
- 转义策略:合理使用 nofilter 与 |escape:html,避免不必要的 htmlspecialchars 调用
- 循环与包含:控制 include 深度上限,防止递归包含导致栈溢出
故障排查指南
- 语法错误:表达式或条件语句非法会抛出运行时异常,包含模板资源名与行号信息
- 过滤器未注册:调用未注册的过滤器名将被忽略,返回原值;检查注册流程
- 转义问题:如需输出原始 HTML,使用 nofilter 或 |escape:html;否则默认可能转义
- 包含递归:超过最大包含深度会抛出异常;检查模板包含逻辑
章节来源
- ExprParser.php:696-707
- RenderContext.php:142-179
- RenderContext.php:87-104
结论
DouPHP 模板系统的过滤器与表达式提供了强大而灵活的数据处理能力。通过 FilterRegistry 与 StandardFilters,开发者可以快速实现常见数据格式化;借助表达式引擎,可以在模板中进行复杂的条件与运算。遵循最佳实践与性能建议,能够在保证安全性的同时获得良好的渲染性能。
附录
- 过滤器参考:详见 StandardFilters 中所有内置过滤器方法与参数
- 表达式参考:详见 ExprParser 与 BuiltinVarResolver 支持的变量与语法
- 标签参考:详见各 TagCompiler 的实现与注释说明