简介
本技术文档围绕 DouPHP 前台主题系统,系统性阐述模板引擎工作原理、MVC 在主题中的落地方式、主题加载与切换机制、模板继承与复用(include)、以及主题与业务逻辑的解耦策略。文档同时提供架构图与流程图,帮助开发者快速理解从请求入口到主题渲染的全链路流程。
项目结构
DouPHP 的前台主题位于 theme 目录下,每个主题是一个独立目录,包含 dwt 模板文件、inc 公共片段、css 样式、js 脚本与 images 资源。默认主题为 default,商业主题以 m 三位数字命名(如 m001)。主题通过配置项 site.site_theme 生效,并在启动阶段由策略类判定是否可用。
graph TB
A["入口 index.php"] --> B["前台初始化 Init::boot()"]
B --> C["视图引擎 setupViewEngine()"]
C --> D["确定主题: SiteThemePolicy::effectiveTheme()"]
D --> E["设置模板目录: theme/{active}/"]
E --> F["控制器返回 ViewResponse -> 渲染 .dwt"]
F --> G["模板 include inc/* 片段"]
核心组件
- 入口与路由分发:index.php 负责引导、解析语言前缀、调用前台 Init 并派发路由,最终输出 Response。
- 前台初始化:Init::boot() 完成核心对象实例化、语言与模块加载、视图引擎装配、通用变量注入、站点关闭检测等。
- 主题策略:SiteThemePolicy 决定当前生效的主题目录名,并对未授权+商业主题+正式域名进行回退保护。
- 控制器基类:BaseController::view() 统一封装视图响应,合并布局变量,交由模板引擎渲染。
- 主题扩展加载器:ThemeExtensionLoader 在路由解析后、控制器执行前,按需加载主题 inc/..from_theme.php,基于当前路由上下文注入模板变量。
- 后台主题管理:ThemeController 提供主题列表、安装、启用、删除、参数同步等能力。
架构总览
下图展示从 HTTP 请求到主题渲染的关键路径,包括主题选择、视图引擎装配、控制器渲染、模板片段复用与资源加载。
sequenceDiagram
participant U as "浏览器"
participant I as "入口 index.php"
participant R as "路由 Router"
participant INIT as "前台 Init"
participant POL as "SiteThemePolicy"
participant ENG as "模板引擎 DouView"
participant CTRL as "业务控制器"
participant TPL as "主题模板 .dwt"
U->>I : 发起请求
I->>R : 设置委托并派发
R-->>I : 返回 Response 或继续
I->>INIT : boot(routeInfo)
INIT->>POL : effectiveTheme()
POL-->>INIT : 返回 activeTheme
INIT->>ENG : 设置 template_dir = theme/{active}
INIT->>INIT : 注入通用变量(语言/站点/特性等)
R->>CTRL : 匹配控制器/动作
CTRL->>CTRL : 准备数据 + view(template, data)
CTRL->>ENG : 渲染 ViewResponse
ENG->>TPL : 编译/渲染 .dwt
TPL->>TPL : include inc/* 片段
ENG-->>U : 输出 HTML
详细组件分析
主题加载与切换机制
- 生效主题判定:SiteThemePolicy::effectiveTheme() 读取配置 site.site_theme,若处于“未授权 + 商业主题(m\d{3}) + 正式域名”场景,则强制回退为 default。
- 视图引擎装配:Init::setupViewEngine() 根据有效主题设置模板根目录 theme/{active},并配置编译目录、分隔符、HTML 转义与前过滤器。
- 主题 URL 修正:将 site.theme_url 指向 theme/{active}/,避免商业主题未授权时路径错位。
- 后台管理:ThemeController 提供主题列表、安装、启用、删除、参数同步等操作,供管理员维护主题状态。
flowchart TD
Start(["请求进入"]) --> ReadCfg["读取 site.site_theme"]
ReadCfg --> CheckAuth{"已授权?"}
CheckAuth -- 否 --> IsCommercial{"是否为 m\\d{3} 商业主题?"}
IsCommercial -- 是 --> DomainCheck{"正式域名?"}
DomainCheck -- 是 --> Fallback["回退为 default"]
DomainCheck -- 否 --> UseCfg["使用配置主题"]
IsCommercial -- 否 --> UseCfg
CheckAuth -- 是 --> UseCfg
UseCfg --> SetDir["设置模板目录 theme/{active}"]
Fallback --> SetDir
SetDir --> Render["渲染主题模板"]
模板引擎与 MVC 应用
- 控制器层:各业务控制器继承 BaseController,通过 view(template, data) 返回 ViewResponse,数据经 layoutVars() 合并后传入模板。
- 视图层:模板引擎 DouView 在 Init 中装配,template_dir 指向主题目录;模板文件采用 .dwt 后缀,支持 include 指令复用片段。
- 模型与服务:业务数据由服务层准备,控制器仅做编排与视图绑定,保持主题只关注表现层。
classDiagram
class BaseController {
+view(template, data, statusCode)
+layoutVars() array
+respond(request, redirectUrl, data, message)
}
class TemplateRendererInterface {
<<interface>>
}
class DouView {
+template_dir string
+compile_dir string
+assign(key, value)
+render(template)
}
class ThemeExtensionLoader {
+loadForRoute(module, action)
}
BaseController --> TemplateRendererInterface : "构造 ViewResponse"
TemplateRendererInterface <|.. DouView : "实现"
ThemeExtensionLoader --> DouView : "注入变量"
模板继承与复用(include)
- 模板组织:主题根目录存放页面级 .dwt 模板,inc 目录存放可复用的片段(如 header.tpl、footer.tpl、code_head.tpl 等)。
- 复用方式:通过 {include file="inc/xxx.tpl"} 引入片段,便于跨页面共享结构与样式。
- 示例参考:默认主题的首页 index.dwt 通过 include 组合头部、轮播、推荐区、在线服务与页脚等片段。
flowchart LR
A["index.dwt"] --> B["include inc/header.tpl"]
A --> C["include inc/slide_show.tpl"]
A --> D["include inc/about.tpl"]
A --> E["include inc/recommend_product.tpl"]
A --> F["include inc/footer.tpl"]
主题与业务逻辑解耦
- 职责边界:控制器负责数据准备与视图绑定;主题仅负责 HTML/CSS/JS 呈现,不直接访问数据库或业务规则。
- 变量注入:Init 在启动阶段向模板注入通用变量(站点信息、语言菜单、特性开关、用户状态等),主题通过 {$site}、{$features}、{$dou} 等安全访问。
- 扩展点:ThemeExtensionLoader 在路由解析后加载主题 inc/..from_theme.php,允许主题按当前路由分支注入特定变量,而不侵入控制器。
依赖关系分析
- 入口依赖路由与前台 Init;Init 依赖主题策略与模板引擎;控制器依赖 Base 控制器与模板接口;主题扩展加载器依赖模板引擎与 Portal 门面。
- 配置项 system.php 定义前台固定模块与保留段,影响路由与导航生成,间接影响主题展示内容。
graph LR
IDX["index.php"] --> INIT["Init"]
INIT --> POL["SiteThemePolicy"]
INIT --> ENG["DouView"]
CTRL["业务控制器"] --> ENG
EXT["ThemeExtensionLoader"] --> ENG
CFG["config/system.php"] --> INIT
性能考虑
- 模板编译缓存:模板编译产物写入 storage/cache/template/front,减少重复编译开销。
- 条件加载:ThemeExtensionLoader 仅在存在 inc/..from_theme.php 且通过安全校验时加载,避免无谓 IO。
- 变量懒求值:BaseController::layoutVars() 仅在真正渲染时才执行,降低 JSON/重定向路径的额外成本。
- 资源路径:site.theme_url 动态指向当前主题目录,避免多余的重定向或错误路径导致的 404。
故障排查指南
- 主题未生效:检查配置 site.site_theme 与 SiteThemePolicy 的禁用规则(未授权+商业主题+正式域名会回退 default)。
- 模板找不到:确认 Init 设置的 template_dir 是否正确指向 theme/{active},并确保 .dwt 文件存在。
- 片段缺失:核对 include 的文件路径是否在 inc 目录内,名称一致。
- 扩展未加载:确认 inc/..from_theme.php 是否存在并通过 SQL 安全校验;检查 Portal 上下文是否正确注入。
- 后台操作异常:ThemeController 的启用/删除/安装流程需确保主题目录权限正确,且参数行已同步。
结论
DouPHP 的主题系统以清晰的职责划分与可插拔机制为核心:入口与路由负责调度,Init 负责装配视图引擎与通用变量,主题策略保障授权与回退,控制器专注数据与视图绑定,模板通过 include 实现高复用。该设计使主题仅关注表现层,易于定制与维护,同时具备良好的扩展性与安全性。
附录
- 主题目录规范建议
- 根目录:页面级 .dwt 模板(如 index.dwt、product.dwt)
- inc:可复用片段(header.tpl、footer.tpl、code_head.tpl 等)
- css:样式文件(bootstrap.min.css、common.css、style.css 等)
- js:脚本文件(bootstrap.min.js、dou.js、slide.show.js 等)
- images:图片资源
- 可选:inc/..from_theme.php 用于按路由注入变量
- 常用模板变量
- {$site}:站点配置(含 root_url、site_name 等)
- {$features}:功能开关(如 product/article/link)
- {$dou.auth.is_login}:登录状态
- {$lang_menu}:多语言菜单
- {$csrf_token}:CSRF 令牌