不是框架坏了,而是默认未注册为单例;需用Container::singleton()或bind(..., ['shared' => true])显式声明,否则每次make()都新建实例,导致Cache等状态丢失。

直接用 app()->make('Cache') 拿不到同一个实例,不是框架坏了,是默认根本没注册成单例——这是绝大多数人卡住的第一步。
为什么 app()->make('Cache') 每次都新建对象
ThinkPHP 容器默认不共享实例:bind() 单独调用时等价于 bind('Cache', Cache::class, ['shared' => false]),make() 每次都会 new 一个全新对象。
- 常见错误现象:
app()->make('Cache')->set('a', 1)后再app()->make('Cache')->get('a')返回null - 这不是缓存失效,是两次拿到的压根就不是同一个
Cache实例 -
get()方法只查instance()手动塞进容器的对象,对bind()+shared=true或singleton()注册的完全不感知 - 别指望靠
get('Cache')“自动取到”单例,它和singleton()是两套机制
正确注册单例的两种写法(必须选其一)
必须显式声明“这个类我要全局只用一个”,不能依赖默认行为。
- 推荐用
Container::singleton('Cache', Cache::class):语义清晰,一次注册,后续make()全部返回缓存实例 - 或用
bind()配数组:app()->bind('Cache', [Cache::class, ['shared' => true]]) - 注意:如果
Cache类构造函数有参数,singleton()会缓存首次解析结果,之后再调make()传参会被忽略 - 千万别在服务提供者里写
app()->bind('Cache', Cache::class)就完事——这等于没设shared
模型/服务里手动 new 出来的对象,$this->app 会报错
ThinkPHP 6.3+ 开始,非容器创建的对象访问 $this->app 直接抛 InvalidArgumentException,不是偶然,是故意暴露问题。
立即学习“PHP免费学习笔记(深入)”;
- 错误写法:
new UserModel()后试图调$this->app->make('Cache') - 正确做法:一律用
app()->make()显式获取依赖,哪怕在模型方法内部也要这样写 - 如果你封装了工具类并希望它也能用容器,别让它继承控制器或模型基类,改用构造注入或静态
app()调用 - 这个限制看似麻烦,实则提前拦住了“对象生命周期失控”的隐患,比如两个 new 出来的模型各自持有一份 Cache 实例却互不知情
instance() 和 singleton() 别混用
两者底层存储位置不同,get() 只认 instance() 塞的,make() 只认 singleton() 或 bind(..., ['shared'=>true]) 绑定的。
- 写了
app()->instance('Cache', new Cache()),就不能指望app()->make('Cache')返回它——make()会走绑定逻辑,不是查instance表 -
instance()会绕过容器的延迟加载和作用域隔离,破坏 ThinkPHP 6.1+ 的生命周期管理设计 - 除非你明确要覆盖某个已注册的单例(比如测试时 mock),否则别碰
instance() - 最稳妥的路径:统一用
Container::singleton()注册,统一用app()->make()获取
真正容易被忽略的是:单例状态是否跨请求保持——答案是否定的。PHP-FPM 每次请求都是新进程,容器实例本身不持久;所谓“单例”只在单次请求生命周期内有效。需要跨请求共享状态,得靠 Redis、数据库或文件,不是靠容器。



















