一. 插件目录结构与文件规范
每个插件必须放在 plugins/ 目录下,目录名即插件名,建议驼峰或下划线风格(如 Demo、UserCenter)。
标准目录结构:
plugins/
└── Demo/
    ├── plugin.json         # 插件元信息(必需,主配置文件)
    ├── menu.php            # 菜单配置(可选,需 features.register_menu=true)
    ├── routes.php          # 路由配置(可选,需 features.register_route=true)
    ├── tasks.php           # 定时任务配置(可选,需 features.register_task=true)
    ├── DemoPlugins.php     # 插件主类(必需,类名=目录名+Plugins)
    ├── hooks/              # 插件钩子目录(可选,放置所有 hook 相关文件)
    ├── assets/             # 静态资源目录(可选,放置样式、JS、图片等)
    │   ├── css/
    │   ├── js/
    │   └── images/
    ├── lib/                # 插件自定义类库(可选,放置本插件的辅助类、第三方库等)
    ├── views/              # 插件模板/视图目录(可选,放置本插件的模板文件)
    └── lang/               # 插件多语言目录(可选,放置本插件的语言包)
系统只会自动读取和处理如下目录及文件:
plugin.json(插件元信息,必需)menu.php(菜单配置,需 features.register_menu=true)routes.php(路由配置,需 features.register_route=true)tasks.php(定时任务配置,需 features.register_task=true)DemoPlugins.php(插件主类,必需)hooks/(钩子目录,系统会自动扫描并注册钩子)assets/(静态资源目录,前端可通过统一路径访问)lib/(插件自定义类库,需在插件主类中手动引入)views/(模板目录,系统模板渲染时会自动查找)lang/(多语言目录,系统多语言加载时会自动查找)
其他目录或文件,系统不会自动读取。
二. plugin.json 配置说明
标准格式:
{
  "name": "Demo",
  "title": "演示插件",
  "author": "作者名",
  "version": "1.0.0",
  "url": "https://your-site.com",
  "description": "插件描述",
  "namespace": "Plugins\Demo",
  "core_class": "DemoPlugins",
  "features": {
    "register_route": true,
    "register_menu": true,
    "register_task": false
  },
  "hooks": [
    "sendMail",
    "onUserRegistered"
  ]
}
features字段用于控制插件的各项注册功能。hooks字段用于声明本插件支持的所有钩子方法名,主程序会根据此字段自动识别和调度插件的 hook。
字段说明表:
| 字段 | 类型 | 必须 | 说明 | 
|---|---|---|---|
| name | string | 是 | 插件目录名,唯一标识 | 
| title | string | 是 | 插件名称 | 
| author | string | 否 | 作者名 | 
| version | string | 是 | 版本号 | 
| url | string | 否 | 插件主页/文档地址 | 
| description | string | 否 | 插件描述 | 
| namespace | string | 是 | 插件主类命名空间 | 
| core_class | string | 是 | 插件主类名 | 
| features | object | 否 | 注册功能控制,见下表 | 
| hooks | array | 否 | 本插件声明支持的所有 hook 方法名 | 
features 字段说明:
| 字段 | 类型 | 必须 | 说明 | 
|---|---|---|---|
| register_route | bool | 否 | 是否注册插件自定义路由 | 
| register_menu | bool | 否 | 是否注册插件自定义菜单 | 
| register_task | bool | 否 | 是否注册插件自定义定时任务 | 
三. hooks 机制与声明
hooks字段为数组,声明本插件支持的所有 hook 方法名。- 主程序会根据 
plugin.json的hooks字段,自动识别哪些插件支持哪些 hook。 - 只有在 
hooks数组中声明的方法,主程序才会调度该插件。 
hooks 字段示例:
"hooks": [
  "sendMail",
  "onUserRegistered",
  "onUserLogin"
]
常见 hook 用法举例:
| hook 名称 | 说明 | 典型参数示例 | 
|---|---|---|
| sendMail | 发送邮件 | ($to, $subject, $body) | 
| onUserRegistered | 用户注册后 | ($userId, $userInfo) | 
| onUserLogin | 用户登录后 | ($userId, $loginTime) | 
四. 菜单、路由、任务、钩子、资源等文件规范
4.1 menu.php(菜单配置)
- 返回菜单数组,支持父菜单+子菜单。
 - 注册时,只有 
features.register_menu为true才会生效。 - 系统会自动写入 
db_id字段到每个菜单项,便于精准删除。 
示例 menu.php:
<?php
return [
    [
        "title" => "演示管理",
        "icon" => "icon-demo",
        "type" => 1,
        "open_type" => 1,
        "href" => "/admin/demo",
        "sort" => 100,
        "permission_ids" => "",
        "status" => 1
    ],
    [
        "title" => "子菜单A",
        "icon" => "icon-a",
        "type" => 2,
        "open_type" => 1,
        "href" => "/admin/demo/a",
        "sort" => 1,
        "permission_ids" => "",
        "status" => 1
    ]
];
4.2 routes.php(路由配置)
- 返回路由数组。
 - 注册时,只有 
features.register_route为true才会生效。 
示例 routes.php:
<?php
return [
    [
        "method" => ["GET"],
        "uri" => "/admin/demo",
        "handler" => "DemoPlugins@apiDemo",
        "namespace" => "Plugins\Demo",
        "middleware" => ["AdminJwtCookie", "AdminJwtAuth"]
    ]
];
handler格式为类名@方法名。namespace必须与主类命名空间一致。
4.3 tasks.php(定时任务配置)
- 返回任务数组。
 - 注册时,只有 
features.register_task为true才会生效。 
示例 tasks.php:
<?php
return [
    'demo_task' => [
        'class'       => '\Plugins\Demo\Task\DemoTask',
        'method'      => 'run',
        'interval'    => 60,
        'priority'    => 'normal',
        'timeout'     => 120,
        'retry'       => 2,
        'retry_delay' => 30,
        'beizhu'      => 'Demo插件的定时任务'
    ]
];
任务配置字段说明:
| 字段 | 类型 | 必须 | 说明 | 
|---|---|---|---|
| class | string | 是 | 任务执行类的完全限定名 | 
| method | string | 是 | 任务执行方法名 | 
| interval | int | 是 | 任务执行间隔(秒) | 
| priority | string | 否 | 任务优先级 high/normal/low | 
| timeout | int | 否 | 超时时间(秒),默认300 | 
| retry | int | 否 | 最大重试次数,默认3 | 
| retry_delay | int | 否 | 重试延迟(秒),默认60 | 
| beizhu | string | 否 | 备注/描述 | 
4.4 hooks/(钩子目录)
- 所有钩子相关文件建议以 
*.hook.php命名,内容为标准 PHP 类或函数。 - 系统会自动扫描并注册钩子。
 - 推荐:所有 hook 方法直接在插件主类实现,并在 
plugin.json的hooks字段声明。 
4.5 assets/(静态资源目录)
- 推荐分为 
css/、js/、images/,前端可通过统一路径访问。 
4.6 lib/(插件自定义类库)
- 放置本插件的辅助类、第三方库等,需在主类中手动引入。
 
4.7 views/(模板/视图目录)
- 放置本插件的模板文件,推荐与主系统模板风格一致。
 
4.8 lang/(多语言目录)
- 多语言文件,推荐以 
zh-cn.php、en-us.php等命名,返回数组。 
五. 插件主类规范
- 主类文件名:DemoPlugins.php(目录名+Plugins)
 - 命名空间:Plugins\Demo
 - 类名:DemoPlugins
 - 建议实现 
onInit(注册时自动调用)、onUninstall(卸载时自动调用)、onUpdate(更新时自动调用)、onDisable(禁用时自动调用)等生命周期方法。 
示例 DemoPlugins.php:
<?php
namespace Plugins\Demo;
class DemoPlugins
{
    /**
     * 生命周期:插件初始化(每次系统启动时调用)
     */
    public function onInit() {}
    /**
     * 生命周期:插件禁用
     */
    public function onDisable() {}
    /**
     * 生命周期:插件卸载
     */
    public function onUninstall() {}
    /**
     * 生命周期:插件更新
     */
    public function onUpdate(){}
    // 路由 handler 示例
    public function apiDemo() {}
    // 示例 hook 方法(需在 plugin.json 的 hooks 字段声明)
    public function sendMail($to, $subject, $body) {
        // 发送邮件逻辑
    }
}
六. 插件启用/停用/卸载流程
- 启用/注册插件
调用syncSinglePlugin('Demo')或syncAllPlugins()。插件信息写入数据库,菜单/路由/任务根据 features 注册。自动写入 db_id 到 plugin.json 和 menu.php。 - 停用插件
调用disableSinglePlugin('Demo')或disableAllPlugins()。仅将 plugin 表中对应插件的 status 字段设为 0,其他数据不变。 - 卸载插件
调用uninstallSinglePlugin('Demo')或uninstallAllPlugins()。精准删除插件表、菜单、路由、任务等所有相关内容(优先用 db_id)。删除后,插件相关菜单、路由、任务等全部失效。 
注意:
- 所有操作遇到异常会直接抛出,便于排查。
 - 插件注册、菜单、路由、任务等,只有 features 对应项为 true 时才会执行。
 
七. 插件接口调用与返回值
常用接口:
$pluginManager = new PluginManager();
// 一键刷新本地插件状态
$allStatus = $pluginManager->refreshAllPlugins();
// $allStatus 是所有插件的本地状态数组
// 启用单个插件
$pluginManager->enablePlugin('Demo');
// 停用单个插件
$pluginManager->disablePlugin('Demo');
// 卸载单个插件
$pluginManager->uninstallPlugin('Demo');
- 所有方法执行成功返回 true,如有异常会直接抛出(需用 try-catch 捕获)。
 - 系统内置通常不需要在开发过程中去调度
 
八. Hook 调用与插件调度
- 主程序可通过 
$pluginManager->callHook('hookName', $args)自动调度所有已启用且声明了该 hook 的插件(按优先级只调第一个)。 - 可通过 
$pluginManager->getPluginsByHook('hookName')获取所有声明了该 hook 的插件列表。 
示例:
$ret = $pluginManager->callHook('sendMail', [$to, $subject, $body]);
if ($ret && $ret['success']) {
    // 成功
} else {
    // 没有插件处理,或插件报错
}
 
九. 常见问题与注意事项
- features 没有某项时,默认视为 false,不会注册。
 - 插件注册、菜单、路由、任务等,只有 features 对应项为 true 时才会执行。
 - 插件注册后,plugin.json 会自动写入 db_id 字段,菜单注册后 menu.php 会写入每个菜单的 db_id 字段。
 - 卸载时,优先用 db_id 精准删除,避免误删。
 - 所有操作遇到异常会直接抛出,建议用 try-catch 捕获。
 - 路由注册时如有冲突会自动跳过并记录。
 - 插件主类建议实现 onInit(注册时自动调用)和 onUninstall(卸载时自动调用)方法。
 - 插件升级建议先停用再升级,升级后重新启用。
 
十. 开发建议与最佳实践
- 强烈建议每个插件都写好 features 字段,明确声明支持哪些注册功能。
 - 插件主类建议统一命名为 插件名Plugins,放在插件目录下。
 - 路由、菜单、任务等配置文件建议返回标准数组,便于自动注册。
 - 插件开发完成后,建议在测试环境多次启用/停用/卸载,确保无残留。
 - 如需扩展更多 features,可在 features 字段中自定义,主程序可按需支持。
 - 如需支持 hook,务必在 plugin.json 的 hooks 字段声明所有支持的 hook 方法名,并在主类实现。
 
十一. 完整插件示例
目录结构:
plugins/
└── Demo/
    ├── plugin.json
    ├── menu.php
    ├── routes.php
    ├── tasks.php
    ├── DemoPlugins.php
    ├── hooks/
    │   └── onUserLogin.hook.php
    ├── assets/
    │   ├── css/
    │   │   └── demo.css
    │   ├── js/
    │   │   └── demo.js
    │   └── images/
    │       └── logo.png
    ├── lib/
    │   └── DemoHelper.php
    ├── views/
    │   └── index.html
    └── lang/
        ├── zh-cn.php
        └── en-us.php
plugin.json
{
  "name": "Demo",
  "title": "演示插件",
  "author": "张三",
  "version": "1.0.0",
  "url": "https://demo.com",
  "description": "这是一个演示插件。",
  "namespace": "Plugins\Demo",
  "core_class": "DemoPlugins",
  "features": {
    "register_route": true,
    "register_menu": true,
    "register_task": true
  },
  "hooks": [
    "sendMail",
    "onUserRegistered"
  ]
}
DemoPlugins.php
<?php
namespace Plugins\Demo;
class DemoPlugins
{
    public function onInit()
    {
        // 插件初始化逻辑
    }
    public function onUninstall()
    {
        // 插件卸载清理逻辑
    }
    public function apiDemo()
    {
        // 路由 handler 示例
    }
    // 示例 hook 方法
    public function sendMail($to, $subject, $body) {
        // 发送邮件逻辑
    }
}
如需更多示例或有特殊需求,欢迎在本帖回复交流!


请登录后查看回复内容