使用PHP进行可靠的用户浏览器检测

41

仅使用PHP检测用户的浏览器,$_SERVER['HTTP_USER_AGENT']是否可靠?我应该选择get_browser函数吗?你认为哪一个能够提供更精确的结果?

如果这种方法是切实可行的,那么将其用于输出相关的CSS链接是否不明智呢?

if(stripos($_SERVER['HTTP_USER_AGENT'],"mozilla")!==false)
   echo '<link type="text/css" href="mozilla.css" />';

我注意到了这个问题,但我想澄清一下这是否适用于基于CSS的检测。

更新: 有些可疑:我在IE 7上尝试了echo $_SERVER['HTTP_USER_AGENT'];,输出结果如下:

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618)

Safari也给出了包含“mozilla”的奇怪结果。这是怎么回事?


“Mozilla/4.0”这一部分是出于遗留原因而存在的……即使在IE8中也是如此。 - scunliffe
IE自相当长的时间以来一直标识自己为Mozilla 4.0。我读过他们这样做是出于兼容性的原因,但现在找不到来源了。如果我必须猜测,我会说这是NetScape/IE时代的残留物。 - Bobby
“User-Agent” 不是很可靠,但这是猜测的唯一方法。 - Gumbo
3
好的,我会尽力进行翻译。@bobby http://webaim.org/blog/user-agent-string-history/ 这是一篇关于用户代理字符串历史的博客文章。 - Knu
在最新版本的Firefox上无问题运行。 - user198989
有许多(好的)浏览器检测工具可用。请尝试在此处列出的其中之一 https://github.com/ThaDafinser/UserAgentParser - ThaDafinser
11个回答

72

检查这段代码,我发现它很有用。不要检查Mozilla,因为大多数浏览器将其用作用户代理字符串。

if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE)
   echo 'Internet explorer';
 elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== FALSE) //For Supporting IE 11
    echo 'Internet explorer';
 elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox') !== FALSE)
   echo 'Mozilla Firefox';
 elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== FALSE)
   echo 'Google Chrome';
 elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mini') !== FALSE)
   echo "Opera Mini";
 elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') !== FALSE)
   echo "Opera";
 elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== FALSE)
   echo "Safari";
 else
   echo 'Something else';

4
不支持IE 11(请查看此处:http://www.nczonline.net/blog/2013/07/02/internet-explorer-11-dont-call-me-ie/) - rap-2-h
@Farhad 你好,我正在使用你的这段代码,但是我只是好奇使用 switch 语句是否会更快。个人感觉 switch 语句可能更适合。但还是谢谢,你解决了我的问题。 - Fernando Silva
无法工作,脚本在 Opera 浏览器下返回 Chrome。 - The EasyLearn Academy
要检查Edge浏览器,您可以在if之后添加以下代码:elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Edge') !== FALSE) echo 'Microsoft Edge'; - Mohammad Salehi
这个很好用,但我不知道为什么。能解释一下吗? - user11480180
更新检测Edge的方法是elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Edg') !== FALSE) echo 'Microsoft Edge';,由于某种原因,他们从代理名称中删除了e。这个方法已经使用了将近8年,仍然非常有效。 - DrCustUmz

21

使用已有的方法(比如get_browser)可能比自己编写更好,因为它具有更好的支持,并且将随着新版本进行更新。也许有一些可用的库可以可靠地获取浏览器ID。

解码$_SERVER ['HTTP_USER_AGENT']很困难,因为许多浏览器具有相似的数据并倾向于(误)使用其自身的利益。但如果您真的想要解码它们,您可以使用此页面上所有可用代理ID的信息。该页面还显示您的示例输出确实属于 IE 7。有关代理ID本身的字段的更多信息可以在此页面找到,但正如我已经说过的,浏览器倾向于将其用于自身的利益,并且它可能采用(稍微)其他格式。


1
这个函数在每个服务器上都不会起作用。尝试了3个服务器 - 0/3。 - ViliusL
2
正如ViliusL所说,它不会在每个服务器上都起作用,因为它需要将其ini文件的路径设置在php.ini或Apache的“http.config”文件中。这意味着您必须能够访问它。 - JSG
顺便说一句,如果有人尝试过使用 ini_set('browscap', '/path/to/browscap.ini'); 在脚本级别设置browscap指令,似乎是不起作用的。这个指令必须在php配置文件中设置(如上面的评论所述)。 - Leo Galleguillos
它不起作用,我遇到了错误“警告:在test.php的第5行中未设置browscap ini指令的get_browser()”,我尝试找到任何解决方案,但我看到了这篇帖子,请检查 https://dev59.com/jHI-5IYBdhLWcg3wCz3y#2036968 - Adeel Ahmed Baloch

7

用户代理可以被伪造,最好不要依赖用户代理字符串。相反,您可以使用CSS3媒体查询来检测方向(您可以探索著名的响应式框架Bootstrap的CSS,以查看如何使用CSS处理方向部分)。

这里是一些CSS:

    @media only screen and (max-width: 999px) {
     /* rules that only apply for canvases narrower than 1000px */
    }

    @media only screen and (device-width: 768px) and (orientation: landscape) {
    /* rules for iPad in landscape orientation */
    }

    @media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
    /* iPhone, Android rules here */
    }    

阅读更多:关于CSS方向检测的最佳方法

或者您可以使用JavaScript找到方向:

    // Listen for orientation changes
    window.addEventListener("orientationchange", function() {
        // Announce the new orientation number
        alert(window.orientation);
    }, false);

阅读更多:关于使用JS检测方向的内容

最后,如果你真的想使用用户代理字符串,那么这段代码会对你有很大帮助,在几乎所有浏览器上都可以正常工作:

<?php
class BrowserDetection {

    private $_user_agent;
    private $_name;
    private $_version;
    private $_platform;

    private $_basic_browser = array (
       'Trident\/7.0' => 'Internet Explorer 11',
    'Beamrise' => 'Beamrise',
    'Opera' => 'Opera',
    'OPR' => 'Opera',
    'Shiira' => 'Shiira',
    'Chimera' => 'Chimera',
    'Phoenix' => 'Phoenix',
    'Firebird' => 'Firebird',
    'Camino' => 'Camino',
    'Netscape' => 'Netscape',
    'OmniWeb' => 'OmniWeb',
    'Konqueror' => 'Konqueror',
    'icab' => 'iCab',
     'Lynx' => 'Lynx',
    'Links' => 'Links',
    'hotjava' => 'HotJava',
    'amaya' => 'Amaya',
    'IBrowse' => 'IBrowse',
    'iTunes' => 'iTunes',
    'Silk' => 'Silk',
    'Dillo' => 'Dillo', 
    'Maxthon' => 'Maxthon',
    'Arora' => 'Arora',
    'Galeon' => 'Galeon',
    'Iceape' => 'Iceape',
    'Iceweasel' => 'Iceweasel',
    'Midori' => 'Midori',
    'QupZilla' => 'QupZilla',
    'Namoroka' => 'Namoroka',
    'NetSurf' => 'NetSurf',
    'BOLT' => 'BOLT',
    'EudoraWeb' => 'EudoraWeb',
    'shadowfox' => 'ShadowFox',
    'Swiftfox' => 'Swiftfox',
    'Uzbl' => 'Uzbl',
    'UCBrowser' => 'UCBrowser',
    'Kindle' => 'Kindle',
    'wOSBrowser' => 'wOSBrowser',
     'Epiphany' => 'Epiphany', 
    'SeaMonkey' => 'SeaMonkey',
    'Avant Browser' => 'Avant Browser',
    'Firefox' => 'Firefox',
    'Chrome' => 'Google Chrome',
    'MSIE' => 'Internet Explorer',
    'Internet Explorer' => 'Internet Explorer',
     'Safari' => 'Safari',
    'Mozilla' => 'Mozilla'  
    );

     private $_basic_platform = array(
        'windows' => 'Windows', 
     'iPad' => 'iPad', 
      'iPod' => 'iPod', 
    'iPhone' => 'iPhone', 
     'mac' => 'Apple', 
    'android' => 'Android', 
    'linux' => 'Linux',
    'Nokia' => 'Nokia',
     'BlackBerry' => 'BlackBerry',
    'FreeBSD' => 'FreeBSD',
     'OpenBSD' => 'OpenBSD',
    'NetBSD' => 'NetBSD',
     'UNIX' => 'UNIX',
    'DragonFly' => 'DragonFlyBSD',
    'OpenSolaris' => 'OpenSolaris',
    'SunOS' => 'SunOS', 
    'OS\/2' => 'OS/2',
    'BeOS' => 'BeOS',
    'win' => 'Windows',
    'Dillo' => 'Linux',
    'PalmOS' => 'PalmOS',
    'RebelMouse' => 'RebelMouse'   
     ); 

    function __construct($ua = '') {
        if(empty($ua)) {
           $this->_user_agent = (!empty($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:getenv('HTTP_USER_AGENT'));
        }
        else {
           $this->_user_agent = $ua;
        }
       }

    function detect() {
        $this->detectBrowser();
        $this->detectPlatform();
        return $this;
    }

    function detectBrowser() {
     foreach($this->_basic_browser as $pattern => $name) {
        if( preg_match("/".$pattern."/i",$this->_user_agent, $match)) {
            $this->_name = $name;
             // finally get the correct version number
            $known = array('Version', $pattern, 'other');
            $pattern_version = '#(?<browser>' . join('|', $known).')[/ ]+(?<version>[0-9.|a-zA-Z.]*)#';
            if (!preg_match_all($pattern_version, $this->_user_agent, $matches)) {
                // we have no matching number just continue
            }
            // see how many we have
            $i = count($matches['browser']);
            if ($i != 1) {
                //we will have two since we are not using 'other' argument yet
                //see if version is before or after the name
                if (strripos($this->_user_agent,"Version") < strripos($this->_user_agent,$pattern)){
                    @$this->_version = $matches['version'][0];
                }
                else {
                    @$this->_version = $matches['version'][1];
                }
            }
            else {
                $this->_version = $matches['version'][0];
            }
            break;
        }
       }
   }

    function detectPlatform() {
      foreach($this->_basic_platform as $key => $platform) {
            if (stripos($this->_user_agent, $key) !== false) {
                $this->_platform = $platform;
                break;
            } 
      }
    }

   function getBrowser() {
      if(!empty($this->_name)) {
           return $this->_name;
      }
   }        

   function getVersion() {
       return $this->_version;
    }

    function getPlatform() {
       if(!empty($this->_platform)) {
          return $this->_platform;
       }
    }

    function getUserAgent() {
        return $this->_user_agent;
     }

     function getInfo() {
         return "<strong>Browser Name:</strong> {$this->getBrowser()}<br/>\n" .
        "<strong>Browser Version:</strong> {$this->getVersion()}<br/>\n" .
        "<strong>Browser User Agent String:</strong> {$this->getUserAgent()}<br/>\n" .
        "<strong>Platform:</strong> {$this->getPlatform()}<br/>";
     }
}  

$obj = new BrowserDetection();

echo $obj->detect()->getInfo();

以上代码在几乎所有浏览器上都可以正常运行,希望能对你有所帮助。


2
哦,伙计,你甚至不知道他想用那些信息做什么,就建议媒体查询... - user151496
1
一个小细节,在Edge中它被检测为Chrome,要修复它只需在_basic_browser数组中在Chrome之前添加'Edg' => 'Microsoft Edge'。 - ciberelfo

5
class Browser { 
    /** 
    Figure out what browser is used, its version and the platform it is 
    running on. 

    The following code was ported in part from JQuery v1.3.1 
    */ 
    public static function detect() { 
        $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']); 

        // Identify the browser. Check Opera and Safari first in case of spoof. Let Google Chrome be identified as Safari. 
        if (preg_match('/opera/', $userAgent)) { 
            $name = 'opera'; 
        } 
        elseif (preg_match('/webkit/', $userAgent)) { 
            $name = 'safari'; 
        } 
        elseif (preg_match('/msie/', $userAgent)) { 
            $name = 'msie'; 
        } 
        elseif (preg_match('/mozilla/', $userAgent) && !preg_match('/compatible/', $userAgent)) { 
            $name = 'mozilla'; 
        } 
        else { 
            $name = 'unrecognized'; 
        } 

        // What version? 
        if (preg_match('/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/', $userAgent, $matches)) { 
            $version = $matches[1]; 
        } 
        else { 
            $version = 'unknown'; 
        } 

        // Running on what platform? 
        if (preg_match('/linux/', $userAgent)) { 
            $platform = 'linux'; 
        } 
        elseif (preg_match('/macintosh|mac os x/', $userAgent)) { 
            $platform = 'mac'; 
        } 
        elseif (preg_match('/windows|win32/', $userAgent)) { 
            $platform = 'windows'; 
        } 
        else { 
            $platform = 'unrecognized'; 
        } 

        return array( 
            'name'      => $name, 
            'version'   => $version, 
            'platform'  => $platform, 
            'userAgent' => $userAgent 
        ); 
    } 
} 


$browser = Browser::detect(); 

1
你为什么要创建一个只有一个方法的类呢?为什么不使用普通函数呢? - Michael Walter
这是你的选择,按照你想要的方式去做。我正在提供一个如何检测浏览器的示例。我从我的一个工作项目中获取了这个示例。此外,对于所有开发人员来说,使用对象更可取。 - LifeInstructor
这对我有用,解决了Safari/Chrome的问题:if ( stripos($userAgent, 'Firefox') !== false ) { $name = 'firefox'; } elseif ( stripos($userAgent, 'MSIE') !== false ) { $name = 'ie'; } elseif ( stripos($userAgent, 'Trident') !== false ) { $name = 'ie'; } elseif ( stripos($userAgent, 'Chrome') !== false ) { $name = 'chrome'; } elseif ( stripos($userAgent, 'Safari') !== false ) { $name = 'safari'; } - Gerardo Jaramillo

4

我发现大部分PHP包都很不错,但是却找不到一个能够提供我所需功能的好包,所以我构建了一个Laravel帮手来结合这3个包。

这些包包括:

jenssegers/agent

whichbrowser/parser

cbschuld/browser.php

以下是我的函数:

function browser($userAgent)
{
    $agent = new \Jenssegers\Agent\Agent();
    $agent->setUserAgent($userAgent);

    $result = new \WhichBrowser\Parser($userAgent);

    $browser = new \Browser($userAgent);

    return new class($agent, $result, $browser)
    {
        public function __construct($agent, $result, $browser)
        {
            $this->agent = $agent;
            $this->result = $result;
            $this->browser = $browser;
        }
        public function device()
        {
            return $this->agent->device() ?: $this->result->device->toString() ?: $this->browser->getPlatform();
        }
        public function browser()
        {
            return $this->agent->browser() ?: $this->result->browser->name ?: $this->browser->getBrowser();
        }
        public function platform()
        {
            return $this->agent->platform() ?: $this->result->os->name ?: $this->browser->getPlatform();
        }
        public function isMobile()
        {
            return $this->agent->isPhone() ?: $this->result->isType('mobile') ?: $this->browser->isMobile();
        }
        public function isTablet()
        {
            return $this->result->isType('tablet') ?: $this->browser->isTablet();
        }
        public function isDesktop()
        {
            return $this->agent->isDesktop() ?: $this->result->isType('desktop') ?: (! $this->browser->isMobile() && ! $this->browser->isTablet());
        }
        public function isRobot()
        {
            return $this->agent->isRobot() ?: $this->browser->isRobot();
        }
    };

这是如何使用它的方法:

$browser = browser($userAgent);

$browser->device();
$browser->platform();
$browser->browser();
$browser->isMobile();
$browser->isTablet();
$browser->isDesktop();
$browser->isRobot();

4

我注意到一个事实,真正的浏览器名称总是在最后一个 (括号) 后面,但 IE 是个例外,在最后一个(括号) 后没有浏览器名称。我想知道是否只阅读最后一个 ) 之后的内容能提高准确性。

您可能在不同的用户代理中注意到这一点:

Google Chrome

Safari

Firefox

Edge

IE (无浏览器定义的例外)

例如:

$userBrowser = (!empty($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:getenv('HTTP_USER_AGENT'));
$userBrowser = explode(')', $userBrowser);
$userBrowser = $userBrowser[count($userBrowser)-1];

echo $userBrowser; //this has many inaccurate browsers stripped out

一个完整的功能:
function getUserBrowser(){
  $fullUserBrowser = (!empty($_SERVER['HTTP_USER_AGENT'])? 
  $_SERVER['HTTP_USER_AGENT']:getenv('HTTP_USER_AGENT'));
  $userBrowser = explode(')', $fullUserBrowser);
  $userBrowser = $userBrowser[count($userBrowser)-1];

  if((!$userBrowser || $userBrowser === '' || $userBrowser === ' ' || strpos($userBrowser, 'like Gecko') === 1) && strpos($fullUserBrowser, 'Windows') !== false){
    return 'Internet-Explorer';
  }else if((strpos($userBrowser, 'Edge/') !== false || strpos($userBrowser, 'Edg/') !== false) && strpos($fullUserBrowser, 'Windows') !== false){
    return 'Microsoft-Edge';
  }else if(strpos($userBrowser, 'Chrome/') === 1 || strpos($userBrowser, 'CriOS/') === 1){
    return 'Google-Chrome';
  }else if(strpos($userBrowser, 'Firefox/') !== false || strpos($userBrowser, 'FxiOS/') !== false){
    return 'Mozilla-Firefox';
  }else if(strpos($userBrowser, 'Safari/') !== false && strpos($fullUserBrowser, 'Mac') !== false){
    return 'Safari';
  }else if(strpos($userBrowser, 'OPR/') !== false && strpos($fullUserBrowser, 'Opera Mini') !== false){
    return 'Opera-Mini';
  }else if(strpos($userBrowser, 'OPR/') !== false){
    return 'Opera';
  }
  return false;
}

echo 'browser detect: '.getUserBrowser();

我还在Chrome、Edge和Firefox Developer上进行了测试,并且它们都能够准确地工作。(尽管我还没有测试过这些浏览器的旧版本)


它在我的桌面Chrome的检查模式和我的移动Safari上都显示“Safari”。 - user3495363

3

如果 stripos($_SERVER['HTTP_USER_AGENT'],"mozilla")!==false)

这不是一个有用的测试,几乎每个浏览器都将自己标识为Mozilla。

$_SERVER['HTTP_USER_AGENT']是否可靠?

不可靠。

我应该选择get_browser函数吗?

也不行。

在服务器端进行浏览器嗅探是一场失败的游戏。除了尝试解析UA字符串、浏览器欺骗、晦涩的浏览器以及头部可能不存在的所有问题外,如果为不同的浏览器返回不同的页面内容,您必须设置Vary头部以包括User-Agent,否则缓存代理可能会将错误版本的页面返回给错误的浏览器。

但是,如果您添加了Vary: User-Agent,IE的破碎缓存代码会混淆并在每次重新加载页面时重新加载页面。所以你赢不了。

如果您必须进行浏览器嗅探,则应在客户端使用JavaScript进行嗅探,特别是在IE的情况下,使用条件注释。幸运的是,支持条件注释的是IE,因为这是您经常需要解决问题的浏览器。(有关最常见的策略,请参见scunliffe的答案。)


2
旧帖子仍会在谷歌中出现。get_browser() 是最好的解决方案,但编辑 php.ini 可能是不可能的。根据 这篇帖子,你不能 ini_set 浏览器属性。那还剩下什么呢?phpbrowscap 似乎可以完成工作。文档不是很好,所以如果你不想让它自动更新(默认为开启),那么你需要设置。
$bc->updateMethod = phpbrowscap\Browscap::UPDATE_LOCAL;
$bc->localFile = __DIR__ . "/php_browscap.ini";

1

我认为仅依赖userAgent有点脆弱,因为它经常被伪造。

如果你想为IE提供CSS,请使用条件注释。

<link type="text/css" rel="stylesheet" href="styles.css"/><!--for all-->
<!--[if IE]>
  <link type="text/css" rel="stylesheet" href="ie_styles.css"/>
<![endif]-->

或者如果你只需要IE6的一个:

<!--[if IE 6]>
  <link type="text/css" rel="stylesheet" href="ie6_styles.css"/>
<![endif]-->

由于这是一条注释,因此浏览器会忽略它...除非IE加载它,如果if测试与当前浏览器匹配。


如果是假的,谁在乎呢?毕竟你并不依赖它来保障安全性,只是为了向用户展示某些内容... - o0'.

0
Check This Code :      
   <?php     

class OS_BR{    
private $agent = "";
private $info = array();

function __construct(){
    $this->agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : NULL;
    $this->getBrowser();
    $this->getOS();
}     
function getBrowser(){     
    $browser = array("Navigator"            => "/Navigator(.*)/i",
                     "Firefox"              => "/Firefox(.*)/i",
                     "Internet Explorer"    => "/MSIE(.*)/i",
                     "Google Chrome"        => "/chrome(.*)/i",
                     "MAXTHON"              => "/MAXTHON(.*)/i",
                     "Opera"                => "/Opera(.*)/i",
                     );
    foreach($browser as $key => $value){
        if(preg_match($value, $this->agent)){
            $this->info = array_merge($this->info,array("Browser" => $key));
            $this->info = array_merge($this->info,array(
              "Version" => $this->getVersion($key, $value, $this->agent)));
            break;
        }else{
            $this->info = array_merge($this->info,array("Browser" => "UnKnown"));
            $this->info = array_merge($this->info,array("Version" => "UnKnown"));
        }
    }
    return $this->info['Browser'];
}
function getOS(){
    $OS = array("Windows"   =>   "/Windows/i",
                "Linux"     =>   "/Linux/i",
                "Unix"      =>   "/Unix/i",
                "Mac"       =>   "/Mac/i"
                );

    foreach($OS as $key => $value){
        if(preg_match($value, $this->agent)){
            $this->info = array_merge($this->info,array("Operating System" => $key));
            break;
        }
    }
    return $this->info['Operating System'];
}

function getVersion($browser, $search, $string){
    $browser = $this->info['Browser'];
    $version = "";
    $browser = strtolower($browser);
    preg_match_all($search,$string,$match);
    switch($browser){
        case "firefox": $version = str_replace("/","",$match[1][0]);
        break;

        case "internet explorer": $version = substr($match[1][0],0,4);
        break;

        case "opera": $version = str_replace("/","",substr($match[1][0],0,5));
        break;

        case "navigator": $version = substr($match[1][0],1,7);
        break;

        case "maxthon": $version = str_replace(")","",$match[1][0]);
        break;

        case "google chrome": $version = substr($match[1][0],1,10);
    }
    return $version;
}

function showInfo($switch){
    $switch = strtolower($switch);
    switch($switch){
        case "browser": return $this->info['Browser'];
        break;

        case "os": return $this->info['Operating System'];
        break;

        case "version": return $this->info['Version'];
        break;

        case "all" : return array($this->info["Version"], 
          $this->info['Operating System'], $this->info['Browser']);
        break;

        default: return "Unkonw";
        break;

    }
}
 }    


$obj = new OS_BR();

echo $obj->showInfo('browser');

echo $obj->showInfo('version');

echo $obj->showInfo('os');

echo "<pre>".print_r($obj->showInfo("all"),true)."</pre>"; 

 ?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接