门面模式通过静态调用(如Cache::get())代理容器解析的实例,需继承think\Facade、重写getFacadeClass()返回有效类名或绑定名,且不可用于类型约束;自定义服务须先绑定容器,路径与命名空间须匹配。

直接用 Cache::get() 这类写法就能工作,前提是门面类已正确定义、容器绑定无误,且没在依赖注入里拿门面类当类型约束。
门面类必须继承 think\Facade 并重写 getFacadeClass()
这是最常卡住的地方。门面不是“随便起个类名就能静态调用”,它靠 __callStatic 拦截调用并转发给容器解析的实例。所以:
-
getFacadeClass()必须返回一个字符串:要么是完整命名空间类名(如'\think\cache\Driver'),要么是容器中已绑定的服务标识(如'cache') - 返回值不能是空、
null或拼错的类名,否则会抛出BadMethodCallException - 类文件路径要符合自动加载规则,比如
app\facade\Cache.php对应app\facade\Cache命名空间 - 别在
getFacadeClass()里写逻辑判断或动态计算——它只应返回一个确定的字符串
自定义服务必须先绑定到容器,再让门面指向它
想用 MyService::doSomething(),光写门面类不够,容器根本不知道 MyService 是谁。得先注册:
- 在服务提供者(如
app\provider\AppServiceProvider)的register()方法里调用$this->app->bind('my_service', MyService::class) - 门面类的
getFacadeClass()就可以返回'my_service'(字符串)或MyService::class(类名) - 如果返回类名,容器会尝试 new 实例;如果返回绑定名,容器会按绑定关系解析——后者更可控,推荐
- 别漏掉
use语句:门面类里要use think\Facade;,业务代码里用use app\facade\MyService;或直接全限定调用
别在类型提示里用门面类
这是 ThinkPHP 官方明确不建议的操作,而且会导致运行时错误:
立即学习“PHP免费学习笔记(深入)”;
- 控制器方法参数写
public function index(Cache $cache)—— 错。PHP 会尝试实例化Cache类,但它只是门面,没有构造逻辑 - 正确做法是用真实类或接口:
public function index(\think\cache\Driver $cache)或public function index(\think\Contracts\CacheHandlerInterface $cache) - 门面只用于“调用侧”语法糖,不是“注入侧”的契约。依赖注入和门面是两套机制,混用会破坏容器解析流程
- 如果你看到
ArgumentCountError或Target class [xxx] does not exist,先检查是不是把门面当类型约束用了
真正容易被忽略的是门面的“单向性”:它只负责把静态调用转成容器实例调用,不参与生命周期管理、不接管构造参数、也不处理接口抽象。一旦你开始琢磨“能不能让门面也支持 mock 测试”,就该意识到——该换用依赖注入了。



















