插件开发指南
本文档详细介绍 Yuan-ICP 系统的插件开发,包括插件架构、开发流程、钩子系统和使用方法。
📋 插件系统概述
Yuan-ICP 采用模块化插件架构,允许开发者通过插件扩展系统功能,而无需修改核心代码。
核心特性
- 钩子系统:提供插件与核心系统的交互机制
- 自动加载:系统启动时自动加载已安装的插件
- 权限控制:支持插件的启用/禁用管理
- 版本管理:插件版本控制和更新机制
🏗️ 插件架构
目录结构
plugins/
├── plugin-name/ # 插件目录
│ ├── plugin.php # 插件主文件(必需)
│ ├── install.php # 安装脚本(可选)
│ ├── uninstall.php # 卸载脚本(可选)
│ ├── assets/ # 静态资源
│ │ ├── css/ # 样式文件
│ │ ├── js/ # JavaScript文件
│ │ └── images/ # 图片资源
│ ├── templates/ # 模板文件
│ └── README.md # 插件说明文档
核心组件
- PluginManager:插件管理类,负责插件的安装、卸载、启用、禁用
- PluginHooks:钩子系统,提供插件与核心的交互接口
- 插件主文件:定义插件信息和主要功能
🔌 插件开发流程
1. 创建插件目录
bash
mkdir -p plugins/my-plugin
cd plugins/my-plugin
2. 创建插件主文件
创建 plugin.php
文件,这是插件的入口点:
php
<?php
/**
* 我的插件
*
* @package Yuan-ICP
* @subpackage MyPlugin
* @version 1.0.0
* @author 开发者姓名
* @license MIT
*/
// 插件信息
return [
'name' => '我的插件',
'identifier' => 'my-plugin',
'version' => '1.0.0',
'author' => '开发者姓名',
'description' => '这是一个示例插件,展示插件开发的基本流程',
'requires' => '1.0.0', // 系统最低版本要求
'category' => 'utility', // 插件分类
'website' => 'https://example.com',
'license' => 'MIT'
];
// 插件初始化
function my_plugin_init() {
// 注册钩子
PluginHooks::add('admin_menu', 'my_plugin_admin_menu', 10);
PluginHooks::add('frontend_header', 'my_plugin_header', 10);
}
// 注册插件初始化钩子
PluginHooks::add('plugins_loaded', 'my_plugin_init', 10);
// 添加管理菜单
function my_plugin_admin_menu($menu) {
$menu[] = [
'title' => '我的插件',
'url' => 'admin/my-plugin.php',
'icon' => 'fas fa-puzzle-piece',
'permission' => 'admin'
];
return $menu;
}
// 前端头部钩子
function my_plugin_header($header) {
$header .= '<link rel="stylesheet" href="/plugins/my-plugin/assets/css/style.css">';
return $header;
}
3. 创建安装脚本
创建 install.php
文件,处理插件安装时的数据库操作:
php
<?php
/**
* 插件安装脚本
*/
function my_plugin_install() {
$db = db();
// 创建插件专用数据表
$sql = "CREATE TABLE IF NOT EXISTS my_plugin_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)";
try {
$db->exec($sql);
// 插入初始数据
$stmt = $db->prepare("INSERT INTO my_plugin_data (title, content) VALUES (?, ?)");
$stmt->execute(['欢迎使用', '这是插件的初始数据']);
return true;
} catch (Exception $e) {
error_log("插件安装失败: " . $e->getMessage());
return false;
}
}
// 执行安装
if (my_plugin_install()) {
echo "插件安装成功!";
} else {
echo "插件安装失败!";
}
4. 创建卸载脚本
创建 uninstall.php
文件,处理插件卸载时的清理工作:
php
<?php
/**
* 插件卸载脚本
*/
function my_plugin_uninstall() {
$db = db();
try {
// 删除插件数据表
$db->exec("DROP TABLE IF EXISTS my_plugin_data");
// 删除插件配置
$stmt = $db->prepare("DELETE FROM system_config WHERE config_key LIKE 'my_plugin_%'");
$stmt->execute();
return true;
} catch (Exception $e) {
error_log("插件卸载失败: " . $e->getMessage());
return false;
}
}
// 执行卸载
if (my_plugin_uninstall()) {
echo "插件卸载成功!";
} else {
echo "插件卸载失败!";
}
🪝 钩子系统详解
可用的钩子类型
1. 管理界面钩子
admin_menu
:添加管理菜单项admin_dashboard
:在仪表盘添加内容admin_footer
:在管理页面底部添加内容
2. 前端界面钩子
frontend_header
:在页面头部添加内容(CSS、Meta标签等)frontend_footer
:在页面底部添加内容(JavaScript等)frontend_content
:在页面内容区域添加内容
3. 数据处理钩子
before_save_application
:保存申请前处理after_save_application
:保存申请后处理before_send_email
:发送邮件前处理after_send_email
:发送邮件后处理
4. 系统事件钩子
plugins_loaded
:插件加载完成后执行system_init
:系统初始化时执行user_login
:用户登录时执行user_logout
:用户登出时执行
钩子使用示例
添加管理菜单
php
function my_plugin_admin_menu($menu) {
$menu[] = [
'title' => '我的功能',
'url' => 'admin/my-feature.php',
'icon' => 'fas fa-star',
'permission' => 'admin',
'children' => [
[
'title' => '子功能1',
'url' => 'admin/my-feature.php?action=sub1'
],
[
'title' => '子功能2',
'url' => 'admin/my-feature.php?action=sub2'
]
]
];
return $menu;
}
PluginHooks::add('admin_menu', 'my_plugin_admin_menu', 10);
处理申请数据
php
function my_plugin_process_application($application) {
// 在保存申请前添加自定义字段
$application['custom_field'] = '自定义值';
// 记录日志
error_log("处理申请: " . $application['id']);
return $application;
}
PluginHooks::add('before_save_application', 'my_plugin_process_application', 10);
自定义邮件模板
php
function my_plugin_email_template($template, $data) {
if ($data['type'] === 'application_approved') {
// 自定义批准邮件的模板
$template = file_get_contents(__DIR__ . '/templates/email_approved.html');
$template = str_replace('{user_name}', $data['user_name'], $template);
$template = str_replace('{icp_number}', $data['icp_number'], $template);
}
return $template;
}
PluginHooks::add('email_template', 'my_plugin_email_template', 10);
📦 插件打包和分发
1. 创建插件包
bash
# 在插件目录外执行
zip -r my-plugin.zip my-plugin/
2. 插件包结构要求
- 插件目录名必须与
identifier
字段一致 - 必须包含
plugin.php
文件 - 建议包含
README.md
说明文档 - 可选包含
install.php
和uninstall.php
3. 插件安装方式
方式一:通过管理界面上传
- 登录管理后台
- 进入"插件管理"页面
- 点击"上传插件"按钮
- 选择插件ZIP文件
- 系统自动解压并安装
方式二:手动安装
- 将插件目录复制到
plugins/
目录 - 确保文件权限正确(755)
- 刷新插件管理页面
- 启用插件
🔧 插件开发最佳实践
1. 命名规范
- 插件标识符:使用小写字母和连字符,如
my-plugin
- 函数名:使用插件前缀,如
my_plugin_function_name
- 数据表名:使用插件前缀,如
my_plugin_data
- 配置键:使用插件前缀,如
my_plugin_setting
2. 错误处理
php
function my_plugin_safe_function() {
try {
// 插件功能代码
$result = some_operation();
return $result;
} catch (Exception $e) {
// 记录错误日志
error_log("插件错误: " . $e->getMessage());
// 返回默认值或抛出异常
return false;
}
}
3. 权限检查
php
function my_plugin_admin_function() {
// 检查用户权限
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
return false;
}
// 执行管理员功能
return true;
}
4. 数据库操作
php
function my_plugin_db_operation() {
$db = db();
if (!$db) {
return false;
}
try {
$stmt = $db->prepare("SELECT * FROM my_plugin_data WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
} catch (Exception $e) {
error_log("数据库操作失败: " . $e->getMessage());
return false;
}
}
5. 配置管理
php
function my_plugin_get_config($key, $default = null) {
$db = db();
if (!$db) {
return $default;
}
try {
$stmt = $db->prepare("SELECT config_value FROM system_config WHERE config_key = ?");
$stmt->execute(['my_plugin_' . $key]);
$value = $stmt->fetchColumn();
return $value ?: $default;
} catch (Exception $e) {
return $default;
}
}
function my_plugin_set_config($key, $value) {
$db = db();
if (!$db) {
return false;
}
try {
$stmt = $db->prepare("REPLACE INTO system_config (config_key, config_value) VALUES (?, ?)");
return $stmt->execute(['my_plugin_' . $key, $value]);
} catch (Exception $e) {
return false;
}
}
🚀 高级插件功能
1. 自定义管理页面
php
// 在 plugin.php 中注册管理页面
function my_plugin_admin_page() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 处理表单提交
my_plugin_handle_form();
}
// 显示管理页面
include __DIR__ . '/templates/admin_page.php';
}
// 注册管理页面路由
function my_plugin_admin_routes($routes) {
$routes['my-plugin'] = 'my_plugin_admin_page';
return $routes;
}
PluginHooks::add('admin_routes', 'my_plugin_admin_routes', 10);
2. 自定义API接口
php
function my_plugin_api_endpoint($request) {
if ($request['action'] === 'my_plugin_action') {
$data = my_plugin_process_request($request);
return json_encode(['success' => true, 'data' => $data]);
}
return null;
}
PluginHooks::add('api_request', 'my_plugin_api_endpoint', 10);
3. 自定义主题模板
php
function my_plugin_theme_template($template, $data) {
if ($template === 'home') {
// 在首页添加自定义内容
$data['custom_content'] = my_plugin_get_content();
}
return $data;
}
PluginHooks::add('theme_template_data', 'my_plugin_theme_template', 10);
🧪 插件测试
1. 功能测试
- 测试插件的所有功能
- 验证钩子是否正确执行
- 检查数据库操作是否正常
- 测试错误处理机制
2. 兼容性测试
- 测试不同PHP版本兼容性
- 验证数据库兼容性(SQLite、MySQL、PostgreSQL)
- 检查主题兼容性
- 测试与其他插件的兼容性
3. 性能测试
- 检查插件对系统性能的影响
- 验证数据库查询效率
- 测试内存使用情况
- 检查页面加载时间
📚 插件开发资源
1. 参考文档
2. 示例插件
- Hello World插件:基础的插件开发示例
- 统计插件:展示数据统计功能
- 通知插件:展示邮件通知功能
- 主题插件:展示主题定制功能
3. 开发工具
- 插件生成器:快速创建插件骨架
- 调试工具:插件开发和调试辅助
- 测试框架:插件功能测试工具
🚨 注意事项
1. 安全考虑
- 始终验证用户输入
- 使用预处理语句防止SQL注入
- 检查用户权限
- 避免暴露敏感信息
2. 性能考虑
- 避免在钩子中执行耗时操作
- 合理使用缓存机制
- 优化数据库查询
- 控制插件大小
3. 兼容性考虑
- 遵循系统编码规范
- 使用系统提供的API函数
- 避免修改核心文件
- 测试不同环境下的兼容性
💡 常见问题
Q: 插件安装后无法启用?
A: 检查插件主文件格式是否正确,确保返回的数组包含必需的字段。
Q: 钩子没有执行?
A: 确认钩子名称正确,检查插件是否正确加载,验证钩子注册代码。
Q: 插件卸载后数据残留?
A: 在 uninstall.php
中正确清理所有插件相关的数据和配置。
Q: 插件与其他插件冲突?
A: 使用唯一的插件标识符,避免函数名冲突,合理设置钩子优先级。
🎯 总结
通过合理使用插件系统,开发者可以:
- 扩展系统功能:添加新的业务逻辑和功能模块
- 定制用户界面:修改和美化系统界面
- 集成第三方服务:连接外部API和服务
- 优化用户体验:提供个性化的功能和服务
记住,好的插件不仅仅是功能完整,更重要的是:
- 代码质量:遵循编码规范和最佳实践
- 用户体验:提供直观易用的界面
- 系统兼容:确保与系统的良好集成
- 持续维护:及时更新和修复问题
通过不断学习和实践,您可以开发出优秀的 Yuan-ICP 插件,为系统用户提供更多价值。