* @license PHP License * @package WB * @subpackage base */ WBClass::load('WBCache'); /** * Browser detection * * @version 0.3.0 * @package WB * @subpackage base * * @todo Well, maybe browscap.ini isn't a so good idea after all. It looks like the URLs are going to change */ class WBInfo_Browser extends WBStdClass { /** * Path to cache file */ const BROWSCAP_PATH = '/var/cache/browscap/browscap.ini'; /** * Download URL */ // const BROWSCAP_URL = 'http://browsers.garykeith.com/stream.asp?BrowsCapINI'; //const BROWSCAP_URL = 'http://tempdownloads.browserscap.com/stream.asp?BrowsCapINI'; const BROWSCAP_URL = 'http://browscap.org/stream?q=PHP_BrowsCapINI' /** * Version identifier */ const VER_TYPE_FULL = 'version'; const VER_TYPE_MAJOR = 'majorver'; const VER_TYPE_MINOR = 'minorver'; /** * list of browser capabilities * @var array */ static private $browscap = array(); /** * list of regular expressions to find user agent in browscap * @var array */ static private $browsreg = array(); /** * Capabilities of detected browser * @var array */ private $cap = array(); /** * Load browscap * * Parse INI file and build regular expressions to detect browsers by user agent. * Store everything in static class variables for later reuse. * * @todo add TTL to browscap.ini * @todo check RSS feed if download is needed * @todo add caching of some sort */ private function initBrowsecap() { if (!empty(self::$browscap)) { return; } $ini = WBClass::create('WBFile'); if (!$ini->exists(self::BROWSCAP_PATH)) { $this->downloadBrowscap($ini); } self::$browscap = parse_ini_file($ini->realpath(), true, INI_SCANNER_RAW); $search = array('(', ')', '@', '+', '*', '?'); $repl = array('\\(', '\\)', '\\@', '\\+', '.*', '.'); foreach (self::$browscap as $key => $v) { $quote = preg_quote($key, '@'); $reg = '@^' . str_replace($search, $repl, $key) . '$@i'; self::$browsreg[$key] = $reg; } } /** * Download browscap from web * * Download and store file. Use special user agent to avoid HTTP 403 - error. * * @param WBFile $des */ private function downloadBrowscap($des) { $co = array( 'user_agent' => 'Browser Capabilities Project - PHP Browscap/2012 02' ); WBClass::load('WBStream'); $des->touch(self::BROWSCAP_PATH); $dfh = fopen($des->realpath(), 'w'); $sfh = WBStream::open(self::BROWSCAP_URL, 'r', false, $co); while (!feof($sfh)) { fputs($dfh, fgets($sfh)); } fclose($dfh); fclose($sfh); } /** * Identify browser using user agent * * @param string $userAgentString */ public function detect($userAgentString = null) { $this->initBrowsecap(); if (empty($userAgentString)) { $userAgentString = $_SERVER['HTTP_USER_AGENT']; } $this->userAgent = $userAgentString; // detect browser by regular expression $cap = array(); foreach (self::$browsreg as $key => $reg) { if (!preg_match($reg, $userAgentString)) { continue; } $cap = self::$browscap[$key]; break; } // merge with parents while (isset($cap['Parent']) && !empty($cap['Parent'])) { $p = $cap['Parent']; unset($cap['Parent']); $cap = array_merge(self::$browscap[$p], $cap); } // type conversion for booleans foreach ($cap as &$v) { if ('false' === $v) { $v = false; } else if ('true' === $v) { $v = true; } } // type conversion for majorver and minorver $cap['MajorVer'] = intval($cap['MajorVer']); $cap['MinorVer'] = intval($cap['MinorVer']); $this->cap = array_change_key_case($cap, CASE_LOWER); } /** * Get user agent string */ public function getUserAgent() { return $this->userAgent; } /** * Get platform * * Get names platform like Linux, Windows, WinXP, Win7, ... * @return string */ public function getPlatform() { $plat = $this->getAny('platform'); if ('unknown' != $plat) { return $plat; } if ($this->cap['win16'] || $this->cap['win32'] || $this->cap['win64']) { return 'Windows'; } return $plat; } /** * Get browser name * * Names like IE, Firefox, Opera * @return string */ public function getName() { return $this->getAny('browser'); } /** * Get browser version * * Return either complet version string or major/minor version * * @param string $type * @return string|int */ public function getVersion($type = self::VER_TYPE_FULL) { return $this->getAny($type); } /** * Tell whether browser is marked as spider * * Browser is known as bot/spider/crawler etc. * @return bool */ public function isCrawler() { return $this->cap['crawler']; } /** * Get any data from cap * * If value exists, return string, null otherwise * * @param string $key * @return string|null */ private function getAny($key) { if (isset($this->cap[$key])) { return $this->cap[$key]; } return null; } }