04-插件生命周期管理
# 第4章 插件生命周期管理
## 4.1 安装 (install.php)
### 4.1.1 触发时机
用户在后台点击"安装"按钮时,系统调用`plugin_install()`函数,该函数:
1. 执行`install.php`
2. 更新`conf.json`中的`installed=1`
3. 清空`tmp/`缓存
### 4.1.2 标准模板
```php
tablepre;
// 创建数据表
$sql = "CREATE TABLE IF NOT EXISTS {$tablepre}my_table (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) unsigned NOT NULL default '0',
`tid` int(11) unsigned NOT NULL default '0',
`create_date` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (id),
KEY (uid),
KEY (tid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
db_exec($sql);
// 给现有表添加字段(先检查是否已存在)
$columns = db_find_index('thread');
if (!in_array('my_field', array_column($columns, 'Column_name'))) {
db_exec("ALTER TABLE {$tablepre}thread ADD COLUMN my_field int(11) DEFAULT '0';");
}
// 初始化插件配置
setting_set('my_plugin', array(
'option1' => 'default_value1',
'option2' => 'default_value2',
'option3' => 0,
));
```
### 4.1.3 常见安装操作
| 操作 | 代码 | 说明 |
|------|------|------|
| 创建表 | `db_exec($sql)` | 使用CREATE TABLE IF NOT EXISTS |
| 添加字段 | `db_exec("ALTER TABLE ... ADD COLUMN ...")` | 先检查字段是否已存在 |
| 初始化配置 | `setting_set('key', $array)` | 使用setting而非kv,便于后台管理 |
| 初始化KV | `kv_set('key', $value)` | 简单键值存储 |
| 初始化缓存 | `cache_set('key', $data, $ttl)` | 带过期时间的缓存 |
### 4.1.4 ALTER TABLE的最佳实践
```php
// 安全添加字段:先检查再添加
$col_exists = db_find_one("SHOW COLUMNS FROM {$tablepre}thread LIKE 'my_field'");
if (empty($col_exists)) {
db_exec("ALTER TABLE {$tablepre}thread ADD COLUMN my_field int(11) DEFAULT '0';");
}
```
> **坑**: 直接执行ALTER TABLE ADD COLUMN时,如果字段已存在会报错。升级场景下必须先检查。
## 4.2 卸载 (unstall.php)
### 4.2.1 触发时机
用户在后台点击"卸载"按钮时,系统调用`plugin_unstall()`函数(注意函数名拼写为unstall而非uninstall)。
### 4.2.2 标准模板
```php
tablepre;
// 删除数据表
db_exec("DROP TABLE IF EXISTS {$tablepre}my_table;");
// 删除添加的字段
db_exec("ALTER TABLE {$tablepre}thread DROP COLUMN my_field;");
// 清理配置
setting_delete('my_plugin');
// 清理缓存
cache_delete('my_plugin_cache');
```
### 4.2.3 卸载策略选择
| 策略 | 适用场景 | 实现 |
|------|---------|------|
| 完全清除 | 临时性插件、测试插件 | DROP TABLE + ALTER DROP COLUMN + 删除配置 |
| 保留数据 | 重要业务数据插件 | 仅删除配置,保留表和数据 |
| 询问用户 | 可选清除 | 在unstall.php中无法实现交互,需在设置页提供"清除数据"选项 |
> **注意**: xn_search的unstall.php是空实现(不删除搜索索引表),这是保留数据的策略。
## 4.3 升级 (upgrade.php)
### 4.3.1 触发时机
当`conf.json`中的`version`字段与已安装版本不一致时,后台会显示"升级"按钮。
### 4.3.2 标准模板
```php
tablepre;
// 获取当前已安装版本
$old_version = kv_get('my_plugin_version');
// 版本1.0 → 1.1: 添加新字段
if (version_compare($old_version, '1.1', '<')) {
$col_exists = db_find_one("SHOW COLUMNS FROM {$tablepre}my_table LIKE 'new_field'");
if (empty($col_exists)) {
db_exec("ALTER TABLE {$tablepre}my_table ADD COLUMN new_field varchar(255) DEFAULT '';");
}
}
// 版本1.1 → 1.2: 添加新表
if (version_compare($old_version, '1.2', '<')) {
db_exec("CREATE TABLE IF NOT EXISTS {$tablepre}my_new_table (...);");
}
// 更新版本号
kv_set('my_plugin_version', '1.2');
```
### 4.3.3 升级注意事项
- 使用`version_compare()`进行版本比较
- 每个版本升级逻辑独立,支持跨版本升级
- ALTER TABLE前必须检查字段/索引是否已存在
- 升级后更新版本号记录
## 4.4 启用/禁用
### 4.4.1 触发时机
用户在后台点击"启用"/"禁用"按钮。
### 4.4.2 系统行为
```php
function plugin_enable($dir) {
$plugins[$dir]['enable'] = 1;
file_replace_var(APP_PATH."plugin/$dir/conf.json", array('enable'=>1), TRUE);
plugin_clear_tmp_dir(); // 清空tmp缓存
}
function plugin_disable($dir) {
$plugins[$dir]['enable'] = 0;
file_replace_var(APP_PATH."plugin/$dir/conf.json", array('enable'=>0), TRUE);
plugin_clear_tmp_dir(); // 清空tmp缓存
}
```
**关键**: 启用/禁用操作仅修改`conf.json`中的`enable`字段并清空缓存,不会执行任何自定义代码。
> **坑**: 禁用插件后,该插件创建的数据库表和配置仍然存在。如果需要在禁用时执行清理逻辑,没有官方支持的方式,只能通过overwrite后台路由来实现。
## 4.5 生命周期完整流程
```
创建插件目录 → 编写代码 → 后台安装(install.php) → 启用 → 使用
↓
禁用 → 重新启用
↓
卸载(unstall.php) → 删除目录
版本更新 → 修改version → 后台升级(upgrade.php) → 继续使用
```
## 4.6 依赖管理
### 4.6.1 声明依赖
在`conf.json`中声明:
```json
{
"dependencies": {
"tt_credits": "1.09"
}
}
```
### 4.6.2 依赖检查
Xiuno BBS**不会自动安装依赖插件**,仅在后台显示依赖提示。开发者需要在`install.php`中手动检查:
```php
// 检查依赖插件是否已安装
if (!isset($plugins['tt_credits']) || empty($plugins['tt_credits']['installed'])) {
message(-1, '请先安装积分插件(tt_credits)');
}
```
> **坑**: 系统不强制检查依赖,如果不在install.php中手动检查,插件安装后可能因缺少依赖而报错。