app()->invoke() 是 ThinkPHP 5.1+(含 TP6/8)里底层反射调用方法,核心用于执行任意类的方法/函数/闭包,并自动做依赖注入,常用于框架内部调度或跨类复用逻辑(但不推荐滥用跨控制器调用)。
下面从“作用 → 用法 → 示例 → 源码 → 避坑”讲清楚。
一、它是什么(作用)
app() 是助手函数,返回 think\App 实例;invoke() 是 App 的方法,底层靠 PHP反射 + 容器依赖注入 实现:
- 实例化类(自动注入构造器依赖)
- 调用指定方法(自动注入方法参数依赖)
- 支持:控制器方法、服务类方法、闭包、函数
等价于:
// 底层封装$app = new \think\App();$app->invoke([类, 方法], 参数);
二、基本用法(TP5.1/6/8 通用)
1)调用类方法(最常用)
格式:
// 方式1:数组 [类名, 方法名]app()->invoke(['完整类名', '方法名'], [参数数组]);// 方式2:字符串 "类名@方法名"app()->invoke('完整类名@方法名', [参数数组]);
2)调用闭包/函数
// 闭包app()->invoke(function($name){return 'Hello ' . $name;}, ['豆包']);// 全局函数app()->invoke('trim', [' abc ']);
三、实战示例(直接复制可用)
示例1:调用服务类方法(推荐)
// app/service/AuthService.phpnamespace app\service;class AuthService {public function check($uid) {return $uid > 0;}}// 控制器中调用$result = app()->invoke(['app\service\AuthService', 'check'], [1001]);// 或字符串形式$result = app()->invoke('app\service\AuthService@check', [1001]);
示例2:跨控制器调用(不推荐,仅应急)
// app/controller/NotifyController.phpnamespace app\controller;class NotifyController {public function sendMail($to) {return '发送给:' . $to;}}// IndexController 中调用$result = app()->invoke(['app\controller\NotifyController', 'sendMail'], ['a@qq.com']);
示例3:自动依赖注入(DI)
// 服务类(依赖 Request)namespace app\service;use think\Request;class UserService {public function getInfo(Request $request, $uid) {return ['uid' => $uid, 'ip' => $request->ip()];}}// 调用时不用传 Request,自动注入$result = app()->invoke('app\service\UserService@getInfo', [1001]);
四、源码核心(TP6 简化版)
think\App.php 里的 invoke 最终调用 invokeMethod:
public function invoke($method, array $vars = []) {return $this->invokeMethod($method, $vars);}protected function invokeMethod($method, $vars) {// 1. 解析类和方法list($class, $method) = $this->parseMethod($method);// 2. 实例化类(依赖注入构造器)$instance = $this->make($class);// 3. 反射方法,绑定参数(依赖注入方法参数)$reflect = new \ReflectionMethod($instance, $method);$args = $this->bindParams($reflect, $vars);// 4. 执行方法return $reflect->invokeArgs($instance, $args);}
核心:make() 实例化 + bindParams() 自动注入参数。
五、避坑指南(重点)
跨控制器调用不推荐
invoke不执行路由、中间件、控制器初始化(如initialize)- 控制器里的
$this->request、$this->view可能未初始化,导致报错
优先用服务类,而非控制器互调
- 复用逻辑抽成
app/service/xxx.php,再用invoke调用,更安全、解耦
- 复用逻辑抽成
参数严格匹配
- 方法参数顺序要和
$vars数组顺序一致 - 依赖注入的参数(如
Request)不用传,框架自动注入
- 方法参数顺序要和
TP5.0 无此方法
- TP5.0 用
action(),TP5.1+ 移除action(),改用app()->invoke()
- TP5.0 用
六、和 call_user_func 的区别
call_user_func:不支持依赖注入,需手动实例化类、传参app()->invoke:容器级调用,自动实例化 + 自动注入依赖,符合 ThinkPHP 的 DI 设计
