插件开发 1 更新于 2026-10-13

插件开发 · 生命周期与钩子

一、生命周期回调

插件入口类可实现以下方法,程序在对应时机自动调用:

方法触发时机典型用途
install()安装后建表、写初始配置
uninstall()卸载前删表(如用户确认)、清理缓存
enable()启用时注册路由、刷新缓存
disable()禁用时清理临时数据
upgrade($from, $to)版本升级字段变更、数据迁移
public function install()
{
    // 建表
    Db::execute("CREATE TABLE IF NOT EXISTS `ws_my_log` (
        `id` int unsigned NOT NULL AUTO_INCREMENT,
        `content` varchar(255) DEFAULT '',
        `create_time` int unsigned DEFAULT 0,
        PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    return true;
}

public function upgrade(string $from, string $to)
{
    if (version_compare($from, '1.2.0', '<')) {
        Db::execute("ALTER TABLE `ws_my_log` ADD COLUMN `ip` varchar(64) DEFAULT ''");
    }
    return true;
}

生命周期回调必须返回 true,返回 false 或抛异常会中断安装/卸载流程。

二、钩子(Hook)机制

文枢CMS 内置 Action(动作)与 Filter(过滤)两类钩子,支持优先级排序。

2.1 注册钩子

use think\facade\Hook;

// 在入口类的 enable() 或 initialize() 中注册
Hook::add('content_after_save', [$this, 'onContentSaved'], 10);
Hook::add('front_head',        [$this, 'injectHead'],      5);

2.2 Action 钩子(无返回值,用于插入行为)

钩子名触发点参数
content_after_save内容保存后$content
content_after_delete内容删除后$id
user_after_register用户注册后$user
order_paid订单支付成功$order
front_head前台 </head> 前—
front_footer前台 </body> 前—
admin_dashboard后台控制台—
public function injectHead()
{
    echo '<meta name="my-plugin" content="1">' . "\n";
}

public function onContentSaved($content)
{
    Db::name('my_log')->insert([
        'content'     => '内容已更新:' . ($content['title'] ?? ''),
        'create_time' => time(),
    ]);
}

2.3 Filter 钩子(有返回值,用于修改数据)

钩子名作用参数
content_item修改单条内容输出$item
content_list修改内容列表$list
seo_meta修改页面 SEO 元信息$meta
upload_save修改上传结果$result
pay_amount修改订单金额$amount, $order
Hook::add('content_item', function ($item) {
    $item['title'] = '【置顶】' . $item['title'];
    return $item;
}, 10);

Filter 钩子必须 return 处理后的数据,否则后续钩子拿到 null。

2.4 优先级

第三个参数为优先级,数值越小越先执行,默认 10。

Hook::add('seo_meta', [$this, 'a'], 5);    // 先执行
Hook::add('seo_meta', [$this, 'b'], 20);   // 后执行

三、路由注册

插件需要对外暴露地址时,在自己的路由文件中注册:

// addons/myplugin/route/route.php
use think\facade\Route;

Route::get('myplugin/callback', 'addons\myplugin\controller\Callback@index');

插件路由在启用时自动加载,禁用后失效。

四、调试建议

  • 开启 APP_DEBUG = true 后,钩子抛出的异常会带完整堆栈
  • 用 Hook::listen('hook_name', $params) 可手动触发,便于单测
  • 注册钩子时避免在 __construct 里做重活,会影响所有请求