从入站我就一直很好奇,一个这么轻量的bbs,怎么能支持那么强的插件系统,几乎无所不能。
今天让AI给我做了个分析(没接触过php,不太懂),感觉大抵和前端SPA有点像嘛,插件、hook。
看了源码,我觉得可以把 bbs1org 的架构总结成这张图:
index.php
├── 启动常量和 autoloader
├── 核心路由表 core_routes()
│ ├── home → home_page()
│ ├── forum → forum_page()
│ ├── topic → topic_page()
│ ├── admin → admin_route()
│ ├── cron → Cron::cron_route()
│ └── plugin_market_* → Plugin::*
├── 插件路由 plugin_route($action)
│ └── 扫描 app/plugins/*/plugin.php 的 manifest['routes']
├── Hook 系统
│ ├── hook_registry() → 聚合所有 enabled 插件的 hooks
│ ├── hook($name, $value, $ctx) → 链式调用,可改值
│ └── fire($name, $ctx) → 无返回值,纯事件
├── 插件管理
│ ├── 同步: Plugin::plugin_registry_sync()
│ ├── 启用/停用
│ ├── 资源打包: plugins.css + plugins.js
│ └── 市场: 远程拉取、安装、分享
└── 兜底 404它是怎么做到“单文件支持插件”的?
核心就 3 个机制:
1. 约定式插件入口
每个插件是一个目录 app/plugins/<id>/plugin.php,文件末尾 return [...] 一个 manifest 数组:
hooks:声明要监听哪些钩子routes:声明自定义路由 actionadmin_tabs:声明后台管理页面assets:声明要注入的 CSS/JScron:声明定时任务install/uninstall:声明安装卸载回调
2. 极简 Hook 系统
// 定义钩子点
$value = hook('topic.can_manage', $allowed, ['topic' => $t]);
// 插件注册
'hooks' => [
'topic.can_manage' => 'my_plugin_check_permission'
]hook()会遍历所有插件的回调,像管道一样链式传递值,后面的插件可以改前面插件的结果fire()是纯事件通知,不传值
3. 延迟加载 + 运行时缓存
plugin_registry_sync()扫描插件目录 → 解析 manifest → 写入app_plugins表- 运行时通过
setting('cache_plugins')缓存,避免每次都读文件 - 调用时才
include $file,不调用不加载 - 插件报错自动停用 (
plugin_disable_after_exception)
路由扩展流程
用户访问 /?a=oauth_authorize
↓
core_routes() 没有这个 key
↓
plugin_route('oauth_authorize')
↓
找到 oauth 插件的 manifest['routes']['oauth_authorize'] = 'oauth_authorize_page'
↓
plugin_call($plugin, fn() => $fn($plugin))
↓
执行 oauth_authorize_page()数据隔离
- 核心表用
app_前缀 - 插件表用
plugin_<id>_前缀(如plugin_oauth_clients) - 插件安装/卸载时自己建表/删表
资源打包
所有插件的 CSS/JS 不单独请求,而是在同步时合并成:
app/assets/plugins.cssapp/assets/plugins.js
这样前端也不需要知道插件存在,只要引入这两个文件即可。
本质上看
bbs1org 的插件系统其实是一个 基于 manifest 的“约定优于配置”系统:
- 没有复杂的服务容器
- 没有 IoC/依赖注入
- 没有中间件栈
- 就是简单的:扫描目录 → 解析 return 数组 → 把钩子和路由注册到全局数组 → 核心框架按需调用
这种设计的代价是 强约定、弱隔离:
- 插件可以直接访问核心函数的全局变量
- 函数命名冲突靠扫描检测(
plugin_function_conflicts) - 插件出错会影响整个请求,但会被自动停用
这确实很“小而美”,适合个人站长或小团队,但如果要做大规模生态,隔离性和稳定性会是瓶颈。