必须写在对应子应用的app/provider.php中,如app/admin/provider.php;因TP8多应用为独立容器实例,根config/app.php的providers仅对主应用生效,子应用启动时不加载全局配置。

多应用下 Facade 绑定该写在哪个 provider.php
Facade 本身不“绑定”,真正要操作的是容器服务注册——而多应用模式下,每个子应用有独立的 app/provider.php,**必须写在对应子应用自己的 app/provider.php 中**,不能写在根目录的 config/app.php 或全局 common.php。
常见错误现象:app\admin\facade\User 在后台控制器里调用报 Class not found 或方法不存在——不是 Facade 类没生成,是它依赖的底层服务(如 app\admin\service\UserService)根本没被当前子应用的容器加载。
- 子应用(如
admin)的容器启动时,只加载app/admin/provider.php,不会读取app/api/provider.php或根config/app.php的providers -
app/admin/provider.php是该子应用的「服务注册入口」,所有绑定、单例、接口映射都应集中在此文件中 - Facade 类(如
app\admin\facade\User)只是代理,它背后依赖的app\admin\service\UserService必须通过$this->app->bind()或$this->app->singleton()注入容器 - 若在
app/admin/provider.php中用了App::bind(UserServiceInterface::class, DbUserService::class),则app\admin\facade\User才能正常转发调用
为什么不能写在 config/app.php 的 providers 数组里
config/app.php 的 providers 数组只对「主应用」(即未指定子应用前缀的请求)生效;一旦路由命中 admin/ 前缀,TP8 就会切换到 admin 子应用上下文,并忽略根配置里的 providers 列表。
更关键的是:子应用的容器实例是独立初始化的,config/app.php 里的 providers 在子应用启动阶段根本不会被遍历。
立即学习“PHP免费学习笔记(深入)”;
- TP8 多应用本质是「多套容器实例 + 多套路由+多套配置」,不是共享一个容器再切命名空间
-
config/app.php中的providers只影响app/下无子目录的控制器(比如app/controller/Index.php) - 写错位置的后果:类存在、Facade 存在、
class_exists()返回 true,但app()->make()仍返回 null —— 因为容器里压根没 bind 过
app/admin/provider.php 的典型写法
这个文件没有固定模板,但必须是可执行 PHP 脚本,且逻辑要跑在子应用容器初始化阶段。推荐结构如下:
<?php
use think\Service;
use app\admin\service\UserService;
use app\admin\service\UserServiceInterface;
return function ($app) {
// 绑定接口实现
$app->bind(UserServiceInterface::class, UserService::class);
// 绑定具体类(非接口)
$app->singleton('admin_user_service', function ($app) {
return new UserService($app->make('cache'));
});
};
注意:return function($app) { ... } 是 TP8 子应用 provider 的标准格式,不要写成类继承 think\Service(那是全局 providers 数组里用的)。
最后别忘了:改完 app/admin/provider.php 后,运行 php think optimize:autoload 并清空 runtime/cache/,否则新绑定可能不生效。
多应用的坑不在语法,而在上下文隔离——你以为 bind 了一次,其实只 bind 给了某个子应用;漏掉一个 provider.php,就等于那个应用里整个服务层失联。



















