Facade类需继承think\Facade并重写getFacadeClass()返回真实服务类完整命名空间,如app\service\SmsService;真实服务类须存在且可实例化,依赖应通过构造函数注入而非方法内new或容器get。

Facade 类怎么定义才能被自动解析
ThinkPHP 的 Facade 本质是静态代理,不是真正的静态类。要让 Sms::send() 这种调用生效,必须确保:
• 对应的 Facade 类继承自 think\Facade
• 类中重写 getFacadeClass() 方法,返回真实服务类的完整命名空间
• 真实服务类(如 app\service\SmsService)已存在且可实例化
• 不需要手动注册到容器,但类名和路径必须与命名空间严格一致(比如 app\facade\Sms 对应 app/facade/Sms.php)
真实服务类里要不要依赖注入客户端
推荐在真实服务类构造函数中接收短信客户端实例,而不是在方法内 new 或从容器 get —— 这样便于单元测试和替换实现。例如:
namespace app\service;
class SmsService
{
protected $client;
public function __construct(\Overtrue\EasySms\EasySms $client)
{
$this->client = $client;
}
public function send(string $phone, string $template, array $data = [])
{
return $this->client->send($phone, [
'template' => $template,
'data' => $data,
]);
}
}
注意:EasySms 实例本身应通过容器绑定(比如在 app/provider.php 中 bind \Overtrue\EasySms\EasySms::class),否则 Facade 调用时会因依赖无法解析而报 Target [xxx] is not instantiable
Facade 类里能不能加自己的方法
可以,但要小心。Facade 类只负责代理,所有业务逻辑必须放在真实服务类里。如果你在 Facade 类中写了 send() 方法,它会直接覆盖代理行为,导致 getFacadeClass() 失效,后续无法享受容器管理、AOP 等特性。
立即学习“PHP免费学习笔记(深入)”;
常见错误写法:
class Sms extends Facade
{
public static function send($p, $t) {
// ❌ 这里手写逻辑,绕过了容器和真实服务类
return 'done';
}
}
正确做法是:Facade 类保持“空”,只返回类名;所有参数校验、模板映射、失败重试等都放在 SmsService 中。
为什么 Sms::send() 报 “Class not found”
最常见三个原因:
• app\facade\Sms 类文件没创建,或路径/命名空间拼错(比如写成 app\facade\SMS)
• getFacadeClass() 返回的类名不存在,或命名空间少了一个 \(例如返回 app\service\SmsService 而不是 app\service\SmsService)
• 应用未开启 Facade 支持(TP6 默认开启,但若手动删过 config/app.php 中的 'facade' => true 就会失效)
调试技巧:在控制器里临时执行 var_dump(class_exists('app\facade\Sms')) 和 var_dump(class_exists('app\service\SmsService')),快速定位哪一环断了。
Facade 封装的关键不在“写得多”,而在“代理得干净”——真实逻辑下沉、依赖显式声明、错误早暴露。一旦 Facade 开始承担业务判断,就等于把门锁焊死了。



















