插件系统
一句话:类似 WordPress 的灵活插件系统,支持插件扫描、加载、激活/停用,通过钩子执行方法。
概述
插件系统允许开发者在不修改核心代码的情况下扩展框架功能。插件位于 server/app/Plugin/ 目录,每个插件都是一个独立的目录。
插件结构
基本结构
text
server/app/Plugin/
└── HelloWorld/
└── Index.php插件文件示例
php
<?php
/**
* Plugin Name: HelloWorld
* Plugin Description: Hello World 插件
* Version: 1.0.0
* Author: YuiNijika
* Plugin URI: https://github.com/YuiNijika
*/
if (!defined('ANON_ALLOWED_ACCESS')) exit;
// 插件主类(类名不区分大小写)
class Anon_System_Plugin_HelloWorld
{
/**
* 插件初始化方法(必需)
* 在插件加载时自动调用
*/
public static function init()
{
// 注册路由
Anon::route('/hello', function () {
Anon::success([
self::index()
], 'Hello World from Plugin');
}, [
'header' => true,
'requireLogin' => false,
'method' => ['GET'],
'token' => false,
'cache' => [
'enabled' => true,
'time' => 3600,
],
]);
}
/**
* 插件激活时调用(可选)
*/
public static function activate()
{
Anon_Debug::info('HelloWorld 插件已激活');
}
/**
* 插件停用时调用(可选)
*/
public static function deactivate()
{
Anon_Debug::info('HelloWorld 插件已停用');
}
/**
* 自定义方法
*/
public static function index()
{
return 'Hello World';
}
}插件元数据
插件元数据通过文件头注释(PHPDoc 风格)定义,包含以下字段:
Plugin Name: 插件名称(必需)Plugin Description: 插件描述Version: 插件版本Author: 作者名称Plugin URI: 插件主页或作者主页
示例:
php
/**
* Plugin Name: HelloWorld
* Plugin Description: Hello World 插件
* Version: 1.0.0
* Author: YuiNijika
* Plugin URI: https://github.com/YuiNijika
*/插件类命名规则
插件类名格式:Anon_Plugin_{插件名称}
- 插件名称从目录名获取
- 类名不区分大小写(系统会自动匹配)
- 例如:目录
HelloWorld→ 类名Anon_Plugin_HelloWorld
配置插件系统
在 server/app/useApp.php 中配置:
php
return [
'app' => [
'plugins' => [
'enabled' => true, // 是否启用插件系统
'active' => [], // 激活的插件列表,空数组表示激活所有插件
],
// ... 其他配置
]
];激活特定插件
php
'plugins' => [
'enabled' => true,
'active' => [
'HelloWorld',
'AnotherPlugin',
],
],激活所有插件
php
'plugins' => [
'enabled' => true,
'active' => [], // 空数组表示激活所有插件
],路由注册
插件可以通过 Anon::route() 方法注册路由,支持完整的路由元数据配置:
php
Anon::route('/hello', function () {
// 路由处理逻辑
}, [
'header' => true, // 是否设置响应头
'requireLogin' => false, // 是否需要登录
'method' => ['GET'], // 允许的 HTTP 方法
'token' => false, // 是否需要 Token 验证
'cache' => [ // 缓存配置
'enabled' => true,
'time' => 3600,
],
]);路由元数据说明
header: 是否设置响应头(默认true)requireLogin: 是否需要登录(默认false)method: 允许的 HTTP 方法,字符串或数组(如['GET', 'POST'])token: 是否需要 Token 验证(true/false/null,null使用全局配置)cache: 缓存配置enabled: 是否启用缓存time: 缓存时间(秒)
插件生命周期
1. 扫描阶段
系统扫描 server/app/Plugin/ 目录,查找所有插件目录。
2. 加载阶段
- 读取插件元数据(
Anon_PluginMeta) - 查找插件主类(
Anon_Plugin_{插件名}) - 检查插件是否在激活列表中(如果配置了激活列表)
3. 初始化阶段
- 调用插件的
init()方法 - 执行插件注册的路由和钩子
4. 激活/停用
activate(): 插件激活时调用deactivate(): 插件停用时调用
钩子系统集成
插件可以使用框架的钩子系统:
php
public static function init()
{
// 添加动作钩子
Anon_System_Hook::add_action('some_action', function() {
// 执行逻辑
});
// 添加过滤器钩子
Anon_System_Hook::add_filter('some_filter', function($value) {
return $value . ' modified';
});
}插件系统钩子
框架为插件系统提供了以下钩子:
plugin_system_before_init: 插件系统初始化前plugin_system_after_init: 插件系统初始化后plugin_before_scan: 插件扫描前plugin_after_scan: 插件扫描后plugin_before_load: 插件加载前plugin_after_load: 插件加载后
注意事项
- 类名不区分大小写:系统会自动匹配类名,但建议使用正确的命名规范
- 必需方法:
init()方法是必需的,插件加载时会自动调用 - 目录结构:插件必须放在
server/app/Plugin/目录下 - 文件命名:插件主文件建议命名为
Index.php - 安全检查:所有插件文件必须包含
if (!defined('ANON_ALLOWED_ACCESS')) exit;
调试
启用调试模式后,可以在调试控制台查看插件加载信息:
- 插件扫描日志
- 插件加载状态
- 插件元数据信息
示例:完整插件
php
<?php
/**
* Plugin Name: UserStats
* Plugin Description: 用户统计插件
* Version: 1.0.0
* Author: Your Name
* Plugin URI: https://example.com
*/
if (!defined('ANON_ALLOWED_ACCESS')) exit;
class Anon_System_Plugin_UserStats
{
public static function init()
{
// 注册用户统计路由
Anon::route('/api/user/stats', function () {
$user = Anon_Http_Request::requireAuth();
$stats = self::getUserStats($user['uid']);
Anon::success($stats, '获取统计数据成功');
}, [
'requireLogin' => true,
'method' => ['GET'],
]);
// 注册钩子
Anon_System_Hook::add_action('user_login', [self::class, 'onUserLogin']);
}
public static function activate()
{
// 创建统计表
$db = Anon_Database::getInstance();
// 检查表是否存在
if (!$db->tableExists('user_stats')) {
$db->createTable('user_stats', [
'user_id' => [
'type' => 'INT',
'null' => false,
'primary' => true,
],
'login_count' => [
'type' => 'INT',
'null' => false,
'default' => 0,
],
'last_login' => [
'type' => 'DATETIME',
'null' => true,
],
]);
}
}
public static function deactivate()
{
Anon_Debug::info('UserStats 插件已停用');
}
private static function getUserStats($userId)
{
$db = Anon_Database::getInstance();
return $db->db('user_stats')
->where('user_id', '=', $userId)
->first();
}
public static function onUserLogin($userId)
{
$db = Anon_Database::getInstance();
// 检查用户统计是否存在
$exists = $db->db('user_stats')
->where('user_id', '=', $userId)
->exists();
if ($exists) {
// 更新现有记录
$current = $db->db('user_stats')
->where('user_id', '=', $userId)
->first();
$db->db('user_stats')
->where('user_id', '=', $userId)
->update([
'login_count' => ($current['login_count'] ?? 0) + 1,
'last_login' => date('Y-m-d H:i:s')
]);
} else {
// 插入新记录
$db->db('user_stats')->insert([
'user_id' => $userId,
'login_count' => 1,
'last_login' => date('Y-m-d H:i:s')
]);
}
}
}