织梦CMS(DedeCMS)是中国早期非常流行的一套开源网站内容管理系统,它以其简单易用、模板分离、标签化调用等特性,在个人博客、企业官网、新闻资讯类网站等领域占据了一席之地,虽然近年来随着WordPress等更现代CMS的崛起,织梦的市场份额有所下降,但其设计思想和架构模式对于理解PHP Web应用开发,特别是国内CMS的发展,依然具有重要的参考价值。

本次解析将围绕以下几个核心模块展开:
- 整体架构与目录结构
- 入口文件与执行流程
- 核心模块解析
- 数据库抽象层
- 核心API类
- 模板引擎
- 安全机制
- 总结与现代视角
整体架构与目录结构
织梦CMS采用了经典的 MVC(Model-View-Controller)思想,但实现得比较松散。
- Model (模型): 主要指与数据库交互的部分,包括数据表的定义、数据操作类等,在织梦中,这些通常以
/include/目录下的类文件和系统模型文件(如/model/)存在。 - View (视图): 即网站的前台模板和后台管理界面,前台模板位于
/templets/目录,后台模板位于/dede/templets/目录。 - Controller (控制器): 接收用户请求,调用模型处理数据,并选择视图进行渲染,在织梦中,控制器通常是各个功能模块的PHP文件,如
/plus/下的列表页、内容页,以及/dede/下的后台管理操作文件。
核心目录结构解析:
dedecms/
├── dede/ # 后台管理目录
│ ├── index.php # 后台入口文件
│ ├── login.php # 登录页面
│ ├── template/ # 后台模板文件
│ └── ... # 各种后台功能模块
├── include/ # 核心类库和公共函数库
│ ├── arc.archives.class.php # 文档发布/修改的核心类
│ ├── channelunit.class.php # 模型处理核心类
│ ├── dedesql.class.php # 数据库操作类
│ ├── dedetag.class.php # 模板引擎解析类
│ └── common.inc.php # 全局公共函数和配置
├── plus/ # 前台功能模块目录
│ ├── list.php # 列表页
│ ├── view.php # 内容页
│ └── ... # 留言、搜索等其他功能
├── member/ # 会员中心
├── special/ # 专题管理
├── templets/ # 前台模板目录
│ ├── default/ # 默认模板
│ └── ... # 其他模板
├── data/ # 系统缓存、配置文件目录
│ ├── cache/ # 模板缓存
│ └── config.cache.inc.php # 核心配置缓存
├── index.php # 网站前台首页入口
├── install/ # 安装目录
└── ... # 其他文件
入口文件与执行流程
织梦的执行流程非常清晰,遵循了PHP Web应用的通用模式。

前台首页执行流程 (index.php):
-
加载全局配置:
index.php首先会引入include/common.inc.php,这个文件是整个系统的“大脑”,它会:- 定义全局常量(如
DEDEROOT、cfg_cmspath等)。 - 加载数据库配置信息(
data/config.inc.php)。 - 初始化数据库连接,创建
DedeSQL类的实例$dsql。 - 加载缓存配置(
data/config.cache.inc.php),避免频繁读取数据库。 - 加载核心函数库和类文件。
- 定义全局常量(如
-
解析请求:
index.php会获取typeid(栏目ID)或aid(文章ID)等参数,判断用户请求的是首页、列表页还是内容页。 -
实例化核心类:
(图片来源网络,侵删)- 如果是内容页 (
plus/view.php), 会实例化include/arc.archives.class.php中的Archives类。 - 如果是列表页 (
plus/list.php), 会实例化include/arc.listview.class.php中的ListView类。
- 如果是内容页 (
-
数据处理: 实例化的核心类会调用
$dsql从数据库中查询相应的数据(文章内容、栏目信息、模型字段等)。 -
模板渲染: 核心类加载对应的模板文件(如
templets/default/article_article.htm),并将查询到的数据以变量形式注入,它调用模板引擎(DedeTag)解析模板中的 织梦标签,最终生成完整的HTML页面并输出。
后台管理执行流程 (dede/index.php):
- 身份验证: 访问后台任何页面,都会先检查登录状态,通常通过
dede/login.php登录后,会在PHP Session中记录用户身份。 - 加载后台环境: 引入后台的全局配置文件,初始化后台专用的类库和数据库连接。
- 权限检查: 根据当前用户的角色和权限,判断其是否有权访问当前功能模块。
- 执行操作: 根据用户在后台提交的表单(如“发布文章”、“修改栏目”),调用相应的处理类(如
archives_do.php)。 - 返回结果: 处理完成后,通常会跳转回一个提示页面或列表页面,告知用户操作结果。
核心模块解析
1 数据库抽象层 (dedesql.class.php)
织梦的数据库操作封装在 DedeSQL 类中,这是一个非常典型的单例模式实现。
- 单例模式: 确保在整个请求生命周期中,只有一个数据库连接实例,节省资源。
- 核心方法:
SetQuery($sql): 设置要执行的SQL语句。Execute(): 执行SQL语句(如INSERT,UPDATE,DELETE)。GetOne(): 获取一行数据,返回一个关联数组。GetArray(): 获取多行数据,返回一个二维数组。GetOne(),GetArray(),GetList()等方法都内置了缓存机制,它们会先检查一个静态缓存数组,如果数据已存在,则直接返回,避免了重复查询数据库,这是织梦性能优化的一个关键点。
示例代码风格:
// 在全局 common.inc.php 中已经实例化
$dsql = new DedeSQL(false);
// 在业务逻辑中使用
$row = $dsql->GetOne("SELECT * FROM `#@__arctype` WHERE id = $typeid");
if($row) {
echo $row['typename'];
}
2 核心API类 (arc.archives.class.php)
这是织梦发布和修改文章的核心,功能极其强大,也相对复杂。
-
主要功能:
- 数据处理: 接收表单提交的文章数据,处理各种自定义字段。
- 模型驱动: 支持多种内容模型(文章、图集、软件等),通过不同的模型类来处理不同类型的数据。
- 索引生成: 生成或更新文章的静态HTML文件。
- 相关标签处理: 处理文章的“相关文章”、“栏目导航”等标签。
- 触发器机制: 在文章发布、修改、删除时,可以触发一系列操作,如更新缓存、发送通知等。
-
执行流程:
Archives类被实例化,并接收文章ID和所有数据。Save()方法被调用,它会首先进行数据验证和过滤。- 根据文章ID判断是新增还是更新,执行相应的
INSERT或UPDATESQL语句。 - 更新与文章相关的数据表,如
#@__arctiny(用于列表)、#@__addonarticle(附加表,存放自定义字段)等。 - 调用
MakeHtml()方法,根据后台配置(是生成静态页还是动态访问)来生成最终的HTML文件,或设置URL规则。 - 更新栏目、首页等相关的缓存文件。
3 模板引擎 (dedetag.class.php)
织梦的标签系统是其最大的特色,它将PHP代码从模板中分离出来,让不懂PHP的网页设计师也能轻松制作网站。
-
标签类型:
- 全局标签: 如
{dede:global.cfg_webname/}(网站名称)、{dede:include filename="head.htm"/}(包含文件)。 - 栏目标签: 用于获取栏目信息,如
{dede:type}[field:typename/]{/dede:type}。 - 列表标签: 用于循环输出文章列表,如
{dede:arclist titlelen='30' row='10'}...{/dede:arclist}。 - 标签: 用于显示文章详情,如
{dede:field.title/}、{dede:field.body/}。
- 全局标签: 如
-
解析原理:
- 当
Archives或ListView类准备渲染模板时,会创建一个DedeTag对象。 DedeTag的Display()方法接收模板内容字符串。- 它使用正则表达式来匹配所有的织梦标签,
/{dede:arclist\s+(.*?)}/is。 - 对于每个匹配到的标签,它会解析出标签名(如
arclist)和属性(如titlelen='30')。 - 根据标签名,调用对应的PHP处理函数(如
lib_arctag.php中的lib_arclist())。 - 处理函数会根据属性和当前环境(如栏目ID),生成相应的PHP代码片段。
- 关键一步:
DedeTag会将原始的织梦标签替换为它生成的PHP代码。{dede:arclist ...}可能被替换为<?php $tags.="<li><a href='".$row['arcurl']."'>".$row['title']."</a></li>"; ?>。
- 整个被“翻译”过的PHP模板字符串被
eval()执行,将数据注入并生成最终的HTML。
- 当
这种“正则替换 + eval()”的方式是织梦标签系统高效灵活的核心,但也带来了安全风险(详见下文)。
安全机制
织梦CMS的安全问题曾备受诟病,了解其安全机制和漏洞对学习Web安全至关重要。
-
早期安全缺陷:
- SQL注入: 早期版本在很多地方直接拼接SQL语句,没有充分过滤用户输入。
$dsql类的封装虽然方便,但如果使用不当(如直接拼接$id到SQL中),仍然存在注入风险。 - 代码执行: 模板引擎中的
eval()是最大的安全隐患,如果攻击者能控制模板内容(通过后台的模板编辑功能或上传漏洞),就能执行任意PHP代码。{dede:php}标签更是直接允许在模板中写PHP代码,风险极高。 - 文件上传漏洞: 后台上传功能是重灾区,早期版本对文件类型、内容检查不严,可能导致上传Webshell。
- CSRF: 部分后台操作没有做CSRF Token验证,可能导致用户在登录状态下被恶意操作。
- SQL注入: 早期版本在很多地方直接拼接SQL语句,没有充分过滤用户输入。
-
安全加固:
- 后期版本改进: 新版本的织梦引入了
htmlspecialchars()、addslashes()等函数对输出和输入进行转义。 - 参数化查询:
$dsql类鼓励使用Execute('SELECT * FROM table WHERE id=?', array($id))的方式,有效防止SQL注入。 - 权限控制: 后台对每个操作都有更严格的权限检查。
- 移除危险标签: 在一些官方模板中,移除了
{dede:php}- 文件白名单: 上传功能引入了更严格的文件类型和内容检查。
- 后期版本改进: 新版本的织梦引入了
使用织梦CMS,必须保持高度的安全意识,及时打补丁,并遵循最佳实践,如不使用{dede:php}、严格过滤用户输入、限制后台IP访问等。
总结与现代视角
织梦CMS的优点:
- 简单易用: 对于中小型网站,开箱即用,后台功能齐全,模板制作简单。
- 标签化模板: 将程序逻辑与页面设计分离,极大地降低了网站维护门槛。
- 社区庞大: 拥有大量的用户、插件和模板资源,遇到问题容易找到解决方案。
- 性能尚可: 通过文件缓存和数据库查询缓存,基本能满足中小型网站的访问需求。
织梦CMS的缺点与时代局限性:
- 架构陈旧: 采用过程式和面向对象的混合编程,代码耦合度高,难以维护和扩展,与现代框架(如Laravel, Symfony)的优雅架构相去甚远。
- 安全风险:
eval()和标签系统是其“原罪”,安全漏洞层出不穷。 - 性能瓶颈: 生成静态页的方式在流量巨大时(如突发新闻)会成为瓶颈,而动态URL则不利于SEO,其缓存机制也相对简单。
- 扩展性差: 插件系统不如WordPress的钩子系统灵活,二次开发需要深入理解其内部逻辑。
现代视角下的织梦:
- 学习价值: 对于初学者,织梦是一个很好的“活化石”,通过它可以直观地理解一个PHP CMS是如何工作的,包括MVC思想、数据库操作、模板渲染、安全防护等核心概念。
- 特定场景: 对于一些功能非常固定、不需要频繁二次开发、且对成本敏感的“企业展示型”网站,织梦依然是一个快速可行的选择。
- 逐步被替代: 在追求开发效率、安全性、可扩展性和现代化的今天,WordPress、Laravel + Vue/React等现代技术栈已经成为主流,织梦正在逐渐退出历史舞台。
希望这份详细的源码解析能帮助你全面地理解织梦CMS的设计哲学和实现细节。
