createFacade() 是 Facade 的核心中转站,负责根据 getFacadeClass() 返回的服务标识符从容器获取实例;若未绑定则报错,绑定优先级高于 getFacadeClass(),且 $newInstance 参数控制单例行为。

createFacade() 是 Facade 的核心中转站
它不直接干活,只负责从容器里捞出真正干活的实例。调用 Db::table() 时,__callStatic() 会立刻调用 createFacade(),然后才把方法转发过去。没这一步,静态调用就断在半路了。
createFacade() 怎么找对实例?关键看 getFacadeClass()
这个方法返回的字符串不是类名,而是容器里的服务标识符(比如 'db'、'config')。常见错误是写成 \app\common\MyService::class 或完整命名空间字符串,但容器根本没绑定过这个 key,结果抛出 InvalidArgumentException: Identifier "xxx" is not registered.
-
getFacadeClass()返回空或 null →createFacade()默认用当前 Facade 类名(如think\facade\Db)去容器查,通常查不到 - 返回值是字符串
'app\common\Test'→ 容器会尝试解析这个类,前提是它已绑定或能被自动反射创建 - 返回值是
'my_service'→ 必须提前用Container::getInstance()->bind('my_service', MyService::class)注册过
createFacade() 的 $newInstance 参数影响单例行为
默认走单例(即多次调用 Db::query() 共享同一个连接实例),但如果 Facade 类里设置了 protected static $alwaysNewInstance = true;,或者手动传入 true,就会每次新建对象。这对需要隔离状态的场景有用(比如并发测试),但多数业务类不该这么干——连接、缓存、日志这些都依赖复用。
注意:createFacade('some_class', [], true) 这种手动调用方式极少出现,基本只在调试或特殊代理逻辑里用。
立即学习“PHP免费学习笔记(深入)”;
别漏掉 bind() 静态绑定的优先级
如果在 base.php 或启动文件里调用了 Facade::bind('app\facade\Test', 'app\common\Test'),那 createFacade() 就完全绕过 getFacadeClass(),直接按绑定关系取实例。这时候删掉 Facade 类里的 getFacadeClass() 方法也不会报错。
容易踩的坑是:既写了 getFacadeClass(),又做了 Facade::bind(),但两个返回值不一致,结果实际生效的是 bind 绑定的那个——而你还在源码里反复检查 getFacadeClass() 的返回值。
真正读源码时,盯住 createFacade() 里三步:取标识符 → 查容器 → 实例化。其余都是干扰项。它本身没逻辑分支,所有“为什么没生效”的问题,90% 出在标识符和容器注册不匹配上。



















