Add UA class for mobile device, tablet, and robot detection

This commit is contained in:
Kijin Sung 2016-03-19 23:28:27 +09:00
parent 5acc339184
commit f1441613a2
2 changed files with 344 additions and 0 deletions

240
common/framework/ua.php Normal file
View file

@ -0,0 +1,240 @@
<?php
namespace Rhymix\Framework;
/**
* The user-agent class.
*/
class UA
{
/**
* Cache to prevent multiple lookups.
*/
protected static $_mobile_cache = array();
protected static $_tablet_cache = array();
protected static $_robot_cache = array();
/**
* Check whether the current visitor is using a mobile device.
*
* @param string $ua (optional)
* @return bool
*/
public static function isMobile($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
if ($ua === null)
{
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
$using_header = true;
}
else
{
$using_header = false;
}
// If the User-Agent header is missing, it's probably not a mobile browser.
if (is_null($ua))
{
return false;
}
// Look for headers that are only used in mobile browsers.
if ($using_header && (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA'])))
{
return true;
}
// Look up the cache.
if (isset(self::$_mobile_cache[$ua]))
{
return self::$_mobile_cache[$ua];
}
// Look for the 'mobile' keyword and common mobile platform names.
if (preg_match('/android|ip(hone|ad|od)|blackberry|nokia|palm|mobile/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
// Look for common non-mobile OS names.
if (preg_match('/windows|linux|os [x9]|bsd/i', $ua))
{
return self::$_mobile_cache[$ua] = false;
}
// Look for other platform, manufacturer, and device names that are known to be mobile.
if (preg_match('/kindle|opera (mini|mobi)|polaris|netfront|fennec|motorola|symbianos|webos/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
if (preg_match('/s[pgc]h-|lgtelecom|sonyericsson|alcatel|vodafone|maemo|minimo|bada/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
// If we're here, it's probably not a mobile device.
return self::$_mobile_cache[$ua] = false;
}
/**
* Check whether the current visitor is using a tablet.
*
* @param string $ua (optional)
* @return bool
*/
public static function isTablet($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// If the User-Agent header is missing, it's probably not a tablet.
if (is_null($ua))
{
return false;
}
// Look up the cache.
if (isset(self::$_tablet_cache[$ua]))
{
return self::$_tablet_cache[$ua];
}
// Check if the user-agent is mobile.
if (!self::isMobile($ua))
{
return self::$_tablet_cache[$ua] = false;
}
// Check for Android tablets without the 'mobile' keyword.
if (stripos($ua, 'android') !== false && stripos($ua, 'mobile') === false)
{
return self::$_tablet_cache[$ua] = true;
}
// Check for common tablet identifiers.
if (preg_match('/tablet|pad\b|tab\b|\bgt-\d+|kindle|nook|playbook|webos|xoom/i', $ua))
{
return self::$_tablet_cache[$ua] = true;
}
// If we're here, it's probably not a tablet.
return self::$_tablet_cache[$ua] = false;
}
/**
* Check whether the current visitor is a robot.
*
* @param string $ua (optional)
* @return bool
*/
public static function isRobot($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// If the User-Agent header is missing, it's probably not a robot.
if (is_null($ua))
{
return false;
}
// Look up the cache.
if (isset(self::$_robot_cache[$ua]))
{
return self::$_robot_cache[$ua];
}
// Look for common search engine names and the 'bot' keyword.
if (preg_match('/bot|slurp|facebook(externalhit|scraper)|ia_archiver|ask jeeves|teoma|baidu|daumoa|lycos|pingdom/i', $ua))
{
return self::$_robot_cache[$ua] = true;
}
// If we're here, it's probably not a robot.
return self::$_robot_cache[$ua] = false;
}
/**
* This method parses the User-Agent string to guess what kind of browser it is.
*
* @param string $ua (optional)
* @return object
*/
public static function getBrowserInfo($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// Initialize the result.
$result = (object)array(
'browser' => null,
'version' => null,
'os' => null,
'is_mobile' => null,
'is_tablet' => null,
);
if (is_null($ua))
{
return $result;
}
// Try to guess the OS.
if (preg_match('#(Windows|Android|Linux|iOS|OS X|Macintosh)#i', $ua, $matches))
{
if ($matches[1] === 'Linux' && strpos($ua, 'Android') !== false)
{
$matches[1] = 'Android';
}
if ($matches[1] === 'Macintosh' && strpos($ua, 'OS X') !== false)
{
$matches[1] = 'OS X';
}
$result->os = $matches[1];
}
// Fill in miscellaneous fields.
$result->is_mobile = self::isMobile($ua);
$result->is_tablet = self::isTablet($ua);
// Try to match some of the most common browsers.
if (preg_match('#Android ([0-9]+\\.[0-9]+)#', $ua, $matches) && strpos($ua, 'Chrome') === false)
{
$result->browser = 'Android';
$result->version = $matches[1];
return $result;
}
if (preg_match('#Edge/([0-9]+\\.)#', $ua, $matches))
{
$result->browser = 'Edge';
$result->version = $matches[1] . '0';
return $result;
}
if (preg_match('#Trident/([0-9]+)\\.[0-9]+#', $ua, $matches))
{
$result->browser = 'IE';
$result->version = ($matches[1] + 4) . '.0';
return $result;
}
if (preg_match('#(MSIE|Chrome|Firefox|Safari)[ /:]([0-9]+\\.[0-9]+)#', $ua, $matches))
{
$result->browser = $matches[1] === 'MSIE' ? 'IE' : $matches[1];
$result->version = $matches[2];
return $result;
}
if (preg_match('#^Opera/.+(?:Opera |Version/)([0-9]+\\.[0-9]+)$#', $ua, $matches))
{
$result->browser = 'Opera';
$result->version = $matches[1];
return $result;
}
if (preg_match('#(?:Konqueror|KHTML)/([0-9]+\\.[0-9]+)$#', $ua, $matches))
{
$result->browser = 'Konqueror';
$result->version = $matches[1];
return $result;
}
return $result;
}
}

View file

@ -0,0 +1,104 @@
<?php
class UATest extends \Codeception\TestCase\Test
{
public function testIsMobile()
{
// Phones
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36'));
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
// Tablets
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Linux; Android 4.4.2; SM-T530 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36'));
// Not mobile
$this->assertFalse(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'));
$this->assertFalse(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'));
$this->assertFalse(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0'));
$this->assertFalse(Rhymix\Framework\UA::isMobile('Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16'));
}
public function testIsTablet()
{
// Phones
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36'));
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
// Tablets
$this->assertTrue(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertTrue(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (Linux; Android 4.4.2; SM-T530 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36'));
// Not mobile
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'));
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)'));
}
public function testIsRobot()
{
// Robot
$this->assertTrue(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)'));
$this->assertTrue(Rhymix\Framework\UA::isRobot('Googlebot/2.1 (+http://www.googlebot.com/bot.html)'));
$this->assertTrue(Rhymix\Framework\UA::isRobot('Yeti/1.0 (NHN Corp.; http://help.naver.com/robots/)'));
// Not robot
$this->assertFalse(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertFalse(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertFalse(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'));
}
public function testGetBrowserInfo()
{
// Android default browser
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; U; Android 4.0.3; Device Name) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30');
$this->assertEquals('Android', $browser->browser);
$this->assertEquals('4.0', $browser->version);
$this->assertEquals('Android', $browser->os);
$this->assertTrue($browser->is_mobile);
$this->assertTrue($browser->is_tablet);
// Mobile Chrome
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36');
$this->assertEquals('Chrome', $browser->browser);
$this->assertEquals('38.0', $browser->version);
$this->assertEquals('Android', $browser->os);
$this->assertTrue($browser->is_mobile);
$this->assertFalse($browser->is_tablet);
// Edge
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136');
$this->assertEquals('Edge', $browser->browser);
$this->assertEquals('12.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
$this->assertFalse($browser->is_mobile);
$this->assertFalse($browser->is_tablet);
// IE 11
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('11.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
// IE 10 in compatibility mode
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/4.0 (Compatible; MSIE 8.0; Windows NT 5.2; Trident/6.0)');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('10.0', $browser->version);
// IE 8 in compatibility mode
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('8.0', $browser->version);
// Linux Chrome
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36');
$this->assertEquals('Chrome', $browser->browser);
$this->assertEquals('41.0', $browser->version);
$this->assertEquals('Linux', $browser->os);
// OS X Firefox
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0');
$this->assertEquals('Firefox', $browser->browser);
$this->assertEquals('33.0', $browser->version);
$this->assertEquals('OS X', $browser->os);
}
}