生命周期
2026/10/26大约 2 分钟
生命周期与 BasePlugin
插件根目录的 Plugin.php 是生命周期 Hook,推荐继承基类 app\basic\BasePlugin。基座通过 PluginManager 以 method_exists 调度钩子,即使不继承基类也能运行(向后兼容)。
钩子一览
| 钩子 | 触发时机 |
|---|---|
boot | 每个 worker 进程启动时,对每个"已启用"插件各调用一次 |
init | 安装/启用流程内的通用初始化 |
beforeInstall / afterInstall | 安装前 / 后 |
beforeEnable / afterEnable | 启用前 / 后 |
beforeDisable / afterDisable | 禁用前 / 后 |
beforeUpgrade / afterUpgrade | 升级前 / 后 |
beforeUninstall | 卸载前 |
shutdown | 禁用(事务提交后、Registry 按 owner 清理前)/ 卸载(删文件前) |
destroy | 卸载时清理插件自有数据 |
backupTables | 声明备份 restore.sql 需包含的插件业务表 |
每个钩子都接收 $plugin 数组(am_plugin_app 的一行记录)。
boot() 是唯一每进程重复执行的钩子
这是插件开发最容易踩的点。Webman 常驻内存、多进程,进程级静态注册(EventRegistry::listen、ServiceRegistry::bind/extend、AdapterRegistry::register、IdentityService::registerProvider、TaskRegistry::schedule、MiddlewareRegistry::add、ChannelAuthRegistry::authorize 等)在进程重启后静态状态会丢失。
铁律
凡是"每次进程启动都要重新装配"的运行时注册,必须写在 boot() 里,而不是 install/enable。
use app\basic\BasePlugin;
class Plugin extends BasePlugin
{
public function boot(array $plugin = []): void
{
// 每个进程都会执行:在这里做静态注册
\app\basic\EventRegistry::listen(
'file.uploaded',
[SomeListener::class, 'handle'],
priority: 0,
owner: 'am_demo' // 归属插件名,卸载/禁用时基座据此清理
);
}
public function backupTables(array $plugin = []): array
{
return [$this->table('order', $plugin)]; // 例:am_demo_order
}
}异常隔离
基座 PluginBootstrapper 逐个调用已启用插件的 boot(),单个插件抛异常会被记录日志并隔离,不会拖垮基座或其它插件。
卸载清理
静态注册(Event/Service/Adapter/Middleware/Task/ChannelAuth)的清理由基座在各 Registry 的 forgetByOwner(插件名) 统一处理,插件无需在 shutdown 里重复清理;shutdown 只用于关闭插件自持有的外部连接、释放文件锁等。
