他说:
webman 多应用的路由解析逻辑,核心在 webman-framework 的 App.php 里,它解析 URL 的第一段作为应用名。所以所有问题都来自同一个根:框架用 URL 路径前缀识别应用,而不是 Host。
整条对话链揭示了一个本质:webman 多应用是用 URL 第一段识别应用,不是用 Host。所有方案都是在绕这个设计,不是真正解决它。
绕法有两种:
1、在请求进 webman 之前改路径(Nginx rewrite)
2、在 webman 内部改路径(PHP 层)
真正最简单、最稳固的方案:全局中间件改 URI
读完文档和帖子后,发现一个被忽视的细节:webman 的路由匹配用的是 $request->path(),而 path() 的来源是 $_SERVER['REQUEST_URI'],这是一个普通的 PHP 超全局变量,在中间件里完全可以修改。
关键验证点
$_SERVER['REQUEST_URI'] 修改是否在路由匹配前生效?是的——webman 的中间件执行顺序是全局中间件 → 路由匹配 → 控制器,@ 超全局中间件是最早执行的,此时路由还没匹配,$_SERVER['REQUEST_URI'] 改了之后,后续 $request->path() 返回的就是新路径,路由匹配拿到的也是新路径。
这才是真正从根上解决问题,不依赖 Nginx,不依赖任何插件,不会有同名控制器冲突。
下面是具体代码:
第一步:创建配置文件
// config/domain.php
return [
'shop.example.com' => 'shop',
'admin.example.com' => 'admin',
'blog.example.com' => 'blog',
];
第二步:创建中间件
<?php
// app/middleware/DomainToApp.php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class DomainToApp implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
$map = config('domain', []);
$host = $request->host(true); // true = 去掉端口号
// 域名不在白名单,直接 404
if (!isset($map[$host])) {
return response('Not Found', 404);
}
$app = $map[$host];
$path = $request->path(); // e.g. "/" 或 "/user/list"
// 有对应应用,且路径还没带前缀,则插入
if ($app !== '' && !str_starts_with($path, '/' . $app)) {
$newPath = '/' . $app . ($path === '/' ? '' : $path);
$qs = $request->queryString();
$_SERVER['REQUEST_URI'] = $newPath . ($qs ? '?' . $qs : '');
}
return $handler($request);
}
}
第三步:注册为超全局中间件
// config/middleware.php
return [
'@' => [
app\middleware\DomainToApp::class,
],
'' => [
// 其他全局中间件...
],
];
@ 是超全局,会在所有路由匹配和其他中间件之前执行,且对插件也生效。
第四步:目录结构确认一下
你的 app/ 目录需要是这样:
app/
├── shop/
│ ├── controller/
│ │ └── IndexController.php
│ └── model/
├── admin/
│ ├── controller/
│ │ └── IndexController.php ← 同名没问题,命名空间不同
│ └── model/
└── blog/
├── controller/
│ └── IndexController.php
└── model/
控制器命名空间分别是 app\shop\controller、app\admin\controller、app\blog\controller,互不干扰。
验证
启动 webman 后:
访问 实际路由到
shop.example.com/user/list app\shop\controller\UserController::list
admin.example.com/user/list app\admin\controller\UserController::list
unknown.example.com/anything 404
本地测试时没有真实域名,可以改 hosts 文件:
127.0.0.1 shop.example.com
127.0.0.1 admin.example.com
127.0.0.1 blog.example.com
然后访问 http://shop.example.com:8787/user/list 即可验证。
新增域名只需改一处
以后加一个应用 api:
config/domain.php 加一行 'api.example.com' => 'api'
创建 app/api/controller/ 目录开始写代码
Nginx 不用动,domain 插件不用装,重启 webman 生效。
额,这个实现方案,很久以前就有了啊