首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 瀏覽器與語言偵測實戰:基於 $_SERVER 的 User-Agent 與 Accept-Language 解析

     編輯:PHP 綜合知識

前言

在 Web 開發中,根據用戶的瀏覽器類型、版本及偏好語言提供差異化體驗(如舊版 IE 降級提示、多語系自動切換、移動端適配)是常見需求。PHP 透過 $_SERVER['HTTP_USER_AGENT'] 與 $_SERVER['HTTP_ACCEPT_LANGUAGE'] 即可實現無依賴的偵測。本文提供生產級的解析函數、正則模式庫、以及現代化的替代方案建議。

一、瀏覽器偵測:User-Agent 解析

1. 核心原理

HTTP_USER_AGENT 字串包含瀏覽器引擎、版本、作業系統、裝置等資訊。例如:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1

2. 經典正則匹配方案(輕量、無依賴)

<?php
/**
 * 解析 User-Agent,返回結構化資訊
 * @return array ['browser'=>名稱, 'version'=>版本, 'engine'=>引擎, 'os'=>作業系統, 'device'=>裝置類型]
 */
function parseUserAgent(string $ua = ''): array {
    $ua = $ua ?: ($_SERVER['HTTP_USER_AGENT'] ?? '');

    $result = [
        'browser' => 'Unknown',
        'version' => '0.0',
        'engine' => 'Unknown',
        'os' => 'Unknown',
        'device' => 'Desktop', // Desktop / Mobile / Tablet / Bot
    ];

    // 1. 機器人/爬蟲優先判斷
    $botPatterns = [
        'Googlebot' => 'Googlebot',
        'Baiduspider' => 'Baiduspider',
        'Bingbot' => 'Bingbot',
        'YandexBot' => 'YandexBot',
        'facebookexternalhit' => 'FacebookBot',
        'Twitterbot' => 'TwitterBot',
        'Slackbot' => 'SlackBot',
        'WhatsApp' => 'WhatsAppBot',
    ];
    foreach ($botPatterns as $pattern => $name) {
        if (stripos($ua, $pattern) !== false) {
            $result['browser'] = $name;
            $result['device'] = 'Bot';
            return $result;
        }
    }

    // 2. 作業系統判斷
    $osPatterns = [
        '/Windows NT 10\.0/' => 'Windows 10/11',
        '/Windows NT 6\.3/' => 'Windows 8.1',
        '/Windows NT 6\.2/' => 'Windows 8',
        '/Windows NT 6\.1/' => 'Windows 7',
        '/Windows NT 6\.0/' => 'Windows Vista',
        '/Windows NT 5\.1/' => 'Windows XP',
        '/Mac OS X (\d+[._]\d+)/' => 'macOS $1',
        '/iPhone OS (\d+[._]\d+)/' => 'iOS $1',
        '/iPad.*OS (\d+[._]\d+)/' => 'iPadOS $1',
        '/Android (\d+[._]\d+)/' => 'Android $1',
        '/Linux/' => 'Linux',
        '/CrOS/' => 'Chrome OS',
    ];
    foreach ($osPatterns as $pattern => $name) {
        if (preg_match($pattern, $ua, $m)) {
            $result['os'] = is_string($m[0]) ? preg_replace($pattern, $name, $m[0]) : $name;
            break;
        }
    }

    // 3. 裝置類型
    if (stripos($ua, 'Mobile') !== false || stripos($ua, 'Android') !== false) {
        $result['device'] = stripos($ua, 'Tablet') !== false || stripos($ua, 'iPad') !== false ? 'Tablet' : 'Mobile';
    }

    // 4. 瀏覽器與引擎判斷(順序很重要:特定瀏覽器優於通用引擎)
    $browserPatterns = [
        // 格式: '正則' => ['browser' => 名稱, 'version_group' => 1, 'engine' => 引擎]
        '/Edg\/(\d+\.\d+)/' => ['browser' => 'Edge', 'version' => 1, 'engine' => 'Blink'],
        '/Chrome\/(\d+\.\d+)/' => ['browser' => 'Chrome', 'version' => 1, 'engine' => 'Blink'],
        '/CriOS\/(\d+\.\d+)/' => ['browser' => 'Chrome iOS', 'version' => 1, 'engine' => 'WebKit'],
        '/Firefox\/(\d+\.\d+)/' => ['browser' => 'Firefox', 'version' => 1, 'engine' => 'Gecko'],
        '/FxiOS\/(\d+\.\d+)/' => ['browser' => 'Firefox iOS', 'version' => 1, 'engine' => 'WebKit'],
        '/Safari\/(\d+\.\d+)/' => ['browser' => 'Safari', 'version' => 1, 'engine' => 'WebKit'],
        '/Version\/(\d+\.\d+).*Safari/' => ['browser' => 'Safari', 'version' => 1, 'engine' => 'WebKit'],
        '/MSIE (\d+\.\d+)/' => ['browser' => 'IE', 'version' => 1, 'engine' => 'Trident'],
        '/Trident\/.*rv:(\d+\.\d+)/' => ['browser' => 'IE', 'version' => 1, 'engine' => 'Trident'],
        '/Opera\/(\d+\.\d+)/' => ['browser' => 'Opera', 'version' => 1, 'engine' => 'Presto'],
        '/OPR\/(\d+\.\d+)/' => ['browser' => 'Opera', 'version' => 1, 'engine' => 'Blink'],
        '/SamsungBrowser\/(\d+\.\d+)/' => ['browser' => 'Samsung Internet', 'version' => 1, 'engine' => 'Blink'],
        '/UCBrowser\/(\d+\.\d+)/' => ['browser' => 'UC Browser', 'version' => 1, 'engine' => 'Blink'],
        '/MQQBrowser\/(\d+\.\d+)/' => ['browser' => 'QQ Browser', 'version' => 1, 'engine' => 'Blink'],
    ];

    foreach ($browserPatterns as $pattern => $info) {
        if (preg_match($pattern, $ua, $m)) {
            $result['browser'] = $info['browser'];
            $result['version'] = $m[$info['version']] ?? '0.0';
            $result['engine'] = $info['engine'] ?? $result['engine'];
            break;
        }
    }

    return $result;
}

// 使用範例
$info = parseUserAgent();
echo "瀏覽器: {$info['browser']} {$info['version']}\n";
echo "引擎: {$info['engine']}\n";
echo "作業系統: {$info['os']}\n";
echo "裝置: {$info['device']}\n";
?>

3. 簡易版:僅判斷主流瀏覽器名稱

<?php
function getBrowserName(string $ua = ''): string {
    $ua = $ua ?: ($_SERVER['HTTP_USER_AGENT'] ?? '');
    if (str_contains($ua, 'Edg/')) return 'Edge';
    if (str_contains($ua, 'Chrome') && !str_contains($ua, 'Edg') && !str_contains($ua, 'OPR')) return 'Chrome';
    if (str_contains($ua, 'Firefox')) return 'Firefox';
    if (str_contains($ua, 'Safari') && !str_contains($ua, 'Chrome')) return 'Safari';
    if (str_contains($ua, 'MSIE') || str_contains($ua, 'Trident/')) return 'Internet Explorer';
    if (str_contains($ua, 'OPR') || str_contains($ua, 'Opera')) return 'Opera';
    return 'Unknown';
}
?>

二、語言偵測:Accept-Language 解析

1. 標準格式

Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7

格式:語言代碼[-地區代碼][;q=權重],權重 0~1,預設 1。按權重降序排列。

2. 解析並返回優先語言

<?php
/**
 * 解析 Accept-Language,返回按優先級排序的語言代碼陣列
 * @return array 如 ['zh-CN', 'zh', 'en-US', 'en']
 */
function parseAcceptLanguage(string $header = ''): array {
    $header = $header ?: ($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '');
    if (empty($header)) return ['zh-CN']; // 預設

    $langs = [];
    foreach (explode(',', $header) as $part) {
        $part = trim($part);
        $q = 1.0;
        if (strpos($part, ';q=') !== false) {
            [$lang, $qStr] = explode(';q=', $part, 2);
            $q = (float)$qStr;
        } else {
            $lang = $part;
        }
        $lang = trim($lang);
        if ($lang) $langs[$lang] = $q;
    }

    // 按權重降序排序
    arsort($langs);
    return array_keys($langs);
}

/**
 * 獲取最佳匹配語言(支援應用支援語言白名單)
 */
function getBestLanguage(array $supported = ['zh-CN', 'zh-TW', 'en-US', 'ja', 'ko']): string {
    $preferred = parseAcceptLanguage();
    foreach ($preferred as $lang) {
        // 精確匹配
        if (in_array($lang, $supported, true)) return $lang;
        // 語言碼匹配(如 zh-CN → zh)
        $primary = explode('-', $lang)[0];
        foreach ($supported as $sup) {
            if (str_starts_with($sup, $primary . '-')) return $sup;
        }
    }
    return $supported[0]; // fallback
}

// 使用範例
$bestLang = getBestLanguage();
echo "最佳語言: $bestLang\n"; // 如 zh-CN
// 設置語系
putenv("LANG=$bestLang");
setlocale(LC_ALL, $bestLang . '.UTF-8');
bindtextdomain('messages', __DIR__ . '/locale');
textdomain('messages');
?>

三、現代化替代方案:使用成熟庫

手寫正則難以覆蓋海量 UA 變體,生產環境強烈建議使用:

1. ua-parser/php (官方 UA 解析庫)

composer require ua-parser/uap-php
<?php
require 'vendor/autoload.php';
use UAParser\Parser;

$parser = Parser::create();
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
$result = $parser->parse($ua);

echo "瀏覽器: {$result->ua->family} {$result->ua->major}.{$result->ua->minor}\n";
echo "作業系統: {$result->os->family} {$result->os->major}\n";
echo "裝置: {$result->device->family} ({$result->device->brand})\n";
?>

2. Symfony HttpFoundation (企業級標準)

composer require symfony/http-foundation
<?php
use Symfony\Component\HttpFoundation\Request;

$request = Request::createFromGlobals();
// 獲取最佳語言(需配置支援語言)
$locale = $request->getPreferredLanguage(['zh_CN', 'zh_TW', 'en_US', 'ja', 'ko']);
// 獲取 User-Agent 物件(需額外安裝 ua-parser 整合)
// $request->headers->get('User-Agent');
?>

四、實務應用場景與最佳實踐

場景技術方案注意事項
多語系自動切換 Middleware 解析 Accept-Language → 設定 Session/Cookie/URL 前綴 提供手動切換入口,優先級:用戶設定 > Cookie > Header > 預設
舊版 IE 降級/阻擋 偵測 IE ≤ 11 → 顯示升級提示、載入 Polyfill、或導向靜態降級頁 IE 11 UA 含 "Trident/7.0; rv:11.0",Edge 含 "Edg/"
移動端適配 偵測 Mobile/Tablet → 輸出不同模板、CSS、圖片尺寸 優先使用響應式設計,UA 偵測作為補充
爬蟲識別/SEO 偵測 Bot → 返回純內容、關閉追蹤碼、預渲染頁面 需維護 Bot UA 白名單,防止偽裝
統計分析 記錄 UA、語言、OS、裝置 → 數據倉庫分析用戶畫像 遵守 GDPR/個資法,匿名化、加密儲存

五、總結

PHP 透過 $_SERVER['HTTP_USER_AGENT'] 與 $_SERVER['HTTP_ACCEPT_LANGUAGE'] 即可實現零依賴的瀏覽器與語言偵測。本文提供了從簡易正則到結構化解析函數的完整實現,並強烈建議生產環境採用 ua-parser/php 或 Symfony HttpFoundation 等成熟庫,以應對龐大且持續演進的 User-Agent 生態。正確的語言協商機制(Header → 用戶設定 → 預設)能顯著提升多語系產品的用戶體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved