: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// Merge all rules together.
* Retrieve the current set of rules.
* @deprecated since version 2.6.9
public function getRules()
if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) {
return self::getMobileDetectionRulesExtended();
return self::getMobileDetectionRules();
* Retrieve the list of mobile operating systems.
* @return array The list of mobile operating systems.
public static function getOperatingSystems()
return self::$operatingSystems;
* Check the HTTP headers for signs of mobile.
* This is the fastest mobile check possible; it's used
* inside isMobile() method.
public function checkHttpHeadersForMobile()
foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) {
if (isset($this->httpHeaders[$mobileHeader])) {
if (is_array($matchType['matches'])) {
foreach ($matchType['matches'] as $_match) {
if (strpos($this->httpHeaders[$mobileHeader], $_match) !== false) {
* Magic overloading method.
* @method boolean is[...]()
* @param array $arguments
* @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
public function __call($name, $arguments)
// make sure the name starts with 'is', otherwise
if (substr($name, 0, 2) !== 'is') {
throw new BadMethodCallException("No such method exists: $name");
$this->setDetectionType(self::DETECTION_TYPE_MOBILE);
return $this->matchUAAgainstKey($key);
* Find a detection rule that matches the current User-agent.
* @param null $userAgent deprecated
protected function matchDetectionRulesAgainstUA($userAgent = null)
foreach ($this->getRules() as $_regex) {
if ($this->match($_regex, $userAgent)) {
* Search for a certain key in the rules array.
* If the key is found then try to match the corresponding
* regex against the User-Agent.
protected function matchUAAgainstKey($key)
// Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
if (false === isset($this->cache[$key])) {
// change the keys to lower case
$_rules = array_change_key_case($this->getRules());
if (false === empty($_rules[$key])) {
$this->cache[$key] = $this->match($_rules[$key]);
if (false === isset($this->cache[$key])) {
$this->cache[$key] = false;
return $this->cache[$key];
* Check if the device is mobile.
* Returns true if any type of mobile device detected, including special ones
* @param null $userAgent deprecated
* @param null $httpHeaders deprecated
public function isMobile($userAgent = null, $httpHeaders = null)
$this->setHttpHeaders($httpHeaders);
$this->setUserAgent($userAgent);
// Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
if ($this->getUserAgent() === 'Amazon CloudFront') {
$cfHeaders = $this->getCfHeaders();
if(array_key_exists('HTTP_CLOUDFRONT_IS_MOBILE_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_MOBILE_VIEWER'] === 'true') {
$this->setDetectionType(self::DETECTION_TYPE_MOBILE);
if ($this->checkHttpHeadersForMobile()) {
return $this->matchDetectionRulesAgainstUA();
* Check if the device is a tablet.
* Return true if any type of tablet device is detected.
* @param string $userAgent deprecated
* @param array $httpHeaders deprecated
public function isTablet($userAgent = null, $httpHeaders = null)
// Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
if ($this->getUserAgent() === 'Amazon CloudFront') {
$cfHeaders = $this->getCfHeaders();
if(array_key_exists('HTTP_CLOUDFRONT_IS_TABLET_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_TABLET_VIEWER'] === 'true') {
$this->setDetectionType(self::DETECTION_TYPE_MOBILE);
foreach (self::$tabletDevices as $_regex) {
if ($this->match($_regex, $userAgent)) {
* This method checks for a certain property in the
* @todo: The httpHeaders part is not yet used.
* @param string $userAgent deprecated
* @param string $httpHeaders deprecated
public function is($key, $userAgent = null, $httpHeaders = null)
// Set the UA and HTTP headers only if needed (eg. batch mode).
$this->setHttpHeaders($httpHeaders);
$this->setUserAgent($userAgent);
$this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
return $this->matchUAAgainstKey($key);
* Some detection rules are relative (not standard),
* because of the diversity of devices, vendors and
* their conventions in representing the User-Agent or
* This method will be used to check custom regexes against
* @param string $userAgent
* @todo: search in the HTTP headers too.
public function match($regex, $userAgent = null)
$match = (bool) preg_match(sprintf('#%s#is', $regex), (false === empty($userAgent) ? $userAgent : $this->userAgent), $matches);
// If positive match is found, store the results for debug.
$this->matchingRegex = $regex;
$this->matchesArray = $matches;
* Get the properties array.
public static function getProperties()
return self::$properties;
* Prepare the version number.
* @todo Remove the error supression from str_replace() call.
* @param string $ver The string version, like "2.6.21.2152";
public function prepareVersionNo($ver)
$ver = str_replace(array('_', ' ', '/'), '.', $ver);
$arrVer = explode('.', $ver, 2);
$arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
return (float) implode('.', $arrVer);
* Check the version of the given property in the User-Agent.
* Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
* @param string $propertyName The name of the property. See self::getProperties() array
* keys for all possible properties.
* @param string $type Either self::VERSION_TYPE_STRING to get a string value or
* self::VERSION_TYPE_FLOAT indicating a float value. This parameter
* is optional and defaults to self::VERSION_TYPE_STRING. Passing an
* invalid parameter will default to the this type as well.
* @return string|float The version of the property we are trying to extract.
public function version($propertyName, $type = self::VERSION_TYPE_STRING)
if (empty($propertyName)) {
// set the $type to the default if we don't recognize the type
if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
$type = self::VERSION_TYPE_STRING;
$properties = self::getProperties();
// Check if the property exists in the properties array.
if (true === isset($properties[$propertyName])) {
// Prepare the pattern to be matched.
// Make sure we always deal with an array (string is converted).
$properties[$propertyName] = (array) $properties[$propertyName];
foreach ($properties[$propertyName] as $propertyMatchString) {
$propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
// Identify and extract the version.
preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
if (false === empty($match[1])) {
$version = ($type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
* Retrieve the mobile grading, using self::MOBILE_GRADE_* constants.
* @return string One of the self::MOBILE_GRADE_* constants.
public function mobileGrade()
$isMobile = $this->isMobile();
// Apple iOS 4-7.0 – Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3 / 5.1 / 6.1), iPad 3 (5.1 / 6.0), iPad Mini (6.1), iPad Retina (7.0), iPhone 3GS (4.3), iPhone 4 (4.3 / 5.1), iPhone 4S (5.1 / 6.0), iPhone 5 (6.0), and iPhone 5S (7.0)
$this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) >= 4.3 ||
$this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) >= 4.3 ||
$this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) >= 4.3 ||
// Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
// Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
// Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
// Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) ||
// Windows Phone 7.5-8 - Tested on the HTC Surround (7.5), HTC Trophy (7.5), LG-E900 (7.5), Nokia 800 (7.8), HTC Mazaa (7.8), Nokia Lumia 520 (8), Nokia Lumia 920 (8), HTC 8x (8)
$this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT) >= 7.5 ||
// Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10)
$this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 6.0 ||
// Blackberry Playbook (1.0-2.0) - Tested on PlayBook
$this->match('Playbook.*Tablet') ||
// Palm WebOS (1.4-3.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad (3.0)
( $this->version('webOS', self::VERSION_TYPE_FLOAT) >= 1.4 && $this->match('Palm|Pre|Pixi') ) ||
// Palm WebOS 3.0 - Tested on HP TouchPad
$this->match('hp.*TouchPad') ||
// Firefox Mobile 18 - Tested on Android 2.3 and 4.1 devices
( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 18 ) ||
// Chrome for Android - Tested on Android 4.0, 4.1 device
( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 4.0 ) ||
// Skyfire 4.1 - Tested on Android 2.3 device
( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT) >= 4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
// Opera Mobile 11.5-12: Tested on Android 2.3
( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11.5 && $this->is('AndroidOS') ) ||
// Meego 1.2 - Tested on Nokia 950 and N9
// Tizen (pre-release) - Tested on early hardware
// Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
// @todo: more tests here!
$this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT) >= 2.0 ||
// UC Browser - Tested on Android 2.3 device
( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
// Kindle 3 and Fire - Tested on the built-in WebKit browser for each
( $this->match('Kindle Fire') ||
$this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT) >= 3.0 ) ||
// Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
$this->is('AndroidOS') && $this->is('NookTablet') ||
// Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7
$this->version('Chrome', self::VERSION_TYPE_FLOAT) >= 16 && !$isMobile ||
// Safari Desktop 5-6 - Tested on OS X 10.7 and Windows 7
$this->version('Safari', self::VERSION_TYPE_FLOAT) >= 5.0 && !$isMobile ||
// Firefox Desktop 10-18 - Tested on OS X 10.7 and Windows 7
$this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 10.0 && !$isMobile ||
// Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
$this->version('IE', self::VERSION_TYPE_FLOAT) >= 7.0 && !$isMobile ||
// Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
$this->version('Opera', self::VERSION_TYPE_FLOAT) >= 10 && !$isMobile
return self::MOBILE_GRADE_A;
$this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 ||
$this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<4.3 ||
$this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT)<4.3 ||
// Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
$this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 ||
//Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
($this->version('Opera Mini', self::VERSION_TYPE_FLOAT) >= 5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT) <= 7.0 &&
($this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 || $this->is('iOS')) ) ||
// Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
$this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
// @todo: report this (tested on Nokia N71)
$this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11 && $this->is('SymbianOS')
return self::MOBILE_GRADE_B;
// Blackberry 4.x - Tested on the Curve 8330
$this->version('BlackBerry', self::VERSION_TYPE_FLOAT) <= 5.0 ||
// Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
$this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT) <= 5.2 ||
// Tested on original iPhone (3.1), iPhone 3 (3.2)
$this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) <= 3.2 ||
$this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) <= 3.2 ||
$this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) <= 3.2 ||
// Internet Explorer 7 and older - Tested on Windows XP
$this->version('IE', self::VERSION_TYPE_FLOAT) <= 7.0 && !$isMobile
return self::MOBILE_GRADE_C;
// All older smartphone platforms and featurephones - Any device that doesn't support media queries
// will receive the basic, C grade experience.
return self::MOBILE_GRADE_C;