TUTORIAL

开源 CMS 源码结构分析

开源 CMS 源码结构分析

开源 CMS 源码结构分析

建站项目里,纯从零开发整套系统的情况并不多见,更多是基于成熟的开源 CMS 做二次开发。WordPress、DedeCMS、PHPCMS 都是常见选择。但拿到一套开源系统的源码,如果不懂它的目录结构和运行逻辑,改起来会处处踩坑——动了一个文件全站报错,升级时自己的修改全被覆盖。本篇以主流 CMS 为例,帮你建立源码结构的整体认知。

一、典型目录结构拆解

主流 CMS 虽然实现细节各异,但目录组织遵循相似的模式。理解这个模式,换一套系统也能快速上手。下面是一个典型 CMS 的目录骨架:

/cms-root/
├── index.php              # 单入口文件,所有请求经此转发
├── admin.php              # 后台入口
├── config/                # 配置文件目录
│   ├── database.php       # 数据库连接配置
│   ├── app.php            # 应用级配置
│   └── route.php          # 路由规则
├── app/                   # 核心应用代码
│   ├── Controller/        # 控制器
│   ├── Model/             # 数据模型
│   ├── View/              # 视图模板
│   └── Service/           # 业务逻辑层
├── public/                # 对外开放的静态资源
│   ├── static/            # CSS/JS/图片
│   └── uploads/           # 用户上传文件
├── runtime/               # 运行时产物(缓存、日志)
├── extend/                # 扩展类库
└── template/              # 前台主题模板
    ├── default/           # 默认主题
    └── mobile/            # 移动端主题

这个结构的核心思想是"分层隔离":配置与代码分离、静态资源与业务代码分离、运行时产物与源码分离。这样做的好处是:升级时只替换 app/config/public/uploads/ 里的用户上传文件不受影响;切换主题只改 template/ 目录,不动核心代码。尧图做二次开发时严格遵循这个边界——该放哪里的代码就放哪里,绝不图省事乱塞。

二、单入口与请求分发

现代 CMS 几乎都采用"单入口"模式:所有请求(除静态资源外)都经过根目录的 index.php,由它加载框架、解析路由、分发到对应控制器。相比早期 PHP 每个页面一个文件的写法,单入口更安全(统一鉴权)、更易维护(公共逻辑集中)、更利于 URL 美化(配合伪静态实现语义化 URL)。

// index.php 单入口简化流程
<?php
// 1. 定义常量
define('APP_PATH', __DIR__ . '/app/');
define('ROOT_PATH', __DIR__ . '/');

// 2. 加载Composer自动加载
require __DIR__ . '/vendor/autoload.php';

// 3. 加载配置
$config = require __DIR__ . '/config/app.php';

// 4. 初始化应用
$app = new Application($config);

// 5. 解析路由并分发请求
// URL: /article/123 → Controller\Article::show(123)
$request = Request::createFromGlobals();
$response = $app->dispatch($request);

// 6. 输出响应
$response->send();

理解单入口的关键是理解"路由分发"。用户访问 /article/123,框架根据路由规则把它映射到 Article 控制器的 show 方法,参数 123 自动传入。这个映射规则定义在 config/route.php 里,二次开发时新增页面只需加一条路由规则、写一个控制器方法,不用新建物理文件。

三、二次开发的正确姿势

二次开发最大的坑是"直接改核心代码"。一旦改了,系统升级时要么放弃升级,要么升级后改动全丢。尧图的原则是:核心代码只读,所有定制通过扩展机制实现。成熟 CMS 都提供了扩展点——WordPress 的主题和插件、DedeCMS 的标签和模块、ThinkPHP 的钩子和行为。把定制代码放进扩展体系,升级核心时完全不受影响。

// 反面示例:直接改核心文件(升级会丢失)
// 文件:app/Controller/Article.php
public function show($id) {
  $article = $this->model->find($id);
  // 直接在核心控制器里加自己的逻辑
  $article['custom_field'] = '我加的字段';  // 升级覆盖!
  return view('article', $article);
}

// 正面示例:通过钩子/事件扩展(升级安全)
// 文件:extend/Hook/ArticleHook.php
class ArticleHook {
  // 监听文章加载完成事件
  public function onArticleLoaded($article) {
    $article['custom_field'] = '我加的字段';
    return $article;
  }
}
// 核心代码里预留了钩子触发点:
// Hook::listen('article_loaded', $article);
// 升级核心不影响你的 Hook 类

模板层是二次开发的主战场。尧图的做法是:复制默认主题到新目录(如 template/yaotu/),在后台切换为当前主题,然后只在新目录里改模板。这样默认主题保持原样,随时可以回退。配置文件用"环境变量覆盖"机制——config/database.php 写默认值,.env 文件写本机实际配置,提交代码时 .env 被 gitignore,避免数据库密码等敏感信息进版本库。把"不改核心、扩展优先、配置分离"这三条原则贯彻到底,二次开发才能既满足定制需求又不丢升级能力。

返回教程列表