文档目录
模板语法基础

简介

本文件面向初学者,系统讲解 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" 实现分类树与内容的联动展示。
添加日期:2026-10-05