仰望星辰工作室

better-staridc-MNBT

Z zfhsh first commit 2 天前
1---
2title: 插件开发手册
3description: MNBT 插件开发手册:设计原则、新建插件(最短路径)、核心 API 详解(3.1-3.11)与完整示例
4---
5
6# MNBT 插件开发手册
7
8本文面向需要**开发 PHP 业务插件**的开发者。
9先读 [插件系统总览](./index.md) 了解启用方式与目录约定。
10
11> **注意**:本文的插件与宝塔侧 `plugins/mnbt_connector`(Python 节点代理)**无关**。
12> PHP 业务插件只放在站点根目录 `app_plugins/` 下。
13
14---
15
16## 1. 设计原则
17
181. **不改核心 URL**:业务接口仍走 `user/ajax.php` / `admin/ajax.php`;页面走 `plugin.php?p=slug&page=...`。
192. **插件只注册、不劫持核心 `gn`**:自定义 AJAX 的 `gn` 必须用前缀,建议 `p_{slug}_{action}`。
203. **配置进 option 表**:用 `MN_plugin_option`,不要往 `MN_config` 加列。
214. **文件只在插件目录内**:页面路径禁止跳出 `app_plugins/{slug}/`。
225. **与主题分离**:主题管外观(`templates/`);插件管业务能力。
236. **升级友好**:自定义代码放在 `app_plugins/`,避免改 `MPHX/`、`user/`、`admin/` 核心文件。
24
25---
26
27## 2. 新建插件(最短路径)
28
29### 步骤 1:创建目录
30
31```text
32app_plugins/
33└── my_plugin/ # 目录名 = 插件 ID(字母数字下划线横线,≤63)
34 ├── plugin.json # 必填
35 ├── bootstrap.php # 必填
36 ├── install.sql # 可选:建插件自有表
37 ├── uninstall.sql # 可选:卸载时清理
38 ├── admin/ # 可选:后台页面
39 │ └── index.php
40 ├── user/ # 可选:用户端页面
41 │ └── index.php
42 └── assets/ # 可选:静态资源
43```
44
45### 步骤 2:编写 `plugin.json`
46
47```json
48{
49 "id": "my_plugin",
50 "name": "我的插件",
51 "version": "1.0.0",
52 "author": "YourName",
53 "description": "插件一句话说明",
54 "requires_mnbt": "1.81",
55 "type": ["business"]
56}
57```
58
59| 字段 | 必填 | 说明 |
60|------|------|------|
61| `id` | 建议 | 与目录名一致;引擎以**目录名**为准 |
62| `name` | 是 | 后台「插件管理」显示名 |
63| `version` | 是 | 版本号 |
64| `author` | 否 | 作者 |
65| `description` | 否 | 简介 |
66| `requires_mnbt` | 否 | 文档用最低版本 |
67| `type` | 否 | 文档分类,如 `business` / `lifecycle` / `integration` |
68
69### 步骤 3:编写 `bootstrap.php`
70
71```php
72<?php
73if (!defined('IN_CRONLITE')) {
74 exit;
75}
76
77mnbt_plugin_register('my_plugin', ['name' => '我的插件']);
78
79// 后台菜单
80mnbt_register_menu('admin', [
81 'title' => '我的插件',
82 'page' => 'index',
83 'icon' => 'mdi-puzzle',
84 'order' => 20,
85 'multitabs' => true,
86]);
87
88// 后台页面(相对插件根目录)
89mnbt_register_page('admin', 'index', 'admin/index.php', '我的插件');
90
91// 可选:出现在「插件管理」页顶部快捷入口
92mnbt_register_settings_tab([
93 'title' => '我的插件设置',
94 'page' => 'index',
95 'order' => 20,
96]);
97
98// AJAX:POST admin/ajax.php gn=p_my_plugin_ping
99mnbt_register_ajax('admin', 'p_my_plugin_ping', function () {
100 mnbt_plugin_require_admin();
101 json_exit_success('pong', ['time' => date('Y-m-d H:i:s')]);
102});
103
104// 生命周期钩子
105mnbt_add_action('host.created', function ($host, $ctx = []) {
106 // $host 为主机行数组;$ctx 含 source 等
107});
108```
109
110### 步骤 4:后台页面示例 `admin/index.php`
111
112```php
113<?php
114if (!defined('IN_CRONLITE')) {
115 exit;
116}
117mnbt_admin_include('head');
118?>
119<div class="container-fluid p-t-15">
120 <div class="card">
121 <div class="card-header"><h4>我的插件</h4></div>
122 <div class="card-body">
123 <button type="button" class="btn btn-primary" id="btn-ping">Ping</button>
124 </div>
125 </div>
126</div>
127<script>
128$('#btn-ping').on('click', function () {
129 $.post('ajax.php', {gn: 'p_my_plugin_ping'}, function (res) {
130 try { res = typeof res === 'string' ? JSON.parse(res) : res; } catch (e) {}
131 alert(res.msg || res.code || JSON.stringify(res));
132 });
133});
134</script>
135```
136
137### 步骤 5:启用
138
1391. 将目录放到 `app_plugins/my_plugin/`
1402. 后台 → 系统管理 → **插件管理** → **安装** → **启用**
1413. **整页刷新**后台(侧栏菜单才会出现)
142
143### 步骤 6:自测清单
144
145- [ ] 插件管理中可见、可启用/禁用
146- [ ] 侧栏菜单可打开页面
147- [ ] AJAX 成功返回 JSON
148- [ ] 禁用后接口/菜单失效
149- [ ] 主机开通/删除等钩子(若用了)有预期行为
150
151---
152
153## 3. 核心 API 详解
154
155引擎文件:`MPHX/plugin.php`(由 `common.php` 在鉴权后启动 `mnbt_plugins_boot()`)。
156
157### 3.1 注册与元信息
158
159| 函数 | 说明 |
160|------|------|
161| `mnbt_plugin_register($id, $meta)` | 注册元信息(可选,推荐写) |
162| `mnbt_plugin_id()` | 当前插件 slug(钩子/AJAX 回调内有效) |
163| `mnbt_plugin_path($slug = null)` | 插件绝对路径,末尾带 `/` |
164| `mnbt_plugin_url($slug = null, $rel = '')` | 资源 URL,如 `/app_plugins/my_plugin/assets/a.css` |
165| `mnbt_plugin_enabled($slug)` | 是否已启用 |
166
167### 3.2 钩子(Action / Filter)
168
169```php
170// 监听
171mnbt_add_action('host.created', function ($host, $ctx = []) { ... }, 10);
172mnbt_add_filter('menu.admin', function ($items) {
173 // 可改菜单数组后 return
174 return $items;
175}, 10);
176
177// 触发(一般由核心调用,插件很少自己 do_action)
178mnbt_do_action('my_event', $arg1, $arg2);
179$value = mnbt_apply_filters('my_filter', $value, $extra);
180```
181
182- 同一钩子可多个回调;`$priority` 数字越小越先执行(默认 `10`)。
183- 回调异常会被捕获并写入 PHP 错误日志,不中断主流程。
184
185### 3.3 AJAX
186
187```php
188mnbt_register_ajax('admin', 'p_my_plugin_save', function ($egn, $side) {
189 mnbt_plugin_require_admin();
190 // 读 $_POST,写 option,返回 JSON
191 json_exit_success('已保存');
192});
193
194mnbt_register_ajax('user', 'p_my_plugin_list', function () {
195 mnbt_plugin_require_user();
196 // 全局 $yhc 为当前主机用户
197 json_exit_success('ok', ['items' => []]);
198});
199```
200
201| 侧 | 请求 | 鉴权 |
202|----|------|------|
203| admin | `POST admin/ajax.php`,`gn=...` | 管理员 cookie;回调内再 `mnbt_plugin_require_admin()` |
204| user | `POST user/ajax.php`,`gn=...` | 用户已登录;回调内再 `mnbt_plugin_require_user()` |
205
206分发顺序:**插件注册表 → 核心 `api/*.php`**。
207重复 `gn` 后注册失败并写错误日志。
208
209**注意:与核心 `gn` 同名的陷阱**
210插件 AJAX 在 `user/ajax.php` 中分发时,**早于**核心条件分支(如 CDN 产品检查 `hxc=='1'`)。
211因此若插件注册了 `gn='tjurl'`,则 CDN 产品的 `tjurl` 请求也会被插件处理器接管,
212即便插件本意只想处理非 CDN 场景。最佳实践:始终使用 `p_{slug}_{action}` 前缀。
213参考 `domain_shop` 插件:原 `tjurl/scurl/seturl` 改名为 `p_domain_tjurl` 等,
214让 CDN 产品继续走核心 `user/api/cdn.php`。
215
216**推荐返回:**
217
218```php
219json_exit_success($msg, $extra); // qk=1
220json_exit_error($msg, $extra); // qk=4
221// 或兼容旧式:
222json_exit('提示文案');
223```
224
225### 3.4 菜单与页面
226
227菜单注册、页面注册、页面接管(Page Override)与 Partial 接管见 **[菜单与页面](./menu.md)**。
228
229### 3.5 配置存储
230
231表:`MN_plugin_option`(`plugin_slug` + `k` + `v`)。
232
233```php
234mnbt_plugin_option_set('my_plugin', 'api_key', 'xxx');
235$v = mnbt_plugin_option_get('my_plugin', 'api_key', '默认值');
236// 数组/对象会自动 JSON 编解码
237mnbt_plugin_option_set('my_plugin', 'flags', ['a' => true]);
238$all = mnbt_plugin_option_all('my_plugin');
239```
240
241### 3.6 仪表盘小部件
242
243```php
244mnbt_register_widget('admin', [
245 'title' => '统计卡片',
246 'order' => 10,
247 'class' => 'col-sm-6',
248 'callback' => function ($side) {
249 echo '<p>内容 HTML</p>';
250 },
251 // 或 'html' => '<p>静态 HTML</p>',
252]);
253```
254
255渲染位置:
256
257- 管理首页:`templates/default/admin/sy.php`
258- 用户仪表盘:`templates/default/user/sy.php`
259
260### 3.7 设置快捷入口
261
262```php
263mnbt_register_settings_tab([
264 'title' => 'Webhook 通知',
265 'page' => 'settings',
266 'order' => 10,
267]);
268```
269
270显示在后台 **插件管理** 页顶部按钮区。
271
272### 3.8 HTTP 出站
273
274```php
275$res = mnbt_http_post('https://example.com/hook', [
276 'event' => 'test',
277], [
278 'timeout' => 10,
279 'headers' => ['X-Token: abc'],
280 // 'insecure' => true, // 跳过 SSL 校验(不推荐)
281 // 'allow_private' => true, // 允许内网(默认禁止)
282]);
283// $res = ['ok'=>bool, 'code'=>int, 'body'=>string, 'error'=>string]
284```
285
286仅允许 `http://` / `https://`;默认拒绝 localhost / 私网 IP。
287
288### 3.9 日志
289
290```php
291mnbt_log($user ?: '系统', '插件-我的插件', '做了某事', '成功', $DB);
292```
293
294### 3.10 首页接管(V1.81 P2)
295
296让插件接管站点根路径 `/` 的响应(重定向 / 渲染 / 关闭三模式),见 **[首页接管与通用路由](./route.md)**。
297
298### 3.11 通用路由(V1.81 P2)
299
300让插件接管任意路径的响应(支持命名参数、方法限定、两种访问方式),见 **[首页接管与通用路由](./route.md)**。
301
302---
303
304## 8. 完整示例:监听开通并写日志
305
306```php
307// app_plugins/open_log/bootstrap.php
308<?php
309if (!defined('IN_CRONLITE')) exit;
310
311mnbt_plugin_register('open_log', ['name' => '开通日志']);
312
313mnbt_add_action('host.created', function ($host, $ctx = []) {
314 $u = is_array($host) ? ($host['user'] ?? '') : '';
315 $src = is_array($ctx) ? ($ctx['source'] ?? '') : '';
316 $line = date('Y-m-d H:i:s') . " open user={$u} source={$src}";
317 $log = mnbt_plugin_option_get('open_log', 'lines', []);
318 if (!is_array($log)) $log = [];
319 array_unshift($log, $line);
320 mnbt_plugin_option_set('open_log', 'lines', array_slice($log, 0, 100));
321});
322```
323
324更完整的可运行示例:
325
326| 目录 | 演示点 |
327|------|--------|
328| `hello_demo/` | 菜单、配置读写、Ping AJAX、主机/订单事件本地日志 |
329| `webhook_notify/` | 设置页、事件开关、HTTP POST、HMAC 签名、投递日志、后台小部件 |
330
331更多章节:钩子与数据库、安全清单、FAQ、文件索引见 **[钩子与数据库](./hooks.md)**;支付插件系统见 **[支付插件系统](./payment.md)**。