Facade类必须继承think\Facade,仅实现getFacadeClass()返回服务标识符或类名,不可写业务逻辑,控制器不再继承think\Controller而应继承think\controller\AbstractController或think\BaseController。

Facade类必须继承 think\Facade
所有自定义或系统内置的 Facade 类,只要想支持静态调用(如 Db::table()),就必须直接继承 think\Facade。这不是可选——它提供了核心的 __callStatic() 魔术方法和 createFacade() 逻辑,缺一不可。
常见错误现象:Fatal error: Uncaught Error: Call to undefined method App\Facade\MyService::doSomething(),本质是忘了继承或继承错了类(比如误继承 think\Base 或空类)。
-
think\Facade是唯一能触发容器代理行为的基类;其他类即使名字带 “Facade” 也没用 - 子类只需实现
getFacadeClass()方法,返回字符串(服务标识符或完整类名) - 不能用
final class声明 Facade 类,否则无法被框架内部反射调用
getFacadeClass() 返回值不是类名就是服务标识符
这个方法决定 Facade 背后真正干活的是谁。返回值类型直接影响容器解析方式:
- 返回
'db'、'cache'这类字符串:表示从容器中取已绑定的服务实例(推荐,符合 TP6+ 的设计习惯) - 返回
'app\common\MyService'这类完整类名:容器会尝试自动 new 实例(要求该类无参构造,或依赖可被自动注入) - 返回不存在的标识符或类名,会抛出
ContainerException或ClassNotFoundException
注意:TP6/8 中系统 Facade(如 think\facade\Db)全部返回小写服务名,不是 think\db\Connection 这种类名。
立即学习“PHP免费学习笔记(深入)”;
别在 Facade 类里写实际业务方法
Facade 是纯代理层,不承载逻辑。一旦你在 app\facade\My 里手动加了 public static function doX(),就破坏了门面语义——它不再是“静态接口 + 动态实现”,而变成混杂体,测试、替换、Mock 全都会失效。
- 所有方法调用最终都应落到
getFacadeClass()指向的那个实例上 - 如果需要扩展能力,应该去改目标类(如
app\common\MyService),而不是 Facade 类本身 - 强行在 Facade 里写逻辑,会导致单元测试时无法通过容器替换依赖
TP6/8 中控制器里别再 use think\Controller
这是高频迁移坑。TP6 起彻底移除了 think\Controller 短名类,但很多人升级后仍保留旧 use,结果运行时报 Class 'think\Controller' not found。
- 控制器应继承
think\controller\AbstractController(TP8)或think\BaseController(TP6) - 视图操作统一走
think\facade\View,日志用think\facade\Log,不再需要use think\Controller - 如果你看到文档或老代码里还有
use think\Controller,一律删掉或替换成对应 Facade 类
Facade 的关键不在“怎么写”,而在“怎么不写”——少写一行错代码,比多写十行代理逻辑更安全。



















