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