本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:8月13日备份/sosite-plugin-dev/plugin-system.md。
SoSite 插件系统介绍
适用版本:SoSite V5.0.0(PHP 8.2 / MySQL 5.7-8.0,自研 MVC,零 Composer 依赖)
统计日期:2026-08-09(与当前源码逐行核对)
关联文档:本目录 SKILL.md(开发规范主入口)、references/01-plugin-structure.md(结构规范)、references/02-hooks-catalog.md(hooks 速查)
一、插件系统架构
1.1 核心设计
| 项 | 说明 |
|---|
| 插件目录 | plugins/{dir}/({dir} 小写下划线,如 daily_checkin) |
| 清单文件 | plugins/{dir}/plugin.json(名称/版本/作者/描述/激活态/hooks 映射) |
| 缓存文件 | plugins/plugins_cache.php(var_export 生成,json_decode 读取;临时文件 + rename 原子写入) |
| 命名空间 | Plugin\{Dir}(Autoloader 自动映射,类如 Plugin\DailyCheckin\CheckinController) |
| 视图解析 | resolveTemplate 插件目录优先:view('plugins/xxx/yyy') → plugins/xxx/views/yyy.php(回落 app/Views) |
| 资源引用 | 直接 /plugins/{dir}/assets/...(勿用 asset(),避免 assets/plugins/ 双份漂移) |
1.2 加载流程(每次请求)
index.php / admin 入口
→ app/init.php: $GLOBALS['__view'] = new TemplateCompiler()
→ \app\Helpers\Plugin::init() # 读缓存(无则扫描目录建缓存)
→ Plugin::hook('init_after') # 初始化钩子
→ 路由注册: Plugin::hook('route_register' | 'admin_route_register')
→ dispatch: Plugin::hook('route_before_dispatch') → 控制器 → Plugin::hook('route_after_dispatch')
1.3 Hook 执行机制(Plugin::hook($name, $params))
foreach (已启用的插件 as $config) {
if ($config['hooks'][$name] 存在) {
$hookFile = plugins/{dir}/ + 相对路径;
// 路径安全:realpath 校验必须在插件目录内(防穿越),违规跳过
include $hookFile; // extract($params) 注入变量
// 异常被 try/catch 捕获,单钩子失败不影响页面
}
}
- 仅启用(plugin.json
activated: true)的插件参与 - 参数经
extract(EXTR_SKIP | EXTR_REFS) 注入(&$data 引用型参数可回写) - 钩子文件内可访问调用点作用域变量(如
$router、$view) - 单个钩子抛异常只 error_log,不影响页面继续
二、插件生命周期
| 阶段 | 触发方式 | 插件可做的事 |
|---|
| 加载 | Plugin::init()(每请求) | — |
| 启用 | 后台"插件管理" → Plugin::activate() | Plugin.php::activate() 建表(幂等 CREATE TABLE IF NOT EXISTS);返回 false 回滚启用 |
| 禁用 | 后台 → Plugin::deactivate() | Plugin::deactivate() 可清理运行时状态 |
| 卸载 | 后台 → Plugin::uninstall() | Plugin::uninstall() 自行 DROP 表/清理数据(框架只删注册与缓存) |
| 缓存刷新 | 改 plugin.json 后 | Plugin::refresh() 重建 plugins_cache.php |
插件类约定(可选):
namespace Plugin\Xxx;
class Plugin {
public static function activate(): bool { return true; }
public static function deactivate(): bool { return true; }
public static function uninstall(): void {}
}
三、核心 Hook 总表(21 个,源码逐行核对)
触发文件 = 调用 Plugin::hook('name', ...) 的位置。
3.1 生命周期与全局
| Hook | 触发文件 | 参数 | 用途 |
|---|
init_after | app/init.php | — | 请求初始化后(懒建表/一次性初始化) |
controller_view_before | app/Core/Controller.php | template、data(引用,可改) | 视图渲染前注入/修改数据 |
route_before_dispatch | index.php | router | 路由执行前拦截/全局处理 |
route_after_dispatch | index.php | — | 请求收尾(计数落库等) |
3.2 路由与导航
| Hook | 触发文件 | 参数 | 用途 |
|---|
route_register | index.php | router | 注册前台路由 |
admin_route_register | index.php(后台分组) | router | 注册后台路由(URL 不带 /admin 前缀) |
admin_sidebar_links | app/Views/admin/_sidebar.php | $view | 后台侧边栏入口 |
nav_main_links | app/Views/layouts/main.php | $view | 前台主导航 |
nav_plugin_links | main.php + _components/left_sidebar.php(两处) | $view(可 null) | "应用"下拉/左栏入口 |
nav_user_menu_items | main.php 用户下拉菜单 | $view | 用户菜单入口(2026-08-09 补触发点,历史曾无调用点) |
3.3 页面注入
| Hook | 触发文件 | 参数 | 用途 |
|---|
layout_head_end | app/Views/layouts/main.php | $view、$bp | </head> 前注入 CSS/JS/meta |
home_sidebar_bottom | right_sidebar.php + mobile_sidebar.php(两处) | — | 侧边栏底部内容 |
announcements_display | home/index.php + forum/index.php(两处) | — | 公告条输出 |
3.4 用户行为
| Hook | 触发文件 | 参数 | 用途 |
|---|
auth_login_after | app/Controllers/AuthController.php | user_id、username | 登录后联动 |
auth_register_before | AuthController.php | — | 注册前校验;错误传 $GLOBALS['auth_register_error'] |
auth_register_after | AuthController.php | user_id、username、email | 注册成功后联动 |
avatar_upload_after | app/Controllers/ProfileController.php | user_id、avatar、old_avatar | 头像上传后(审核等) |
3.5 内容行为
| Hook | 触发文件 | 参数 | 用途 |
|---|
thread_create_after | app/Controllers/PostController.php | thread_id、user_id、category_id | 发帖后 |
post_create_after | app/Controllers/ThreadController.php | post_id、thread_id、user_id | 回帖后 |
thread_edit_after | ThreadController.php | thread_id、user_id | 编辑主题后 |
post_edit_after | PostController.php | post_id、thread_id、user_id | 编辑回复后 |
四、插件 Hook 注册分布(14 个插件)
[启用] = plugin.json activated: true;[停用] = 注册但未启用(钩子不执行)。
| 插件 | 版本 | 状态 | 注册的 hooks |
|---|
| announcements 站点公告 | 1.2.0 | [启用] | init_after, announcements_display, admin_route_register, admin_sidebar_links |
| content_review 内容审核 | 2.0.0 | [停用] | init_after, route_register, admin_route_register, admin_sidebar_links, thread_create_after, post_create_after, auth_register_after, avatar_upload_after, controller_view_before, nav_user_menu_items, nav_main_links, layout_head_end |
| daily_checkin 每日签到 | 1.0.0 | [启用] | init_after, route_register, controller_view_before, nav_plugin_links, nav_user_menu_items |
| dice 摇骰子 | 1.0.0 | [启用] | init_after, route_register, nav_main_links, nav_plugin_links |
| edit_info 编辑信息 | 1.5.0 | [启用] | init_after, post_edit_after, thread_edit_after, controller_view_before |
| friend_links 友情链接 | 2.0.0 | [启用] | init_after, route_register, admin_route_register, admin_sidebar_links, home_sidebar_bottom |
| icon_generator 图标生成器 | 1.0.0 | [启用] | route_register, nav_plugin_links |
| invite 邀请注册 | 1.5.0 | [停用] | init_after, route_register, auth_register_before, auth_register_after, nav_plugin_links, layout_head_end |
| post_favorite 帖子收藏 | 1.0.0 | [启用] | init_after, route_register, nav_user_menu_items, layout_head_end |
| qa 问答系统 | 3.0.0 | [停用] | init_after, route_register, admin_route_register, admin_sidebar_links, nav_main_links, nav_user_menu_items |
| quiz_duel 猜题对决 | 1.0.0 | [启用] | init_after, route_register, nav_plugin_links |
| red_packet 积分红包 | 1.8.0 | [启用] | init_after, route_register, nav_plugin_links |
| seo SEO 优化 | 1.2.0 | [启用] | init_after, route_register, admin_route_register, admin_sidebar_links, layout_head_end |
| user_profile 用户主页 | 1.0.0 | [启用] | init_after, route_register, admin_route_register, admin_sidebar_links, nav_plugin_links |
汇总:14 个插件、11 启用 / 3 停用(content_review、invite、qa);插件共注册 18 个不同 hook 名。
五、Hook 使用统计(注册次数)
| Hook | 注册插件数 | 说明 |
|---|
init_after | 13 | 几乎每个插件都用(建表/初始化) |
route_register | 12 | 前台路由注册标配 |
nav_plugin_links | 7 | 应用入口 |
admin_route_register / admin_sidebar_links | 各 6 | 后台管理类 |
nav_user_menu_items / layout_head_end | 各 4 | 用户菜单 / head 注入 |
controller_view_before / nav_main_links | 各 3 | 视图注入 / 导航 |
auth_register_after | 2 | 注册联动 |
| 其余(announcements_display、auth_login_after、auth_register_before、avatar_upload_after、home_sidebar_bottom、post_create_after、post_edit_after、thread_create_after、thread_edit_after) | 各 1 | 专用钩子 |
六、插件系统 API(app\Helpers\Plugin)
Plugin::init(); // 加载插件(缓存优先)
Plugin::hook($name, $params); // 触发钩子
Plugin::getPlugins(); // 全部插件配置数组
Plugin::getPlugin($name); // 单个插件配置
Plugin::isActivated($name); // 是否启用
Plugin::activate($name); // 启用(写 plugin.json + 调 Plugin.php::activate + 缓存)
Plugin::deactivate($name); // 禁用
Plugin::updateConfig($name, $data); // 更新白名单字段(name/version/author/description/activated/hooks)
Plugin::refresh(); // 重建缓存(改 plugin.json 后必调)
Plugin::uninstall($name); // 卸载(调 Plugin.php::uninstall + 移除注册)
后台入口:/admin/plugins(列表/启用/禁用/编辑配置/卸载),由 app/Controllers/Admin/PluginController 驱动。
七、已知坑与注意点(本系统特有)
nav_user_menu_items 历史无触发点:插件注册了该钩子但核心长期无 Plugin::hook('nav_user_menu_items') 调用(用户菜单入口不显示);2026-08-09 已在 app/Views/layouts/main.php 移动端用户下拉菜单补触发点。若仍不生效,检查核心布局是否有该调用。- 同一 hook 多处触发(nav_plugin_links 两处、home_sidebar_bottom 两处、announcements_display 两处):hook 文件会被 include 多次,输出会重复——用静态变量守卫或把只输出一次的逻辑放 init_after。
auth_register_before 传错约定:必须写 $GLOBALS['auth_register_error'],框架 hook 后读取非空即拦截注册。- 钩子内禁止
exit/die:会终止整页;抛异常会被捕获。 - 视图对象可能为 null:hook 内用
$view = $GLOBALS['__view'] ?? null; 并兜底(icon 不可用时用 emoji/纯文本)。 - PDO 两条铁律(全局
ATTR_EMULATE_PREPARES=false):同一命名占位符不能复用(HY093);SHOW 语句不能有占位符(1064,$conn->quote() 内联)。 - 插件资源引用:直接
/plugins/{dir}/assets/...,勿用 asset()(会解析到 assets/plugins/ 发布目录,双份漂移)。 plugins_cache.php 是运行时产物:上传插件目录时不要覆盖它(含服务器上的启停配置),改 plugin.json 后 Plugin::refresh()。
八、配套文档索引
| 文档 | 内容 |
|---|
SKILL.md | 开发主入口:触发条件/硬规则/工作流 |
references/01-plugin-structure.md | 目录结构、plugin.json、生命周期、最小示例 |
references/02-hooks-catalog.md | hooks 速查(本文件的精简版) |
references/03-api-cheatsheet.md | API 速查(11 大类) |
references/04-security.md | 安全规范与已知坑 |
references/05-ui-templates.md | 模板/UI/资源引用规范 |
references/06-troubleshooting.md | 排错清单 |
references/07-plugin-patterns.md | 典型插件模式与实现模板 |
plugin-system.md(本文件) | 插件系统架构、全部 hooks、注册分布、生命周期 |