本文目录导读:

在PHP项目中实现泛域名解析并适配项目路由,通常需要结合Web服务器配置和PHP路由处理两个层面,以下是完整的实现方案:
Web服务器配置(以Nginx为例)
server {
listen 80;
server_name ~^(?<subdomain>.+)\.yourdomain\.com$;
root /path/to/your/project/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SUBDOMAIN $subdomain; # 传递子域名到PHP
include fastcgi_params;
}
}
PHP路由适配实现
基础路由类
<?php
class Router {
private $routes = [];
private $subdomain;
public function __construct() {
// 获取子域名(从Nginx或Apache传递)
$this->subdomain = $_SERVER['SUBDOMAIN'] ?? $this->parseSubdomain();
}
// 解析子域名
private function parseSubdomain() {
$host = $_SERVER['HTTP_HOST'];
$domains = explode('.', $host);
// 判断是否是泛域名(blog.yourdomain.com)
if (count($domains) >= 3) {
return $domains[0];
}
return null;
}
// 添加路由规则
public function addRoute($pattern, $callback, $options = []) {
$this->routes[] = [
'pattern' => $pattern,
'callback' => $callback,
'options' => $options,
'subdomain' => $options['subdomain'] ?? null
];
}
// 匹配并执行路由
public function dispatch($uri) {
$uri = parse_url($uri, PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];
foreach ($this->routes as $route) {
// 检查子域名匹配
if ($route['subdomain'] !== null) {
if ($this->subdomain !== $route['subdomain']) {
continue;
}
}
// 将路由模式转换为正则表达式
$pattern = preg_replace('/\{(\w+)\}/', '(?P<$1>[^/]+)', $route['pattern']);
$pattern = '#^' . $pattern . '$#';
if (preg_match($pattern, $uri, $matches)) {
// 提取参数
$params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
// 执行回调
return call_user_func_array($route['callback'], [$params, $this->subdomain]);
}
}
// 404处理
http_response_code(404);
echo "404 Not Found";
}
}
// 使用示例
$router = new Router();
$router->addRoute('/user/{id}', function($params, $subdomain) {
echo "User ID: " . $params['id'] . " from subdomain: " . $subdomain;
}, ['subdomain' => 'api']);
$router->addRoute('/blog/{slug}', function($params, $subdomain) {
echo "Blog post: " . $params['slug'] . " from subdomain: " . $subdomain;
}, ['subdomain' => 'blog']);
$router->dispatch($_SERVER['REQUEST_URI']);
进阶:使用中间件模式
<?php
class SubdomainRouter {
private $subdomainRoutes = [];
private $middleware = [];
public function __construct() {
$this->parseSubdomain();
}
private function parseSubdomain() {
$host = $_SERVER['HTTP_HOST'];
// 支持多级子域名
preg_match('/^(?:(?<subdomain>[^.]+)\.)?(?<domain>[^.]+\.[^.]+)$/', $host, $matches);
$this->currentSubdomain = $matches['subdomain'] ?? '';
$this->domain = $matches['domain'];
}
public function addSubdomainRoute($subdomain, $callback, $middleware = []) {
$this->subdomainRoutes[$subdomain] = [
'callback' => $callback,
'middleware' => $middleware
];
}
public function addMiddleware($subdomain, $middleware) {
$this->middleware[$subdomain][] = $middleware;
}
public function run() {
$subdomain = $this->currentSubdomain;
// 检查是否有匹配的子域名路由
if (isset($this->subdomainRoutes[$subdomain])) {
$route = $this->subdomainRoutes[$subdomain];
// 执行中间件
foreach ($route['middleware'] as $middleware) {
if (is_callable($middleware)) {
$result = $middleware($this);
if ($result === false) {
return;
}
}
}
// 执行路由回调
return call_user_func($route['callback'], $this);
}
// 默认处理(www或其他)
return $this->handleDefault();
}
private function handleDefault() {
// 默认路由处理
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
echo "Default route for: " . $uri;
}
}
// 使用示例
$app = new SubdomainRouter();
// 添加子域名路由
$app->addSubdomainRoute('api', function($app) {
echo "API Router for subdomain: " . $app->currentSubdomain;
}, ['auth', 'rate-limit']);
$app->addSubdomainRoute('blog', function($app) {
echo "Blog Router for subdomain: " . $app->currentSubdomain;
});
// 添加中间件
$app->addMiddleware('api', function($app) {
// 验证API密钥
$apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
if (empty($apiKey)) {
http_response_code(401);
echo "Unauthorized";
return false;
}
return true;
});
$app->run();
Apache配置(如果使用Apache)
<VirtualHost *:80>
ServerName yourdomain.com
ServerAlias *.yourdomain.com
DocumentRoot /path/to/project/public
<Directory /path/to/project/public>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# 传递子域名
SetEnvIf Host ^(.*?)\.yourdomain\.com$ SUBDOMAIN=$1
</VirtualHost>
数据库驱动的动态路由
<?php
class DynamicSubdomainRouter {
private $db;
private $tenantCache = [];
public function __construct(PDO $db) {
$this->db = $db;
$this->parseSubdomain();
}
public function resolveTenant() {
$subdomain = $this->currentSubdomain;
// 缓存查询
if (!isset($this->tenantCache[$subdomain])) {
$stmt = $this->db->prepare("SELECT * FROM tenants WHERE subdomain = ?");
$stmt->execute([$subdomain]);
$this->tenantCache[$subdomain] = $stmt->fetch(PDO::FETCH_ASSOC);
}
return $this->tenantCache[$subdomain];
}
public function route() {
$tenant = $this->resolveTenant();
if (!$tenant) {
// 子域名不存在
http_response_code(404);
echo "Tenant not found";
return;
}
// 设置租户上下文
$this->setTenantContext($tenant);
// 根据租户类型路由
switch ($tenant['type']) {
case 'store':
$this->routeStore($tenant);
break;
case 'blog':
$this->routeBlog($tenant);
break;
default:
$this->routeDefault($tenant);
}
}
private function setTenantContext($tenant) {
define('TENANT_ID', $tenant['id']);
define('TENANT_SUBDOMAIN', $tenant['subdomain']);
// 设置数据库连接等
}
}
最佳实践建议
- 缓存DNS解析:使用Redis或APC缓存子域名对应的配置
- 安全性考虑:
- 限制允许的子域名字符
- 防止SSRF攻击
- 检查子域名白名单
- 性能优化:
- 使用FastRoute等高性能路由库
- 启用OPcache
- 测试:
- 使用
/etc/hosts文件模拟多个子域名 - 编写单元测试覆盖不同子域名场景
- 使用
这个方案可以灵活地处理带有子域名的多租户系统、API网关或多站点应用。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播