简介
本文件面向初学者,系统讲解 DouPHP 的 DWT 模板引擎(DouView)的基础语法与用法。内容涵盖变量输出、条件判断、循环遍历、标签调用、参数传递、注释与调试技巧,并配合流程图与时序图帮助理解编译与渲染过程。通过循序渐进的学习路径,帮助你快速上手模板开发。
项目结构
DouPHP 的模板引擎位于 core/web/template 目录下,采用“词法分析 → 语法分析 → 代码生成”的编译式流水线,并在运行期通过上下文执行生成的 PHP 代码。关键入口与职责如下:
- 词法分析器:将模板源切分为 Token 流,识别注释、字面量区、PHP 块与普通标签。
- 语法分析器:将 Token 流解析为 AST,校验控制结构的配对关系。
- 标签编译器:针对 foreach/if/echo/category 等标签生成具体 PHP 代码。
- 渲染主类:负责模板路径解析、编译缓存、上下文绑定与最终输出。
graph TB
A["模板源(.dwt/.htm/.html/.tpl)"] --> B["词法分析(Lexer)"]
B --> C["语法分析(Parser)"]
C --> D["标签编译器(Tag Compilers)"]
D --> E["编译产物(PHP)"]
E --> F["运行期上下文(RenderContext)"]
F --> G["HTML输出"]
图示来源
- Lexer.php:21-33
- Parser.php:24-30
- DouView.php:24-33
章节来源
- DouView.php:42-76
- Lexer.php:21-33
- Parser.php:24-30
核心组件
- 渲染主类(DouView)
- 提供 assign/fetch/display/renderResource/compileSource 等方法,管理模板目录、编译缓存、全局 HTML 转义开关、前置过滤器等。
- 支持 .dwt/.htm/.html/.tpl 扩展名白名单,并对模板路径进行安全校验。
- 词法分析器(Lexer)
- 单趟扫描,识别 {... } 注释、{literal}...{/literal}、{php}...{/php} 与普通 {tag}。
- 对文本块中的疑似 PHP 起止符进行转义,防止注入。
- 语法分析器(Parser)
- 构建 AST,校验 if/foreach/list/category/strip 等控制结构的配对。
- 维护扁平文本块序列,供后续还原空白处理口径。
- 标签编译器(Tag Compilers)
- ForeachTagCompiler:实现 {foreach} 及 {foreachelse},支持 from/item/key/name/limit/offset。
- IfTagCompiler:实现 {if}/{elseif}/{else}/{/if},条件由表达式层 lowering。
- EchoTagCompiler:实现 {$var|modifier} 与三元表达式 {$a ? x : y},支持全局 HTML 转义与 nofilter。
- CategoryTagCompiler:实现分类树遍历标签 {category},支持 with="items" 等属性。
章节来源
- DouView.php:84-179
- Lexer.php:44-141
- Parser.php:66-111
- ForeachTagCompiler.php:25-47
- IfTagCompiler.php:25-52
- EchoTagCompiler.php:26-55
- CategoryTagCompiler.php:23-33
架构总览
下图展示从模板到输出的完整流程,包括编译管线与运行期上下文交互。
sequenceDiagram
participant V as "DouView(渲染主类)"
participant L as "Lexer(词法分析)"
participant P as "Parser(语法分析)"
participant T as "标签编译器"
participant C as "编译产物(PHP)"
participant R as "RenderContext(运行期上下文)"
V->>V : renderResource(模板资源名)
V->>V : compileSource(必要时)
V->>L : tokenize(模板源)
L-->>V : TokenStream
V->>P : parse(TokenStream, 资源名)
P-->>V : AST
V->>T : 按节点类型编译
T-->>V : 生成PHP片段
V->>C : 写入编译产物
V->>R : 绑定vars/loops等上下文
R-->>V : 执行并输出HTML
图示来源
- DouView.php:198-230
- Lexer.php:44-141
- Parser.php:66-111
- ForeachTagCompiler.php:35-47
- IfTagCompiler.php:34-52
- EchoTagCompiler.php:36-55
详细组件分析
变量输出与修饰器({$var|modifier})
- 基本用法
- 在模板中使用 {$var} 输出变量;可通过修饰器链进行转换,如大小写、格式化等。
- 当开启全局 HTML 转义时,输出会自动进行 htmlspecialchars 转义,除非使用 nofilter 或显式包含 HTML 转义修饰器。
- 三元表达式
- 支持 {$a ? x : y} 形式,用于简单条件输出。
- 安全与转义
- 全局转义可在渲染主类中配置;也可在修饰器层面控制是否跳过转义。
flowchart TD
Start(["开始:解析 {$var|modifier}"]) --> Parse["解析变量与修饰器"]
Parse --> CheckEscape{"是否启用全局HTML转义?"}
CheckEscape --> |是| Escape["包裹htmlspecialchars"]
CheckEscape --> |否| NoEscape["直接输出"]
Escape --> Emit["生成echo语句"]
NoEscape --> Emit
Emit --> End(["结束:输出HTML"])
图示来源
- EchoTagCompiler.php:36-55
- EchoTagCompiler.php:64-73
章节来源
- EchoTagCompiler.php:26-75
条件判断({if}/{elseif}/{else}/{/if})
- 基本用法
- 使用 {if} 定义条件分支,{elseif} 添加更多分支,{else} 兜底,{/if} 闭合。
- 条件表达式由表达式层进行 lowering,支持常见比较与逻辑运算。
- 嵌套与组合
- 可与循环标签嵌套使用,注意保持正确的开闭标签配对。
flowchart TD
S(["进入{if}"]) --> Eval["计算条件表达式"]
Eval --> Cond{"条件为真?"}
Cond --> |是| Then["执行then分支"]
Cond --> |否| ElseCheck{"存在{elseif}?"}
ElseCheck --> |是| NextElse["计算下一个{elseif}条件"]
ElseCheck --> |否| ElseBranch["执行{else}分支"]
NextElse --> Eval
Then --> End(["结束"])
ElseBranch --> End
图示来源
- IfTagCompiler.php:34-52
章节来源
- IfTagCompiler.php:25-54
循环遍历({foreach}/{list}/{category})
- {foreach}
- 常用属性:from(数据源)、item(当前项)、key(键名,可选)、name(循环名,可选)、limit(限制条数)、offset(偏移)。
- 支持 {foreachelse} 在无数据时渲染默认内容。
- 循环元数据(如 total、iteration)可通过上下文获取。
- {list}
- 通用列表循环,语义与 {foreach} 类似,适用于不同数据源场景。
- {category}
- 分类树遍历,支持 with="items" 以同时获取每类的子项列表。
- 可设置 perCat、children、cur、excerpt 等属性控制展示。
flowchart TD
Start(["开始:{foreach}"]) --> Init["初始化$_from与循环元数据"]
Init --> Slice{"是否设置limit/offset?"}
Slice --> |是| ApplySlice["应用array_slice截取"]
Slice --> |否| LoopStart["进入循环体"]
ApplySlice --> LoopStart
LoopStart --> Body["渲染循环体内模板"]
Body --> Next{"还有下一项?"}
Next --> |是| LoopStart
Next --> |否| EndCheck{"是否存在{foreachelse}?"}
EndCheck --> |是| ElseBody["渲染else分支"]
EndCheck --> |否| End(["结束"])
ElseBody --> End
图示来源
- ForeachTagCompiler.php:56-112
章节来源
- ForeachTagCompiler.php:25-114
- CategoryTagCompiler.php:23-33
模板函数与工具标签
- include:引入其他模板片段,便于复用公共区域。
- url:生成 URL,常用于链接与表单提交地址。
- assign:在模板内赋值变量,便于中间结果复用。
- 这些标签由各自编译器生成对应 PHP 片段,遵循统一的编译上下文。
章节来源
- Parser.php:227-243
注释与字面量区
- 注释:使用 { ... } 包裹,编译后不会出现在输出中,适合开发调试与说明。
- 字面量区:使用 {literal}...{/literal} 包裹,内部内容原样输出,避免被模板引擎解析。
- 安全机制:文本块中的疑似 PHP 起止符会被转义为 echo 字面,防止注入。
章节来源
- Lexer.php:71-119
- Lexer.php:161-181
依赖关系分析
- 渲染主类依赖词法分析器与语法分析器完成模板编译。
- 语法分析器依赖标签分类器识别三元、命令与修饰器。
- 标签编译器统一通过 TagCompileContext 输出 PHP 片段,共享表达式解析能力。
- 运行期上下文提供变量作用域与循环状态管理。
graph LR
DV["DouView"] --> LEX["Lexer"]
DV --> PAR["Parser"]
PAR --> TAGS["标签编译器集合"]
TAGS --> CTX["TagCompileContext"]
CTX --> EXPR["表达式解析器"]
DV --> RC["RenderContext"]
图示来源
- DouView.php:279-298
- Parser.php:33-41
- ForeachTagCompiler.php:35-47
章节来源
- DouView.php:279-298
- Parser.php:33-41
性能注意事项
- 编译缓存:模板首次编译后产物持久化,后续请求直接 include,减少解析开销。
- 增量重编:仅在源文件更新或强制重编时重新编译,提升运行时性能。
- 循环优化:{foreach} 支持 limit/offset 截取,避免不必要的数据传输与渲染。
- 全局转义:按需开启,避免不必要的字符串处理开销。
故障排查指南
- 未闭合标签:语法分析阶段会检测未闭合的控制结构并抛出错误,定位到具体行号。
- 非法标签:遇到无法识别的标签会报错,检查拼写或是否属于内置标签集。
- break/continue 位置:必须在循环体内使用,否则编译期报错。
- PHP 注入防护:文本块中的 PHP 起止符会被转义,若需嵌入原生 PHP,请使用受保护区或后端逻辑。
章节来源
- Parser.php:104-111
- Parser.php:245-251
- Parser.php:322-342
- Lexer.php:161-181
结论
DouPHP 的 DWT 模板引擎通过清晰的编译流水线与丰富的标签体系,提供了易用的变量输出、条件判断、循环遍历与工具标签。借助注释与字面量区,开发者可以高效编写可维护的模板。结合全局转义与安全机制,模板渲染既灵活又安全。建议初学者从基础标签入手,逐步掌握高级特性与最佳实践。
附录:学习路径与速查
- 入门步骤
- 了解变量输出与修饰器:{$var|modifier}、{$a ? x : y}。
- 掌握条件判断:{if}/{elseif}/{else}/{/if}。
- 学会循环遍历:{foreach}/{list}/{category},熟悉 from/item/key/name/limit/offset。
- 使用工具标签:include、url、assign。
- 调试技巧
- 使用 { ... } 注释临时屏蔽代码段。
- 使用 {literal}...{/literal} 输出原始内容。
- 关注编译期错误信息,定位到具体行号修正语法。
- 进阶要点
- 合理设置 limit/offset 优化大数据渲染。
- 根据业务需求决定是否启用全局 HTML 转义。
- 利用 category 的 with="items" 实现分类树与内容的联动展示。