ThinkPHP依赖注入核心是控制反转,依赖容器三步协同完成:反射读构造函数、数组映射查绑定、递归make解析依赖链;需确保路径命名空间准确、autoload刷新、接口显式绑定、避免singleton与make混用,并在支持场景(如方法参数)中触发注入。

ThinkPHP 的依赖注入(DI)和容器绑定,核心是“控制反转”——把对象创建和依赖管理的权力,从类内部交给外部容器。它不是魔法,而是靠三步扎实动作协同完成:反射读构造函数、数组映射查绑定、递归 make 解析依赖链。
反射是起点:容器先看类长什么样
当你调用 App::make(UserService::class),容器第一步不是直接 new,而是用 PHP 的反射机制读取 UserService 的构造函数签名。比如它发现 __construct(UserRepositoryInterface $repo, CacheInterface $cache),就明确知道需要两个依赖。但反射只认类型名,不认实现——它只知道要 UserRepositoryInterface,却不知道该实例化哪个具体类。
绑定是桥梁:告诉容器“接口对应谁”
如果没有显式绑定,容器拿到接口类型后无从下手,既不会报错也不会猜测,而是静默失败,最终抛出 Binding not found 或返回 null。所以必须提前声明映射关系:
- 接口绑定:
App::bind(UserRepositoryInterface::class, DbUserRepository::class) - 闭包决策:
App::singleton(CacheInterface::class, function($app) { return $app->isDebug() ? new FileCache() : new RedisCache(); }) - 服务提供者集中注册:在
app/provider.php中统一写入绑定数组
注意:类路径与命名空间必须严格一致(大小写敏感),且需确保 Composer autoload 已刷新,否则 class_exists() 返回 false,反射根本不会启动。
立即学习“PHP免费学习笔记(深入)”;
递归 make 是执行:一层层造出完整对象树
容器拿到依赖类型后,对每个参数重复上述过程:反射 → 查绑定 → 实例化 → 注入。例如 UserService 依赖 DbUserRepository,而后者又依赖 Connection,容器会自动递归解析整条链。这个过程默认每次调用都新建实例;若需单例,必须用 singleton() 显式注册,且不能和 make() 混用——否则容器会忽略单例策略,重新触发反射和构建。
注入发生在哪里:不是所有地方都走容器
控制器默认是手动 new 出来的,__construct 不会被容器调用。真正触发依赖注入的典型场景有:
- 控制器操作方法的参数类型提示(如
public function index(UserService $service)) - 路由闭包、中间件、事件监听器的方法参数
- 使用
invoke('Foo')手动触发类的构造函数注入 - 在
app/provider.php中注册控制器类,让容器接管其实例化
不走容器流程的地方,类型提示只是摆设,不会自动注入。



















