$instances数组是container类的单例缓存池,键为抽象名(经getalias处理),值为已实例化的对象,make()默认存入、get()直接返回,与bind无关,需手动清空且不会自动失效。

instances数组是容器的单例缓存池
$this->instances 是 Container 类里最直接的“对象复用开关”。它不参与类绑定逻辑,也不管你绑的是接口、闭包还是字符串标识,只干一件事:把已经成功实例化过的对象按键名存起来,下次再 get() 或 make() 同一个抽象名时,直接返回这个现成的对象,跳过反射、构造函数解析、依赖注入全过程。
它本质上就是个 PHP 数组,键是抽象名(比如 'app'、'think\Cache'、'app\service\UserService'),值是对应的真实对象实例。注意:键名经过 getAlias() 处理,所以 $app->bind('cache', 'think\Cache') 之后,$app->get('cache') 查的是 $instances['cache'],不是 ['think\Cache']。
- 只有调用
instance($abstract, $instance)才会直接往$instances里塞值(比如框架启动时$this->instance('app', $this)) -
make()在成功创建新实例后,**默认也会存进去**——除非你传了$newInstance = true - 它和
$this->bind完全无关:$bind存的是“怎么造”,$instances存的是“造好了的” - 清空单例?没有内置方法,只能手动
$container->instances = [](测试时有用,生产慎用)
为什么有时候get()返回旧实例,有时候却新建?
关键看三处:是否命中 $instances、是否传了 $newInstance = true、以及有没有在 make() 前被显式清除。
常见误判场景:
- 你在控制器里写
$this->app->get('Db'),结果拿到的是上一次请求残留的连接——因为Db被singleton()绑定过,且$instances['Db']一直存在 - 单元测试中没重置容器,
$instances里还留着上一个测试的UserService实例,导致 mock 失效 - 你调用了
bind('logger', function() { return new Logger(); }),但每次get('logger')都是新对象——因为闭包没进$instances,它走的是$bind分支 +invokeFunction
想强制刷新?要么传 pull('Db', [], true),要么在 bind 时用 singleton('Db', ...) 显式接管生命周期。
instances 和 bind 的分工必须分清
很多人混淆 $instances 和 $bind,以为 bind 了就自动单例。其实完全不是:
-
bind('cache', 'think\Cache')→ 写入$bind['cache'] = 'think\Cache',后续make('cache')会去反射创建think\Cache,并默认存进$instances['cache'] -
instance('cache', $obj)→ 直接写入$instances['cache'] = $obj,绕过所有绑定和反射,get('cache')下次就秒回 -
singleton('cache', function() { return new Cache(); })→ 同时操作两处:把闭包存$bind,并在首次make后把结果塞进$instances
真正容易被忽略的点是:$instances 不会自动失效。哪怕你改了配置、换了环境、甚至 reload 了服务,只要容器实例没重建,$instances 里的对象就一直活着——包括它们持有的数据库连接、文件句柄、静态属性状态。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











