XFAdminPHP插件开发文档

一. 插件目录结构与文件规范

每个插件必须放在 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.phpen-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) {
        // 发送邮件逻辑
    }
}

如需更多示例或有特殊需求,欢迎在本帖回复交流!

请登录后发表评论

    请登录后查看回复内容