PageRenderTime 29ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/agent.php

https://bitbucket.org/sriedel/iccrm-wip
PHP | 529 lines | 304 code | 74 blank | 151 comment | 27 complexity | 8eb5fa9a630b75ff63496705b1aab154 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.0
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2012 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. /**
  14. * Identifies the platform, browser, robot, or mobile device from the user agent string
  15. *
  16. * This class uses PHP's get_browser() to get details from the browsers user agent
  17. * string. If not available, it can use a coded alternative using the php_browscap.ini
  18. * file from http://browsers.garykeith.com.
  19. *
  20. * @package Fuel
  21. * @subpackage Core
  22. * @category Core
  23. * @author Harro Verton
  24. */
  25. class Agent
  26. {
  27. /**
  28. * @var array information about the current browser
  29. */
  30. protected static $properties = array(
  31. 'browser' => 'unknown',
  32. 'version' => 0,
  33. 'majorver' => 0,
  34. 'minorver' => 0,
  35. 'platform' => 'unknown',
  36. 'alpha' => false,
  37. 'beta' => false,
  38. 'win16' => false,
  39. 'win32' => false,
  40. 'win64' => false,
  41. 'frames' => false,
  42. 'iframes' => false,
  43. 'tables' => false,
  44. 'cookies' => false,
  45. 'backgroundsounds' => false,
  46. 'javascript' => false,
  47. 'vbscript' => false,
  48. 'javaapplets' => false,
  49. 'activexcontrols' => false,
  50. 'isbanned' => false,
  51. 'ismobiledevice' => false,
  52. 'issyndicationreader' => false,
  53. 'crawler' => false,
  54. 'cssversion' => 0,
  55. 'aolversion' => 0,
  56. );
  57. /**
  58. * @var array property to cache key mapping
  59. */
  60. protected static $keys = array(
  61. 'browser' => 'A',
  62. 'version' => 'B',
  63. 'majorver' => 'C',
  64. 'minorver' => 'D',
  65. 'platform' => 'E',
  66. 'alpha' => 'F',
  67. 'beta' => 'G',
  68. 'win16' => 'H',
  69. 'win32' => 'I',
  70. 'win64' => 'J',
  71. 'frames' => 'K',
  72. 'iframes' => 'L',
  73. 'tables' => 'M',
  74. 'cookies' => 'N',
  75. 'backgroundsounds' => 'O',
  76. 'javascript' => 'P',
  77. 'vbscript' => 'Q',
  78. 'javaapplets' => 'R',
  79. 'activexcontrols' => 'S',
  80. 'isbanned' => 'T',
  81. 'ismobiledevice' => 'U',
  82. 'issyndicationreader' => 'V',
  83. 'crawler' => 'W',
  84. 'cssversion' => 'X',
  85. 'aolversion' => 'Y',
  86. );
  87. /**
  88. * @var array global config defaults
  89. */
  90. protected static $defaults = array(
  91. 'browscap' => array(
  92. 'enabled' => true,
  93. 'url' => 'http://browsers.garykeith.com/stream.asp?Lite_PHP_BrowsCapINI',
  94. 'method' => 'wrapper',
  95. 'file' => '',
  96. ),
  97. 'cache' => array(
  98. 'driver' => '',
  99. 'expiry' => 604800,
  100. 'identifier' => 'fuel.agent',
  101. ),
  102. );
  103. /**
  104. * @var array global config items
  105. */
  106. protected static $config = array(
  107. );
  108. /**
  109. * @var string detected user agent string
  110. */
  111. protected static $user_agent = '';
  112. // --------------------------------------------------------------------
  113. // public static methods
  114. // --------------------------------------------------------------------
  115. /**
  116. * map the user agent string to browser specifications
  117. *
  118. * @return void
  119. */
  120. public static function _init()
  121. {
  122. // fetch and store the user agent
  123. static::$user_agent = \Input::server('http_user_agent', '');
  124. // fetch and process the configuration
  125. \Config::load('agent', true);
  126. static::$config = array_merge(static::$defaults, \Config::get('agent', array()));
  127. // validate the browscap configuration
  128. if ( ! is_array(static::$config['browscap']))
  129. {
  130. static::$config['browscap'] = static::$defaults['browscap'];
  131. }
  132. else
  133. {
  134. if ( ! array_key_exists('enabled', static::$config['browscap']) or ! is_bool(static::$config['browscap']['enabled']))
  135. {
  136. static::$config['browscap']['enabled'] = true;
  137. }
  138. if ( ! array_key_exists('url', static::$config['browscap']) or ! is_string(static::$config['browscap']['url']))
  139. {
  140. static::$config['browscap']['url'] = static::$defaults['browscap']['url'];
  141. }
  142. if ( ! array_key_exists('file', static::$config['browscap']) or ! is_string(static::$config['browscap']['file']))
  143. {
  144. static::$config['browscap']['file'] = static::$defaults['browscap']['file'];
  145. }
  146. if ( ! array_key_exists('method', static::$config['browscap']) or ! is_string(static::$config['browscap']['method']))
  147. {
  148. static::$config['browscap']['method'] = static::$defaults['browscap']['method'];
  149. }
  150. static::$config['browscap']['method'] = strtolower(static::$config['browscap']['method']);
  151. }
  152. // validate the cache configuration
  153. if ( ! is_array(static::$config['cache']))
  154. {
  155. static::$config['cache'] = static::$defaults['cache'];
  156. }
  157. else
  158. {
  159. if ( ! array_key_exists('driver', static::$config['cache']) or ! is_string(static::$config['cache']['driver']))
  160. {
  161. static::$config['cache']['driver'] = static::$defaults['cache']['driver'];
  162. }
  163. if ( ! array_key_exists('expiry', static::$config['cache']) or ! is_numeric(static::$config['cache']['expiry']) or static::$config['cache']['expiry'] < 7200)
  164. {
  165. static::$config['cache']['expiry'] = static::$defaults['cache']['expiry'];
  166. }
  167. if ( ! array_key_exists('identifier', static::$config['cache']) or ! is_string(static::$config['cache']['identifier']))
  168. {
  169. static::$config['cache']['identifier'] = static::$defaults['cache']['identifier'];
  170. }
  171. }
  172. // try the build in get_browser() method
  173. if (ini_get('browscap') == '' or false === $browser = get_browser(null, true))
  174. {
  175. // if it fails, emulate get_browser()
  176. $browser = static::get_from_browscap();
  177. }
  178. if ($browser)
  179. {
  180. // save it for future reference
  181. static::$properties = array_change_key_case($browser);
  182. }
  183. }
  184. // --------------------------------------------------------------------
  185. /**
  186. * get the normalized browser name
  187. *
  188. * @return string
  189. */
  190. public static function browser()
  191. {
  192. return static::$properties['browser'];
  193. }
  194. // --------------------------------------------------------------------
  195. /**
  196. * Get the browser platform
  197. *
  198. * @return string
  199. */
  200. public static function platform()
  201. {
  202. return static::$properties['platform'];
  203. }
  204. // --------------------------------------------------------------------
  205. /**
  206. * Get the Browser Version
  207. *
  208. * @return string
  209. */
  210. public static function version()
  211. {
  212. return static::$properties['version'];
  213. }
  214. // --------------------------------------------------------------------
  215. /**
  216. * Get any browser property
  217. *
  218. * @return string
  219. */
  220. public static function property($property = null)
  221. {
  222. $property = strtolower($property);
  223. return array_key_exists($property, static::$properties) ? static::$properties[$property] : null;
  224. }
  225. // --------------------------------------------------------------------
  226. /**
  227. * Get all browser properties
  228. *
  229. * @return array
  230. */
  231. public static function properties()
  232. {
  233. return static::$properties;
  234. }
  235. // --------------------------------------------------------------------
  236. /**
  237. * check if the current browser is a robot or crawler
  238. *
  239. * @return bool
  240. */
  241. public static function is_robot()
  242. {
  243. return static::$properties['crawler'];
  244. }
  245. // --------------------------------------------------------------------
  246. /**
  247. * check if the current browser is mobile device
  248. *
  249. * @return bool
  250. */
  251. public static function is_mobiledevice()
  252. {
  253. return static::$properties['ismobiledevice'];
  254. }
  255. // --------------------------------------------------------------------
  256. /**
  257. * check if the current browser accepts a specific language
  258. *
  259. * @param string $language optional ISO language code, defaults to 'en'
  260. * @return bool
  261. */
  262. public static function accepts_language($language = 'en')
  263. {
  264. return (in_array(strtolower($language), static::languages(), true)) ? true : false;
  265. }
  266. // --------------------------------------------------------------------
  267. /**
  268. * check if the current browser accepts a specific character set
  269. *
  270. * @param string $charset optional character set, defaults to 'utf-8'
  271. * @return bool
  272. */
  273. public static function accepts_charset($charset = 'utf-8')
  274. {
  275. return (in_array(strtolower($charset), static::charsets(), true)) ? true : false;
  276. }
  277. // --------------------------------------------------------------------
  278. /**
  279. * get the list of browser accepted languages
  280. *
  281. * @return array
  282. */
  283. public static function languages()
  284. {
  285. return explode(',', preg_replace('/(;q=[0-9\.]+)/i', '', strtolower(trim(\Input::server('http_accept_language')))));
  286. }
  287. // --------------------------------------------------------------------
  288. /**
  289. * get the list of browser accepted charactersets
  290. *
  291. * @return array
  292. */
  293. public static function charsets()
  294. {
  295. return explode(',', preg_replace('/(;q=.+)/i', '', strtolower(trim(\Input::server('http_accept_charset')))));
  296. }
  297. // --------------------------------------------------------------------
  298. // internal static methods
  299. // --------------------------------------------------------------------
  300. /**
  301. * use the parsed php_browscap.ini file to find a user agent match
  302. *
  303. * @return mixed array if a match is found, of false if not cached yet
  304. */
  305. protected static function get_from_browscap()
  306. {
  307. $cache = \Cache::forge(static::$config['cache']['identifier'].'.browscap', static::$config['cache']['driver']);
  308. // load the cached browscap data
  309. try
  310. {
  311. $browscap = $cache->get();
  312. }
  313. // browscap not cached
  314. catch (\Exception $e)
  315. {
  316. $browscap = static::$config['browscap']['enabled'] ? static::parse_browscap() : array();
  317. }
  318. $search = array('\*', '\?');
  319. $replace = array('.*', '.');
  320. $result = false;
  321. // find a match for the user agent string
  322. foreach($browscap as $browser => $properties)
  323. {
  324. $pattern = '@^'.str_replace($search, $replace, preg_quote($browser, '@')).'$@i';
  325. if (preg_match($pattern, static::$user_agent))
  326. {
  327. // store the browser name
  328. $properties['browser'] = $browser;
  329. // fetch possible parent info
  330. if (array_key_exists('Parent', $properties))
  331. {
  332. if ($properties['Parent'] > 0)
  333. {
  334. $parent = array_slice($browscap, $properties['Parent'], 1);
  335. unset($properties['Parent']);
  336. $properties = array_merge(current($parent), $properties);
  337. // store the browser name
  338. $properties['browser'] = key($parent);
  339. }
  340. }
  341. // normalize keys
  342. $properties = \Arr::replace_key($properties, array_flip(static::$keys));
  343. // merge it with the defaults to add missing values
  344. $result = array_merge(static::$properties, $properties);
  345. break;
  346. }
  347. }
  348. return $result;
  349. }
  350. // --------------------------------------------------------------------
  351. /**
  352. * download and parse the browscap file
  353. *
  354. * @return array array with parsed download info, or empty if the download is disabled of failed
  355. */
  356. protected static function parse_browscap()
  357. {
  358. // get the browscap.ini file
  359. switch (static::$config['browscap']['method'])
  360. {
  361. case 'local':
  362. if ( ! file_exists(static::$config['browscap']['file']) or filesize(static::$config['browscap']['file']) == 0)
  363. {
  364. throw new \Exception('Agent class: could not open the local browscap.ini file.');
  365. }
  366. $data = @file_get_contents(static::$config['browscap']['file']);
  367. break;
  368. // socket connections are not implemented yet!
  369. case 'sockets':
  370. $data = false;
  371. break;
  372. case 'curl':
  373. $curl = curl_init();
  374. curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1);
  375. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  376. curl_setopt($curl, CURLOPT_MAXREDIRS, 5);
  377. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  378. curl_setopt($curl, CURLOPT_HEADER, 0);
  379. curl_setopt($curl, CURLOPT_USERAGENT, 'Fuel PHP framework - Agent class (http://fuelphp.com)');
  380. curl_setopt($curl, CURLOPT_URL, static::$config['browscap']['url']);
  381. $data = curl_exec($curl);
  382. curl_close($curl);
  383. break;
  384. case 'wrapper':
  385. ini_set('user_agent', 'Fuel PHP framework - Agent class (http://fuelphp.com)');
  386. $data = file_get_contents(static::$config['browscap']['url']);
  387. default:
  388. break;
  389. }
  390. if ($data === false)
  391. {
  392. logger(\Fuel::L_ERROR, 'Failed to download browscap.ini file.', 'Agent::parse_browscap');
  393. }
  394. // parse the downloaded data
  395. $browsers = @parse_ini_string($data, true, INI_SCANNER_RAW) or $browsers = array();
  396. // remove the version and timestamp entry
  397. array_shift($browsers);
  398. $result = array();
  399. // reverse sort on key string length
  400. uksort($browsers, function($a, $b) { return strlen($a) < strlen($b) ? 1 : -1; } );
  401. $index = array();
  402. $count = 0;
  403. // reduce the array keys
  404. foreach($browsers as $browser => $properties)
  405. {
  406. $index[$browser] = $count++;
  407. // fix any type issues
  408. foreach ($properties as $var => $value)
  409. {
  410. if (is_numeric($value))
  411. {
  412. $properties[$var] = $value + 0;
  413. }
  414. elseif ($value == 'true')
  415. {
  416. $properties[$var] = true;
  417. }
  418. elseif ($value == 'false')
  419. {
  420. $properties[$var] = false;
  421. }
  422. }
  423. $result[$browser] = \Arr::replace_key($properties, static::$keys);
  424. }
  425. // reduce parent links to
  426. foreach($result as $browser => &$properties)
  427. {
  428. if (array_key_exists('Parent', $properties))
  429. {
  430. if ($properties['Parent'] == 'DefaultProperties')
  431. {
  432. unset($properties['Parent']);
  433. }
  434. else
  435. {
  436. if (array_key_exists($properties['Parent'], $index))
  437. {
  438. $properties['Parent'] = $index[$properties['Parent']];
  439. }
  440. else
  441. {
  442. unset($properties['Parent']);
  443. }
  444. }
  445. }
  446. }
  447. // save the result to the cache
  448. if ( ! empty($result))
  449. {
  450. $cache = \Cache::forge(static::$config['cache']['identifier'].'.browscap', static::$config['cache']['driver']);
  451. $cache->set($result, static::$config['cache']['expiry']);
  452. }
  453. return $result;
  454. }
  455. }