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

PHP 判斷瀏覽器類型與用戶語言:$_SERVER 變量解析與現代 User-Agent 解析庫推薦

     編輯:PHP 相關知識

通过 $_SERVER['HTTP_USER_AGENT'] 和 $_SERVER['HTTP_ACCEPT_LANGUAGE'] 可以获取浏览器标识和用户偏好语言。但原始字符串解析复杂、易出错,现代项目推荐使用成熟解析库。本文提供原生解析代码、常见陷阱、及推荐库对比。

原生解析:简单但脆弱

<?php
/**
 * 简单浏览器检测 (仅供演示,生产勿用)
 * 缺点:UA 字符串极其复杂、易伪造、新浏览器不识别、维护成本高
 */
function detectBrowserSimple(string $ua = ''): string {
    $ua = $ua ?: ($_SERVER['HTTP_USER_AGENT'] ?? '');
    $ua = strtolower($ua);

    // 顺序很重要:更具体的先判断
    if (str_contains($ua, 'edg/')) return 'Microsoft Edge';
    if (str_contains($ua, 'chrome/') && !str_contains($ua, 'chromium')) return 'Google Chrome';
    if (str_contains($ua, 'firefox/')) return 'Mozilla Firefox';
    if (str_contains($ua, 'safari/') && !str_contains($ua, 'chrome')) return 'Apple Safari';
    if (str_contains($ua, 'opera/') || str_contains($ua, 'opr/')) return 'Opera';
    if (str_contains($ua, 'msie ') || str_contains($ua, 'trident/')) return 'Internet Explorer';
    if (str_contains($ua, 'curl/')) return 'cURL';
    if (str_contains($ua, 'bot') || str_contains($ua, 'spider') || str_contains($ua, 'crawler')) return 'Bot/Crawler';

    return 'Unknown';
}

/**
 * 简单语言检测
 * HTTP_ACCEPT_LANGUAGE 格式:zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7
 */
function detectLanguageSimple(string $header = ''): string {
    $header = $header ?: ($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '');
    // 解析 q 值排序
    $langs = [];
    foreach (explode(',', $header) as $part) {
        $part = trim($part);
        if (preg_match('/^([a-z-]+)(?:;q=([0-9.]+))?$/i', $part, $m)) {
            $langs[$m[1]] = (float)($m[2] ?? 1.0);
        }
    }
    arsort($langs);
    $top = array_key_first($langs) ?? 'en';

    // 归一化常见代码
    $map = [
        'zh-cn' => 'zh-CN', 'zh-hans' => 'zh-CN', 'zh-sg' => 'zh-CN',
        'zh-tw' => 'zh-TW', 'zh-hk' => 'zh-TW', 'zh-mo' => 'zh-TW', 'zh-hant' => 'zh-TW',
        'en-us' => 'en-US', 'en-gb' => 'en-GB',
    ];
    return $map[strtolower($top)] ?? $top;
}

// 使用
echo "Browser: " . detectBrowserSimple() . "\n";
echo "Language: " . detectLanguageSimple() . "\n";
?>

现代推荐:专业 UA 解析库

库安裝特點適用場景
ua-parser/uap-phpcomposer require ua-parser/uap-php官方 ua-parser 實現、正則庫自動更新、支援設備/作業系統/瀏覽器三維度通用專案、統計分析、設備適配
jenssegers/agentcomposer require jenssegers/agentLaravel 風格、基於 ua-parser、提供 isMobile()/isDesktop() 等便利方法Laravel 專案、視圖邏輯判斷
piwik/device-detectorcomposer require device-detector/device-detectorMatomo 同款、支援客戶端提示 (Client Hints)、解析速度快、緩存機制高流量、需解析 Client Hints、詳細設備資訊
donatj/phpuseragentparsercomposer require donatj/phpuseragentparser輕量、無依賴、純 PHP、解析快微服務、無 Composer 環境、嵌入式

ua-parser/uap-php 使用範例

<?php
require 'vendor/autoload.php';
use UAParser\Parser;

$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
$parser = Parser::create(); // 內建 regexes.yaml

$result = $parser->parse($ua);

echo "瀏覽器: {$result->ua->family} {$result->ua->major}.{$result->ua->minor}.{$result->ua->patch}\n";
echo "作業系統: {$result->os->family} {$result->os->major}.{$result->os->minor}\n";
echo "設備: {$result->device->family} ({$result->device->brand})\n";
echo "是否行動裝置: " . ($result->device->isMobile() ? '是' : '否') . "\n";
echo "是否機器人: " . ($result->ua->family === 'Bot' ? '是' : '否') . "\n";

/*
輸出範例:
瀏覽器: Chrome 120.0.6099.109
作業系統: Windows 10
設備: Other ()
是否行動裝置: 否
是否機器人: 否
*/
?>

DeviceDetector 進階用法

<?php
require 'vendor/autoload.php';
use DeviceDetector\DeviceDetector;
use DeviceDetector\Cache\DoctrineCache;
use Doctrine\Common\Cache\ArrayCache;

$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
$headers = getallheaders(); // 獲取 Client Hints 標頭

$dd = new DeviceDetector($ua, $headers);
// 設定快取 (生產環境建議 Redis/Memcached)
$dd->setCache(new DoctrineCache(new ArrayCache()));

$dd->parse();

if ($dd->isBot()) {
    echo "Bot: {$dd->getBotName()}\n";
} else {
    echo "客戶端: {$dd->getClient()->getName()} {$dd->getClient()->getVersion()}\n";
    echo "引擎: {$dd->getClient()->getEngine()}\n";
    echo "作業系統: {$dd->getOs()->getName()} {$dd->getOs()->getVersion()}\n";
    echo "設備: {$dd->getDeviceName()} ({$dd->getBrand()})\n";
    echo "型號: {$dd->getModel()}\n";
    echo "是否行動: " . ($dd->isMobile() ? '是' : '否') . "\n";
    echo "是否桌面: " . ($dd->isDesktop() ? '是' : '否') . "\n";
    echo "是否平板: " . ($dd->isTablet() ? '是' : '否') . "\n";
}
?>

语言协商最佳实践

<?php
/**
 * 标准语言协商:根據 HTTP_ACCEPT_LANGUAGE 選擇最佳匹配
 * @param array $supported 支援的語言代碼陣列,如 ['zh-TW', 'zh-CN', 'en-US', 'en']
 * @return string 最佳匹配語言
 */
function negotiateLanguage(array $supported = ['zh-TW', 'en-US']): string {
    $header = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'en-US';
    $accept = [];

    foreach (explode(',', $header) as $part) {
        $part = trim($part);
        if (preg_match('/^([a-zA-Z-]+)(?:;q=([0-9.]+))?$/', $part, $m)) {
            $lang = strtolower($m[1]);
            $q = (float)($m[2] ?? 1.0);
            $accept[$lang] = $q;
        }
    }
    arsort($accept);

    // 標準化支援語言
    $supportedNorm = array_combine(
        array_map('strtolower', $supported),
        $supported
    );

    // 精確匹配
    foreach ($accept as $lang => $q) {
        if (isset($supportedNorm[$lang])) return $supportedNorm[$lang];
    }
    // 前綴匹配 (zh-CN 匹配 zh)
    foreach ($accept as $lang => $q) {
        $prefix = explode('-', $lang)[0];
        foreach ($supportedNorm as $supLang => $orig) {
            if (str_starts_with(strtolower($supLang), $prefix . '-')) {
                return $orig;
            }
        }
    }
    // 預設返回第一個支援語言
    return $supported[0];
}

// 使用
$locale = negotiateLanguage(['zh-TW', 'zh-CN', 'en-US', 'ja-JP']);
echo "選擇語言: $locale\n";
// 設定應用語言
putenv("LANG=$locale");
setlocale(LC_ALL, $locale . '.UTF-8');
bindtextdomain('messages', __DIR__ . '/locale');
textdomain('messages');
?>

常见陷阱

  • UA 可偽造:用戶可修改 User-Agent,絕不可用於安全判斷** (如權限控制、價格歧視)
  • Client Hints 取代 UA:Chrome 101+ 漸進凍結 UA,改用 Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform 等標頭。解析庫已支援。
  • 緩存正則庫:ua-parser 的 regexes.yaml 較大,首次載入慢,生產環境必須快取 (APCu/Redis/檔案)
  • 語言代碼大小寫:標準為 zh-TW (語言-地區),但瀏覽器可能發送 zh-tw、zh_TW,需標準化比對
  • 無 HTTP_ACCEPT_LANGUAGE:爬蟲、舊瀏覽器可能不發送,需有預設值

總結

判斷瀏覽器與語言:

  1. 簡單場景:原生 str_contains + HTTP_ACCEPT_LANGUAGE 解析即可
  2. 生產專案:必須使用成熟庫 (ua-parser、DeviceDetector、jenssegers/agent)
  3. 語言協商:實作標準 RFC 7231 內容協商邏輯,支援精確匹配、前綴匹配、預設回退
  4. 安全原則:UA 僅用於統計、分析、UI 適配,**不可用於授權、定價、功能開關**
  5. 未來趨勢:遷移至 Client Hints,庫已支援自動解析
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved