ThinkPHP 5.1/5.2 是唯一支持原生 Hook::listen() 的稳定版本,TP6+ 已移除该机制;行为类必须实现 run($params = null) 方法,由框架在预设节点自动调用,参数由框架注入且不可手动传参,仅 app/tags.php 可注册,自定义钩子需手动触发。

ThinkPHP 5.1/5.2 是目前唯一能用原生 Hook::listen() 的稳定版本;TP6+ 已彻底移除该机制,硬套会直接报错 —— Class 'think\Hook' not found 或调用无响应。
TP5.1/5.2 中行为类必须实现 run() 方法且不能手动传参
行为类不是普通服务类,它由框架在预设节点自动实例化并调用 run(),参数由框架注入(如 $request、$params),你不能自己加构造函数或改方法签名。
- 行为类必须放在可自动加载的命名空间下(如
app\behavior\CheckAuth) -
run()方法签名固定为public function run($params = null),即使你不需要参数也得保留 - 框架传入的
$params类型取决于钩子点:比如app_init传的是Request对象,action_begin传的是控制器实例 - 别在
run()里写耗时操作(如远程请求、大文件读写),否则阻塞整个请求流程
tags.php 是唯一有效的注册位置,写在控制器或中间件里等于没写
行为绑定只在应用启动时通过 tags.php 加载一次,之后全靠框架内部调度。写在控制器里只是个普通对象调用,根本不会进钩子生命周期。
- 文件路径必须是
app/tags.php(不是config/tags.php或其他路径) - 返回数组格式严格为:
'钩子名' => ['完整类名', '完整类名'],键名必须是框架内置标签(如app_init、view_filter)或你自定义的标签(需手动触发) - 多个行为按数组顺序执行,前一个行为若修改了
$params(注意是引用传递),后一个能感知到 - 别试图用
Hook::add()在运行时动态注册 —— 它只影响后续listen(),但框架核心流程里的钩子早已注册完毕,加了也白加
自定义钩子要手动触发,且不能依赖框架自动调度
框架内置钩子(如 app_begin)由 App::run() 自动调用;你定义的新钩子(比如 user_login_success)必须在业务代码里显式写 Hook::listen('user_login_success', $data) 才会执行。
立即学习“PHP免费学习笔记(深入)”;
- 自定义钩子名建议全小写+下划线,避免和内置冲突(如不要叫
app_init) -
Hook::listen()第二个参数是你要传给所有行为的$params,类型任意,但所有绑定的行为类run()方法都得能处理它 - 如果在模型的
save()后触发Hook::listen('after_save'),要注意事务场景:钩子执行时事务可能尚未提交,此时查数据库可能看不到最新数据 - 调试时可用
trace()或日志确认是否进入run(),但别依赖dump()—— 输出可能被缓冲或截断
最易忽略的一点:TP5 行为类不支持依赖注入,$this->app 或 $this->request 这类容器属性在 run() 里不可用,必须从参数里取或用门面(think\facade\Request);而 TP6 的事件监听器可以自由构造,这点差异常导致迁移时静默失败。



















