Skip to content

插件开发指南

本文档详细介绍 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.phpuninstall.php

3. 插件安装方式

方式一:通过管理界面上传

  1. 登录管理后台
  2. 进入"插件管理"页面
  3. 点击"上传插件"按钮
  4. 选择插件ZIP文件
  5. 系统自动解压并安装

方式二:手动安装

  1. 将插件目录复制到 plugins/ 目录
  2. 确保文件权限正确(755)
  3. 刷新插件管理页面
  4. 启用插件

🔧 插件开发最佳实践

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: 使用唯一的插件标识符,避免函数名冲突,合理设置钩子优先级。

🎯 总结

通过合理使用插件系统,开发者可以:

  1. 扩展系统功能:添加新的业务逻辑和功能模块
  2. 定制用户界面:修改和美化系统界面
  3. 集成第三方服务:连接外部API和服务
  4. 优化用户体验:提供个性化的功能和服务

记住,好的插件不仅仅是功能完整,更重要的是:

  • 代码质量:遵循编码规范和最佳实践
  • 用户体验:提供直观易用的界面
  • 系统兼容:确保与系统的良好集成
  • 持续维护:及时更新和修复问题

通过不断学习和实践,您可以开发出优秀的 Yuan-ICP 插件,为系统用户提供更多价值。

基于 MIT 协议发布