AbstractDevice.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Http
  17. * @subpackage UserAgent
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. require_once 'Zend/Http/UserAgent/Device.php';
  22. /**
  23. * Abstract Class to define a browser device.
  24. *
  25. * @category Zend
  26. * @package Zend_Http
  27. * @subpackage UserAgent
  28. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  29. * @license http://framework.zend.com/license/new-bsd New BSD License
  30. */
  31. abstract class Zend_Http_UserAgent_AbstractDevice
  32. implements Zend_Http_UserAgent_Device
  33. {
  34. /**
  35. * Browser signature
  36. *
  37. * @var string
  38. */
  39. protected $_browser = '';
  40. /**
  41. * Browser version
  42. *
  43. * @var string
  44. */
  45. protected $_browserVersion = '';
  46. /**
  47. * Configuration
  48. *
  49. * @var array
  50. */
  51. protected $_config;
  52. /**
  53. * User Agent chain
  54. *
  55. * @var string
  56. */
  57. protected $_userAgent;
  58. /**
  59. * Server variable
  60. *
  61. * @var array
  62. */
  63. protected $_server;
  64. /**
  65. * Image types
  66. *
  67. * @var array
  68. */
  69. protected $_images = array(
  70. 'jpeg',
  71. 'gif',
  72. 'png',
  73. 'pjpeg',
  74. 'x-png',
  75. 'bmp',
  76. );
  77. /**
  78. * Browser/Device features
  79. *
  80. * @var array
  81. */
  82. protected $_aFeatures = array();
  83. /**
  84. * Constructor
  85. *
  86. * @param null|string|array $userAgent If array, restores from serialized version
  87. * @param array $server
  88. * @param array $config
  89. * @return void
  90. */
  91. public function __construct($userAgent = null, array $server = array(), array $config = array())
  92. {
  93. if (is_array($userAgent)) {
  94. // Restoring from serialized array
  95. $this->_restoreFromArray($userAgent);
  96. } else {
  97. // Constructing new object
  98. $this->setUserAgent($userAgent);
  99. $this->_server = $server;
  100. $this->_config = $config;
  101. $this->_getDefaultFeatures();
  102. $this->_defineFeatures();
  103. }
  104. }
  105. /**
  106. * Serialize object
  107. *
  108. * @return string
  109. */
  110. public function serialize()
  111. {
  112. $spec = array(
  113. '_aFeatures' => $this->_aFeatures,
  114. '_browser' => $this->_browser,
  115. '_browserVersion' => $this->_browserVersion,
  116. '_userAgent' => $this->_userAgent,
  117. '_images' => $this->_images,
  118. );
  119. return serialize($spec);
  120. }
  121. /**
  122. * Unserialize
  123. *
  124. * @param string $serialized
  125. * @return void
  126. */
  127. public function unserialize($serialized)
  128. {
  129. $spec = unserialize($serialized);
  130. $this->_restoreFromArray($spec);
  131. }
  132. /**
  133. * Restore object state from array
  134. *
  135. * @param array $spec
  136. * @return void
  137. */
  138. protected function _restoreFromArray(array $spec)
  139. {
  140. foreach ($spec as $key => $value) {
  141. if (property_exists($this, $key)) {
  142. $this->{$key} = $value;
  143. }
  144. }
  145. }
  146. /**
  147. * Look for features
  148. *
  149. * @return array|null
  150. */
  151. protected function _defineFeatures()
  152. {
  153. $features = $this->_loadFeaturesAdapter();
  154. if (is_array($features)) {
  155. $this->_aFeatures = array_merge($this->_aFeatures, $features);
  156. }
  157. return $this->_aFeatures;
  158. }
  159. /**
  160. * Gets the browser type identifier
  161. *
  162. * @return string
  163. */
  164. abstract public function getType();
  165. /**
  166. * Check a feature for the current browser/device.
  167. *
  168. * @param string $feature The feature to check.
  169. * @return bool
  170. */
  171. public function hasFeature($feature)
  172. {
  173. return (!empty($this->_aFeatures[$feature]));
  174. }
  175. /**
  176. * Gets the value of the current browser/device feature
  177. *
  178. * @param string $feature Feature to search
  179. * @return string|null
  180. */
  181. public function getFeature($feature)
  182. {
  183. if ($this->hasFeature($feature)) {
  184. return $this->_aFeatures[$feature];
  185. }
  186. }
  187. /**
  188. * Set a feature for the current browser/device.
  189. *
  190. * @param string $feature The feature to set.
  191. * @param string $value (option) feature value.
  192. * @param string $group (option) Group to associate with the feature
  193. * @return Zend_Http_UserAgent_AbstractDevice
  194. */
  195. public function setFeature($feature, $value = false, $group = '')
  196. {
  197. $this->_aFeatures[$feature] = $value;
  198. if (!empty($group)) {
  199. $this->setGroup($group, $feature);
  200. }
  201. return $this;
  202. }
  203. /**
  204. * Affects a feature to a group
  205. *
  206. * @param string $group Group name
  207. * @param string $feature Feature name
  208. * @return Zend_Http_UserAgent_AbstractDevice
  209. */
  210. public function setGroup($group, $feature)
  211. {
  212. if (!isset($this->_aGroup[$group])) {
  213. $this->_aGroup[$group] = array();
  214. }
  215. if (!in_array($feature, $this->_aGroup[$group])) {
  216. $this->_aGroup[$group][] = $feature;
  217. }
  218. return $this;
  219. }
  220. /**
  221. * Gets an array of features associated to a group
  222. *
  223. * @param string $group Group param
  224. * @return array
  225. */
  226. public function getGroup($group)
  227. {
  228. return $this->_aGroup[$group];
  229. }
  230. /**
  231. * Gets all the browser/device features
  232. *
  233. * @return array
  234. */
  235. public function getAllFeatures()
  236. {
  237. return $this->_aFeatures;
  238. }
  239. /**
  240. * Gets all the browser/device features' groups
  241. *
  242. * @return array
  243. */
  244. public function getAllGroups()
  245. {
  246. return $this->_aGroup;
  247. }
  248. /**
  249. * Sets all the standard features extracted from the User Agent chain and $this->_server
  250. * vars
  251. *
  252. * @return void
  253. */
  254. protected function _getDefaultFeatures()
  255. {
  256. $server = array();
  257. // gets info from user agent chain
  258. $uaExtract = $this->extractFromUserAgent($this->getUserAgent());
  259. if (is_array($uaExtract)) {
  260. foreach ($uaExtract as $key => $info) {
  261. $this->setFeature($key, $info, 'product_info');
  262. }
  263. }
  264. if (isset($uaExtract['browser_name'])) {
  265. $this->_browser = $uaExtract['browser_name'];
  266. }
  267. if (isset($uaExtract['browser_version'])) {
  268. $this->_browserVersion = $uaExtract['browser_version'];
  269. }
  270. if (isset($uaExtract['device_os'])) {
  271. $this->device_os = $uaExtract['device_os_name'];
  272. }
  273. /* browser & device info */
  274. $this->setFeature('is_wireless_device', false, 'product_info');
  275. $this->setFeature('is_mobile', false, 'product_info');
  276. $this->setFeature('is_desktop', false, 'product_info');
  277. $this->setFeature('is_tablet', false, 'product_info');
  278. $this->setFeature('is_bot', false, 'product_info');
  279. $this->setFeature('is_email', false, 'product_info');
  280. $this->setFeature('is_text', false, 'product_info');
  281. $this->setFeature('device_claims_web_support', false, 'product_info');
  282. $this->setFeature('is_' . strtolower($this->getType()), true, 'product_info');
  283. /* sets the browser name */
  284. if (isset($this->list) && empty($this->_browser)) {
  285. $lowerUserAgent = strtolower($this->getUserAgent());
  286. foreach ($this->list as $browser_signature) {
  287. if (strpos($lowerUserAgent, $browser_signature) !== false) {
  288. $this->_browser = strtolower($browser_signature);
  289. $this->setFeature('browser_name', $this->_browser, 'product_info');
  290. }
  291. }
  292. }
  293. /* sets the client IP */
  294. if (isset($this->_server['remote_addr'])) {
  295. $this->setFeature('client_ip', $this->_server['remote_addr'], 'product_info');
  296. } elseif (isset($this->_server['http_x_forwarded_for'])) {
  297. $this->setFeature('client_ip', $this->_server['http_x_forwarded_for'], 'product_info');
  298. } elseif (isset($this->_server['http_client_ip'])) {
  299. $this->setFeature('client_ip', $this->_server['http_client_ip'], 'product_info');
  300. }
  301. /* sets the server infos */
  302. if (isset($this->_server['server_software'])) {
  303. if (strpos($this->_server['server_software'], 'Apache') !== false || strpos($this->_server['server_software'], 'LiteSpeed') !== false) {
  304. $server['version'] = 1;
  305. if (strpos($this->_server['server_software'], 'Apache/2') !== false) {
  306. $server['version'] = 2;
  307. }
  308. $server['server'] = 'apache';
  309. }
  310. if (strpos($this->_server['server_software'], 'Microsoft-IIS') !== false) {
  311. $server['server'] = 'iis';
  312. }
  313. if (strpos($this->_server['server_software'], 'Unix') !== false) {
  314. $server['os'] = 'unix';
  315. if (isset($_ENV['MACHTYPE'])) {
  316. if (strpos($_ENV['MACHTYPE'], 'linux') !== false) {
  317. $server['os'] = 'linux';
  318. }
  319. }
  320. } elseif (strpos($this->_server['server_software'], 'Win') !== false) {
  321. $server['os'] = 'windows';
  322. }
  323. if (preg_match('/Apache\/([0-9\.]*)/', $this->_server['server_software'], $arr)) {
  324. if ($arr[1]) {
  325. $server['version'] = $arr[1];
  326. $server['server'] = 'apache';
  327. }
  328. }
  329. }
  330. $this->setFeature('php_version', phpversion(), 'server_info');
  331. if (isset($server['server'])) {
  332. $this->setFeature('server_os', $server['server'], 'server_info');
  333. }
  334. if (isset($server['version'])) {
  335. $this->setFeature('server_os_version', $server['version'], 'server_info');
  336. }
  337. if (isset($this->_server['http_accept'])) {
  338. $this->setFeature('server_http_accept', $this->_server['http_accept'], 'server_info');
  339. }
  340. if (isset($this->_server['http_accept_language'])) {
  341. $this->setFeature('server_http_accept_language', $this->_server['http_accept_language'], 'server_info');
  342. }
  343. if (isset($this->_server['server_addr'])) {
  344. $this->setFeature('server_ip', $this->_server['server_addr'], 'server_info');
  345. }
  346. if (isset($this->_server['server_name'])) {
  347. $this->setFeature('server_name', $this->_server['server_name'], 'server_info');
  348. }
  349. }
  350. /**
  351. * Extract and sets informations from the User Agent chain
  352. *
  353. * @param string $userAgent User Agent chain
  354. * @return array
  355. */
  356. public static function extractFromUserAgent($userAgent)
  357. {
  358. $userAgent = trim($userAgent);
  359. /**
  360. * @see http://www.texsoft.it/index.php?c=software&m=sw.php.useragent&l=it
  361. */
  362. $pattern = "(([^/\s]*)(/(\S*))?)(\s*\[[a-zA-Z][a-zA-Z]\])?\s*(\\((([^()]|(\\([^()]*\\)))*)\\))?\s*";
  363. preg_match("#^$pattern#", $userAgent, $match);
  364. $comment = array();
  365. if (isset($match[7])) {
  366. $comment = explode(';', $match[7]);
  367. }
  368. // second part if exists
  369. $end = substr($userAgent, strlen($match[0]));
  370. if (!empty($end)) {
  371. $result['others']['full'] = $end;
  372. }
  373. $match2 = array();
  374. if (isset($result['others'])) {
  375. preg_match_all('/(([^\/\s]*)(\/)?([^\/\(\)\s]*)?)(\s\((([^\)]*)*)\))?/i', $result['others']['full'], $match2);
  376. }
  377. $result['user_agent'] = trim($match[1]);
  378. $result['product_name'] = isset($match[2]) ? trim($match[2]) : '';
  379. $result['browser_name'] = $result['product_name'];
  380. if (isset($match[4]) && trim($match[4])) {
  381. $result['product_version'] = trim($match[4]);
  382. $result['browser_version'] = trim($match[4]);
  383. }
  384. if (count($comment) && !empty($comment[0])) {
  385. $result['comment']['full'] = trim($match[7]);
  386. $result['comment']['detail'] = $comment;
  387. $result['compatibility_flag'] = trim($comment[0]);
  388. if (isset($comment[1])) {
  389. $result['browser_token'] = trim($comment[1]);
  390. }
  391. if (isset($comment[2])) {
  392. $result['device_os_token'] = trim($comment[2]);
  393. }
  394. }
  395. if (empty($result['device_os_token']) && !empty($result['compatibility_flag'])) {
  396. // some browsers do not have a platform token
  397. $result['device_os_token'] = $result['compatibility_flag'];
  398. }
  399. if ($match2) {
  400. $i = 0;
  401. $max = count($match2[0]);
  402. for ($i = 0; $i < $max; $i ++) {
  403. if (!empty($match2[0][$i])) {
  404. $result['others']['detail'][] = array(
  405. $match2[0][$i],
  406. $match2[2][$i],
  407. $match2[4][$i],
  408. );
  409. }
  410. }
  411. }
  412. /** Security level */
  413. $security = array(
  414. 'N' => 'no security',
  415. 'U' => 'strong security',
  416. 'I' => 'weak security',
  417. );
  418. if (!empty($result['browser_token'])) {
  419. if (isset($security[$result['browser_token']])) {
  420. $result['security_level'] = $security[$result['browser_token']];
  421. unset($result['browser_token']);
  422. }
  423. }
  424. $product = strtolower($result['browser_name']);
  425. // Mozilla : true && false
  426. $compatibleOrIe = false;
  427. if (isset($result['compatibility_flag']) && isset($result['comment'])) {
  428. $compatibleOrIe = ($result['compatibility_flag'] == 'compatible' || strpos($result['comment']['full'], "MSIE") !== false);
  429. }
  430. if ($product == 'mozilla' && $compatibleOrIe) {
  431. if (!empty($result['browser_token'])) {
  432. // Classic Mozilla chain
  433. preg_match_all('/([^\/\s].*)(\/|\s)(.*)/i', $result['browser_token'], $real);
  434. } else {
  435. // MSIE specific chain with 'Windows' compatibility flag
  436. foreach ($result['comment']['detail'] as $v) {
  437. if (strpos($v, 'MSIE') !== false) {
  438. $real[0][1] = trim($v);
  439. $result['browser_engine'] = "MSIE";
  440. $real[1][0] = "Internet Explorer";
  441. $temp = explode(' ', trim($v));
  442. $real[3][0] = $temp[1];
  443. }
  444. if (strpos($v, 'Win') !== false) {
  445. $result['device_os_token'] = trim($v);
  446. }
  447. }
  448. }
  449. if (!empty($real[0])) {
  450. $result['browser_name'] = $real[1][0];
  451. $result['browser_version'] = $real[3][0];
  452. } else {
  453. $result['browser_name'] = $result['browser_token'];
  454. $result['browser_version'] = '??';
  455. }
  456. } elseif ($product == 'mozilla' && $result['browser_version'] < 5.0) {
  457. // handles the real Mozilla (or old Netscape if version < 5.0)
  458. $result['browser_name'] = 'Netscape';
  459. }
  460. /** windows */
  461. if ($result['browser_name'] == 'MSIE') {
  462. $result['browser_engine'] = 'MSIE';
  463. $result['browser_name'] = 'Internet Explorer';
  464. }
  465. if (isset($result['device_os_token'])) {
  466. if (strpos($result['device_os_token'], 'Win') !== false) {
  467. $windows = array(
  468. 'Windows NT 6.1' => 'Windows 7',
  469. 'Windows NT 6.0' => 'Windows Vista',
  470. 'Windows NT 5.2' => 'Windows Server 2003',
  471. 'Windows NT 5.1' => 'Windows XP',
  472. 'Windows NT 5.01' => 'Windows 2000 SP1',
  473. 'Windows NT 5.0' => 'Windows 2000',
  474. 'Windows NT 4.0' => 'Microsoft Windows NT 4.0',
  475. 'WinNT' => 'Microsoft Windows NT 4.0',
  476. 'Windows 98; Win 9x 4.90' => 'Windows Me',
  477. 'Windows 98' => 'Windows 98',
  478. 'Win98' => 'Windows 98',
  479. 'Windows 95' => 'Windows 95',
  480. 'Win95' => 'Windows 95',
  481. 'Windows CE' => 'Windows CE',
  482. );
  483. if (isset($windows[$result['device_os_token']])) {
  484. $result['device_os_name'] = $windows[$result['device_os_token']];
  485. } else {
  486. $result['device_os_name'] = $result['device_os_token'];
  487. }
  488. }
  489. }
  490. // iphone
  491. $apple_device = array(
  492. 'iPhone',
  493. 'iPod',
  494. 'iPad',
  495. );
  496. if (isset($result['compatibility_flag'])) {
  497. if (in_array($result['compatibility_flag'], $apple_device)) {
  498. $result['device'] = strtolower($result['compatibility_flag']);
  499. $result['device_os_token'] = 'iPhone OS';
  500. $result['browser_language'] = trim($comment[3]);
  501. $result['browser_version'] = $result['others']['detail'][1][2];
  502. if (!empty($result['others']['detail'][2])) {
  503. $result['firmware'] = $result['others']['detail'][2][2];
  504. }
  505. if (!empty($result['others']['detail'][3])) {
  506. $result['browser_name'] = $result['others']['detail'][3][1];
  507. $result['browser_build'] = $result['others']['detail'][3][2];
  508. }
  509. }
  510. }
  511. // Safari
  512. if (isset($result['others'])) {
  513. if ($result['others']['detail'][0][1] == 'AppleWebKit') {
  514. $result['browser_engine'] = 'AppleWebKit';
  515. if ($result['others']['detail'][1][1] == 'Version') {
  516. $result['browser_version'] = $result['others']['detail'][1][2];
  517. } else {
  518. $result['browser_version'] = $result['others']['detail'][count($result['others']['detail']) - 1][2];
  519. }
  520. $result['browser_language'] = trim($comment[3]);
  521. $last = $result['others']['detail'][count($result['others']['detail']) - 1][1];
  522. if (empty($result['others']['detail'][2][1]) || $result['others']['detail'][2][1] == 'Safari') {
  523. $result['browser_name'] = ($result['others']['detail'][1][1] && $result['others']['detail'][1][1] != 'Version' ? $result['others']['detail'][1][1] : 'Safari');
  524. $result['browser_version'] = ($result['others']['detail'][1][2] ? $result['others']['detail'][1][2] : $result['others']['detail'][0][2]);
  525. } else {
  526. $result['browser_name'] = $result['others']['detail'][2][1];
  527. $result['browser_version'] = $result['others']['detail'][2][2];
  528. // mobile version
  529. if ($result['browser_name'] == 'Mobile') {
  530. $result['browser_name'] = 'Safari ' . $result['browser_name'];
  531. if ($result['others']['detail'][1][1] == 'Version') {
  532. $result['browser_version'] = $result['others']['detail'][1][2];
  533. }
  534. }
  535. }
  536. // For Safari < 2.2, AppleWebKit version gives the Safari version
  537. if (strpos($result['browser_version'], '.') > 2 || (int) $result['browser_version'] > 20) {
  538. $temp = explode('.', $result['browser_version']);
  539. $build = (int) $temp[0];
  540. $awkVersion = array(
  541. 48 => '0.8',
  542. 73 => '0.9',
  543. 85 => '1.0',
  544. 103 => '1.1',
  545. 124 => '1.2',
  546. 300 => '1.3',
  547. 400 => '2.0',
  548. );
  549. foreach ($awkVersion as $k => $v) {
  550. if ($build >= $k) {
  551. $result['browser_version'] = $v;
  552. }
  553. }
  554. }
  555. }
  556. // Gecko (Firefox or compatible)
  557. if ($result['others']['detail'][0][1] == 'Gecko') {
  558. $searchRV = true;
  559. if (!empty($result['others']['detail'][1][1]) && !empty($result['others']['detail'][count($result['others']['detail']) - 1][2]) || strpos(strtolower($result['others']['full']), 'opera') !== false) {
  560. $searchRV = false;
  561. $result['browser_engine'] = $result['others']['detail'][0][1];
  562. // the name of the application is at the end indepenently
  563. // of quantity of information in $result['others']['detail']
  564. $last = count($result['others']['detail']) - 1;
  565. // exception : if the version of the last information is
  566. // empty we take the previous one
  567. if (empty($result['others']['detail'][$last][2])) {
  568. $last --;
  569. }
  570. // exception : if the last one is 'Red Hat' or 'Debian' =>
  571. // use rv: to find browser_version */
  572. if (in_array($result['others']['detail'][$last][1], array(
  573. 'Debian',
  574. 'Hat',
  575. ))) {
  576. $searchRV = true;
  577. }
  578. $result['browser_name'] = $result['others']['detail'][$last][1];
  579. $result['browser_version'] = $result['others']['detail'][$last][2];
  580. if (isset($comment[4])) {
  581. $result['browser_build'] = trim($comment[4]);
  582. }
  583. $result['browser_language'] = trim($comment[3]);
  584. // Netscape
  585. if ($result['browser_name'] == 'Navigator' || $result['browser_name'] == 'Netscape6') {
  586. $result['browser_name'] = 'Netscape';
  587. }
  588. }
  589. if ($searchRV) {
  590. // Mozilla alone : the version is identified by rv:
  591. $result['browser_name'] = 'Mozilla';
  592. if (isset($result['comment']['detail'])) {
  593. foreach ($result['comment']['detail'] as $rv) {
  594. if (strpos($rv, 'rv:') !== false) {
  595. $result['browser_version'] = trim(str_replace('rv:', '', $rv));
  596. }
  597. }
  598. }
  599. }
  600. }
  601. // Netscape
  602. if ($result['others']['detail'][0][1] == 'Netscape') {
  603. $result['browser_name'] = 'Netscape';
  604. $result['browser_version'] = $result['others']['detail'][0][2];
  605. }
  606. // Opera
  607. // Opera: engine Presto
  608. if ($result['others']['detail'][0][1] == 'Presto') {
  609. $result['browser_engine'] = 'Presto';
  610. if (!empty($result['others']['detail'][1][2])) {
  611. $result['browser_version'] = $result['others']['detail'][1][2];
  612. }
  613. }
  614. // UA ends with 'Opera X.XX'
  615. if ($result['others']['detail'][0][1] == 'Opera') {
  616. $result['browser_name'] = $result['others']['detail'][0][1];
  617. $result['browser_version'] = $result['others']['detail'][1][1];
  618. }
  619. // Opera Mini
  620. if (isset($result["browser_token"])) {
  621. if (strpos($result["browser_token"], 'Opera Mini') !== false) {
  622. $result['browser_name'] = 'Opera Mini';
  623. }
  624. }
  625. // Symbian
  626. if ($result['others']['detail'][0][1] == 'SymbianOS') {
  627. $result['device_os_token'] = 'SymbianOS';
  628. }
  629. }
  630. // UA ends with 'Opera X.XX'
  631. if (isset($result['browser_name']) && isset($result['browser_engine'])) {
  632. if ($result['browser_name'] == 'Opera' && $result['browser_engine'] == 'Gecko' && empty($result['browser_version'])) {
  633. $result['browser_version'] = $result['others']['detail'][count($result['others']['detail']) - 1][1];
  634. }
  635. }
  636. // cleanup
  637. if (isset($result['browser_version']) && isset($result['browser_build'])) {
  638. if ($result['browser_version'] == $result['browser_build']) {
  639. unset($result['browser_build']);
  640. }
  641. }
  642. // compatibility
  643. $compatibility['AppleWebKit'] = 'Safari';
  644. $compatibility['Gecko'] = 'Firefox';
  645. $compatibility['MSIE'] = 'Internet Explorer';
  646. $compatibility['Presto'] = 'Opera';
  647. if (!empty($result['browser_engine'])) {
  648. if (isset($compatibility[$result['browser_engine']])) {
  649. $result['browser_compatibility'] = $compatibility[$result['browser_engine']];
  650. }
  651. }
  652. ksort($result);
  653. return $result;
  654. }
  655. /**
  656. * Loads the Features Adapter if it's defined in the $config array
  657. * Otherwise, nothing is done
  658. *
  659. * @param string $browserType Browser type
  660. * @return array
  661. */
  662. protected function _loadFeaturesAdapter()
  663. {
  664. $config = $this->_config;
  665. $browserType = $this->getType();
  666. if (!isset($config[$browserType]) || !isset($config[$browserType]['features'])) {
  667. return array();
  668. }
  669. $config = $config[$browserType]['features'];
  670. if (empty($config['classname'])) {
  671. require_once 'Zend/Http/UserAgent/Exception.php';
  672. throw new Zend_Http_UserAgent_Exception('The ' . $this->getType() . ' features adapter must have a "classname" config parameter defined');
  673. }
  674. $className = $config['classname'];
  675. if (!class_exists($className)) {
  676. if (isset($config['path'])) {
  677. $path = $config['path'];
  678. } else {
  679. require_once 'Zend/Http/UserAgent/Exception.php';
  680. throw new Zend_Http_UserAgent_Exception('The ' . $this->getType() . ' features adapter must have a "path" config parameter defined');
  681. }
  682. if (false === include_once ($path)) {
  683. require_once 'Zend/Http/UserAgent/Exception.php';
  684. throw new Zend_Http_UserAgent_Exception('The ' . $this->getType() . ' features adapter path that does not exist');
  685. }
  686. }
  687. return call_user_func(array($className, 'getFromRequest'), $this->_server, $this->_config);
  688. }
  689. /**
  690. * Retrieve image format support
  691. *
  692. * @return array
  693. */
  694. public function getImageFormatSupport()
  695. {
  696. return $this->_images;
  697. }
  698. /**
  699. * Get maximum image height supported by this device
  700. *
  701. * @return int
  702. */
  703. public function getMaxImageHeight()
  704. {
  705. return null;
  706. }
  707. /**
  708. * Get maximum image width supported by this device
  709. *
  710. * @return int
  711. */
  712. public function getMaxImageWidth()
  713. {
  714. return null;
  715. }
  716. /**
  717. * Get physical screen height of this device
  718. *
  719. * @return int
  720. */
  721. public function getPhysicalScreenHeight()
  722. {
  723. return null;
  724. }
  725. /**
  726. * Get physical screen width of this device
  727. *
  728. * @return int
  729. */
  730. public function getPhysicalScreenWidth()
  731. {
  732. return null;
  733. }
  734. /**
  735. * Get preferred markup type
  736. *
  737. * @return string
  738. */
  739. public function getPreferredMarkup()
  740. {
  741. return 'xhtml';
  742. }
  743. /**
  744. * Get supported X/HTML version
  745. *
  746. * @return int
  747. */
  748. public function getXhtmlSupportLevel()
  749. {
  750. return 4;
  751. }
  752. /**
  753. * Does the device support Flash?
  754. *
  755. * @return bool
  756. */
  757. public function hasFlashSupport()
  758. {
  759. return true;
  760. }
  761. /**
  762. * Does the device support PDF?
  763. *
  764. * @return bool
  765. */
  766. public function hasPdfSupport()
  767. {
  768. return true;
  769. }
  770. /**
  771. * Does the device have a phone number associated with it?
  772. *
  773. * @return bool
  774. */
  775. public function hasPhoneNumber()
  776. {
  777. return false;
  778. }
  779. /**
  780. * Does the device support HTTPS?
  781. *
  782. * @return bool
  783. */
  784. public function httpsSupport()
  785. {
  786. return true;
  787. }
  788. /**
  789. * Get the browser type
  790. *
  791. * @return string
  792. */
  793. public function getBrowser()
  794. {
  795. return $this->_browser;
  796. }
  797. /**
  798. * Get the browser version
  799. *
  800. * @return string
  801. */
  802. public function getBrowserVersion()
  803. {
  804. return $this->_browserVersion;
  805. }
  806. /**
  807. * Get the user agent string
  808. *
  809. * @return string
  810. */
  811. public function getUserAgent()
  812. {
  813. return $this->_userAgent;
  814. }
  815. /**
  816. * @return the $_images
  817. */
  818. public function getImages()
  819. {
  820. return $this->_images;
  821. }
  822. /**
  823. * @param string $browser
  824. */
  825. public function setBrowser($browser)
  826. {
  827. $this->_browser = $browser;
  828. }
  829. /**
  830. * @param string $browserVersion
  831. */
  832. public function setBrowserVersion($browserVersion)
  833. {
  834. $this->_browserVersion = $browserVersion;
  835. }
  836. /**
  837. * @param string $userAgent
  838. */
  839. public function setUserAgent($userAgent)
  840. {
  841. $this->_userAgent = $userAgent;
  842. return $this;
  843. }
  844. /**
  845. * @param array $_images
  846. */
  847. public function setImages($_images)
  848. {
  849. $this->_images = $_images;
  850. }
  851. /**
  852. * Match a user agent string against a list of signatures
  853. *
  854. * @param string $userAgent
  855. * @param array $signatures
  856. * @return bool
  857. */
  858. protected static function _matchAgentAgainstSignatures($userAgent, $signatures)
  859. {
  860. $userAgent = strtolower($userAgent);
  861. foreach ($signatures as $signature) {
  862. if (!empty($signature)) {
  863. if (strpos($userAgent, $signature) !== false) {
  864. // Browser signature was found in user agent string
  865. return true;
  866. }
  867. }
  868. }
  869. return false;
  870. }
  871. }