Browser_Detection
[ class tree: Browser_Detection ] [ index: Browser_Detection ] [ all elements ]

Source for file BrowserDetection.php

Documentation is available at BrowserDetection.php

  1. <?php
  2.  
  3. /**
  4.  * Browser detection class file.
  5.  * This file contains everything required to use the BrowserDetection class. Tested with PHP 5.3.29 - 7.2.4.
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
  8.  * Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
  9.  * later version (if any).
  10.  *
  11.  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  12.  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  13.  * details at: http://www.gnu.org/licenses/lgpl.html
  14.  *
  15.  * @package Browser_Detection
  16.  * @version 2.9.3
  17.  * @last-modified March 27, 2019
  18.  * @author Alexandre Valiquette
  19.  * @copyright Copyright (c) 2019, Wolfcast
  20.  * @link https://wolfcast.com/
  21.  */
  22.  
  23.  
  24. namespace Wolfcast;
  25.  
  26.  
  27. /**
  28.  * The BrowserDetection class facilitates the identification of the user's environment such as Web browser, version,
  29.  * platform and device type.
  30.  *
  31.  * Typical usage:
  32.  *
  33.  * $browser = new Wolfcast\BrowserDetection();
  34.  * if ($browser->getName() == Wolfcast\BrowserDetection::BROWSER_FIREFOX &&
  35.  *     $browser->compareVersions($browser->getVersion(), '5.0') >= 0) {
  36.  *     echo 'You are using FireFox version 5 or greater.';
  37.  * }
  38.  *
  39.  * The class is a rewrite of Chris Schuld's Browser class version 1.9 which is mostly unmaintained since August 20th,
  40.  * 2010. Chris' class was based on the original work from Gary White.
  41.  *
  42.  * Updates:
  43.  *
  44.  * 2019-03-27: Version 2.9.3
  45.  *  + Fixed Edge detection on Android.
  46.  *  + Added Android Q detection.
  47.  *  + Now filtering superglobals.
  48.  *
  49.  * 2019-02-28: Version 2.9.2
  50.  *  + Fixed Opera detection.
  51.  *
  52.  * 2018-08-23: Version 2.9.1
  53.  *  + Fixed Chrome detection under iOS.
  54.  *  + Added Android Pie detection.
  55.  *  + Added macOS Mojave detection.
  56.  *
  57.  * 2018-07-15: Version 2.9.0
  58.  *  + WARNING! Breaking change: new Wolfcast namespace. Use new Wolfcast\BrowserDetection().
  59.  *  + iPad, iPhone and iPod are all under iOS now.
  60.  *  + Added Android Oreo detection.
  61.  *  + Added macOS High Sierra detection.
  62.  *  + Added UC Browser detection.
  63.  *  + Improved regular expressions (even less false positives).
  64.  *  + Removed AOL detection.
  65.  *  + Removed the following Web browsers detection: Amaya, Galeon, NetPositive, OmniWeb, Vivaldi detection (use
  66.  *    addCustomBrowserDetection()).
  67.  *  + Removed the following legacy platforms detection: BeOS, OS/2, SunOS (use addCustomPlatformDetection()).
  68.  *
  69.  * 2016-11-28: Version 2.5.1
  70.  *  + Better detection of 64-bit platforms.
  71.  *
  72.  * 2016-08-19: Version 2.5.0
  73.  *  + Platform version and platform version name are now supported for Mac.
  74.  *  + Fixed platform version name for Android.
  75.  *
  76.  * 2016-08-02: Version 2.4.0
  77.  *  + Platform version and platform version name are now supported for Android.
  78.  *  + Added support for the Samsung Internet browser.
  79.  *  + Added support for the Vivaldi browser.
  80.  *  + Better support for legacy Windows versions.
  81.  *
  82.  * 2016-02-11: Version 2.3.0
  83.  *  + WARNING! Breaking change: public method getBrowser() is renamed to getName().
  84.  *  + WARNING! Breaking change: changed the compareVersions() return values to be more in line with other libraries.
  85.  *  + You can now get the exact platform version (name or version numbers) on which the browser is run on with
  86.  *    getPlatformVersion(). Only working with Windows operating systems at the moment.
  87.  *  + You can now determine if the browser is executed from a 64-bit platform with is64bitPlatform().
  88.  *  + Better detection of mobile platform for Googlebot.
  89.  *
  90.  * 2016-01-04: Version 2.2.0
  91.  *  + Added support for Microsoft Edge.
  92.  *
  93.  * 2014-12-30: Version 2.1.2
  94.  *  + Better detection of Opera.
  95.  *
  96.  * 2014-07-11: Version 2.1.1
  97.  *  + Better detection of mobile devices and platforms.
  98.  *
  99.  * 2014-06-04: Version 2.1.0
  100.  *  + Added support for IE 11+.
  101.  *
  102.  * 2013-05-27: Version 2.0.0 which is (almost) a complete rewrite based on Chris Schuld's Browser class version 1.9 plus
  103.  * changes below.
  104.  *  + Added support for Opera Mobile
  105.  *  + Added support for the Windows Phone (formerly Windows Mobile) platform
  106.  *  + Added support for BlackBerry Tablet OS and BlackBerry 10
  107.  *  + Added support for the Symbian platform
  108.  *  + Added support for Bingbot
  109.  *  + Added support for the Yahoo! Multimedia crawler
  110.  *  + Removed iPhone/iPad/iPod browsers since there are not browsers but platforms - test them with getPlatform()
  111.  *  + Removed support for Shiretoko (Firefox 3.5 alpha/beta) and MSN Browser
  112.  *  + Merged Nokia and Nokia S60
  113.  *  + Updated some deprecated browser names
  114.  *  + Many public methods are now protected
  115.  *  + Documentation updated
  116.  *
  117.  * 2010-07-04:
  118.  *  + Added detection of IE compatibility view - test with getIECompatibilityView()
  119.  *  + Added support for all (deprecated) Netscape versions
  120.  *  + Added support for Safari < 3.0
  121.  *  + Better Firefox version parsing
  122.  *  + Better Opera version parsing
  123.  *  + Better Mozilla detection
  124.  *
  125.  * @package Browser_Detection
  126.  * @version 2.9.3
  127.  * @last-modified March 27, 2019
  128.  * @author Alexandre Valiquette, Chris Schuld, Gary White
  129.  * @copyright Copyright (c) 2019, Wolfcast
  130.  * @license http://www.gnu.org/licenses/lgpl.html
  131.  * @link https://wolfcast.com/
  132.  * @link https://wolfcast.com/open-source/browser-detection/tutorial.php
  133.  * @link http://chrisschuld.com/
  134.  * @link http://www.apptools.com/phptools/browser/
  135.  */
  136. {
  137.  
  138.     /**#@+
  139.      * Constant for the name of the Web browser.
  140.      */
  141.     const BROWSER_ANDROID 'Android';
  142.     const BROWSER_BINGBOT 'Bingbot';
  143.     const BROWSER_BLACKBERRY 'BlackBerry';
  144.     const BROWSER_CHROME 'Chrome';
  145.     const BROWSER_EDGE 'Edge';
  146.     const BROWSER_FIREBIRD 'Firebird';
  147.     const BROWSER_FIREFOX 'Firefox';
  148.     const BROWSER_GOOGLEBOT 'Googlebot';
  149.     const BROWSER_ICAB 'iCab';
  150.     const BROWSER_ICECAT 'GNU IceCat';
  151.     const BROWSER_ICEWEASEL 'GNU IceWeasel';
  152.     const BROWSER_IE 'Internet Explorer';
  153.     const BROWSER_IE_MOBILE 'Internet Explorer Mobile';
  154.     const BROWSER_KONQUEROR 'Konqueror';
  155.     const BROWSER_LYNX 'Lynx';
  156.     const BROWSER_MOZILLA 'Mozilla';
  157.     const BROWSER_MSNBOT 'MSNBot';
  158.     const BROWSER_MSNTV 'MSN TV';
  159.     const BROWSER_NETSCAPE 'Netscape';
  160.     const BROWSER_NOKIA 'Nokia Browser';
  161.     const BROWSER_OPERA 'Opera';
  162.     const BROWSER_OPERA_MINI 'Opera Mini';
  163.     const BROWSER_OPERA_MOBILE 'Opera Mobile';
  164.     const BROWSER_PHOENIX 'Phoenix';
  165.     const BROWSER_SAFARI 'Safari';
  166.     const BROWSER_SAMSUNG 'Samsung Internet';
  167.     const BROWSER_SLURP 'Yahoo! Slurp';
  168.     const BROWSER_TABLET_OS 'BlackBerry Tablet OS';
  169.     const BROWSER_UC 'UC Browser';
  170.     const BROWSER_UNKNOWN 'unknown';
  171.     const BROWSER_W3CVALIDATOR 'W3C Validator';
  172.     const BROWSER_YAHOO_MM 'Yahoo! Multimedia';
  173.     /**#@-*/
  174.  
  175.     /**#@+
  176.      * Constant for the name of the platform on which the Web browser runs.
  177.      */
  178.     const PLATFORM_ANDROID 'Android';
  179.     const PLATFORM_BLACKBERRY 'BlackBerry';
  180.     const PLATFORM_FREEBSD 'FreeBSD';
  181.     const PLATFORM_IOS 'iOS';
  182.     const PLATFORM_LINUX 'Linux';
  183.     const PLATFORM_MACINTOSH 'Macintosh';
  184.     const PLATFORM_NETBSD 'NetBSD';
  185.     const PLATFORM_NOKIA 'Nokia';
  186.     const PLATFORM_OPENBSD 'OpenBSD';
  187.     const PLATFORM_OPENSOLARIS 'OpenSolaris';
  188.     const PLATFORM_SYMBIAN 'Symbian';
  189.     const PLATFORM_UNKNOWN 'unknown';
  190.     const PLATFORM_VERSION_UNKNOWN 'unknown';
  191.     const PLATFORM_WINDOWS 'Windows';
  192.     const PLATFORM_WINDOWS_CE 'Windows CE';
  193.     const PLATFORM_WINDOWS_PHONE 'Windows Phone';
  194.     /**#@-*/
  195.  
  196.     /**
  197.      * Version unknown constant.
  198.      */
  199.     const VERSION_UNKNOWN 'unknown';
  200.  
  201.  
  202.     /**
  203.      * @var string 
  204.      * @access private
  205.      */
  206.     private $_agent = '';
  207.  
  208.     /**
  209.      * @var string 
  210.      * @access private
  211.      */
  212.     private $_browserName = '';
  213.  
  214.     /**
  215.      * @var string 
  216.      * @access private
  217.      */
  218.     private $_compatibilityViewName = '';
  219.  
  220.     /**
  221.      * @var string 
  222.      * @access private
  223.      */
  224.     private $_compatibilityViewVer = '';
  225.  
  226.     /**
  227.      * @var array 
  228.      * @access private
  229.      */
  230.     private $_customBrowserDetection = array();
  231.  
  232.     /**
  233.      * @var array 
  234.      * @access private
  235.      */
  236.     private $_customPlatformDetection = array();
  237.  
  238.     /**
  239.      * @var boolean 
  240.      * @access private
  241.      */
  242.     private $_is64bit = false;
  243.  
  244.     /**
  245.      * @var boolean 
  246.      * @access private
  247.      */
  248.     private $_isMobile = false;
  249.  
  250.     /**
  251.      * @var boolean 
  252.      * @access private
  253.      */
  254.     private $_isRobot = false;
  255.  
  256.     /**
  257.      * @var string 
  258.      * @access private
  259.      */
  260.     private $_platform = '';
  261.  
  262.     /**
  263.      * @var string 
  264.      * @access private
  265.      */
  266.     private $_platformVersion = '';
  267.  
  268.     /**
  269.      * @var string 
  270.      * @access private
  271.      */
  272.     private $_version = '';
  273.  
  274.  
  275.     //--- MAGIC METHODS ------------------------------------------------------------------------------------------------
  276.  
  277.  
  278.     /**
  279.      * BrowserDetection class constructor.
  280.      * @param string $useragent (optional) The user agent to work with. Leave empty for the current user agent
  281.      *  (contained in $_SERVER['HTTP_USER_AGENT']).
  282.      */
  283.     public function __construct($useragent '')
  284.     {
  285.         $this->setUserAgent($useragent);
  286.     }
  287.  
  288.     /**
  289.      * Determine how the class will react when it is treated like a string.
  290.      * @return string Returns an HTML formatted string with a summary of the browser informations.
  291.      */
  292.     public function __toString()
  293.     {
  294.         $result '';
  295.  
  296.         $values array();
  297.         $values[array('label' => 'User agent''value' => $this->getUserAgent());
  298.         $values[array('label' => 'Browser name''value' => $this->getName());
  299.         $values[array('label' => 'Browser version''value' => $this->getVersion());
  300.         $values[array('label' => 'Platform family''value' => $this->getPlatform());
  301.         $values[array('label' => 'Platform version''value' => $this->getPlatformVersion(true));
  302.         $values[array('label' => 'Platform version name''value' => $this->getPlatformVersion());
  303.         $values[array('label' => 'Platform is 64-bit''value' => $this->is64bitPlatform('true' 'false');
  304.         $values[array('label' => 'Is mobile''value' => $this->isMobile('true' 'false');
  305.         $values[array('label' => 'Is robot''value' => $this->isRobot('true' 'false');
  306.         $values[array('label' => 'IE is in compatibility view''value' => $this->isInIECompatibilityView('true' 'false');
  307.         $values[array('label' => 'Emulated IE version''value' => $this->isInIECompatibilityView($this->getIECompatibilityView('Not applicable');
  308.         $values[array('label' => 'Is Chrome Frame''value' => $this->isChromeFrame('true' 'false');
  309.  
  310.         foreach ($values as $currVal{
  311.             $result .= '<strong>' htmlspecialchars($currVal['label']ENT_NOQUOTES':</strong> ' $currVal['value''<br />' PHP_EOL;
  312.         }
  313.  
  314.         return $result;
  315.     }
  316.  
  317.  
  318.     //--- PUBLIC MEMBERS -----------------------------------------------------------------------------------------------
  319.  
  320.  
  321.     /**
  322.      * Dynamically add support for a new Web browser.
  323.      * @param string $browserName The Web browser name (used for display).
  324.      * @param mixed $uaNameToLookFor (optional) The string (or array of strings) representing the browser name to find
  325.      *  in the user agent. If omitted, $browserName will be used.
  326.      * @param boolean $isMobile (optional) Determines if the browser is from a mobile device.
  327.      * @param boolean $isRobot (optional) Determines if the browser is a robot or not.
  328.      * @param string $separator (optional) The separator string used to split the browser name and the version number in
  329.      *  the user agent.
  330.      * @param boolean $uaNameFindWords (optional) Determines if the browser name to find should match a word instead of
  331.      *  a part of a word. For example "Bar" would not be found in "FooBar" when true but would be found in "Foo Bar".
  332.      *  When set to false, the browser name can be found anywhere in the user agent string.
  333.      * @see removeCustomBrowserDetection()
  334.      * @return boolean Returns true if the custom rule has been added, false otherwise.
  335.      */
  336.     public function addCustomBrowserDetection($browserName$uaNameToLookFor ''$isMobile false$isRobot false$separator '/'$uaNameFindWords true)
  337.     {
  338.         if ($browserName == ''{
  339.             return false;
  340.         }
  341.         if (array_key_exists($browserName$this->_customBrowserDetection)) {
  342.             unset($this->_customBrowserDetection[$browserName]);
  343.         }
  344.         if ($uaNameToLookFor == ''{
  345.             $uaNameToLookFor $browserName;
  346.         }
  347.         $this->_customBrowserDetection[$browserNamearray('uaNameToLookFor' => $uaNameToLookFor'isMobile' => $isMobile == true'isRobot' => $isRobot == true,
  348.                                                              'separator' => $separator'uaNameFindWords' => $uaNameFindWords == true);
  349.         return true;
  350.     }
  351.  
  352.     /**
  353.      * Dynamically add support for a new platform.
  354.      * @param string $platformName The platform name (used for display).
  355.      * @param mixed $platformNameToLookFor (optional) The string (or array of strings) representing the platform name to
  356.      *  find in the user agent. If omitted, $platformName will be used.
  357.      * @param boolean $isMobile (optional) Determines if the platform is from a mobile device.
  358.      * @see removeCustomPlatformDetection()
  359.      * @return boolean Returns true if the custom rule has been added, false otherwise.
  360.      */
  361.     public function addCustomPlatformDetection($platformName$platformNameToLookFor ''$isMobile false)
  362.     {
  363.         if ($platformName == ''{
  364.             return false;
  365.         }
  366.         if (array_key_exists($platformName$this->_customPlatformDetection)) {
  367.             unset($this->_customPlatformDetection[$platformName]);
  368.         }
  369.         if ($platformNameToLookFor == ''{
  370.             $platformNameToLookFor $platformName;
  371.         }
  372.         $this->_customPlatformDetection[$platformNamearray('platformNameToLookFor' => $platformNameToLookFor'isMobile' => $isMobile == true);
  373.         return true;
  374.     }
  375.  
  376.     /**
  377.      * Compare two version number strings.
  378.      * @param string $sourceVer The source version number.
  379.      * @param string $compareVer The version number to compare with the source version number.
  380.      * @return int Returns -1 if $sourceVer < $compareVer, 0 if $sourceVer == $compareVer or 1 if $sourceVer >
  381.      *  $compareVer.
  382.      */
  383.     public function compareVersions($sourceVer$compareVer)
  384.     {
  385.         $sourceVer explode('.'$sourceVer);
  386.         foreach ($sourceVer as $k => $v{
  387.             $sourceVer[$k$this->parseInt($v);
  388.         }
  389.  
  390.         $compareVer explode('.'$compareVer);
  391.         foreach ($compareVer as $k => $v{
  392.             $compareVer[$k$this->parseInt($v);
  393.         }
  394.  
  395.         if (count($sourceVer!= count($compareVer)) {
  396.             if (count($sourceVercount($compareVer)) {
  397.                 for ($i count($compareVer)$i count($sourceVer)$i++{
  398.                     $compareVer[$i0;
  399.                 }
  400.             else {
  401.                 for ($i count($sourceVer)$i count($compareVer)$i++{
  402.                     $sourceVer[$i0;
  403.                 }
  404.             }
  405.         }
  406.  
  407.         foreach ($sourceVer as $i => $srcVerPart{
  408.             if ($srcVerPart $compareVer[$i]{
  409.                 return 1;
  410.             else {
  411.                 if ($srcVerPart $compareVer[$i]{
  412.                     return -1;
  413.                 }
  414.             }
  415.         }
  416.  
  417.         return 0;
  418.     }
  419.  
  420.     /**
  421.      * Get the name of the browser. All of the return values are class constants. You can compare them like this:
  422.      * $myBrowserInstance->getName() == BrowserDetection::BROWSER_FIREFOX.
  423.      * @return string Returns the name of the browser.
  424.      */
  425.     public function getName()
  426.     {
  427.         return $this->_browserName;
  428.     }
  429.  
  430.     /**
  431.      * Get the name and version of the browser emulated in the compatibility view mode (if any). Since Internet
  432.      * Explorer 8, IE can be put in compatibility mode to make websites that were created for older browsers, especially
  433.      * IE 6 and 7, look better in IE 8+ which renders web pages closer to the standards and thus differently from those
  434.      * older versions of IE.
  435.      * @param boolean $asArray (optional) Determines if the return value must be an array (true) or a string (false).
  436.      * @return mixed If a string was requested, the function returns the name and version of the browser emulated in
  437.      *  the compatibility view mode or an empty string if the browser is not in compatibility view mode. If an array was
  438.      *  requested, an array with the keys 'browser' and 'version' is returned.
  439.      */
  440.     public function getIECompatibilityView($asArray false)
  441.     {
  442.         if ($asArray{
  443.             return array('browser' => $this->_compatibilityViewName'version' => $this->_compatibilityViewVer);
  444.         else {
  445.             return trim($this->_compatibilityViewName . ' ' $this->_compatibilityViewVer);
  446.         }
  447.     }
  448.  
  449.     /**
  450.      * Return the BrowserDetection class version.
  451.      * @return string Returns the version as a sting with the #.#.# format.
  452.      */
  453.     public function getLibVersion()
  454.     {
  455.         return '2.9.3';
  456.     }
  457.  
  458.     /**
  459.      * Get the name of the platform family on which the browser is run on (such as Windows, Apple, etc.). All of
  460.      * the return values are class constants. You can compare them like this:
  461.      * $myBrowserInstance->getPlatform() == BrowserDetection::PLATFORM_ANDROID.
  462.      * @return string Returns the name of the platform or BrowserDetection::PLATFORM_UNKNOWN if unknown.
  463.      */
  464.     public function getPlatform()
  465.     {
  466.         return $this->_platform;
  467.     }
  468.  
  469.     /**
  470.      * Get the platform version on which the browser is run on. It can be returned as a string number like 'NT 6.3' or
  471.      * as a name like 'Windows 8.1'. When returning version string numbers for Windows NT OS families the number is
  472.      * prefixed by 'NT ' to differentiate from older Windows 3.x & 9x release. At the moment only the Windows and
  473.      * Android operating systems are supported.
  474.      * @param boolean $returnVersionNumbers (optional) Determines if the return value must be versions numbers as a
  475.      *  string (true) or the version name (false).
  476.      * @param boolean $returnServerFlavor (optional) Since some Windows NT versions have the same values, this flag
  477.      *  determines if the Server flavor is returned or not. For instance Windows 8.1 and Windows Server 2012 R2 both use
  478.      *  version 6.3. This parameter is only useful when testing for Windows.
  479.      * @return string Returns the version name/version numbers of the platform or the constant PLATFORM_VERSION_UNKNOWN
  480.      *  if unknown.
  481.      */
  482.     public function getPlatformVersion($returnVersionNumbers false$returnServerFlavor false)
  483.     {
  484.         if ($this->_platformVersion == self::PLATFORM_VERSION_UNKNOWN || $this->_platformVersion == ''{
  485.             return self::PLATFORM_VERSION_UNKNOWN;
  486.         }
  487.  
  488.         if ($returnVersionNumbers{
  489.             return $this->_platformVersion;
  490.         else {
  491.             switch ($this->getPlatform()) {
  492.                 case self::PLATFORM_WINDOWS:
  493.                     if (substr($this->_platformVersion03== 'NT '{
  494.                         return $this->windowsNTVerToStr(substr($this->_platformVersion3)$returnServerFlavor);
  495.                     else {
  496.                         return $this->windowsVerToStr($this->_platformVersion);
  497.                     }
  498.                     break;
  499.  
  500.                 case self::PLATFORM_MACINTOSH:
  501.                     return $this->macVerToStr($this->_platformVersion);
  502.  
  503.                 case self::PLATFORM_ANDROID:
  504.                     return $this->androidVerToStr($this->_platformVersion);
  505.  
  506.                 case self::PLATFORM_IOS:
  507.                     return $this->iOSVerToStr($this->_platformVersion);
  508.  
  509.                 defaultreturn self::PLATFORM_VERSION_UNKNOWN;
  510.             }
  511.         }
  512.     }
  513.  
  514.     /**
  515.      * Get the user agent value used by the class to determine the browser details.
  516.      * @return string The user agent string.
  517.      */
  518.     public function getUserAgent()
  519.     {
  520.         return $this->_agent;
  521.     }
  522.  
  523.     /**
  524.      * Get the version of the browser.
  525.      * @return string Returns the version of the browser or BrowserDetection::VERSION_UNKNOWN if unknown.
  526.      */
  527.     public function getVersion()
  528.     {
  529.         return $this->_version;
  530.     }
  531.  
  532.     /**
  533.      * Determine if the browser is executed from a 64-bit platform. Keep in mind that not all platforms/browsers report
  534.      * this and the result may not always be accurate.
  535.      * @return boolean Returns true if the browser is executed from a 64-bit platform.
  536.      */
  537.     public function is64bitPlatform()
  538.     {
  539.         return $this->_is64bit;
  540.     }
  541.  
  542.     /**
  543.      * Determine if the browser runs Google Chrome Frame (it's a plug-in designed for Internet Explorer 6+ based on the
  544.      * open-source Chromium project - it's like a Chrome browser within IE).
  545.      * @return boolean Returns true if the browser is using Google Chrome Frame, false otherwise.
  546.      */
  547.     public function isChromeFrame()
  548.     {
  549.         return $this->containString($this->_agent'chromeframe');
  550.     }
  551.  
  552.     /**
  553.      * Determine if the browser is in compatibility view or not. Since Internet Explorer 8, IE can be put in
  554.      * compatibility mode to make websites that were created for older browsers, especially IE 6 and 7, look better in
  555.      * IE 8+ which renders web pages closer to the standards and thus differently from those older versions of IE.
  556.      * @return boolean Returns true if the browser is in compatibility view, false otherwise.
  557.      */
  558.     public function isInIECompatibilityView()
  559.     {
  560.         return ($this->_compatibilityViewName != ''|| ($this->_compatibilityViewVer != '');
  561.     }
  562.  
  563.     /**
  564.      * Determine if the browser is from a mobile device or not.
  565.      * @return boolean Returns true if the browser is from a mobile device, false otherwise.
  566.      */
  567.     public function isMobile()
  568.     {
  569.         return $this->_isMobile;
  570.     }
  571.  
  572.     /**
  573.      * Determine if the browser is a robot (Googlebot, Bingbot, Yahoo! Slurp...) or not.
  574.      * @return boolean Returns true if the browser is a robot, false otherwise.
  575.      */
  576.     public function isRobot()
  577.     {
  578.         return $this->_isRobot;
  579.     }
  580.  
  581.     /**
  582.      * Remove support for a previously added Web browser.
  583.      * @param string $browserName The Web browser name as used when added.
  584.      * @see addCustomBrowserDetection()
  585.      * @return boolean Returns true if the custom rule has been found and removed, false otherwise.
  586.      */
  587.     public function removeCustomBrowserDetection($browserName)
  588.     {
  589.         if (array_key_exists($browserName$this->_customBrowserDetection)) {
  590.             unset($this->_customBrowserDetection[$browserName]);
  591.             return true;
  592.         }
  593.  
  594.         return false;
  595.     }
  596.  
  597.     /**
  598.      * Remove support for a previously added platform.
  599.      * @param string $platformName The platform name as used when added.
  600.      * @see addCustomPlatformDetection()
  601.      * @return boolean Returns true if the custom rule has been found and removed, false otherwise.
  602.      */
  603.     public function removeCustomPlatformDetection($platformName)
  604.     {
  605.         if (array_key_exists($platformName$this->_customPlatformDetection)) {
  606.             unset($this->_customPlatformDetection[$platformName]);
  607.             return true;
  608.         }
  609.  
  610.         return false;
  611.     }
  612.  
  613.     /**
  614.      * Set the user agent to use with the class.
  615.      * @param string $agentString (optional) The value of the user agent. If an empty string is sent (default),
  616.      *  $_SERVER['HTTP_USER_AGENT'] will be used.
  617.      */
  618.     public function setUserAgent($agentString '')
  619.     {
  620.         if (!is_string($agentString|| trim($agentString== ''{
  621.             //https://bugs.php.net/bug.php?id=49184
  622.             if (filter_has_var(INPUT_SERVER'HTTP_USER_AGENT')) {
  623.                 $agentString filter_input(INPUT_SERVER'HTTP_USER_AGENT'FILTER_SANITIZE_STRINGFILTER_FLAG_STRIP_LOW);
  624.             else if (array_key_exists('HTTP_USER_AGENT'$_SERVER&& is_string($_SERVER['HTTP_USER_AGENT'])) {
  625.                 $agentString filter_var($_SERVER['HTTP_USER_AGENT']FILTER_SANITIZE_STRINGFILTER_FLAG_STRIP_LOW);
  626.             else {
  627.                 $agentString '';
  628.             }
  629.  
  630.             if ($agentString === false || $agentString === NULL{
  631.                 //filter_input or filter_var failed
  632.                 $agentString '';
  633.             }
  634.         }
  635.  
  636.         $this->reset();
  637.         $this->_agent = $agentString;
  638.         $this->detect();
  639.     }
  640.  
  641.  
  642.     //--- PROTECTED MEMBERS --------------------------------------------------------------------------------------------
  643.  
  644.  
  645.     /**
  646.      * Convert the Android version numbers to the operating system name. For instance '1.6' returns 'Donut'.
  647.      * @access protected
  648.      * @param string $androidVer The Android version numbers as a string.
  649.      * @return string The operating system name or the constant PLATFORM_VERSION_UNKNOWN if nothing match the version
  650.      *  numbers.
  651.      */
  652.     protected function androidVerToStr($androidVer)
  653.     {
  654.         //https://en.wikipedia.org/wiki/Android_version_history
  655.  
  656.         if ($this->compareVersions($androidVer'10'>= && $this->compareVersions($androidVer'11'0{
  657.             return 'Android Q'//Still in beta will replace with final name when released
  658.         else if ($this->compareVersions($androidVer'9'>= && $this->compareVersions($androidVer'10'0{
  659.             return 'Pie';
  660.         else if ($this->compareVersions($androidVer'8'>= && $this->compareVersions($androidVer'9'0{
  661.             return 'Oreo';
  662.         else if ($this->compareVersions($androidVer'7'>= && $this->compareVersions($androidVer'8'0{
  663.             return 'Nougat';
  664.         else if ($this->compareVersions($androidVer'6'>= && $this->compareVersions($androidVer'7'0{
  665.             return 'Marshmallow';
  666.         else if ($this->compareVersions($androidVer'5'>= && $this->compareVersions($androidVer'5.2'0{
  667.             return 'Lollipop';
  668.         else if ($this->compareVersions($androidVer'4.4'>= && $this->compareVersions($androidVer'4.5'0{
  669.             return 'KitKat';
  670.         else if ($this->compareVersions($androidVer'4.1'>= && $this->compareVersions($androidVer'4.4'0{
  671.             return 'Jelly Bean';
  672.         else if ($this->compareVersions($androidVer'4'>= && $this->compareVersions($androidVer'4.1'0{
  673.             return 'Ice Cream Sandwich';
  674.         else if ($this->compareVersions($androidVer'3'>= && $this->compareVersions($androidVer'3.3'0{
  675.             return 'Honeycomb';
  676.         else if ($this->compareVersions($androidVer'2.3'>= && $this->compareVersions($androidVer'2.4'0{
  677.             return 'Gingerbread';
  678.         else if ($this->compareVersions($androidVer'2.2'>= && $this->compareVersions($androidVer'2.3'0{
  679.             return 'Froyo';
  680.         else if ($this->compareVersions($androidVer'2'>= && $this->compareVersions($androidVer'2.2'0{
  681.             return 'Eclair';
  682.         else if ($this->compareVersions($androidVer'1.6'== 0{
  683.             return 'Donut';
  684.         else if ($this->compareVersions($androidVer'1.5'== 0{
  685.             return 'Cupcake';
  686.         else {
  687.             return self::PLATFORM_VERSION_UNKNOWN//Unknown/unnamed Android version
  688.         }
  689.     }
  690.  
  691.     /**
  692.      * Determine if the browser is the Android browser (based on the WebKit layout engine and coupled with Chrome's
  693.      * JavaScript engine) or not.
  694.      * @access protected
  695.      * @return boolean Returns true if the browser is the Android browser, false otherwise.
  696.      */
  697.     protected function checkBrowserAndroid()
  698.     {
  699.         //Android don't use the standard "Android/1.0", it uses "Android 1.0;" instead
  700.         return $this->checkSimpleBrowserUA('Android'$this->_agentself::BROWSER_ANDROIDtrue);
  701.     }
  702.  
  703.     /**
  704.      * Determine if the browser is the Bingbot crawler or not.
  705.      * @access protected
  706.      * @link http://www.bing.com/webmaster/help/which-crawlers-does-bing-use-8c184ec0
  707.      * @return boolean Returns true if the browser is Bingbot, false otherwise.
  708.      */
  709.     protected function checkBrowserBingbot()
  710.     {
  711.         return $this->checkSimpleBrowserUA('bingbot'$this->_agentself::BROWSER_BINGBOTfalsetrue);
  712.     }
  713.  
  714.     /**
  715.      * Determine if the browser is the BlackBerry browser or not.
  716.      * @access protected
  717.      * @link http://supportforums.blackberry.com/t5/Web-and-WebWorks-Development/How-to-detect-the-BlackBerry-Browser/ta-p/559862
  718.      * @return boolean Returns true if the browser is the BlackBerry browser, false otherwise.
  719.      */
  720.     protected function checkBrowserBlackBerry()
  721.     {
  722.         $found false;
  723.  
  724.         //Tablet OS check
  725.         if ($this->checkSimpleBrowserUA('RIM Tablet OS'$this->_agentself::BROWSER_TABLET_OStrue)) {
  726.             return true;
  727.         }
  728.  
  729.         //Version 6, 7 & 10 check (versions 8 & 9 does not exists)
  730.         if ($this->checkBrowserUAWithVersion(array('BlackBerry''BB10')$this->_agentself::BROWSER_BLACKBERRYtrue)) {
  731.             if ($this->getVersion(== self::VERSION_UNKNOWN{
  732.                 $found true;
  733.             else {
  734.                 return true;
  735.             }
  736.         }
  737.  
  738.         //Version 4.2 to 5.0 check
  739.         if ($this->checkSimpleBrowserUA('BlackBerry'$this->_agentself::BROWSER_BLACKBERRYtruefalse'/'false)) {
  740.             if ($this->getVersion(== self::VERSION_UNKNOWN{
  741.                 $found true;
  742.             else {
  743.                 return true;
  744.             }
  745.         }
  746.  
  747.         return $found;
  748.     }
  749.  
  750.     /**
  751.      * Determine if the browser is Chrome or not.
  752.      * @access protected
  753.      * @link http://www.google.com/chrome/
  754.      * @return boolean Returns true if the browser is Chrome, false otherwise.
  755.      */
  756.     protected function checkBrowserChrome()
  757.     {
  758.         return $this->checkSimpleBrowserUA(array('Chrome''CriOS')$this->_agentself::BROWSER_CHROME);
  759.     }
  760.  
  761.     /**
  762.      * Determine if the browser is among the custom browser rules or not. Rules are checked in the order they were
  763.      * added.
  764.      * @access protected
  765.      * @return boolean Returns true if we found the browser we were looking for in the custom rules, false otherwise.
  766.      */
  767.     protected function checkBrowserCustom()
  768.     {
  769.         foreach ($this->_customBrowserDetection as $browserName => $customBrowser{
  770.             $uaNameToLookFor $customBrowser['uaNameToLookFor'];
  771.             $isMobile $customBrowser['isMobile'];
  772.             $isRobot $customBrowser['isRobot'];
  773.             $separator $customBrowser['separator'];
  774.             $uaNameFindWords $customBrowser['uaNameFindWords'];
  775.             if ($this->checkSimpleBrowserUA($uaNameToLookFor$this->_agent$browserName$isMobile$isRobot$separator$uaNameFindWords)) {
  776.                 return true;
  777.             }
  778.         }
  779.         return false;
  780.     }
  781.  
  782.     /**
  783.      * Determine if the browser is Edge or not.
  784.      * @access protected
  785.      * @return boolean Returns true if the browser is Edge, false otherwise.
  786.      */
  787.     protected function checkBrowserEdge()
  788.     {
  789.         return $this->checkSimpleBrowserUA(array('Edge''EdgA')$this->_agentself::BROWSER_EDGE);
  790.     }
  791.  
  792.     /**
  793.      * Determine if the browser is Firebird or not. Firebird was the name of Firefox from version 0.6 to 0.7.1.
  794.      * @access protected
  795.      * @return boolean Returns true if the browser is Firebird, false otherwise.
  796.      */
  797.     protected function checkBrowserFirebird()
  798.     {
  799.         return $this->checkSimpleBrowserUA('Firebird'$this->_agentself::BROWSER_FIREBIRD);
  800.     }
  801.  
  802.     /**
  803.      * Determine if the browser is Firefox or not.
  804.      * @access protected
  805.      * @link http://www.mozilla.org/en-US/firefox/new/
  806.      * @return boolean Returns true if the browser is Firefox, false otherwise.
  807.      */
  808.     protected function checkBrowserFirefox()
  809.     {
  810.         //Safari heavily matches with Firefox, ensure that Safari is filtered out...
  811.         if (preg_match('/.*Firefox[ (\/]*([a-z0-9.-]*)/i'$this->_agent$matches&&
  812.                 !$this->containString($this->_agent'Safari')) {
  813.             $this->setBrowser(self::BROWSER_FIREFOX);
  814.             $this->setVersion($matches[1]);
  815.             $this->setMobile(false);
  816.             $this->setRobot(false);
  817.  
  818.             return true;
  819.         }
  820.  
  821.         return false;
  822.     }
  823.  
  824.     /**
  825.      * Determine if the browser is the Googlebot crawler or not.
  826.      * @access protected
  827.      * @return boolean Returns true if the browser is Googlebot, false otherwise.
  828.      */
  829.     protected function checkBrowserGooglebot()
  830.     {
  831.         if ($this->checkSimpleBrowserUA('Googlebot'$this->_agentself::BROWSER_GOOGLEBOTfalsetrue)) {
  832.  
  833.             if ($this->containString($this->_agent'googlebot-mobile')) {
  834.                 $this->setMobile(true);
  835.             }
  836.  
  837.             return true;
  838.         }
  839.  
  840.         return false;
  841.     }
  842.  
  843.     /**
  844.      * Determine if the browser is iCab or not.
  845.      * @access protected
  846.      * @link http://www.icab.de/
  847.      * @return boolean Returns true if the browser is iCab, false otherwise.
  848.      */
  849.     protected function checkBrowserIcab()
  850.     {
  851.         //Some (early) iCab versions don't use the standard "iCab/1.0", they uses "iCab 1.0;" instead
  852.         return $this->checkSimpleBrowserUA('iCab'$this->_agentself::BROWSER_ICAB);
  853.     }
  854.  
  855.     /**
  856.      * Determine if the browser is GNU IceCat (formerly known as GNU IceWeasel) or not.
  857.      * @access protected
  858.      * @link http://www.gnu.org/software/gnuzilla/
  859.      * @return boolean Returns true if the browser is GNU IceCat, false otherwise.
  860.      */
  861.     protected function checkBrowserIceCat()
  862.     {
  863.         return $this->checkSimpleBrowserUA('IceCat'$this->_agentself::BROWSER_ICECAT);
  864.     }
  865.  
  866.     /**
  867.      * Determine if the browser is GNU IceWeasel (now know as GNU IceCat) or not.
  868.      * @access protected
  869.      * @see checkBrowserIceCat()
  870.      * @return boolean Returns true if the browser is GNU IceWeasel, false otherwise.
  871.      */
  872.     protected function checkBrowserIceWeasel()
  873.     {
  874.         return $this->checkSimpleBrowserUA('Iceweasel'$this->_agentself::BROWSER_ICEWEASEL);
  875.     }
  876.  
  877.     /**
  878.      * Determine if the browser is Internet Explorer or not.
  879.      * @access protected
  880.      * @link http://www.microsoft.com/ie/
  881.      * @link http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
  882.      * @return boolean Returns true if the browser is Internet Explorer, false otherwise.
  883.      */
  884.     protected function checkBrowserInternetExplorer()
  885.     {
  886.         //Test for Internet Explorer Mobile (formerly Pocket Internet Explorer)
  887.         if ($this->checkSimpleBrowserUA(array('IEMobile''MSPIE')$this->_agentself::BROWSER_IE_MOBILEtrue)) {
  888.             return true;
  889.         }
  890.  
  891.         //Several browsers uses IE compatibility UAs filter these browsers out (but after testing for IE Mobile)
  892.         if ($this->containString($this->_agent'Opera'|| $this->containString($this->_agentarray('BlackBerry''Nokia')truefalse)) {
  893.             return false;
  894.         }
  895.  
  896.         //Test for Internet Explorer 1
  897.         if ($this->checkSimpleBrowserUA('Microsoft Internet Explorer'$this->_agentself::BROWSER_IE)) {
  898.             if ($this->getVersion(== self::VERSION_UNKNOWN{
  899.                 if (preg_match('/308|425|426|474|0b1/i'$this->_agent)) {
  900.                     $this->setVersion('1.5');
  901.                 else {
  902.                     $this->setVersion('1.0');
  903.                 }
  904.             }
  905.             return true;
  906.         }
  907.  
  908.         //Test for Internet Explorer 2+
  909.         if ($this->containString($this->_agentarray('MSIE''Trident'))) {
  910.             $version '';
  911.  
  912.             if ($this->containString($this->_agent'Trident')) {
  913.                 //Test for Internet Explorer 11+ (check the rv: string)
  914.                 if ($this->containString($this->_agent'rv:'truefalse)) {
  915.                     if ($this->checkSimpleBrowserUA('Trident'$this->_agentself::BROWSER_IEfalsefalse'rv:')) {
  916.                         return true;
  917.                     }
  918.                 else {
  919.                     //Test for Internet Explorer 8, 9 & 10 (check the Trident string)
  920.                     if (preg_match('/Trident\/([\d]+)/i'$this->_agent$foundVersion)) {
  921.                         //Trident started with version 4.0 on IE 8
  922.                         $verFromTrident $this->parseInt($foundVersion[1]4;
  923.                         if ($verFromTrident >= 8{
  924.                             $version $verFromTrident '.0';
  925.                         }
  926.                     }
  927.                 }
  928.  
  929.                 //If we have the IE version from Trident, we can check for the compatibility view mode
  930.                 if ($version != ''{
  931.                     $emulatedVer '';
  932.                     preg_match_all('/MSIE\s*([^\s;$]+)/i'$this->_agent$foundVersions);
  933.                     foreach ($foundVersions[1as $currVer{
  934.                         //Keep the lowest MSIE version for the emulated version (in compatibility view mode)
  935.                         if ($emulatedVer == '' || $this->compareVersions($emulatedVer$currVer== 1{
  936.                             $emulatedVer $currVer;
  937.                         }
  938.                     }
  939.                     //Set the compatibility view mode if $version != $emulatedVer
  940.                     if ($this->compareVersions($version$emulatedVer!= 0{
  941.                         $this->_compatibilityViewName = self::BROWSER_IE;
  942.                         $this->_compatibilityViewVer = $this->cleanVersion($emulatedVer);
  943.                     }
  944.                 }
  945.             }
  946.  
  947.             //Test for Internet Explorer 2-7 versions if needed
  948.             if ($version == ''{
  949.                 preg_match_all('/MSIE\s+([^\s;$]+)/i'$this->_agent$foundVersions);
  950.                 foreach ($foundVersions[1as $currVer{
  951.                     //Keep the highest MSIE version
  952.                     if ($version == '' || $this->compareVersions($version$currVer== -1{
  953.                         $version $currVer;
  954.                     }
  955.                 }
  956.             }
  957.  
  958.             $this->setBrowser(self::BROWSER_IE);
  959.             $this->setVersion($version);
  960.             $this->setMobile(false);
  961.             $this->setRobot(false);
  962.  
  963.             return true;
  964.         }
  965.  
  966.         return false;
  967.     }
  968.  
  969.     /**
  970.      * Determine if the browser is Konqueror or not.
  971.      * @access protected
  972.      * @link http://www.konqueror.org/
  973.      * @return boolean Returns true if the browser is Konqueror, false otherwise.
  974.      */
  975.     protected function checkBrowserKonqueror()
  976.     {
  977.         return $this->checkSimpleBrowserUA('Konqueror'$this->_agentself::BROWSER_KONQUEROR);
  978.     }
  979.  
  980.     /**
  981.      * Determine if the browser is Lynx or not. It is the oldest web browser currently in general use and development.
  982.      * It is a text-based only Web browser.
  983.      * @access protected
  984.      * @link http://en.wikipedia.org/wiki/Lynx
  985.      * @return boolean Returns true if the browser is Lynx, false otherwise.
  986.      */
  987.     protected function checkBrowserLynx()
  988.     {
  989.         return $this->checkSimpleBrowserUA('Lynx'$this->_agentself::BROWSER_LYNX);
  990.     }
  991.  
  992.     /**
  993.      * Determine if the browser is Mozilla or not.
  994.      * @access protected
  995.      * @return boolean Returns true if the browser is Mozilla, false otherwise.
  996.      */
  997.     protected function checkBrowserMozilla()
  998.     {
  999.         return $this->checkSimpleBrowserUA('Mozilla'$this->_agentself::BROWSER_MOZILLAfalsefalse'rv:');
  1000.     }
  1001.  
  1002.     /**
  1003.      * Determine if the browser is the MSNBot crawler or not. In October 2010 it was replaced by the Bingbot robot.
  1004.      * @access protected
  1005.      * @see checkBrowserBingbot()
  1006.      * @return boolean Returns true if the browser is MSNBot, false otherwise.
  1007.      */
  1008.     protected function checkBrowserMsnBot()
  1009.     {
  1010.         return $this->checkSimpleBrowserUA('msnbot'$this->_agentself::BROWSER_MSNBOTfalsetrue);
  1011.     }
  1012.  
  1013.     /**
  1014.      * Determine if the browser is MSN TV (formerly WebTV) or not.
  1015.      * @access protected
  1016.      * @link http://en.wikipedia.org/wiki/MSN_TV
  1017.      * @return boolean Returns true if the browser is WebTv, false otherwise.
  1018.      */
  1019.     protected function checkBrowserMsnTv()
  1020.     {
  1021.         return $this->checkSimpleBrowserUA('webtv'$this->_agentself::BROWSER_MSNTV);
  1022.     }
  1023.  
  1024.     /**
  1025.      * Determine if the browser is Netscape or not. Official support for this browser ended on March 1st, 2008.
  1026.      * @access protected
  1027.      * @link http://en.wikipedia.org/wiki/Netscape
  1028.      * @return boolean Returns true if the browser is Netscape, false otherwise.
  1029.      */
  1030.     protected function checkBrowserNetscape()
  1031.     {
  1032.         //BlackBerry & Nokia UAs can conflict with Netscape UAs
  1033.         if ($this->containString($this->_agentarray('BlackBerry''Nokia')truefalse)) {
  1034.             return false;
  1035.         }
  1036.  
  1037.         //Netscape v6 to v9 check
  1038.         if ($this->checkSimpleBrowserUA(array('Netscape''Navigator''Netscape6')$this->_agentself::BROWSER_NETSCAPE)) {
  1039.             return true;
  1040.         }
  1041.  
  1042.         //Netscape v1-4 (v5 don't exists)
  1043.         $found false;
  1044.         if ($this->containString($this->_agent'Mozilla'&& !$this->containString($this->_agent'rv:'truefalse)) {
  1045.             $version '';
  1046.             $verParts explode('/'stristr($this->_agent'Mozilla'));
  1047.             if (count($verParts1{
  1048.                 $verParts explode(' '$verParts[1]);
  1049.                 $verParts explode('.'$verParts[0]);
  1050.  
  1051.                 $majorVer $this->parseInt($verParts[0]);
  1052.                 if ($majorVer && $majorVer 5{
  1053.                     $version implode('.'$verParts);
  1054.                     $found true;
  1055.  
  1056.                     if (strtolower(substr($version-4)) == '-sgi'{
  1057.                         $version substr($version0-4);
  1058.                     else {
  1059.                         if (strtolower(substr($version-4)) == 'gold'{
  1060.                             $version substr($version0-4' Gold'//Doubles spaces (if any) will be normalized by setVersion()
  1061.                         }
  1062.                     }
  1063.                 }
  1064.             }
  1065.         }
  1066.  
  1067.         if ($found{
  1068.             $this->setBrowser(self::BROWSER_NETSCAPE);
  1069.             $this->setVersion($version);
  1070.             $this->setMobile(false);
  1071.             $this->setRobot(false);
  1072.         }
  1073.  
  1074.         return $found;
  1075.     }
  1076.  
  1077.     /**
  1078.      * Determine if the browser is a Nokia browser or not.
  1079.      * @access protected
  1080.      * @link http://www.developer.nokia.com/Community/Wiki/User-Agent_headers_for_Nokia_devices
  1081.      * @return boolean Returns true if the browser is a Nokia browser, false otherwise.
  1082.      */
  1083.     protected function checkBrowserNokia()
  1084.     {
  1085.         if ($this->containString($this->_agentarray('Nokia5800''Nokia5530''Nokia5230')truefalse)) {
  1086.             $this->setBrowser(self::BROWSER_NOKIA);
  1087.             $this->setVersion('7.0');
  1088.             $this->setMobile(true);
  1089.             $this->setRobot(false);
  1090.  
  1091.             return true;
  1092.         }
  1093.  
  1094.         if ($this->checkSimpleBrowserUA(array('NokiaBrowser''BrowserNG''Series60''S60''S40OviBrowser')$this->_agentself::BROWSER_NOKIAtrue)) {
  1095.             return true;
  1096.         }
  1097.  
  1098.         return false;
  1099.     }
  1100.  
  1101.     /**
  1102.      * Determine if the browser is Opera or not.
  1103.      * @access protected
  1104.      * @link http://www.opera.com/
  1105.      * @link http://www.opera.com/mini/
  1106.      * @link http://www.opera.com/mobile/
  1107.      * @link http://my.opera.com/community/openweb/idopera/
  1108.      * @return boolean Returns true if the browser is Opera, false otherwise.
  1109.      */
  1110.     protected function checkBrowserOpera()
  1111.     {
  1112.         if ($this->checkBrowserUAWithVersion('Opera Mobi'$this->_agentself::BROWSER_OPERA_MOBILEtrue)) {
  1113.             return true;
  1114.         }
  1115.  
  1116.         if ($this->checkSimpleBrowserUA('Opera Mini'$this->_agentself::BROWSER_OPERA_MINItrue)) {
  1117.             return true;
  1118.         }
  1119.  
  1120.         $version '';
  1121.         $found $this->checkBrowserUAWithVersion('Opera'$this->_agentself::BROWSER_OPERA);
  1122.         if ($found && $this->getVersion(!= self::VERSION_UNKNOWN{
  1123.             $version $this->getVersion();
  1124.         }
  1125.  
  1126.         if (!$found || $version == ''{
  1127.             if ($this->checkSimpleBrowserUA('Opera'$this->_agentself::BROWSER_OPERA)) {
  1128.                 return true;
  1129.             }
  1130.         }
  1131.  
  1132.         if (!$found && $this->checkSimpleBrowserUA('Chrome'$this->_agentself::BROWSER_CHROME) ) {
  1133.             if ($this->checkSimpleBrowserUA('OPR'$this->_agentself::BROWSER_OPERA)) {
  1134.                 return true;
  1135.             }
  1136.         }
  1137.  
  1138.         return $found;
  1139.     }
  1140.  
  1141.     /**
  1142.      * Determine if the browser is Phoenix or not. Phoenix was the name of Firefox from version 0.1 to 0.5.
  1143.      * @access protected
  1144.      * @return boolean Returns true if the browser is Phoenix, false otherwise.
  1145.      */
  1146.     protected function checkBrowserPhoenix()
  1147.     {
  1148.         return $this->checkSimpleBrowserUA('Phoenix'$this->_agentself::BROWSER_PHOENIX);
  1149.     }
  1150.  
  1151.     /**
  1152.      * Determine what is the browser used by the user.
  1153.      * @access protected
  1154.      * @return boolean Returns true if the browser has been identified, false otherwise.
  1155.      */
  1156.     protected function checkBrowsers()
  1157.     {
  1158.         //Changing the check order can break the class detection results!
  1159.         return
  1160.                /* Major browsers and browsers that need to be detected in a special order */
  1161.                $this->checkBrowserCustom(||           /* Customs rules are always checked first */
  1162.                $this->checkBrowserMsnTv(||            /* MSN TV is based on IE so we must check for MSN TV before IE */
  1163.                $this->checkBrowserInternetExplorer(||
  1164.                $this->checkBrowserOpera(||            /* Opera must be checked before Firefox, Netscape and Chrome to avoid conflicts */
  1165.                $this->checkBrowserEdge(||             /* Edge must be checked before Firefox, Safari and Chrome to avoid conflicts */
  1166.                $this->checkBrowserSamsung(||          /* Samsung Internet browser must be checked before Chrome and Safari to avoid conflicts */
  1167.                $this->checkBrowserUC(||               /* UC Browser must be checked before Chrome and Safari to avoid conflicts */
  1168.                $this->checkBrowserChrome(||           /* Chrome must be checked before Netscaoe and Mozilla to avoid conflicts */
  1169.                $this->checkBrowserIcab(||             /* Check iCab before Netscape since iCab have Mozilla UAs */
  1170.                $this->checkBrowserNetscape(||         /* Must be checked before Firefox since Netscape 8-9 are based on Firefox */
  1171.                $this->checkBrowserIceCat(||           /* Check IceCat and IceWeasel before Firefox since they are GNU builds of Firefox */
  1172.                $this->checkBrowserIceWeasel(||
  1173.                $this->checkBrowserFirefox(||
  1174.                /* Current browsers that don't need to be detected in any special order */
  1175.                $this->checkBrowserKonqueror(||
  1176.                $this->checkBrowserLynx(||
  1177.                /* Mobile */
  1178.                $this->checkBrowserAndroid(||
  1179.                $this->checkBrowserBlackBerry(||
  1180.                $this->checkBrowserNokia(||
  1181.                /* Bots */
  1182.                $this->checkBrowserGooglebot(||
  1183.                $this->checkBrowserBingbot(||
  1184.                $this->checkBrowserMsnBot(||
  1185.                $this->checkBrowserSlurp(||
  1186.                $this->checkBrowserYahooMultimedia(||
  1187.                $this->checkBrowserW3CValidator(||
  1188.                /* WebKit base check (after most other checks) */
  1189.                $this->checkBrowserSafari(||
  1190.                /* Deprecated browsers that don't need to be detected in any special order */
  1191.                $this->checkBrowserFirebird(||
  1192.                $this->checkBrowserPhoenix(||
  1193.                /* Mozilla is such an open standard that it must be checked last */
  1194.                $this->checkBrowserMozilla();
  1195.     }
  1196.  
  1197.     /**
  1198.      * Determine if the browser is Safari or not.
  1199.      * @access protected
  1200.      * @link http://www.apple.com/safari/
  1201.      * @link http://web.archive.org/web/20080514173941/http://developer.apple.com/internet/safari/uamatrix.html
  1202.      * @link http://en.wikipedia.org/wiki/Safari_version_history#Release_history
  1203.      * @return boolean Returns true if the browser is Safari, false otherwise.
  1204.      */
  1205.     protected function checkBrowserSafari()
  1206.     {
  1207.         $version '';
  1208.  
  1209.         //Check for current versions of Safari
  1210.         $found $this->checkBrowserUAWithVersion(array('Safari''AppleWebKit')$this->_agentself::BROWSER_SAFARI);
  1211.         if ($found && $this->getVersion(!= self::VERSION_UNKNOWN{
  1212.             $version $this->getVersion();
  1213.         }
  1214.  
  1215.         //Safari 1-2 didn't had a "Version" string in the UA, only a WebKit build and/or Safari build, extract version from these...
  1216.         if (!$found || $version == ''{
  1217.             if (preg_match('/.*Safari[ (\/]*([a-z0-9.-]*)/i'$this->_agent$matches)) {
  1218.                 $version $this->safariBuildToSafariVer($matches[1]);
  1219.                 $found true;
  1220.             }
  1221.         }
  1222.         if (!$found || $version == ''{
  1223.             if (preg_match('/.*AppleWebKit[ (\/]*([a-z0-9.-]*)/i'$this->_agent$matches)) {
  1224.                 $version $this->webKitBuildToSafariVer($matches[1]);
  1225.                 $found true;
  1226.             }
  1227.         }
  1228.  
  1229.         if ($found{
  1230.             $this->setBrowser(self::BROWSER_SAFARI);
  1231.             $this->setVersion($version);
  1232.             $this->setMobile(false);
  1233.             $this->setRobot(false);
  1234.         }
  1235.  
  1236.         return $found;
  1237.     }
  1238.  
  1239.     /**
  1240.      * Determine if the browser is the Samsung Internet browser or not.
  1241.      * @access protected
  1242.      * @return boolean Returns true if the browser is the the Samsung Internet browser, false otherwise.
  1243.      */
  1244.     protected function checkBrowserSamsung()
  1245.     {
  1246.         return $this->checkSimpleBrowserUA('SamsungBrowser'$this->_agentself::BROWSER_SAMSUNGtrue);
  1247.     }
  1248.  
  1249.     /**
  1250.      * Determine if the browser is the Yahoo! Slurp crawler or not.
  1251.      * @access protected
  1252.      * @return boolean Returns true if the browser is Yahoo! Slurp, false otherwise.
  1253.      */
  1254.     protected function checkBrowserSlurp()
  1255.     {
  1256.         return $this->checkSimpleBrowserUA('Yahoo! Slurp'$this->_agentself::BROWSER_SLURPfalsetrue);
  1257.     }
  1258.  
  1259.     /**
  1260.      * Test the user agent for a specific browser that use a "Version" string (like Safari and Opera). The user agent
  1261.      * should look like: "Version/1.0 Browser name/123.456" or "Browser name/123.456 Version/1.0".
  1262.      * @access protected
  1263.      * @param mixed $uaNameToLookFor The string (or array of strings) representing the browser name to find in the user
  1264.      *  agent.
  1265.      * @param string $userAgent The user agent string to work with.
  1266.      * @param string $browserName The literal browser name. Always use a class constant!
  1267.      * @param boolean $isMobile (optional) Determines if the browser is from a mobile device.
  1268.      * @param boolean $isRobot (optional) Determines if the browser is a robot or not.
  1269.      * @param boolean $findWords (optional) Determines if the needle should match a word to be found. For example "Bar"
  1270.      *  would not be found in "FooBar" when true but would be found in "Foo Bar". When set to false, the needle can be
  1271.      *  found anywhere in the haystack.
  1272.      * @return boolean Returns true if we found the browser we were looking for, false otherwise.
  1273.      */
  1274.     protected function checkBrowserUAWithVersion($uaNameToLookFor$userAgent$browserName$isMobile false$isRobot false$findWords true)
  1275.     {
  1276.         if (!is_array($uaNameToLookFor)) {
  1277.             $uaNameToLookFor array($uaNameToLookFor);
  1278.         }
  1279.  
  1280.         foreach ($uaNameToLookFor as $currUANameToLookFor{
  1281.             if ($this->containString($userAgent$currUANameToLookFortrue$findWords)) {
  1282.                 $version '';
  1283.                 $verParts explode('/'stristr($this->_agent'Version'));
  1284.                 if (count($verParts1{
  1285.                     $verParts explode(' '$verParts[1]);
  1286.                     $version $verParts[0];
  1287.                 }
  1288.  
  1289.                 $this->setBrowser($browserName);
  1290.                 $this->setVersion($version);
  1291.  
  1292.                 $this->setMobile($isMobile);
  1293.                 $this->setRobot($isRobot);
  1294.  
  1295.                 return true;
  1296.             }
  1297.         }
  1298.  
  1299.         return false;
  1300.     }
  1301.  
  1302.     /**
  1303.      * Determine if the browser is UC Browser or not.
  1304.      * @access protected
  1305.      * @return boolean Returns true if the browser is UC Browser, false otherwise.
  1306.      */
  1307.     protected function checkBrowserUC()
  1308.     {
  1309.         return $this->checkSimpleBrowserUA('UCBrowser'$this->_agentself::BROWSER_UCtruefalse);
  1310.     }
  1311.  
  1312.     /**
  1313.      * Determine if the browser is the W3C Validator or not.
  1314.      * @access protected
  1315.      * @link http://validator.w3.org/
  1316.      * @return boolean Returns true if the browser is the W3C Validator, false otherwise.
  1317.      */
  1318.     protected function checkBrowserW3CValidator()
  1319.     {
  1320.         //Since the W3C validates pages with different robots we will prefix our versions with the part validated on the page...
  1321.  
  1322.         //W3C Link Checker (prefixed with "Link-")
  1323.         if ($this->checkSimpleBrowserUA('W3C-checklink'$this->_agentself::BROWSER_W3CVALIDATORfalsetrue)) {
  1324.             if ($this->getVersion(!= self::VERSION_UNKNOWN{
  1325.                 $this->setVersion('Link-' $this->getVersion());
  1326.             }
  1327.             return true;
  1328.         }
  1329.  
  1330.         //W3C CSS Validation Service (prefixed with "CSS-")
  1331.         if ($this->checkSimpleBrowserUA('Jigsaw'$this->_agentself::BROWSER_W3CVALIDATORfalsetrue)) {
  1332.             if ($this->getVersion(!= self::VERSION_UNKNOWN{
  1333.                 $this->setVersion('CSS-' $this->getVersion());
  1334.             }
  1335.             return true;
  1336.         }
  1337.  
  1338.         //W3C mobileOK Checker (prefixed with "mobileOK-")
  1339.         if ($this->checkSimpleBrowserUA('W3C-mobileOK'$this->_agentself::BROWSER_W3CVALIDATORfalsetrue)) {
  1340.             if ($this->getVersion(!= self::VERSION_UNKNOWN{
  1341.                 $this->setVersion('mobileOK-' $this->getVersion());
  1342.             }
  1343.             return true;
  1344.         }
  1345.  
  1346.         //W3C Markup Validation Service (no prefix)
  1347.         return $this->checkSimpleBrowserUA('W3C_Validator'$this->_agentself::BROWSER_W3CVALIDATORfalsetrue);
  1348.     }
  1349.  
  1350.     /**
  1351.      * Determine if the browser is the Yahoo! multimedia crawler or not.
  1352.      * @access protected
  1353.      * @return boolean Returns true if the browser is the Yahoo! multimedia crawler, false otherwise.
  1354.      */
  1355.     protected function checkBrowserYahooMultimedia()
  1356.     {
  1357.         return $this->checkSimpleBrowserUA('Yahoo-MMCrawler'$this->_agentself::BROWSER_YAHOO_MMfalsetrue);
  1358.     }
  1359.  
  1360.     /**
  1361.      * Determine the user's platform.
  1362.      * @access protected
  1363.      */
  1364.     protected function checkPlatform()
  1365.     {
  1366.         if (!$this->checkPlatformCustom()) /* Customs rules are always checked first */
  1367.             /* Mobile platforms */
  1368.             if ($this->containString($this->_agentarray('Windows Phone''IEMobile'))) /* Check Windows Phone (formerly Windows Mobile) before Windows */
  1369.                 $this->setPlatform(self::PLATFORM_WINDOWS_PHONE);
  1370.                 $this->setMobile(true);
  1371.             else if ($this->containString($this->_agent'Windows CE')) /* Check Windows CE before Windows */
  1372.                 $this->setPlatform(self::PLATFORM_WINDOWS_CE);
  1373.                 $this->setMobile(true);
  1374.             else if ($this->containString($this->_agentarray('CPU OS''CPU iPhone OS''iPhone''iPad''iPod'))) /* Check iOS (iPad/iPod/iPhone) before Macintosh */
  1375.                 $this->setPlatform(self::PLATFORM_IOS);
  1376.                 $this->setMobile(true);
  1377.             else if ($this->containString($this->_agent'Android')) {
  1378.                 $this->setPlatform(self::PLATFORM_ANDROID);
  1379.                 $this->setMobile(true);
  1380.             else if ($this->containString($this->_agent'BlackBerry'truefalse|| $this->containString($this->_agentarray('BB10''RIM Tablet OS'))) {
  1381.                 $this->setPlatform(self::PLATFORM_BLACKBERRY);
  1382.                 $this->setMobile(true);
  1383.             else if ($this->containString($this->_agent'Nokia'truefalse)) {
  1384.                 $this->setPlatform(self::PLATFORM_NOKIA);
  1385.                 $this->setMobile(true);
  1386.  
  1387.             /* Desktop platforms */
  1388.             else if ($this->containString($this->_agent'Windows')) {
  1389.                 $this->setPlatform(self::PLATFORM_WINDOWS);
  1390.             else if ($this->containString($this->_agent'Macintosh')) {
  1391.                 $this->setPlatform(self::PLATFORM_MACINTOSH);
  1392.             else if ($this->containString($this->_agent'Linux')) {
  1393.                 $this->setPlatform(self::PLATFORM_LINUX);
  1394.             else if ($this->containString($this->_agent'FreeBSD')) {
  1395.                 $this->setPlatform(self::PLATFORM_FREEBSD);
  1396.             else if ($this->containString($this->_agent'OpenBSD')) {
  1397.                 $this->setPlatform(self::PLATFORM_OPENBSD);
  1398.             else if ($this->containString($this->_agent'NetBSD')) {
  1399.                 $this->setPlatform(self::PLATFORM_NETBSD);
  1400.  
  1401.             /* Discontinued */
  1402.             else if ($this->containString($this->_agentarray('Symbian''SymbianOS'))) {
  1403.                 $this->setPlatform(self::PLATFORM_SYMBIAN);
  1404.                 $this->setMobile(true);
  1405.             else if ($this->containString($this->_agent'OpenSolaris')) {
  1406.                 $this->setPlatform(self::PLATFORM_OPENSOLARIS);
  1407.  
  1408.             /* Generic */
  1409.             else if ($this->containString($this->_agent'Win'truefalse)) {
  1410.                 $this->setPlatform(self::PLATFORM_WINDOWS);
  1411.             else if ($this->containString($this->_agent'Mac'truefalse)) {
  1412.                 $this->setPlatform(self::PLATFORM_MACINTOSH);
  1413.             }
  1414.         }
  1415.  
  1416.         //Check if it's a 64-bit platform
  1417.         if ($this->containString($this->_agentarray('WOW64''Win64''AMD64''x86_64''x86-64''ia64''IRIX64',
  1418.                 'ppc64''sparc64''x64;''x64_64'))) {
  1419.             $this->set64bit(true);
  1420.         }
  1421.  
  1422.         $this->checkPlatformVersion();
  1423.     }
  1424.  
  1425.     /**
  1426.      * Determine if the platform is among the custom platform rules or not. Rules are checked in the order they were
  1427.      * added.
  1428.      * @access protected
  1429.      * @return boolean Returns true if we found the platform we were looking for in the custom rules, false otherwise.
  1430.      */
  1431.     protected function checkPlatformCustom()
  1432.     {
  1433.         foreach ($this->_customPlatformDetection as $platformName => $customPlatform{
  1434.             $platformNameToLookFor $customPlatform['platformNameToLookFor'];
  1435.             $isMobile $customPlatform['isMobile'];
  1436.             if ($this->containString($this->_agent$platformNameToLookFor)) {
  1437.                 $this->setPlatform($platformName);
  1438.                 if ($isMobile{
  1439.                     $this->setMobile(true);
  1440.                 }
  1441.                 return true;
  1442.             }
  1443.         }
  1444.  
  1445.         return false;
  1446.     }
  1447.  
  1448.     /**
  1449.      * Determine the user's platform version.
  1450.      * @access protected
  1451.      */
  1452.     protected function checkPlatformVersion()
  1453.     {
  1454.         $result '';
  1455.         switch ($this->getPlatform()) {
  1456.             case self::PLATFORM_WINDOWS:
  1457.                 if (preg_match('/Windows NT\s*(\d+(?:\.\d+)*)/i'$this->_agent$foundVersion)) {
  1458.                     $result 'NT ' $foundVersion[1];
  1459.                 else {
  1460.                     //https://support.microsoft.com/en-us/kb/158238
  1461.  
  1462.                     if ($this->containString($this->_agentarray('Windows XP''WinXP''Win XP'))) {
  1463.                         $result '5.1';
  1464.                     else if ($this->containString($this->_agent'Windows 2000''Win 2000''Win2000')) {
  1465.                         $result '5.0';
  1466.                     else if ($this->containString($this->_agentarray('Win 9x 4.90''Windows ME''WinME''Win ME'))) {
  1467.                         $result '4.90.3000'//Windows Me version range from 4.90.3000 to 4.90.3000A
  1468.                     else if ($this->containString($this->_agentarray('Windows 98''Win98''Win 98'))) {
  1469.                         $result '4.10'//Windows 98 version range from 4.10.1998 to 4.10.2222B
  1470.                     else if ($this->containString($this->_agentarray('Windows 95''Win95''Win 95'))) {
  1471.                         $result '4.00'//Windows 95 version range from 4.00.950 to 4.03.1214
  1472.                     else if (($foundAt stripos($this->_agent'Windows 3')) !== false{
  1473.                         $result '3';
  1474.                         if (preg_match('/\d+(?:\.\d+)*/'substr($this->_agent$foundAt strlen('Windows 3'))$foundVersion)) {
  1475.                             $result .= '.' $foundVersion[0];
  1476.                         }
  1477.                     else if ($this->containString($this->_agent'Win16')) {
  1478.                         $result '3.1';
  1479.                     }
  1480.                 }
  1481.                 break;
  1482.  
  1483.             case self::PLATFORM_MACINTOSH:
  1484.                 if (preg_match('/Mac OS X\s*(\d+(?:_\d+)+)/i'$this->_agent$foundVersion)) {
  1485.                     $result str_replace('_''.'$this->cleanVersion($foundVersion[1]));
  1486.                 else if ($this->containString($this->_agent'Mac OS X')) {
  1487.                     $result '10';
  1488.                 }
  1489.                 break;
  1490.  
  1491.             case self::PLATFORM_ANDROID:
  1492.                 if (preg_match('/Android\s+([^\s;$]+)/i'$this->_agent$foundVersion)) {
  1493.                     $result $this->cleanVersion($foundVersion[1]);
  1494.                 }
  1495.                 break;
  1496.  
  1497.             case self::PLATFORM_IOS:
  1498.                 if (preg_match('/(?:CPU OS|iPhone OS|iOS)[\s_]*([\d_]+)/i'$this->_agent$foundVersion)) {
  1499.                     $result str_replace('_''.'$this->cleanVersion($foundVersion[1]));
  1500.                 }
  1501.                 break;
  1502.         }
  1503.  
  1504.         if (trim($result== ''{
  1505.             $result self::PLATFORM_VERSION_UNKNOWN;
  1506.         }
  1507.         $this->setPlatformVersion($result);
  1508.     }
  1509.  
  1510.     /**
  1511.      * Test the user agent for a specific browser where the browser name is immediately followed by the version number.
  1512.      * The user agent should look like: "Browser name/1.0" or "Browser 1.0;".
  1513.      * @access protected
  1514.      * @param mixed $uaNameToLookFor The string (or array of strings) representing the browser name to find in the user
  1515.      *  agent.
  1516.      * @param string $userAgent The user agent string to work with.
  1517.      * @param string $browserName The literal browser name. Always use a class constant!
  1518.      * @param boolean $isMobile (optional) Determines if the browser is from a mobile device.
  1519.      * @param boolean $isRobot (optional) Determines if the browser is a robot or not.
  1520.      * @param string $separator (optional) The separator string used to split the browser name and the version number in
  1521.      *  the user agent.
  1522.      * @param boolean $uaNameFindWords (optional) Determines if the browser name to find should match a word instead of
  1523.      *  a part of a word. For example "Bar" would not be found in "FooBar" when true but would be found in "Foo Bar".
  1524.      *  When set to false, the browser name can be found anywhere in the user agent string.
  1525.      * @return boolean Returns true if we found the browser we were looking for, false otherwise.
  1526.      */
  1527.     protected function checkSimpleBrowserUA($uaNameToLookFor$userAgent$browserName$isMobile false$isRobot false$separator '/'$uaNameFindWords true)
  1528.     {
  1529.         if (!is_array($uaNameToLookFor)) {
  1530.             $uaNameToLookFor array($uaNameToLookFor);
  1531.         }
  1532.  
  1533.         foreach ($uaNameToLookFor as $currUANameToLookFor{
  1534.  
  1535.             if ($this->containString($userAgent$currUANameToLookFortrue$uaNameFindWords)) {
  1536.                 //Many browsers don't use the standard "Browser/1.0" format, they uses "Browser 1.0;" instead
  1537.                 if (stripos($userAgent$currUANameToLookFor $separator=== false{
  1538.                     $userAgent str_ireplace($currUANameToLookFor ' '$currUANameToLookFor $separator$this->_agent);
  1539.                 }
  1540.  
  1541.                 $version '';
  1542.                 $verParts explode($separatorstristr($userAgent$currUANameToLookFor));
  1543.                 if (count($verParts1{
  1544.                     $verParts explode(' '$verParts[1]);
  1545.                     $version $verParts[0];
  1546.                 }
  1547.  
  1548.                 $this->setBrowser($browserName);
  1549.                 $this->setVersion($version);
  1550.  
  1551.                 $this->setMobile($isMobile);
  1552.                 $this->setRobot($isRobot);
  1553.  
  1554.                 return true;
  1555.             }
  1556.         }
  1557.  
  1558.         return false;
  1559.     }
  1560.  
  1561.     /**
  1562.      * Find if one or more substring is contained in a string.
  1563.      * @access protected
  1564.      * @param string $haystack The string to search in.
  1565.      * @param mixed $needle The string to search for. Can be a string or an array of strings if multiples values are to
  1566.      *  be searched.
  1567.      * @param boolean $insensitive (optional) Determines if we do a case-sensitive search (false) or a case-insensitive
  1568.      *  one (true).
  1569.      * @param boolean $findWords (optional) Determines if the needle should match a word to be found. For example "Bar"
  1570.      *  would not be found in "FooBar" when true but would be found in "Foo Bar". When set to false, the needle can be
  1571.      *  found anywhere in the haystack.
  1572.      * @return boolean Returns true if the needle (or one of the needles) has been found in the haystack, false
  1573.      *  otherwise.
  1574.      */
  1575.     protected function containString($haystack$needle$insensitive true$findWords true)
  1576.     {
  1577.         if (!is_array($needle)) {
  1578.             $needle array($needle);
  1579.         }
  1580.  
  1581.         foreach ($needle as $currNeedle{
  1582.             if ($findWords{
  1583.                  $found $this->wordPos($haystack$currNeedle$insensitive!== false;
  1584.             else {
  1585.                 if ($insensitive{
  1586.                     $found stripos($haystack$currNeedle!== false;
  1587.                 else {
  1588.                     $found strpos($haystack$currNeedle!== false;
  1589.                 }
  1590.             }
  1591.  
  1592.             if ($found{
  1593.                 return true;
  1594.             }
  1595.         }
  1596.  
  1597.         return false;
  1598.     }
  1599.  
  1600.     /**
  1601.      * Detect the user environment from the details in the user agent string.
  1602.      * @access protected
  1603.      */
  1604.     protected function detect()
  1605.     {
  1606.         $this->checkBrowsers();
  1607.         $this->checkPlatform()//Check the platform after the browser since some platforms can change the mobile value
  1608.     }
  1609.  
  1610.     /**
  1611.      * Clean a version string from unwanted characters.
  1612.      * @access protected
  1613.      * @param string $version The version string to clean.
  1614.      * @return string Returns the cleaned version number string.
  1615.      */
  1616.     protected function cleanVersion($version)
  1617.     {
  1618.         //Clear anything that is in parentheses (and the parentheses themselves) - will clear started but unclosed ones too
  1619.         $cleanVer preg_replace('/\([^)]+\)?/'''$version);
  1620.         //Replace with a space any character which is NOT an alphanumeric, dot (.), hyphen (-), underscore (_) or space
  1621.         $cleanVer preg_replace('/[^0-9.a-zA-Z_ -]/'' '$cleanVer);
  1622.  
  1623.         //Remove trailing and leading spaces
  1624.         $cleanVer trim($cleanVer);
  1625.  
  1626.         //Remove trailing dot (.), hyphen (-), underscore (_)
  1627.         while (in_array(substr($cleanVer-1)array('.''-''_'))) {
  1628.             $cleanVer substr($cleanVer0-1);
  1629.         }
  1630.         //Remove leading dot (.), hyphen (-), underscore (_) and character v
  1631.         while (in_array(substr($cleanVer01)array('.''-''_''v''V'))) {
  1632.             $cleanVer substr($cleanVer1);
  1633.         }
  1634.  
  1635.         //Remove double spaces if any
  1636.         while (strpos($cleanVer'  '!== false{
  1637.             $cleanVer str_replace('  '' '$cleanVer);
  1638.         }
  1639.  
  1640.         return trim($cleanVer);
  1641.     }
  1642.  
  1643.     /**
  1644.      * Convert the iOS version numbers to the operating system name. For instance '2.0' returns 'iPhone OS 2.0'.
  1645.      * @access protected
  1646.      * @param string $iOSVer The iOS version numbers as a string.
  1647.      * @return string The operating system name.
  1648.      */
  1649.     protected function iOSVerToStr($iOSVer)
  1650.     {
  1651.         if ($this->compareVersions($iOSVer'3.0'<= 0{
  1652.             return 'iPhone OS ' $iOSVer;
  1653.         else {
  1654.             return 'iOS ' $iOSVer;
  1655.         }
  1656.     }
  1657.  
  1658.     /**
  1659.      * Convert the macOS version numbers to the operating system name. For instance '10.7' returns 'Mac OS X Lion'.
  1660.      * @access protected
  1661.      * @param string $macVer The macOS version numbers as a string.
  1662.      * @return string The operating system name or the constant PLATFORM_VERSION_UNKNOWN if nothing match the version
  1663.      *  numbers.
  1664.      */
  1665.     protected function macVerToStr($macVer)
  1666.     {
  1667.         //https://en.wikipedia.org/wiki/OS_X#Release_history
  1668.  
  1669.         if ($this->_platformVersion === '10'{
  1670.             return 'Mac OS X'//Unspecified Mac OS X version
  1671.         else if ($this->compareVersions($macVer'10.14'>= && $this->compareVersions($macVer'10.15'0{
  1672.             return 'macOS Mojave';
  1673.         else if ($this->compareVersions($macVer'10.13'>= && $this->compareVersions($macVer'10.14'0{
  1674.             return 'macOS High Sierra';
  1675.         else if ($this->compareVersions($macVer'10.12'>= && $this->compareVersions($macVer'10.13'0{
  1676.             return 'macOS Sierra';
  1677.         else if ($this->compareVersions($macVer'10.11'>= && $this->compareVersions($macVer'10.12'0{
  1678.             return 'OS X El Capitan';
  1679.         else if ($this->compareVersions($macVer'10.10'>= && $this->compareVersions($macVer'10.11'0{
  1680.             return 'OS X Yosemite';
  1681.         else if ($this->compareVersions($macVer'10.9'>= && $this->compareVersions($macVer'10.10'0{
  1682.             return 'OS X Mavericks';
  1683.         else if ($this->compareVersions($macVer'10.8'>= && $this->compareVersions($macVer'10.9'0{
  1684.             return 'OS X Mountain Lion';
  1685.         else if ($this->compareVersions($macVer'10.7'>= && $this->compareVersions($macVer'10.8'0{
  1686.             return 'Mac OS X Lion';
  1687.         else if ($this->compareVersions($macVer'10.6'>= && $this->compareVersions($macVer'10.7'0{
  1688.             return 'Mac OS X Snow Leopard';
  1689.         else if ($this->compareVersions($macVer'10.5'>= && $this->compareVersions($macVer'10.6'0{
  1690.             return 'Mac OS X Leopard';
  1691.         else if ($this->compareVersions($macVer'10.4'>= && $this->compareVersions($macVer'10.5'0{
  1692.             return 'Mac OS X Tiger';
  1693.         else if ($this->compareVersions($macVer'10.3'>= && $this->compareVersions($macVer'10.4'0{
  1694.             return 'Mac OS X Panther';
  1695.         else if ($this->compareVersions($macVer'10.2'>= && $this->compareVersions($macVer'10.3'0{
  1696.             return 'Mac OS X Jaguar';
  1697.         else if ($this->compareVersions($macVer'10.1'>= && $this->compareVersions($macVer'10.2'0{
  1698.             return 'Mac OS X Puma';
  1699.         else if ($this->compareVersions($macVer'10.0'>= && $this->compareVersions($macVer'10.1'0{
  1700.             return 'Mac OS X Cheetah';
  1701.         else {
  1702.             return self::PLATFORM_VERSION_UNKNOWN//Unknown/unnamed Mac OS version
  1703.         }
  1704.     }
  1705.  
  1706.     /**
  1707.      * Get the integer value of a string variable.
  1708.      * @access protected
  1709.      * @param string $intStr The scalar value being converted to an integer.
  1710.      * @return int The integer value of $intStr on success, or 0 on failure.
  1711.      */
  1712.     protected function parseInt($intStr)
  1713.     {
  1714.         return intval($intStr10);
  1715.     }
  1716.  
  1717.     /**
  1718.      * Reset all the properties of the class.
  1719.      * @access protected
  1720.      */
  1721.     protected function reset()
  1722.     {
  1723.         $this->_agent = '';
  1724.         $this->_browserName = self::BROWSER_UNKNOWN;
  1725.         $this->_compatibilityViewName = '';
  1726.         $this->_compatibilityViewVer = '';
  1727.         $this->_is64bit = false;
  1728.         $this->_isMobile = false;
  1729.         $this->_isRobot = false;
  1730.         $this->_platform = self::PLATFORM_UNKNOWN;
  1731.         $this->_platformVersion = self::PLATFORM_VERSION_UNKNOWN;
  1732.         $this->_version = self::VERSION_UNKNOWN;
  1733.     }
  1734.  
  1735.     /**
  1736.      * Convert a Safari build number to a Safari version number.
  1737.      * @access protected
  1738.      * @param string $version A string representing the version number.
  1739.      * @link http://web.archive.org/web/20080514173941/http://developer.apple.com/internet/safari/uamatrix.html
  1740.      * @return string Returns the Safari version string. If the version can't be determined, an empty string is
  1741.      *  returned.
  1742.      */
  1743.     protected function safariBuildToSafariVer($version)
  1744.     {
  1745.         $verParts explode('.'$version);
  1746.  
  1747.         //We need a 3 parts version (version 2 will becomes 2.0.0)
  1748.         while (count($verParts3{
  1749.             $verParts[0;
  1750.         }
  1751.         foreach ($verParts as $i => $currPart{
  1752.             $verParts[$i$this->parseInt($currPart);
  1753.         }
  1754.  
  1755.         switch ($verParts[0]{
  1756.             case 419$result '2.0.4';
  1757.                 break;
  1758.             case 417$result '2.0.3';
  1759.                 break;
  1760.             case 416$result '2.0.2';
  1761.                 break;
  1762.  
  1763.             case 412:
  1764.                 if ($verParts[1>= 5{
  1765.                     $result '2.0.1';
  1766.                 else {
  1767.                     $result '2.0';
  1768.                 }
  1769.                 break;
  1770.  
  1771.             case 312:
  1772.                 if ($verParts[1>= 5{
  1773.                     $result '1.3.2';
  1774.                 else {
  1775.                     if ($verParts[1>= 3{
  1776.                         $result '1.3.1';
  1777.                     else {
  1778.                         $result '1.3';
  1779.                     }
  1780.                 }
  1781.                 break;
  1782.  
  1783.             case 125:
  1784.                 if ($verParts[1>= 11{
  1785.                     $result '1.2.4';
  1786.                 else {
  1787.                     if ($verParts[1>= 9{
  1788.                         $result '1.2.3';
  1789.                     else {
  1790.                         if ($verParts[1>= 7{
  1791.                             $result '1.2.2';
  1792.                         else {
  1793.                             $result '1.2';
  1794.                         }
  1795.                     }
  1796.                 }
  1797.                 break;
  1798.  
  1799.             case 100:
  1800.                 if ($verParts[1>= 1{
  1801.                     $result '1.1.1';
  1802.                 else {
  1803.                     $result '1.1';
  1804.                 }
  1805.                 break;
  1806.  
  1807.             case 85:
  1808.                 if ($verParts[1>= 8{
  1809.                     $result '1.0.3';
  1810.                 else {
  1811.                     if ($verParts[1>= 7{
  1812.                         $result '1.0.2';
  1813.                     else {
  1814.                         $result '1.0';
  1815.                     }
  1816.                 }
  1817.                 break;
  1818.  
  1819.             case 73$result '0.9';
  1820.                 break;
  1821.             case 51$result '0.8.1';
  1822.                 break;
  1823.             case 48$result '0.8';
  1824.                 break;
  1825.  
  1826.             default$result '';
  1827.         }
  1828.  
  1829.         return $result;
  1830.     }
  1831.  
  1832.     /**
  1833.      * Set if the browser is executed from a 64-bit platform.
  1834.      * @access protected
  1835.      * @param boolean $is64bit Value that tells if the browser is executed from a 64-bit platform.
  1836.      */
  1837.     protected function set64bit($is64bit)
  1838.     {
  1839.         $this->_is64bit = $is64bit == true;
  1840.     }
  1841.  
  1842.     /**
  1843.      * Set the name of the browser.
  1844.      * @access protected
  1845.      * @param string $browserName The name of the browser.
  1846.      */
  1847.     protected function setBrowser($browserName)
  1848.     {
  1849.         $this->_browserName = $browserName;
  1850.     }
  1851.  
  1852.     /**
  1853.      * Set the browser to be from a mobile device or not.
  1854.      * @access protected
  1855.      * @param boolean $isMobile (optional) Value that tells if the browser is on a mobile device or not.
  1856.      */
  1857.     protected function setMobile($isMobile true)
  1858.     {
  1859.         $this->_isMobile = $isMobile == true;
  1860.     }
  1861.  
  1862.     /**
  1863.      * Set the platform on which the browser is on.
  1864.      * @access protected
  1865.      * @param string $platform The name of the platform.
  1866.      */
  1867.     protected function setPlatform($platform)
  1868.     {
  1869.         $this->_platform = $platform;
  1870.     }
  1871.  
  1872.     /**
  1873.      * Set the platform version on which the browser is on.
  1874.      * @access protected
  1875.      * @param string $platformVer The version numbers of the platform.
  1876.      */
  1877.     protected function setPlatformVersion($platformVer)
  1878.     {
  1879.         $this->_platformVersion = $platformVer;
  1880.     }
  1881.  
  1882.     /**
  1883.      * Set the browser to be a robot (crawler) or not.
  1884.      * @access protected
  1885.      * @param boolean $isRobot (optional) Value that tells if the browser is a robot or not.
  1886.      */
  1887.     protected function setRobot($isRobot true)
  1888.     {
  1889.         $this->_isRobot = $isRobot == true;
  1890.     }
  1891.  
  1892.     /**
  1893.      * Set the version of the browser.
  1894.      * @access protected
  1895.      * @param string $version The version of the browser.
  1896.      */
  1897.     protected function setVersion($version)
  1898.     {
  1899.         $cleanVer $this->cleanVersion($version);
  1900.  
  1901.         if ($cleanVer == ''{
  1902.             $this->_version = self::VERSION_UNKNOWN;
  1903.         else {
  1904.             $this->_version = $cleanVer;
  1905.         }
  1906.     }
  1907.  
  1908.     /**
  1909.      * Convert a WebKit build number to a Safari version number.
  1910.      * @access protected
  1911.      * @param string $version A string representing the version number.
  1912.      * @link http://web.archive.org/web/20080514173941/http://developer.apple.com/internet/safari/uamatrix.html
  1913.      * @return string Returns the Safari version string. If the version can't be determined, an empty string is
  1914.      *  returned.
  1915.      */
  1916.     protected function webKitBuildToSafariVer($version)
  1917.     {
  1918.         $verParts explode('.'$version);
  1919.  
  1920.         //We need a 3 parts version (version 2 will becomes 2.0.0)
  1921.         while (count($verParts3{
  1922.             $verParts[0;
  1923.         }
  1924.         foreach ($verParts as $i => $currPart{
  1925.             $verParts[$i$this->parseInt($currPart);
  1926.         }
  1927.  
  1928.         switch ($verParts[0]{
  1929.             case 419$result '2.0.4';
  1930.                 break;
  1931.  
  1932.             case 418:
  1933.                 if ($verParts[1>= 8{
  1934.                     $result '2.0.4';
  1935.                 else {
  1936.                     $result '2.0.3';
  1937.                 }
  1938.                 break;
  1939.  
  1940.             case 417$result '2.0.3';
  1941.                 break;
  1942.  
  1943.             case 416$result '2.0.2';
  1944.                 break;
  1945.  
  1946.             case 412:
  1947.                 if ($verParts[1>= 7{
  1948.                     $result '2.0.1';
  1949.                 else {
  1950.                     $result '2.0';
  1951.                 }
  1952.                 break;
  1953.  
  1954.             case 312:
  1955.                 if ($verParts[1>= 8{
  1956.                     $result '1.3.2';
  1957.                 else {
  1958.                     if ($verParts[1>= 5{
  1959.                         $result '1.3.1';
  1960.                     else {
  1961.                         $result '1.3';
  1962.                     }
  1963.                 }
  1964.                 break;
  1965.  
  1966.             case 125:
  1967.                 if ($this->compareVersions('5.4'$verParts[1'.' $verParts[2]== -1{
  1968.                     $result '1.2.4'//125.5.5+
  1969.                 else {
  1970.                     if ($verParts[1>= 4{
  1971.                         $result '1.2.3';
  1972.                     else {
  1973.                         if ($verParts[1>= 2{
  1974.                             $result '1.2.2';
  1975.                         else {
  1976.                             $result '1.2';
  1977.                         }
  1978.                     }
  1979.                 }
  1980.                 break;
  1981.  
  1982.             //WebKit 100 can be either Safari 1.1 (Safari build 100) or 1.1.1 (Safari build 100.1)
  1983.             //for this reason, check the Safari build before the WebKit build.
  1984.             case 100$result '1.1.1';
  1985.                 break;
  1986.  
  1987.             case 85:
  1988.                 if ($verParts[1>= 8{
  1989.                     $result '1.0.3';
  1990.                 else {
  1991.                     if ($verParts[1>= 7{
  1992.                         //WebKit 85.7 can be either Safari 1.0 (Safari build 85.5) or 1.0.2 (Safari build 85.7)
  1993.                         //for this reason, check the Safari build before the WebKit build.
  1994.                         $result '1.0.2';
  1995.                     else {
  1996.                         $result '1.0';
  1997.                     }
  1998.                 }
  1999.                 break;
  2000.  
  2001.             case 73$result '0.9';
  2002.                 break;
  2003.             case 51$result '0.8.1';
  2004.                 break;
  2005.             case 48$result '0.8';
  2006.                 break;
  2007.  
  2008.             default$result '';
  2009.         }
  2010.  
  2011.         return $result;
  2012.     }
  2013.  
  2014.     /**
  2015.      * Convert the Windows NT family version numbers to the operating system name. For instance '5.1' returns
  2016.      * 'Windows XP'.
  2017.      * @access protected
  2018.      * @param string $winVer The Windows NT family version numbers as a string.
  2019.      * @param boolean $returnServerFlavor (optional) Since some Windows NT versions have the same values, this flag
  2020.      *  determines if the Server flavor is returned or not. For instance Windows 8.1 and Windows Server 2012 R2 both use
  2021.      *  version 6.3.
  2022.      * @return string The operating system name or the constant PLATFORM_VERSION_UNKNOWN if nothing match the version
  2023.      *  numbers.
  2024.      */
  2025.     protected function windowsNTVerToStr($winVer$returnServerFlavor false)
  2026.     {
  2027.         //https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions
  2028.  
  2029.         $cleanWinVer explode('.'$winVer);
  2030.         while (count($cleanWinVer2{
  2031.             array_pop($cleanWinVer);
  2032.         }
  2033.         $cleanWinVer implode('.'$cleanWinVer);
  2034.  
  2035.         if ($this->compareVersions($cleanWinVer'11'>= 0{
  2036.             //Future versions of Windows
  2037.             return self::PLATFORM_WINDOWS ' ' $winVer;
  2038.         else if ($this->compareVersions($cleanWinVer'10'>= 0{
  2039.             //Current version of Windows
  2040.             return $returnServerFlavor (self::PLATFORM_WINDOWS ' Server 2016'(self::PLATFORM_WINDOWS ' 10');
  2041.         else if ($this->compareVersions($cleanWinVer'7'0{
  2042.             if ($this->compareVersions($cleanWinVer'6.3'== 0{
  2043.                 return $returnServerFlavor (self::PLATFORM_WINDOWS ' Server 2012 R2'(self::PLATFORM_WINDOWS ' 8.1');
  2044.             else if ($this->compareVersions($cleanWinVer'6.2'== 0{
  2045.                 return $returnServerFlavor (self::PLATFORM_WINDOWS ' Server 2012'(self::PLATFORM_WINDOWS ' 8');
  2046.             else if ($this->compareVersions($cleanWinVer'6.1'== 0{
  2047.                 return $returnServerFlavor (self::PLATFORM_WINDOWS ' Server 2008 R2'(self::PLATFORM_WINDOWS ' 7');
  2048.             else if ($this->compareVersions($cleanWinVer'6'== 0{
  2049.                 return $returnServerFlavor (self::PLATFORM_WINDOWS ' Server 2008'(self::PLATFORM_WINDOWS ' Vista');
  2050.             else if ($this->compareVersions($cleanWinVer'5.2'== 0{
  2051.                 return $returnServerFlavor (self::PLATFORM_WINDOWS ' Server 2003 / ' self::PLATFORM_WINDOWS ' Server 2003 R2'(self::PLATFORM_WINDOWS ' XP x64 Edition');
  2052.             else if ($this->compareVersions($cleanWinVer'5.1'== 0{
  2053.                 return self::PLATFORM_WINDOWS ' XP';
  2054.             else if ($this->compareVersions($cleanWinVer'5'== 0{
  2055.                 return self::PLATFORM_WINDOWS ' 2000';
  2056.             else if ($this->compareVersions($cleanWinVer'5'&& $this->compareVersions($cleanWinVer'3'>= 0{
  2057.                 return self::PLATFORM_WINDOWS ' NT ' $winVer;
  2058.             }
  2059.         }
  2060.  
  2061.         return self::PLATFORM_VERSION_UNKNOWN//Invalid Windows NT version
  2062.     }
  2063.  
  2064.     /**
  2065.      * Convert the Windows 3.x & 9x family version numbers to the operating system name. For instance '4.10.1998'
  2066.      * returns 'Windows 98'.
  2067.      * @access protected
  2068.      * @param string $winVer The Windows 3.x or 9x family version numbers as a string.
  2069.      * @return string The operating system name or the constant PLATFORM_VERSION_UNKNOWN if nothing match the version
  2070.      *  numbers.
  2071.      */
  2072.     protected function windowsVerToStr($winVer)
  2073.     {
  2074.         //https://support.microsoft.com/en-us/kb/158238
  2075.  
  2076.         if ($this->compareVersions($winVer'4.90'>= && $this->compareVersions($winVer'4.91'0{
  2077.             return self::PLATFORM_WINDOWS ' Me'//Normally range from 4.90.3000 to 4.90.3000A
  2078.         else if ($this->compareVersions($winVer'4.10'>= && $this->compareVersions($winVer'4.11'0{
  2079.             return self::PLATFORM_WINDOWS ' 98'//Normally range from 4.10.1998 to 4.10.2222B
  2080.         else if ($this->compareVersions($winVer'4'>= && $this->compareVersions($winVer'4.04'0{
  2081.             return self::PLATFORM_WINDOWS ' 95'//Normally range from 4.00.950 to 4.03.1214
  2082.         else if ($this->compareVersions($winVer'3.1'== || $this->compareVersions($winVer'3.11'== 0{
  2083.             return self::PLATFORM_WINDOWS ' ' $winVer;
  2084.         else if ($this->compareVersions($winVer'3.10'== 0{
  2085.             return self::PLATFORM_WINDOWS ' 3.1';
  2086.         else {
  2087.             return self::PLATFORM_VERSION_UNKNOWN//Invalid Windows version
  2088.         }
  2089.     }
  2090.  
  2091.     /**
  2092.      * Find the position of the first occurrence of a word in a string.
  2093.      * @access protected
  2094.      * @param string $haystack The string to search in.
  2095.      * @param string $needle The string to search for.
  2096.      * @param boolean $insensitive (optional) Determines if we do a case-sensitive search (false) or a case-insensitive
  2097.      *  one (true).
  2098.      * @param int $offset If specified, search will start this number of characters counted from the beginning of the
  2099.      *  string. If the offset is negative, the search will start this number of characters counted from the end of the
  2100.      *  string.
  2101.      * @param string $foundString String buffer that will contain the exact matching needle found. Set to NULL when
  2102.      *  return value of the function is false.
  2103.      * @return mixed Returns the position of the needle (int) if found, false otherwise. Warning this function may
  2104.      *  return Boolean false, but may also return a non-Boolean value which evaluates to false.
  2105.      */
  2106.     protected function wordPos($haystack$needle$insensitive true$offset 0&$foundString NULL)
  2107.     {
  2108.         if ($offset != 0{
  2109.             $haystack substr($haystack$offset);
  2110.         }
  2111.  
  2112.         $parts explode(' '$needle);
  2113.         foreach ($parts as $i => $currPart{
  2114.             $parts[$ipreg_quote($currPart'/');
  2115.         }
  2116.  
  2117.         $regex '/(?<=\A|[\s\/\\.,;:_()-])' implode('[\s\/\\.,;:_()-]'$parts'(?=[\s\/\\.,;:_()-]|$)/';
  2118.         if ($insensitive{
  2119.              $regex .= 'i';
  2120.         }
  2121.  
  2122.         if (preg_match($regex$haystack$matchesPREG_OFFSET_CAPTURE)) {
  2123.             $foundString $matches[0][0];
  2124.             return (int)$matches[0][1];
  2125.         }
  2126.  
  2127.         return false;
  2128.     }
  2129. }

Documentation generated on Wed, 27 Mar 2019 15:49:11 -0400 by phpDocumentor 1.4.3