thinkphp多商户需通过request()->host()获取域名并提取子域作为商户标识,禁止用url或路由参数;应查库/缓存映射域名到商户id,配置、路由、数据库、模板、日志等均需按商户动态隔离,并修正url::build()等生成链接逻辑以适配当前域名。

ThinkPHP 怎么从域名里拿到商户标识
直接用 request()->host() 或 $_SERVER['HTTP_HOST'] 读域名,别碰 url() 或路由参数——那些是路径层面的,和域名无关。多商户场景下,域名本身就是关键路由因子,比如 shop1.example.com 和 shop2.example.com 必须在入口就区分,不能等进到控制器才判断。
常见错误是把域名解析逻辑写在中间件里却没设为「全局前置」,导致某些请求(如静态资源、跨域预检)绕过,商户识别失效;或者用 parse_url() 处理带端口的开发环境域名(如 localhost:8080),结果子域名提取错乱。
- 生产环境优先用
request()->host(),它已自动处理了端口剥离 - 本地开发若用
localhost,需手动切分:用explode('.', $_SERVER['HTTP_HOST'], 2)取第一段,但要加count() > 1判断防崩 - 如果用了泛域名 SSL(如
*.example.com),确保 Nginx/Apache 把原始 Host 透传给 PHP,别让反向代理覆盖掉
怎么把域名映射成有效的商户 ID 或配置
不能硬编码 if ($host === 'a.example.com') { $mid = 1; } ——部署时改代码太危险。应该查数据库或读缓存,且必须加兜底逻辑:查不到就抛异常或跳默认页,绝不能静默 fallback 到主站。
性能上,每次请求都查库会拖慢首屏,得用 think\Cache 缓存映射关系,键名建议用 'domain_map_' . md5($host),避免特殊字符污染缓存系统;过期时间设 600 秒足够,商户增删不频繁,但要有后台清缓存接口。
- 数据库表至少包含
domain(唯一索引)、merchant_id、status(启用状态)三字段 - 查库前先校验域名格式:
filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME),防注入或非法字符 - 缓存未命中时,用
Db::name('merchant_domain')->where('domain', $host)->find(),别用like模糊查
多商户下路由和配置如何动态隔离
ThinkPHP 的 config() 是运行时生效的,但不能在控制器里反复调用 config($key, $value) 覆盖全局——这会影响后续请求。正确做法是在应用初始化阶段(如 app\common\middleware\DomainInit.php)根据域名加载专属配置文件,例如 config/shop1/database.php。
路由也一样:别指望一个 route.php 文件扛所有商户,要用 Route::import() 按需载入,路径拼接用 config('app.root_path') . 'config/route/' . $mid . '.php',文件不存在就报错,不默默跳过。
- 数据库连接名必须动态化,比如
Db::connect('shop_' . $mid),对应配置里定义'shop_1' => [...] - 模板路径也要隔离:
view_replace_str中的静态资源前缀可按$mid动态生成,避免 A 商户看到 B 的 CSS - 日志目录建议按商户分:
log/2024/06/shop1/,用Log::init(['path' => RUNTIME_PATH . 'log/' . $mid . '/'])
为什么 Url::build() 生成的链接还是主站域名
因为 ThinkPHP 默认不感知当前域名上下文,Url::build() 生成的是相对路径或基于 app.url 配置的绝对地址。多商户下必须显式传参:Url::build('index/index', [], true, 'https://' . $host),否则所有生成链接都会指向主站,用户点一下就跳出当前商户。
更稳妥的做法是封装一个 shopUrl() 辅助函数,在里面统一处理协议、域名、子路径前缀(比如有些商户要挂 /shop1/ 前缀),而不是到处手写 Url::build()。
- 千万别在模板里用
{:url('index/index')}这种写法,它完全不认当前商户上下文 - 如果用了 CDN 或二级域名托管静态资源,
__STATIC__这类替换变量也得按商户动态算,不能写死在 config.php 里 - API 接口返回的图片 URL 同理,后端拼接时必须用当前商户的域名,前端不要自己拼
最麻烦的其实是 HTTPS 协议判断——Nginx 反代后 $_SERVER['HTTPS'] 常为空,得靠 $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' 补充,这个逻辑漏掉,生成的链接就会混用 http/https,浏览器直接拦截混合内容。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











