架构总览

Clara 无框架自研架构:核心类、控制器与路由、目录组织与运行机制,理解系统全貌的起点。

01 · 架构总览

1. 技术栈与设计哲学

维度选型说明
语言PHP 7.4 – 8.5兼容宽版本区间,虚拟主机友好
数据库MySQL 5.7+ / 8.0(PDO)utf8mb4,表名统一前缀
依赖零 Composer 依赖SMTP、云存储签名(七牛/OSS/COS)均为原生实现
前端原生 JS + CSS(无构建步骤)`assets/js/app.js`、`assets/css/app.css` 直接引用
架构单入口 MVC前台 `index.php`、后台 `admin.php` 双入口
扩展钩子(Hook)+ 插件(Plugin)参照 WordPress / Typecho 插件机制

设计哲学可概括为三点:

  1. 静态门面风格:核心能力(`Db`、`Auth`、`View`、`Settings`、`Plugin`、`Hook`…)全部以静态类提供,控制器与视图内直接调用,无容器、无依赖注入。
  2. 约定优于配置:后台路由按 `Admin{Module}Controller` 命名自动映射;插件按目录名 + `plugin.php` 约定自动发现。
  3. 插件代码与核心解耦:所有扩展点收敛到 `Hook::fire()/filter()`,插件抛错只记日志不阻断主流程。

2. 目录结构

luntan/
├── index.php                 # 前台入口(未安装时跳转 install/)
├── admin.php                 # 后台入口
├── restore.php               # 数据库还原 CLI(仅命令行运行,配套后台备份下载)
├── config/
│   └── config.php            # 安装程序生成的配置(db / prefix / timezone / key)
├── app/
│   ├── bootstrap.php         # 引导 + App 调度器(自动加载/会话/错误处理)
│   ├── helpers.php           # 全局辅助函数聚合入口(require 13 个域文件,v2026.09.15 拆分)
│   ├── helpers/              # 辅助函数域文件(13 个:general/icon/user/economy/render/attachment/mention/notify/forum/perm/captcha/admin-sec/runtime;173 函数 + 3 个顶级 Cron 注册,函数体与拆分前零差异)
│   ├── routes.php            # 前台路由表
│   ├── admin_routes.php      # 后台路由表(含 AdminRouter 约定映射)
│   ├── Core/                 # 核心框架类(15 个)
│   │   ├── Router.php        #   路由器({param} 占位符 / .html 伪静态)
│   │   ├── Db.php            #   PDO 封装(q/one/all/val/insert/update…)
│   │   ├── Auth.php          #   认证与权限(会话/记住我/组权限/版块权限/会员)
│   │   ├── Plugin.php        #   插件管理(扫描/启停/配置/元信息解析)
│   │   ├── Hook.php          #   钩子系统(action: fire / filter: filter)
│   │   ├── View.php          #   视图渲染(render/display/renderFile)
│   │   ├── Settings.php      #   站点设置 KV 缓存(文件缓存 60s + 写穿)
│   │   ├── SessionStore.php  #   自定义文件会话存储(读不锁/写原子,并发优化)
│   │   ├── Cache.php         #   KV 缓存抽象层(Redis/文件双驱动,v2026.09.06)
│   │   ├── Cron.php          #   系统计划任务(懒触发 + 外部触发,v2026.09.06)
│   │   ├── Request.php       #   请求封装(GET/POST/IP/UA/wantsJson)
│   │   ├── Csrf.php          #   CSRF 令牌(表单 + X-CSRF-Token 头)
│   │   ├── Validator.php     #   输入校验器(required/email/unique…)
│   │   ├── Controller.php    #   前台控制器基类(POST 自动 CSRF)
│   │   └── AdminController.php # 后台控制器基类(套 admin/layout)
│   ├── Controllers/          # 前台业务控制器(23 个)
│   ├── Admin/                # 后台控制器(26 个,Admin{Module}Controller 命名)
│   ├── Services/             # 服务层
│   │   ├── GeoService.php    #   GEO/AI 搜索优化(answers/llms/JSON-LD/推送/体检)
│   │   ├── CreditRuleService.php #  积分规则引擎(事件钩子 → 货币发放)
│   │   ├── Mailer.php        #   原生 SMTP 客户端
│   │   ├── MedalService.php  #   勋章自动检测与授予
│   │   ├── SeoService.php    #   sitemap / RSS 生成
│   │   └── Storage/          #   云存储工厂 + 4 驱动(local/qiniu/oss/cos)
│   └── Views/                # 视图(PHP 模板)
│       ├── layout.php        #   前台主布局(导航/页脚/钩子点位)
│       ├── *.php             #   前台页面(home/thread/forum/uc/answers/pm/…)
│       ├── partials/         #   复用组件(editor/thread-row/left-quick-nav/cat-nav…)
│       └── admin/            #   后台视图(layout.php + 各管理页)
├── content/plugins/          # 插件目录(14 个,每插件一个子目录)
│   ├── ai_reply/             #   AI 智能回复(v1.4.1)
│   ├── ai_writer/            #   AI 智能写作(v1.5.5)
│   ├── api_hub/              #   API 工具箱(v1.0.2)
│   ├── site_rank/            #   社区排行榜(v1.0.0)
│   ├── member_wall/          #   首页会员墙(v1.0.0)
│   ├── ad_self/              #   广告位自助投放(v1.7.0)
│   ├── lottery/              #   幸运抽奖(v1.3.0)
│   ├── ks_exam/              #   入站考试(v1.0.0)
│   ├── geo_qa_factory/       #   GEO 问答工厂(v1.0.0)
│   ├── brand_landing/        #   品牌落地页(v1.0.0)
│   ├── quick_reply/          #   快速回帖(v1.1.0)
│   ├── thread_visitors/      #   帖子访客展示(v1.0.1)
│   ├── copy_tail/            #   复制保护小尾巴(v1.0.0)
│   └── mourning/             #   黑白哀悼模式(v1.3.0)
├── assets/                   # 静态资源
│   ├── js/  (app.js / admin.js / editor.js)
│   ├── css/ (app.css / admin.css / editor.css / theme-trae.css)
│   ├── avatar/ (随机默认头像池)
│   ├── gif/ (表情包)
│   ├── img/ (内置图片/favicon)
│   └── music/ (通知提示音)
├── install/                  # 安装向导(index.php 四步流程 + schema.php 49 张表 DDL)
├── storage/                  # 运行时数据(cache / logs / sessions)
└── uploads/                  # 上传文件(按 年月/日 分目录)

3. 请求生命周期

以一次前台访问 `GET /thread-123.html` 为例(入口 [index.php](../index.php),引导 [bootstrap.php](../app/bootstrap.php)):

HTTP 请求
  │
  ├─ index.php:定义 BASE_PATH / APP_START → require app/bootstrap.php
  │    └─ 未安装且存在 install/ → 302 跳转安装向导
  │
  ├─ bootstrap.php 启动序列(仅在 config/config.php 存在时):
  │    1. spl_autoload_register:按 Core→Controllers→Admin→Services→Services/Storage 顺序查找类文件
  │    2. require app/helpers.php(全局函数)
  │    3. 加载 config/config.php → $GLOBALS['__config'],define('PREFIX', 前缀)
  │    4. 错误日志指向 storage/logs/php_error.log,设定时区
  │    5. Db::init($config['db'])          ← PDO 连接(异常模式 / FETCH_ASSOC / 禁用模拟预处理)
  │    6. 会话:回退 storage/sessions、cookie 名 clarabbs、HttpOnly + SameSite=Lax;
  │       注册 SessionStore 自定义处理器(读不持锁 + 写原子替换,同用户并发请求不串行;目录不可写回退默认)
  │    7. Settings::load()                  ← settings 表载入(文件缓存 60s + 写穿,未命中才查库)
  │    8. Plugin::boot()                    ← 逐个 include 已启用插件的 plugin.php(注册钩子)
  │       (helpers 聚合入口在此前 require:app/helpers/ 下 13 个域文件,含 3 个顶级 Cron::register 清理任务)
  │    9. CreditRuleService::boot()         ← 积分规则引擎注册业务事件钩子(表未建静默关闭)
  │   10. GeoService::boot()                ← GEO:新帖推送钩子 + 每日体检 Cron(未就绪静默降级)
  │   11. list_cache_boot()                 ← 列表页游客缓存失效钩子(发帖/回帖 bump list_ver)
  │
  ├─ App::run('web')
  │    1. canonical_host_redirect()         ← 配置 site_url 后非主域 308 统一跳转(防会话丢失)
  │    2. GeoService::recordBotHit()        ← AI 爬虫 UA 访问监控(非爬虫纳秒级返回)
  │    3. require app/routes.php            ← 注册核心前台路由
  │    4. Hook::fire('routes_register', $router) ← 插件追加自有路由
  │    5. register_shutdown_function(Cron::lazyTick) ← 计划任务懒触发(shutdown 阶段必达)
  │    6. maintenance_guard()               ← 维护模式拦截(503 + Retry-After;后台入口不受影响)
  │    7. $router->dispatch()               ← 见下
  │
  ├─ Router::dispatch()
  │    · URI 剥离子目录前缀、/index.php、.html 伪静态后缀
  │    · 逐条正则匹配 → 命中后参数写入 $GLOBALS['__params']
  │    · invoke:'ThreadController@show' → (new ThreadController())->show()
  │    · 未命中 → 404(wantsJson 则输出 JSON)
  │
  ├─ Controller 构造(Controller 基类)
  │    · POST 请求自动 Csrf::check()
  │    · Auth::boot()(惰性解析当前用户:session uid + remember cookie 兜底)
  │
  ├─ Action 业务逻辑
  │    · 调用 Db / Auth / 服务类 / helpers 全局函数
  │    · Hook::fire('thread_created'|'post_created'|…) 通知插件
  │
  └─ 渲染响应
       · View::display('thread', $vars) → 先渲染视图为 $content,再套 layout.php 输出
       · 或 json_out() 输出 JSON(自动 no-store 防缓存)
       · 异常:写入 storage/logs/error_Ymd.log → 500 页 / JSON 错误

后台请求走 `admin.php` → `App::run('admin')` → `admin_routes.php`,先 `Hook::fire('admin_routes_register', $router)` 让插件注册后台页,再由 `/admin.php/{module}/{action}` 通配路由交给 `AdminRouter::dispatch()`(登录 + 管理员 + CSRF 三重守卫,详见 [04-后台管理模块](04-后台管理模块.md))。

4. 分层架构与依赖方向

┌──────────────────────────────────────────────────────┐
│  入口层    index.php / admin.php / install/index.php  │
├──────────────────────────────────────────────────────┤
│  路由层    routes.php / admin_routes.php (+ 插件路由) │
├──────────────────────────────────────────────────────┤
│  控制层    Controllers/*(前台)  Admin/*(后台)      │
│            ↑ 继承 Controller / AdminController        │
├──────────────────────────────────────────────────────┤
│  服务层    Services/*(Geo/CreditRule/Mailer/Medal/Seo/Storage)│
├──────────────────────────────────────────────────────┤
│  核心层    Core/*(Router/Db/Auth/View/Hook/Plugin/   │
│            Settings/Request/Csrf/Validator)          │
│            helpers.php 全局函数                        │
├──────────────────────────────────────────────────────┤
│  数据层    MySQL(clara_* 表 + plugin_* 插件表)       │
├──────────────────────────────────────────────────────┤
│  扩展层    content/plugins/*(只经 Hook 与 Plugin 接触核心)│
└──────────────────────────────────────────────────────┘

依赖规则:

  • 核心层不依赖业务层:`Core/*` 只互相引用(如 Controller → Csrf/Auth),从不 include 控制器。
  • 业务层向下依赖:控制器可用核心类、服务类、helpers;服务类只用核心类 + Db。
  • 插件单向依赖核心:插件可调用一切核心 API(Db/Auth/Hook/View/Plugin::config),核心对插件只通过 `Plugin::boot()` 加载与 `Hook` 回调感知,未启用的插件代码永不执行。
  • 视图层最末端:视图只用 helpers 函数 + `$vars` 数据,不做业务查询(少量展示性查询如 `avatar_html()` 内置的兜底查询除外)。

5. 关键横切机制

机制实现位置说明
自动加载`bootstrap.php` spl\_autoload按固定目录列表查找 `{ClassName}.php`,类名即文件名
CSRF`Core/Csrf.php` + `Controller` 基类所有 POST 自动校验;AJAX 走 `X-CSRF-Token` 头
权限`Core/Auth.php`用户组 perms JSON 合并 + 版块级覆盖 + 版主判定 + 管理员全通
会话`bootstrap.php` + `Core/SessionStore.php`cookie `clarabbs`,HttpOnly + SameSite=Lax + HTTPS 下自动 Secure(`Request::isHttps()`);登录成功 `session_regenerate_id` 防固定;自定义存储读不锁/写原子(同用户并发不串行)
错误处理`App::run()` catch生产不显示错误,全部落 `storage/logs/error_Ymd.log`
设置缓存`Core/Settings.php`settings 表载入内存 + 文件缓存(60s TTL),`set()` 同步写库并写穿缓存
插件容错`Core/Hook.php`回调 try/catch,异常写 `plugin_error_Ymd.log`,不影响主流程

6. 前后端交互模式

  • 传统表单为主:多数页面为服务端渲染 + POST 表单(带 `csrf_field()`),成功后 `redirect()` + flash 消息。
  • 局部 AJAX 增强(`assets/js/app.js`):
  • 通知轮询:`GET /notifications/poll`,15 秒一次,支持提示音与桌面提醒
  • 新回复检测:`GET /thread-{id}/sync`(帖子页 10 秒 / 版块与首页 20 秒),有新回复自动刷新并保持滚动位置
  • 点赞 / 收藏 / 礼物 / 删除等操作走 `fetch` + JSON
  • JSON 输出约定:`json_out(array('ok' => bool, 'msg' => string, ...))`;未登录 AJAX 返回 `{'ok':false,'need_login':true}` 由前端引导登录。
  • CSRF 自愈:AJAX 校验失败时服务端返回 `csrf_expired` 标记,前端自动 `GET /csrf/token` 拉取当前会话新 token 重试一次(editor.js 已内置,复杂插件 JS 可复用同协议)。

本文档随系统发布维护;如发现与当前版本不符,欢迎到社区反馈。