PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/library/tera-wurfl/tera_wurfl.php

https://github.com/Macarse/trifiori
PHP | 750 lines | 331 code | 32 blank | 387 comment | 78 complexity | 413a62260bba5d7cb7f333eb4b11454d MD5 | raw file
  1. <?php
  2. /*
  3. * Tera_WURFL - PHP MySQL driven WURFL
  4. *
  5. * Tera-WURFL was written by Steve Kamerman, Tera Technologies and is based on the
  6. * WURFL PHP Tools from http://wurfl.sourceforge.net/. This version uses a MySQL database
  7. * to store the entire WURFL file to provide extreme performance increases.
  8. *
  9. * @package tera_wurfl
  10. * @author Steve Kamerman, Tera Technologies (kamermans AT teratechnologies DOT net)
  11. * @version Stable 1.5.1 $Date: 2007/05/09 20:09:13 $
  12. * @license http://www.mozilla.org/MPL/ MPL Vesion 1.1
  13. * $Id: tera_wurfl.php,v 1.1.4.6.2.20 2007/05/09 20:09:13 kamermans Exp $
  14. * $RCSfile: tera_wurfl.php,v $
  15. *
  16. * Based On: WURFL PHP Tools by Andrea Trasatti ( atrasatti AT users DOT sourceforge DOT net )
  17. *
  18. */
  19. if ( !defined('WURFL_CONFIG') )
  20. require_once(dirname(__FILE__).'/tera_wurfl_config.php');
  21. if ( !defined('WURFL_CONFIG') )
  22. die("NO CONFIGURATION");
  23. if ( defined('WURFL_PARSER_FILE') )
  24. require_once(WURFL_PARSER_FILE);
  25. else
  26. require_once(dirname(__FILE__)."/tera_wurfl_parser.php");
  27. /**
  28. * Tera-WURFL was written by Steve Kamerman, Tera Technologies and is based on the
  29. * WURFL PHP Tools from http://wurfl.sourceforge.net/. This version uses a MySQL database
  30. * to store the entire WURFL file to provide extreme performance increases.
  31. *
  32. * See the documentation for specific usage information.
  33. * Quick Example:
  34. * <code>
  35. * $myDevice = new tera_wurfl();
  36. * $myDevice->getDeviceCapabilitiesFromAgent($_SERVER['HTTP_USER_AGENT');
  37. * // see if this device is really a mobile browser
  38. * if($myDevice->browser_is_wap){
  39. * // check capabilities
  40. * if($myDevice->capabilities['downloadfun']['downloadfun_support']){
  41. * echo "downloadfun supported<br />";
  42. * }else{
  43. * echo "WAP is supported, downloadfun is not<br />";
  44. * }
  45. * if($myDevice->device_image != ''){
  46. * // display device image
  47. * echo '<img src="'.$myDevice->device_image.'" /><br />';
  48. * }
  49. * }
  50. * </code>
  51. *
  52. * @package tera_wurfl
  53. */
  54. class tera_wurfl {
  55. /**
  56. * Internal tracking of the WURFL ID
  57. * @var string
  58. * @access private
  59. */
  60. var $id="";
  61. /**
  62. * If true, Openwave's GUI (mostly wml 1.3) is supported
  63. * @var bool
  64. * @access public
  65. */
  66. var $GUI=false;
  67. /**
  68. * Device brand (manufacturer)
  69. * @var string
  70. * @access public
  71. */
  72. var $brand='';
  73. /**
  74. * Device model name
  75. * @var string
  76. * @access public
  77. */
  78. var $model='';
  79. /**
  80. * If this is a WAP device, this is set to true
  81. * @var boolean
  82. * @access public
  83. */
  84. var $browser_is_wap=false;
  85. /**
  86. * associative array with all the device's capabilities.
  87. *
  88. * Example: $this->capabilities['downloadfun']['downloadfun_support']
  89. * true if downloadfun is supported, otherwise false
  90. *
  91. * @var associative array
  92. * @access public
  93. */
  94. var $capabilities=array();
  95. /**
  96. * HTTP_ACCEPT request headers
  97. * Use this to manually set the http-accept string:
  98. *
  99. * Example: $this->http_accept = "text/vnd.wap.wml";
  100. * @var string
  101. * @access public
  102. */
  103. var $http_accept="";
  104. /**
  105. * Ignore desktop browser
  106. * Set this to false if you want to search the WURFL/patch
  107. * for desktop web browsers as well.
  108. *
  109. * @var string
  110. * @access public
  111. */
  112. var $ignoreBrowser=false;
  113. /**
  114. * Track errors
  115. * Anytime a LOG_ERR level event occurs, a description is
  116. * added to the end of the array.
  117. * Example: echo count($this->$errors); // echos number of errors
  118. * @var array
  119. * @access public
  120. */
  121. var $errors = array();
  122. /**
  123. * Internal database link resource
  124. * @var resource
  125. * @access private
  126. */
  127. var $dbcon = '';
  128. /**
  129. * Internal device table tracking. Used to provide some members with the name
  130. * of the current device table. It will be either DB_DEVICE_TABLE or DB_HYBRID_TABLE
  131. * @var string
  132. * @access private
  133. */
  134. var $devtable = '';
  135. /**
  136. * WURFL ID of the ancestoral device root
  137. * This is the ID of the actual device, not a firmware revision
  138. *
  139. * @var string
  140. * @access public
  141. */
  142. var $device_root = '';
  143. /**
  144. * Device image path and filename, relative to the class file
  145. *
  146. * @var string
  147. * @access public
  148. */
  149. var $device_image = '';
  150. /**
  151. * Total number of queries performed
  152. *
  153. * @var number
  154. * @access public
  155. */
  156. var $num_queries = 0;
  157. /**
  158. * True if the UA was found in the cache
  159. *
  160. * @var boolean
  161. * @access public
  162. */
  163. var $found_in_cache = false;
  164. /**
  165. * Constructor, sets the http_accept property and connects to the WURFL database.
  166. * You don't need to call the constructor - it is called when you instantiate the class
  167. *
  168. * @access public
  169. * @return boolean success
  170. *
  171. */
  172. function tera_wurfl()
  173. {
  174. // connect to database
  175. $this->dbcon = @mysql_connect(DB_HOST,DB_USER,DB_PASS);
  176. if (!$this->dbcon)
  177. {
  178. throw new Exception("ERROR: Could not connect to MySQL" .
  179. "Server (".DB_HOST."): ".mysql_error());
  180. return False;
  181. }
  182. // select schema
  183. if ( !@mysql_select_db(DB_SCHEMA,$this->dbcon) )
  184. {
  185. throw new Exception("ERROR: Connected to MySQL Server but" .
  186. "could not select database (".DB_SCHEMA."): ".mysql_error());
  187. return False;
  188. }
  189. if(WURFL_PATCH_ENABLE)
  190. {
  191. $this->devtable = DB_HYBRID_TABLE;
  192. }
  193. else
  194. {
  195. $this->devtable = DB_DEVICE_TABLE;
  196. }
  197. //TODO: remove this test - it's too slow
  198. // make sure the device table exists
  199. //$test = @mysql_query("SELECT COUNT(deviceID) AS num FROM ".$this->devtable) or die("ERROR: Device table not found (".$this->devtable."): ".mysql_error()."<br/><br/><strong>If this is a new installation, please <a href=\"admin/\">update the database</a>.");
  200. //$this->num_queries++;
  201. // make sure the device table is not empty
  202. //if(mysql_result($test,0,"num") == 0)die("ERROR: The device table (".$this->devtable.") is empty. Please update the WURFL database.");
  203. // set the default http-accept
  204. $this->http_accept = $_SERVER['HTTP_ACCEPT'];
  205. $this->_toLog('constructor', '-----Class Initiated-----', LOG_NOTICE);
  206. return(true);
  207. }
  208. /**
  209. * Given the device's id reads all its capabilities and each
  210. * parent (fall_back) devices' capabilities and merges them
  211. *
  212. * @param string the device's id from the WURFL database
  213. * @access private
  214. * @return boolean success
  215. */
  216. function _GetFullCapabilities($_id) {
  217. if(count($this->errors) != 0) return(false);
  218. $this->_toLog('_GetFullCapabilities', "searching for $_id", LOG_INFO);
  219. $_curr_device = $this->_getDeviceCapabilitiesFromId($_id);
  220. // array of full records
  221. $_capabilities[] = $_curr_device;
  222. // keep the while loop from running away on an error
  223. $iteration_limit = 20;
  224. $i = 0;
  225. while ( $_curr_device['fall_back'] != 'generic' && $_curr_device['fall_back'] != 'root' && $i <= $iteration_limit) {
  226. $this->_toLog('_GetFullCapabilities', 'parent device:'.$_curr_device['fall_back'].' now going to read its capabilities', LOG_INFO);
  227. $_curr_device = $this->_getDeviceCapabilitiesFromId($_curr_device['fall_back']);
  228. array_unshift($_capabilities,$_curr_device);
  229. $i++;
  230. }
  231. if($i >= $iteration_limit){
  232. // the while loop ran away
  233. $this->_toLog('_GetFullCapabilities', 'Killing runaway while loop - $_id='.$_id, LOG_ERR);
  234. return(false);
  235. }
  236. $this->_toLog('_GetFullCapabilities', 'reading capabilities of \'generic\' device', LOG_INFO);
  237. $generic = $this->_getDeviceCapabilitiesFromId('generic');
  238. $_final = $generic;
  239. // the generic devices are already at the top of the array because I used array_unshift()
  240. foreach($_capabilities as $curr_device){
  241. //TODO: Why don't I just array_merge the whole record???? Good question!
  242. foreach($curr_device as $key => $val) {
  243. if ( is_array($val) ) {
  244. $_final[$key] = array_merge($_final[$key], $val);
  245. } else {
  246. $_final[$key] = $val;
  247. }
  248. }
  249. }
  250. $this->capabilities = $_final;
  251. //$this->brand = $this->capabilities['product_info']['brand_name'];
  252. //$this->model = $this->capabilities['product_info']['model_name'];
  253. //$this->id = $this->capabilities['id'];
  254. return(true);
  255. }
  256. /**
  257. * Given a device id reads its capabilities
  258. *
  259. * @param string device's wurfl_id
  260. * @access private
  261. * @return mixed boolean false if not identified or array capabilities
  262. *
  263. */
  264. function _getDeviceCapabilitiesFromId($_id) {
  265. if(count($this->errors) != 0) return(false);
  266. $this->_toLog('_getDeviceCapabilitiesFromId', "reading id:$_id", LOG_INFO);
  267. if ( $_id == 'upgui_generic' ) {
  268. $this->GUI = true;
  269. }
  270. $res = mysql_query("SELECT * FROM ".$this->devtable." WHERE deviceID=".$this->_sqlPrep($_id),$this->dbcon) or die(mysql_error($this->dbcon));
  271. $this->num_queries++;
  272. if(mysql_num_rows($res) > 0){
  273. $device = mysql_fetch_assoc($res);
  274. //print_r($device);
  275. if($this->device_root == '' && $device['actual_device_root'] == 1){
  276. $this->_toLog("_getDeviceCapabilitiesFromId","device root detected: ".$device['deviceID'], LOG_INFO);
  277. $this->device_root = $device['deviceID'];
  278. $image = IMAGE_DIR.$device['deviceID'].".gif";
  279. // PHP evaluates from left to right, so "file_exists" will not get
  280. // called if IMAGE_CHECKING is false
  281. if(IMAGE_CHECKING && file_exists($image)){
  282. $this->device_image = $image;
  283. $this->_toLog("_getDeviceCapabilitiesFromId","device image found: $image",LOG_INFO);
  284. }
  285. }
  286. $cap = unserialize($device['capabilities']);
  287. //echo "<pre>".print_r($cap,true)."</pre>";
  288. return($cap);
  289. }else{
  290. // device is not in the WURFL
  291. // deal with it appropriately
  292. $this->_toLog('_getDeviceCapabilitiesFromId', "the id $_id is not present in wurfl", LOG_WARNING);
  293. //die("the id $_id is not present in wurfl_agents");
  294. echo("the id ($_id) is not present in wurfl DB<br />");
  295. // I should never get here!!
  296. return(false);
  297. }
  298. }
  299. /**
  300. * Given the user_agent reads the device's capabilities.
  301. * This method will return true or false based on its success.
  302. * After calling this function, the following properties will be accessible (on success):
  303. * <ul>
  304. * <li>$this->brand</li>
  305. * <li>$this->model</li>
  306. * <li>$this->browser_is_wap</li>
  307. * <li>$this->capabilities</li>
  308. * <li>$this->device_root</li>
  309. * <li>$this->device_image (if enabled)</li>
  310. * <li>$this->errors</li>
  311. * <li>$this->GUI</li>
  312. * </ul>
  313. *
  314. * Example:
  315. * <code>
  316. * $myDevice = new tera_wurfl();
  317. * // get the capabilities of your device
  318. * $myDevice->getDeviceCapabilitiesFromAgent($_SERVER['HTTP_USER_AGENT');
  319. * echo "Device: ".$myDevice->brand." ".$myDevice->model."<br />";
  320. * </code>
  321. *
  322. * @param string device's user_agent
  323. * @param boolean check the HTTP-ACCEPT headers if needed
  324. * @access public
  325. * @return boolean success
  326. *
  327. **/
  328. function getDeviceCapabilitiesFromAgent($_user_agent, $_check_accept=false) {
  329. if(count($this->errors) != 0) return(false);
  330. //TODO: Would be cool to log user agent and headers for future use to feed WURFL
  331. // clear any existing device root and image
  332. $this->device_root = '';
  333. $this->device_image = '';
  334. // Resetting properties
  335. $this->user_agent = $_user_agent; // the class prop will remain unchanged
  336. // FIXME: I'm not sure if people use these, but they're not getting set all the time
  337. $this->wurfl_agent = '';
  338. $this->GUI = false;
  339. $this->capabilities = array();
  340. $this->capabilities['product_info'] = array();
  341. // set this to false by default
  342. $this->capabilities['product_info']['is_wireless_device'] = false;
  343. $this->capabilities['product_info']['brand_name'] = '';
  344. $this->capabilities['product_info']['model_name'] = '';
  345. // As of version 1.4.5 the short names are references to the actual capabilities
  346. $this->browser_is_wap = &$this->capabilities['product_info']['is_wireless_device'];
  347. $this->brand = &$this->capabilities['product_info']['brand_name'];
  348. $this->model = &$this->capabilities['product_info']['model_name'];
  349. $this->id = &$this->capabilities['id'];
  350. $this->num_queries = 0;
  351. $this->found_in_cache = false;
  352. // if caching is enabled, let's check the cache
  353. if(WURFL_CACHE_ENABLE){
  354. $cache_data = $this->_UserAgentInCache($this->user_agent);
  355. if($cache_data !== false){
  356. $this->_toLog('_UserAgentInCache', 'UA was found in the cache', LOG_INFO);
  357. $this->found_in_cache = true;
  358. $cache_data = unserialize($cache_data);
  359. //die(print_r($cache_data));
  360. $this->capabilities = $cache_data['capabilities'];
  361. if($cache_data['device_root'] != ''){
  362. $this->_toLog("_UserAgentInCache","device root detected: ".$cache_data['device_root'], LOG_INFO);
  363. $this->device_root = $cache_data['device_root'];
  364. $image = IMAGE_DIR.$cache_data['device_root'].".gif";
  365. // PHP evaluates from left to right, so "file_exists" will not get
  366. // called if IMAGE_CHECKING is false
  367. if(IMAGE_CHECKING && file_exists($image)){
  368. $this->device_image = $image;
  369. $this->_toLog("_UserAgentInCache","device image found: $image",LOG_INFO);
  370. }
  371. }
  372. return(true);
  373. }else{
  374. $this->_toLog('_UserAgentInCache', 'UA was not found in the cache', LOG_INFO);
  375. }
  376. }
  377. // removing the possible Openwave MAG tag
  378. // at this point a user agent like this: "UP.Link/6.3.0.0.0" will
  379. // result in an null $_user_agent
  380. //FIXME: why do we really need to get rid of this anyway???
  381. $_user_agent = trim(ereg_replace("UP.Link.*", "", $_user_agent));
  382. // if(trim($_user_agent) == '' || !$_user_agent)
  383. //FIXME: PocketPCs (Windows Mobile) contain "MSIE 5"
  384. // like the Cingular 8125
  385. /**
  386. * The following logic has been removed because it has been deemed unmaintainable
  387. * with the introduction of so many mobile versions of common desktop browsers.
  388. */
  389. /* if ( ( stristr($_user_agent, 'Opera') && stristr($_user_agent, 'Windows') )
  390. || ( stristr($_user_agent, 'Opera') && stristr($_user_agent, 'Linux') )
  391. || stristr($_user_agent, 'Gecko')
  392. || ( (stristr($_user_agent, 'MSIE 6') || stristr($_user_agent, 'MSIE 5') ) && !stristr($_user_agent, 'MIDP') && !stristr($_user_agent, 'Windows CE') && !stristr($_user_agent, 'Symbian') )
  393. ) {
  394. // This is a web browser. Setting the defaults
  395. $this->_toLog('constructor', 'Web browser', LOG_INFO);
  396. $this->browser_is_wap=false;
  397. $this->capabilities['product_info']['brand_name'] = 'Generic Web browser';
  398. $this->capabilities['product_info']['model_name'] = '1.0';
  399. $this->capabilities['product_info']['is_wireless_device'] = false;
  400. $this->capabilities['product_info']['device_claims_web_support'] = true;
  401. if($this->ignoreBrowser){
  402. // choosing not to waste time looking up desktop browsers
  403. return(true);
  404. }
  405. }
  406. */
  407. // TODO: Spend some time on this code and make it MUCH more robust.
  408. // FIXME: I'm not sure that this even actually does anything :( - I think 'browser_is_wap'
  409. // get's overwritten anyway. Hmmmm.... Also, I changed it from LOG_WARNING to LOG_NOTICE
  410. if ($_check_accept == true) {
  411. if (!eregi('wml', $this->http_accept) && !eregi('wap', $this->http_accept) && !eregi('xhtml', $this->http_accept)) {
  412. $this->_toLog('getDeviceCapabilitiesFromAgent', 'This browser does not support wml, wap, or xhtml', LOG_NOTICE);
  413. $this->browser_is_wap=false;
  414. }else{
  415. // We can assume this is a mobile device since it accepts wml || wap || xhtml
  416. $this->browser_is_wap=true;
  417. }
  418. }
  419. $this->_toLog('getDeviceCapabilitiesFromAgent', 'searching for '.$_user_agent, LOG_INFO);
  420. if ( trim($_user_agent) == '' || !$_user_agent ) {
  421. // NO USER AGENT??? This is not a WAP device
  422. $this->_toLog('getDeviceCapabilitiesFromAgent', 'No user agent', LOG_ERR);
  423. $this->browser_is_wap=false;
  424. return(false);
  425. }
  426. $curr_device = $this->_UserAgentInDB($this->user_agent);
  427. if(is_array($curr_device)){
  428. // the exact user agent was in the WURFL - Great!
  429. //DEBUG: echo "Found exact UA in DB!<br />".print_r($curr_device,true)."<br />";
  430. $this->_GetFullCapabilities($curr_device['deviceID']);
  431. // in case you have desktop web browsers in your patch file
  432. // It was suggested by MOLABIB on 22Dec2006 that the following line
  433. // is incorrect and should be replaced:
  434. // $this->browser_is_wap = $this->capabilities['browser_is_wap'];
  435. // the following is it's replacement:
  436. //$this->browser_is_wap = $this->capabilities['product_info']['is_wireless_device'];
  437. //$this->brand = $this->capabilities['product_info']['brand_name'];
  438. //$this->model = $this->capabilities['product_info']['model_name'];
  439. $this->_AddUAToCache($this->user_agent,$this->capabilities,$this->device_root);
  440. return(true);
  441. }
  442. // the user agent was NOT in the WURFL - try to dissect it
  443. $_ua = $_user_agent;
  444. // Used to be $_ua_len = strlen($_ua) - 1 but that resulted in erroneous results since
  445. // the full string could still match UAs longer than itself in the DB
  446. // Thanks to Christian Aune Thomassen [christian.thomassen(at)waptheweb.no] for finding this bug!
  447. $_ua_len = strlen($_ua);
  448. // Searching in wurfl_agents
  449. // The user_agent should not become shorter than 4 characters
  450. $this->_toLog('getDeviceCapabilitiesFromAgent', 'Searching the DB ('.$this->devtable.')', LOG_INFO);
  451. // I request to set a short list of UA's among which I should search an unknown user agent
  452. $_short_ua_len = 4;
  453. $_last_good_short_ua = array();
  454. $_min_len = 4;
  455. // track number of queries
  456. $niceua = rtrim($this->_sqlPrep(substr($_ua, 0, $_min_len)),"'")."%'";
  457. $minquery = "SELECT COUNT(deviceID) AS num FROM ".$this->devtable." WHERE user_agent LIKE $niceua";
  458. $res = mysql_query($minquery,$this->dbcon);
  459. $this->num_queries++;
  460. $_short_wurfl_ua = array();
  461. if(mysql_result($res,0,'num') == 0){
  462. //DEBUG: echo "no devices match the UA down to the min chars in the DB<br />$minquery";
  463. // no devices match the UA down to the min chars in the DB
  464. // basically you're not going to get a match.
  465. // look for acceptable generic ID
  466. if(!$this->_getGenericID($_user_agent)){
  467. // no generic ID found - assuming this is not a WAP device
  468. return(true);
  469. }
  470. }else{
  471. while ( $_ua_len > $_min_len) {
  472. //DEBUG: echo "--trying user agent length $_ua_len<br />";
  473. $_short_wurfl_ua = array();
  474. $_short_ua = substr($_ua, 0, $_ua_len);
  475. // take the user agent and prep it (escapes chars and adds single quotes
  476. // then remove the right most quote and put %' in it's place which will
  477. // make it this MOT- look like this: 'MOT-%' - that will work for MySQL LIKE queries
  478. $niceua = rtrim($this->_sqlPrep($_short_ua),"'")."%'";
  479. $res = mysql_query("SELECT user_agent FROM ".$this->devtable." WHERE user_agent LIKE $niceua",$this->dbcon);
  480. $this->num_queries++;
  481. while($row = mysql_fetch_assoc($res)){
  482. // load the $_short_wurfl_ua array with matching user agents
  483. $_short_wurfl_ua[] = $row['user_agent'];
  484. }
  485. if ( $_ua_len <= $_min_len && count($_short_wurfl_ua) == 0 ) {
  486. // Probably impossible to get here, but if it does there is no match
  487. // DEBUG fast search echo "no match even for the first 4 chars<br>\n";
  488. break;
  489. }
  490. if ( count($_short_wurfl_ua) > 0 ) {
  491. // This is the FIRST time that ANY matching user agents were found
  492. //DEBUG: echo "list has ".count($_short_wurfl_ua)." elements<br />";
  493. break;
  494. }
  495. // shortening the agent by one each time
  496. $_ua_len--;
  497. }
  498. }
  499. //DEBUG: die("Done.<br />Original UA: $_user_agent<br />Best match UA: ".$_short_wurfl_ua[0]."<br />");
  500. //FIXME: probably shouldn't blindly grab the first UA, so improve...
  501. if(count($_short_wurfl_ua) > 0){
  502. $bestmatch = array_shift($_short_wurfl_ua);
  503. $device = $this->_UserAgentInDB($bestmatch);
  504. $this->_GetFullCapabilities($device['deviceID']);
  505. $this->_AddUAToCache($this->user_agent,$this->capabilities,$this->device_root);
  506. $this->_toLog('getDeviceCapabilitiesFromAgent', 'Match found in DB after '.$this->num_queries.' queries', LOG_INFO);
  507. return(true);
  508. }else{
  509. // no match was found
  510. $this->_AddUAToCache($this->user_agent,$this->capabilities,$this->device_root);
  511. $this->_toLog('getDeviceCapabilitiesFromAgent', 'No match was found in DB', LOG_INFO);
  512. return(false);
  513. }
  514. }
  515. /**
  516. * Checks to see if a given user agent is in the WURFL database
  517. *
  518. * @param string user agent
  519. * @return mixed false if not in DB, else full device record array
  520. * @access private
  521. */
  522. function _UserAgentInDB($ua){
  523. //TODO: using LIKE will do the search case-insensitive, but = is much faster
  524. $res = mysql_query("SELECT * FROM ".$this->devtable." WHERE user_agent LIKE ".$this->_sqlPrep($ua),$this->dbcon);
  525. $this->num_queries++;
  526. $curr_device = mysql_fetch_assoc($res);
  527. $ret = (mysql_num_rows($res) > 0)? $curr_device: false;
  528. return($ret);
  529. }
  530. /**
  531. * Checks to see if a given user agent is in the cache
  532. *
  533. * @param string user agent
  534. * @return mixed false if not in cache, else full device cache data array
  535. * @access private
  536. */
  537. function _UserAgentInCache($ua){
  538. //TODO: using LIKE will do the search case-insensitive, but = is much faster
  539. $query = "SELECT * FROM ".DB_CACHE_TABLE." WHERE user_agent = ".$this->_sqlPrep($ua);
  540. $res = mysql_query($query,$this->dbcon);
  541. $this->num_queries++;
  542. $cache_data = mysql_fetch_assoc($res);
  543. $ret = (mysql_num_rows($res) > 0)? $cache_data['cache_data']: false;
  544. //if(!$ret)die($query);
  545. return($ret);
  546. }
  547. /**
  548. * Adds a user agent and its associated data to the cache
  549. *
  550. * @param string user agent
  551. * @param array capabilities
  552. * @param string device root
  553. * @return mixed false if not in cache, else full device cache data array
  554. * @access private
  555. */
  556. function _AddUAToCache($ua,$cap,$devroot){
  557. if(!WURFL_CACHE_ENABLE)return(true);
  558. // don't cache UAs that are too short to be real
  559. if(strlen($ua) < 3)return(true);
  560. $cache_data = serialize(array("capabilities"=>$cap,"device_root"=>$devroot));
  561. // FIXME: DELAYED INSERTS only work on MyIASM
  562. mysql_query("INSERT DELAYED INTO ".DB_CACHE_TABLE." (user_agent,cache_data) VALUES (".$this->_sqlPrep($ua).",".$this->_sqlPrep($cache_data).")",$this->dbcon);
  563. $this->_toLog('_AddUAToCache', 'Added UA to cache: '.$ua, LOG_INFO);
  564. $this->num_queries++;
  565. return(true);
  566. }
  567. /**
  568. * Given a capability name returns the value (true|false|<anythingelse>). This is
  569. * helpful if you don't know the group that the capability is in.
  570. *
  571. * Example:
  572. * <code>
  573. * $myDevice = new tera_wurfl();
  574. * // get the capabilities of your device
  575. * $callstring $myDevice->getDeviceCapability('wml_make_phone_call_string');
  576. * echo "<a href=\"$callstring6161234567\">Call me</a>";
  577. * </code>
  578. *
  579. * @param string capability name as a string
  580. * @access public
  581. * @return mixed boolean success or other string if specified in the WURFL
  582. *
  583. */
  584. function getDeviceCapability($capability) {
  585. $this->_toLog('_getDeviceCapability', 'Searching for '.$capability.' as a capability', LOG_INFO);
  586. $deviceCapabilities = $this->capabilities;
  587. foreach ( $deviceCapabilities as $group ) {
  588. if ( !is_array($group) ) {
  589. continue;
  590. }
  591. while ( list($key, $value)=each($group) ) {
  592. if ($key==$capability) {
  593. $this->_toLog('_getDeviceCapability', 'I found it, value is '.$value, LOG_INFO);
  594. return $value;
  595. }
  596. }
  597. }
  598. $this->_toLog('_getDeviceCapability', 'I could not find the requested capability, returning false', LOG_WARNING);
  599. return false;
  600. }
  601. /**
  602. * Clears any LOG_ERR level errors that were detected. This will
  603. * allow you to continue testing other user agents after an error
  604. * is detected.
  605. *
  606. * Example:
  607. * <code>
  608. * $myDevice = new tera_wurfl();
  609. * // this will throw an error and prevent the methods from working
  610. * $myDevice->getDeviceCapabilitiesFromAgent("");
  611. * // clear any errors
  612. * $myDevice->clearErrors();
  613. * // now we can check another user agent
  614. * $myDevice->getDeviceCapabilitiesFromAgent("MOT-V3");
  615. * </code>
  616. *
  617. * @access public
  618. * @return boolean true
  619. */
  620. function clearErrors(){
  621. $this->errors = array();
  622. return(true);
  623. }
  624. /**
  625. * This function checks and prepares the text to be logged
  626. *
  627. * @access private
  628. * @param string Function name
  629. * @param string Reason text/description
  630. * @param int Log level (LOG_ERR|LOG_WARN|LOG_NOTICE|LOG_INFO)
  631. * Any of the PHP LOG contstants will work, but if you
  632. * throw a LOG_ERR, the script will stop and return false
  633. */
  634. function _toLog($func, $text, $requestedLogLevel=LOG_NOTICE){
  635. if($requestedLogLevel == LOG_ERR) $this->errors[] = $text;
  636. if ( !defined('LOG_LEVEL') || LOG_LEVEL == 0 || ($requestedLogLevel-1) >= LOG_LEVEL ) {
  637. return;
  638. }
  639. if ( $requestedLogLevel == LOG_ERR ) {
  640. $warn_banner = 'ERROR: ';
  641. } else if ( $requestedLogLevel == LOG_WARNING ) {
  642. $warn_banner = 'WARNING: ';
  643. } else {
  644. $warn_banner = '';
  645. }
  646. // Thanks laacz
  647. $_textToLog = date('r')." [".php_uname('n')." ".getmypid()."]"."[$func] ".$warn_banner . $text;
  648. $_logFP = fopen(WURFL_LOG_FILE, "a+");
  649. fputs($_logFP, $_textToLog."\n");
  650. fclose($_logFP);
  651. return(true);
  652. }
  653. /**
  654. * Kills the script on fatal database errors
  655. *
  656. * @access private
  657. */
  658. function _dberror(){
  659. die("Error in query: ".mysql_error($this->dbcon));
  660. }
  661. /**
  662. * This function checks the user agent for signs that it's a WAP device
  663. * Returns true if a generic ID is found, otherwise false.
  664. * This is a last resort function that is only called if the device in question
  665. * does not exist in the WURFL and the class is forced to find another way to
  666. * identify the device.
  667. *
  668. * @param string User agent
  669. * @access private
  670. * @return boolean success
  671. */
  672. function _getGenericID($_user_agent){
  673. $this->_toLog('getGenericID', "I couldn't find the device in my list, the headers are my last chance", LOG_NOTICE);
  674. if ( strstr($_user_agent, 'UP.Browser/') && strstr($_user_agent, '(GUI)') ) {
  675. $this->browser_is_wap = true;
  676. $this->user_agent = $_user_agent;
  677. $this->wurfl_agent = 'upgui_generic';
  678. $this->id = 'upgui_generic';
  679. } else if ( strstr($_user_agent, 'UP.Browser/') ) {
  680. $this->browser_is_wap = true;
  681. $this->user_agent = $_user_agent;
  682. $this->wurfl_agent = 'uptext_generic';
  683. $this->id = 'uptext_generic';
  684. } else if ( eregi('wml', $this->http_accept) || eregi('wap', $this->http_accept) ) {
  685. $this->browser_is_wap = true;
  686. $this->user_agent = $_user_agent;
  687. $this->wurfl_agent = 'generic';
  688. $this->id = 'generic';
  689. } else {
  690. $this->_toLog('getGenericID', 'This should not be a WAP device, quitting', LOG_NOTICE);
  691. $this->browser_is_wap=false;
  692. $this->user_agent = $_user_agent;
  693. $this->wurfl_agent = 'generic';
  694. $this->id = 'generic';
  695. return(false);
  696. }
  697. return(true);
  698. }
  699. /**
  700. * Given a string, will escape any unfriendly characters and return
  701. * a single quoted string to be used directly in an SQL statement to a
  702. * MySQL server.
  703. * Given a real number it will return the number without quotes so MySQL
  704. * sees it as a number instead of a string.
  705. * Given a null string ('') it will return the MySQL keyword NULL.
  706. *
  707. * Example: it's a fine day -> 'it\'s a fine day'
  708. * @param mixed Input variable to be prepared
  709. * @access private
  710. */
  711. function _sqlPrep($value){
  712. if (get_magic_quotes_gpc()) $value = stripslashes($value);
  713. if($value == '') $value = 'NULL';
  714. else if (!is_numeric($value) || $value[0] == '0') $value = "'" . mysql_real_escape_string($value) . "'"; //Quote if not integer
  715. return($value);
  716. }
  717. }
  718. ?>