🔌 插件注册
ThinkAdmin 提供完善的插件注册服务机制,基于 ThinkPHP 的服务层,使每个插件能够像独立的子应用一样运作。
📚 基础概念
🤔 基本介绍
插件注册是 ThinkAdmin 框架提供的服务机制,用于注册和管理插件。
简单理解:就像给插件"上户口",告诉系统这个插件叫什么、在哪里、能做什么。
实际例子:
// 插件注册后,系统会自动识别和加载插件
// 1. 系统启动时,自动加载所有插件的 Service 类
// 2. 执行 Service 类的 register() 方法
// 3. 注册插件的路由、命令、事件等🤔 使用优势
问题1:插件无法自动加载
不使用插件注册时,插件无法自动加载:
// ❌ 不使用插件注册:需要手动加载插件
require_once 'plugin/account/src/Service.php';
$service = new \plugin\account\Service();
$service->register();问题2:插件功能无法自动初始化
不使用插件注册时,插件的功能无法自动初始化:
// ❌ 需要手动初始化插件的路由、命令等
app()->route->get('account/api', 'plugin-account/api/index');
// 每个插件都要手动注册,代码重复使用插件注册的优势:
// ✅ 使用插件注册:自动加载和初始化
// 系统启动时,自动执行所有插件的 register() 方法
// 无需手动编写代码🎯 核心功能
功能1:自动加载插件
系统启动时,自动加载所有已安装的插件:
// 系统自动扫描 composer.json 中的插件配置
// 自动加载插件的 Service 类
// 自动执行 register() 方法功能2:注册插件服务
注册插件的路由、命令、事件等:
public function register(): void
{
// 注册路由
$this->app->route->get('account/api', 'plugin-account/api/index');
// 注册命令
$this->commands([AccountCommand::class]);
// 注册事件
$this->app->event->listen('AccountEvent', function() {});
}功能3:定义插件菜单
定义插件在后台显示的菜单:
public static function menu(): array
{
return [
[
'name' => '账号管理',
'subs' => [
['name' => '账号列表', 'node' => 'plugin-account/index'],
],
],
];
}🚀 主要功能
- 独立运行: 每个插件像独立的子应用一样运作
- 服务层集成: 基于 ThinkPHP 服务层机制
- 自动初始化: 通过 Service 类完成全局初始化
- 配置管理: 支持 composer.json 配置文件
- 依赖管理: 自动管理插件依赖关系
- 灵活扩展: 提供灵活的插件扩展机制
📋 插件类型
ThinkAdmin 支持三种插件类型,各有特点:
1. 本地插件
直接放置在本地目录的插件,适合开发和测试。
特点:
- 存储方式: 直接放置在本地目录
- 配置文件: 根目录需要包含
composer.json文件 - 版本要求: 必需指定版本号为
dev-master或其他开发分支 - 自动加载: 需要配置
autoload自动加载规则
使用场景:
- 插件开发阶段
- 本地测试
- 私有插件(不发布到公共平台)
2. 开放插件
发布到 Composer 平台的插件,适合公开发布。
特点:
- 发布平台: 发布到 Composer 管理平台
- 安装方式: 通过
composer require vendor/package安装 - 版本管理: 支持版本控制和更新管理
- 依赖解析: 自动解析和安装依赖
使用场景:
- 公开发布的插件
- 需要版本管理的插件
- 开源插件
3. 私有插件
上传到 ThinkAdmin 专属平台的插件,适合商业插件。
特点:
- 专属平台: 上传到 ThinkAdmin 专属平台
- Token 认证: 通过插件中心登录生成 token
- 安装方式: 与开放插件相同的安装方式
- 状态: 目前未上线
使用场景:
- 商业插件
- 需要授权的插件
- 私有插件分发
⚙️ 配置要求
必需文件
- composer.json: 插件元数据和依赖配置
- Service 类: 插件服务注册类
- 自动加载: 配置 autoload 自动加载规则
初始化机制
- 普通模块: 使用
sys.php进行全局初始化 - 插件模块: 依赖 Service 类完成全局初始化
- 服务注册: 通过 Service 类注册插件服务
插件的三种类型
- 本地插件,插件直接放置在本地某个目录,其根目录需要包含 composer.json 文件;
- 开放插件,将插件发布到 Composer 管理平台,通过
composer require vendor/package安装; - 私有插件,将插件上传到 ThinkAdmin 专属平台,通过插件中心登录生成 token,同上面的安装方式 ( 未上线 );
1. 本地插件特性
本地插件是基于 Composer 的 repositories.type 为 path 实现,参数 url 指向到插件根目录。
项目根 composer.json 配置 repositories 对象并设置插件所在位置,本地插件必需指定版本号为 dev-master 或其他开发分支。
注意事项:
- 需要在插件的
composer.json配置autoload自动加载规则,否则无法实现自动加载Class类; - 本地插件不建议使用
ThinkAdmin插件的自毁机制 (clear),有可能造成原插件目录被意外删除;
以下项目根 composer.json 的配置案例中 zoujingli/think-plugs-admin 为插件包名,需要替换为你自己的包名。
{
"type": "project",
"require": {
"zoujingli/think-plugs-admin": "dev-master"
},
"repositories": {
"ThinkAdminPlugs": {
"type": "path",
"url": "../think-plugs-admin"
}
},
"autoload": {
"psr-0": {
"": "extend"
},
"psr-4": {
"app\\": "app"
}
}
}2. 开放插件特性:
将应用插件发布到 Composer 平台,使用 composer require plugs-package-name 安装。
3. 私有插件特性:
私有插件与线上插件一致,区别在于发布的平台不同,后期会针对此类型详细描述!
插件的配置参数
应用插件 composer.json 具体配置参数如下:
- 插件类型 ( 固定的 ):think-admin-plugin
- 插件名称 ( 自定义 ):zouingli/think-plugs-admin
- 插件信息 ( 自定义 ):可选,配置插件基本信息,展示在插件说明
- 安装配置 ( 自定义 ):可选,支持安装的规则请阅读《插件安装规则》
- 插件服务 ( 自定义 ):需要继承
think\admin\Plugin并在composer.json里面注册; - 如果是本地插件且未发布到线上,需要增加
version:"dev-master"配置;
{
"type": "think-admin-plugin",
"name": "zoujingli/think-plugs-admin",
"license": "MIT",
"homepage": "https://thinkadmin.top",
"description": "Admin Plugin for ThinkAdmin",
"authors": [
{
"name": "Anyon",
"email": "zoujingli@qq.com"
}
],
"require": {
"php": ">7.1",
"ext-json": "*",
"topthink/framework": "^6.0|^8.0",
"zoujingli/think-library": "^6.1|@dev"
},
"autoload": {
"psr-4": {
"app\\admin\\": "src"
}
},
"extra": {
"think": {
"services": [
"app\\admin\\Service"
]
},
"config": {
"type": "module",
"name": "系统后台管理",
"cover": "可选,插件的封面图片,仅支持 https 协议的图片链接",
"document": "https://thinkadmin.top/plugin/think-plugs-admin.html",
"platforms": "可选,插件支持的平台,如:['wxapp','wxweb','h5wap','h5web']",
"description": "后台基础管理模块,系统账号及安全配置管理。",
"license": "可选,插件授权协议,如:['MIT', 'VIP']"
},
"plugin": {
"copy": {
"src": "!app/admin",
"stc/database": "database/migrations"
},
"clear": true
}
}
}配置说明:
- type: 固定为
think-admin-plugin - name: 插件包名,格式为
vendor/package-name - autoload: PSR-4 自动加载规则,定义插件的命名空间映射
- extra.think.services: 插件服务注册类,必须指向继承
Plugin的 Service 类 - extra.config: 插件基本信息配置
type: 插件类型,module表示模块插件,plugin表示普通插件name: 插件中文名称document: 插件文档链接description: 插件描述license: 授权协议(数组格式)
- extra.plugin: 插件安装配置
copy: 文件复制规则,!开头表示绝对复制(先删除再复制)clear: 是否在安装后清理原安装目录
插件服务注册类
插件的服务类需要继承 think\admin\Plugin 类,必须实现静态方法 menu() 来定义插件菜单。
基础属性
在插件的服务类中,可以配置以下属性:
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
$package | string | 是 | 插件包名(如:zoujingli/think-plugs-admin) |
$appCode | string | 否 | 插件编码(自动从命名空间计算,如:admin、plugin-account) |
$appName | string | 是 | 插件名称(如:系统管理) |
$appPath | string | 否 | 插件路径(自动计算) |
$appAlias | string | 否 | 插件别名(用于路由映射) |
$appSpace | string | 否 | 插件命名空间(自动从类命名空间计算) |
$appService | string | 否 | 服务注册类(默认为当前类) |
基础示例
<?php
declare(strict_types=1);
namespace app\admin;
use think\admin\Plugin;
/**
* 插件服务注册
* @class Service
* @package app\admin
*/
class Service extends Plugin
{
/**
* 定义插件名称
* @var string
*/
protected $appName = '系统管理';
/**
* 定义安装包名
* @var string
*/
protected $package = 'zoujingli/think-plugs-admin';
/**
* 定义插件中心菜单
* @return array
*/
public static function menu(): array
{
return [
[
'name' => '系统配置',
'subs' => [
['name' => '系统参数配置', 'icon' => 'layui-icon layui-icon-set', 'node' => 'admin/config/index'],
['name' => '系统任务管理', 'icon' => 'layui-icon layui-icon-log', 'node' => 'admin/queue/index'],
['name' => '系统日志管理', 'icon' => 'layui-icon layui-icon-form', 'node' => 'admin/oplog/index'],
['name' => '数据字典管理', 'icon' => 'layui-icon layui-icon-code-circle', 'node' => 'admin/base/index'],
['name' => '系统文件管理', 'icon' => 'layui-icon layui-icon-carousel', 'node' => 'admin/file/index'],
['name' => '系统菜单管理', 'icon' => 'layui-icon layui-icon-layouts', 'node' => 'admin/menu/index'],
],
],
[
'name' => '权限管理',
'subs' => [
['name' => '系统权限管理', 'icon' => 'layui-icon layui-icon-vercode', 'node' => 'admin/auth/index'],
['name' => '系统用户管理', 'icon' => 'layui-icon layui-icon-username', 'node' => 'admin/user/index'],
],
],
];
}
}高级示例(带服务注册)
<?php
declare(strict_types=1);
namespace app\wechat;
use app\wechat\command\Auto;
use app\wechat\command\Fans;
use app\wechat\service\AutoService;
use think\admin\Plugin;
use think\Request;
/**
* 组件注册服务
* @class Service
* @package app\wechat
*/
class Service extends Plugin
{
/**
* 定义插件名称
* @var string
*/
protected $appName = '微信管理';
/**
* 定义安装包名
* @var string
*/
protected $package = 'zoujingli/think-plugs-wechat';
/**
* 注册组件服务
* @return void
*/
public function register(): void
{
// 注册模块指令
$this->commands([Fans::class, Auto::class]);
// 注册事件监听
$this->app->event->listen('WechatFansSubscribe', static function ($openid) {
AutoService::register($openid);
});
// 注册路由
$this->app->route->any('/plugin-wxpay-notify/:vars', static function (Request $request) {
// 处理支付通知
return PaymentService::notify($data);
});
}register() 方法详解
register() 方法用于在插件初始化时注册各种服务,包括命令、事件监听和路由等。
1. 注册命令(Commands)
通过 $this->commands() 方法注册插件自定义的命令类:
// 注册单个命令
$this->commands(Fans::class);
// 注册多个命令(数组方式)
$this->commands([Fans::class, Auto::class, Clear::class]);
// 注册命令并指定命令名称(键值对方式)
$this->commands(['xadmin:worker' => Worker::class]);2. 注册事件监听(Event Listeners)
通过 $this->app->event->listen() 注册事件监听器:
// 注册单个事件监听
$this->app->event->listen('WechatFansSubscribe', static function ($openid) {
AutoService::register($openid);
});
// 注册多个事件监听
$this->app->event->listen('PluginAdminChangePassword', static function ($data) {
// 处理密码修改事件
sysoplog('密码修改', "用户[{$data['uuid']}]修改密码");
});3. 注册路由(Routes)
通过 $this->app->route 注册自定义路由:
// 注册任意请求方式的路由(带异常处理)
$this->app->route->any('/plugin-wxpay-notify/:vars', static function (Request $request) {
try {
$data = json_decode(CodeExtend::deSafe64($request->param('vars')), true);
return PaymentService::notify($data);
} catch (\Exception|\Error $exception) {
return "Error: {$exception->getMessage()}";
}
});
// 注册 GET 请求路由
$this->app->route->get('/plugin-api/status', static function () {
return json(['code' => 1, 'msg' => 'OK']);
});
// 注册 POST 请求路由
$this->app->route->post('/plugin-api/submit', static function (Request $request) {
$data = $request->post();
return json(['code' => 1, 'data' => $data]);
});注意事项:
register()方法在插件初始化时自动调用,无需手动触发命令类需要继承
think\admin\Command类事件名称建议使用驼峰命名,如
PluginAdminChangePassword路由注册后立即生效,无需额外配置
/**
- 定义插件菜单
- @return array */ public static function menu(): array { $code = app(static::class)->appCode; return [ [ 'name' => '微信管理', 'subs' => [ ['name' => '微信接口配置', 'icon' => 'layui-icon layui-icon-set', 'node' => "{$code}/config/options"], ['name' => '微信支付配置', 'icon' => 'layui-icon layui-icon-rmb', 'node' => "{$code}/config/payment"], ], ], [ 'name' => '微信定制', 'subs' => [ ['name' => '微信粉丝管理', 'icon' => 'layui-icon layui-icon-username', 'node' => "{$code}/fans/index"], ['name' => '微信图文管理', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/news/index"], ['name' => '微信菜单配置', 'icon' => 'layui-icon layui-icon-cellphone', 'node' => "{$code}/menu/index"], ], ], ]; } }
#### 使用 appCode 动态获取插件编码
在菜单定义中,可以使用 `app(static::class)->appCode` 来动态获取插件编码:
```php
public static function menu(): array
{
$code = app(static::class)->appCode; // 获取插件编码,如:plugin-account
return [
[
'name' => '用户管理',
'subs' => [
['name' => '用户账号管理', 'node' => "{$code}/master/index"],
['name' => '终端账号管理', 'node' => "{$code}/device/index"],
],
],
];
}合并其他插件菜单
插件可以合并其他插件的菜单,例如支付插件合并账号插件的菜单:
<?php
namespace plugin\payment;
use plugin\account\Service as AccountService;
use think\admin\Plugin;
class Service extends Plugin
{
protected $appName = '支付管理';
protected $package = 'zoujingli/think-plugs-payment';
public static function menu(): array
{
$code = app(static::class)->appCode;
// 合并账号插件的菜单
return array_merge(AccountService::menu(), [
[
'name' => '支付管理',
'subs' => [
['name' => '支付配置管理', 'node' => "{$code}/config/index"],
['name' => '支付行为管理', 'node' => "{$code}/record/index"],
],
]
]);
}
}