Source for file tera_wurfl.php
Documentation is available at tera_wurfl.php
* Tera_WURFL - PHP MySQL driven WURFL
* Tera-WURFL was written by Steve Kamerman, Tera Technologies and is based on the
* WURFL PHP Tools from http://wurfl.sourceforge.net/. This version uses a MySQL database
* to store the entire WURFL file to provide extreme performance increases.
* @author Steve Kamerman, Tera Technologies (kamermans AT teratechnologies DOT net)
* @version Stable 1.5.0 $Date: 2007/04/27 17:26:50 $
* @license http://www.mozilla.org/MPL/ MPL Vesion 1.1
* $Id: tera_wurfl.php,v 1.1.4.6.2.19 2007/04/27 17:26:50 kamermans Exp $
* $RCSfile: tera_wurfl.php,v $
* Based On: WURFL PHP Tools by Andrea Trasatti ( atrasatti AT users DOT sourceforge DOT net )
require_once(dirname(__FILE__ ). '/tera_wurfl_config.php');
if ( defined('WURFL_PARSER_FILE') )
require_once(WURFL_PARSER_FILE);
require_once(dirname(__FILE__ ). "/tera_wurfl_parser.php");
* Tera-WURFL was written by Steve Kamerman, Tera Technologies and is based on the
* WURFL PHP Tools from http://wurfl.sourceforge.net/. This version uses a MySQL database
* to store the entire WURFL file to provide extreme performance increases.
* See the documentation for specific usage information.
* $myDevice = new tera_wurfl();
* $myDevice->getDeviceCapabilitiesFromAgent($_SERVER['HTTP_USER_AGENT');
* // see if this device is really a mobile browser
* if($myDevice->browser_is_wap){
* if($myDevice->capabilities['downloadfun']['downloadfun_support']){
* echo "downloadfun supported<br />";
* echo "WAP is supported, downloadfun is not<br />";
* if($myDevice->device_image != ''){
* // display device image
* echo '<img src="'.$myDevice->device_image.'" /><br />';
* Internal tracking of the WURFL ID
* If true, Openwave's GUI (mostly wml 1.3) is supported
* Device brand (manufacturer)
* If this is a WAP device, this is set to true
* associative array with all the device's capabilities.
* Example: $this->capabilities['downloadfun']['downloadfun_support']
* true if downloadfun is supported, otherwise false
* HTTP_ACCEPT request headers
* Use this to manually set the http-accept string:
* Example: $this->http_accept = "text/vnd.wap.wml";
* Set this to false if you want to search the WURFL/patch
* for desktop web browsers as well.
* Anytime a LOG_ERR level event occurs, a description is
* added to the end of the array.
* Example: echo count($this->$errors); // echos number of errors
* Internal database link resource
* Internal device table tracking. Used to provide some members with the name
* of the current device table. It will be either DB_DEVICE_TABLE or DB_HYBRID_TABLE
* WURFL ID of the ancestoral device root
* This is the ID of the actual device, not a firmware revision
* Device image path and filename, relative to the class file
* Total number of queries performed
* True if the UA was found in the cache
* Constructor, sets the http_accept property and connects to the WURFL database.
* You don't need to call the constructor - it is called when you instantiate the class
* @return boolean success
//TODO: remove this test - it's too slow
// make sure the device table exists
//$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>.");
// make sure the device table is not empty
//if(mysql_result($test,0,"num") == 0)die("ERROR: The device table (".$this->devtable.") is empty. Please update the WURFL database.");
// set the default http-accept
$this->_toLog('constructor', '-----Class Initiated-----', LOG_NOTICE);
* Given the device's id reads all its capabilities and each
* parent (fall_back) devices' capabilities and merges them
* @param string the device's id from the WURFL database
* @return boolean success
function _GetFullCapabilities($_id) {
$this->_toLog('_GetFullCapabilities', "searching for $_id", LOG_INFO);
$_curr_device = $this->_getDeviceCapabilitiesFromId($_id);
$_capabilities[] = $_curr_device;
// keep the while loop from running away on an error
while ( $_curr_device['fall_back'] != 'generic' && $_curr_device['fall_back'] != 'root' && $i <= $iteration_limit) {
$this->_toLog('_GetFullCapabilities', 'parent device:'. $_curr_device['fall_back']. ' now going to read its capabilities', LOG_INFO);
$_curr_device = $this->_getDeviceCapabilitiesFromId($_curr_device['fall_back']);
if($i >= $iteration_limit){
// the while loop ran away
$this->_toLog('_GetFullCapabilities', 'Killing runaway while loop - $_id='. $_id, LOG_ERR);
$this->_toLog('_GetFullCapabilities', 'reading capabilities of \'generic\' device', LOG_INFO);
$generic = $this->_getDeviceCapabilitiesFromId('generic');
// the generic devices are already at the top of the array because I used array_unshift()
foreach($_capabilities as $curr_device){
//TODO: Why don't I just array_merge the whole record???? Good question!
foreach($curr_device as $key => $val) {
//$this->brand = $this->capabilities['product_info']['brand_name'];
//$this->model = $this->capabilities['product_info']['model_name'];
//$this->id = $this->capabilities['id'];
* Given a device id reads its capabilities
* @param string device's wurfl_id
* @return mixed boolean false if not identified or array capabilities
function _getDeviceCapabilitiesFromId($_id) {
$this->_toLog('_getDeviceCapabilitiesFromId', "reading id:$_id", LOG_INFO);
if ( $_id == 'upgui_generic' ) {
$res = mysql_query("SELECT * FROM ". $this->devtable. " WHERE deviceID=". $this->_sqlPrep($_id),$this->dbcon) or die(mysql_error($this->dbcon));
if($this->device_root == '' && $device['actual_device_root'] == 1){
$this->_toLog("_getDeviceCapabilitiesFromId","device root detected: ". $device['deviceID'], LOG_INFO);
$image = IMAGE_DIR. $device['deviceID']. ".gif";
// PHP evaluates from left to right, so "file_exists" will not get
// called if IMAGE_CHECKING is false
$this->_toLog("_getDeviceCapabilitiesFromId","device image found: $image",LOG_INFO);
//echo "<pre>".print_r($cap,true)."</pre>";
// device is not in the WURFL
// deal with it appropriately
$this->_toLog('_getDeviceCapabilitiesFromId', "the id $_id is not present in wurfl", LOG_WARNING);
//die("the id $_id is not present in wurfl_agents");
echo ("the id ($_id) is not present in wurfl DB<br />");
// I should never get here!!
* Given the user_agent reads the device's capabilities.
* This method will return true or false based on its success.
* After calling this function, the following properties will be accessible (on success):
* <li>$this->browser_is_wap</li>
* <li>$this->capabilities</li>
* <li>$this->device_root</li>
* <li>$this->device_image (if enabled)</li>
* $myDevice = new tera_wurfl();
* // get the capabilities of your device
* $myDevice->getDeviceCapabilitiesFromAgent($_SERVER['HTTP_USER_AGENT');
* echo "Device: ".$myDevice->brand." ".$myDevice->model."<br />";
* @param string device's user_agent
* @param boolean check the HTTP-ACCEPT headers if needed
* @return boolean success
//TODO: Would be cool to log user agent and headers for future use to feed WURFL
// clear any existing device root and image
$this->user_agent = $_user_agent; // the class prop will remain unchanged
// FIXME: I'm not sure if people use these, but they're not getting set all the time
// set this to false by default
$this->capabilities['product_info']['is_wireless_device'] = false;
// As of version 1.4.5 the short names are references to the actual capabilities
// if caching is enabled, let's check the cache
$cache_data = $this->_UserAgentInCache($this->user_agent);
if($cache_data !== false){
$this->_toLog('_UserAgentInCache', 'UA was found in the cache', LOG_INFO);
//die(print_r($cache_data));
if($cache_data['device_root'] != ''){
$this->_toLog("_UserAgentInCache","device root detected: ". $cache_data['device_root'], LOG_INFO);
$image = IMAGE_DIR. $cache_data['device_root']. ".gif";
// PHP evaluates from left to right, so "file_exists" will not get
// called if IMAGE_CHECKING is false
$this->_toLog("_UserAgentInCache","device image found: $image",LOG_INFO);
$this->_toLog('_UserAgentInCache', 'UA was not found in the cache', LOG_INFO);
// removing the possible Openwave MAG tag
// at this point a user agent like this: "UP.Link/6.3.0.0.0" will
// result in an null $_user_agent
//FIXME: why do we really need to get rid of this anyway???
// if(trim($_user_agent) == '' || !$_user_agent)
//FIXME: PocketPCs (Windows Mobile) contain "MSIE 5"
// like the Cingular 8125
* The following logic has been removed because it has been deemed unmaintainable
* with the introduction of so many mobile versions of common desktop browsers.
/* if ( ( stristr($_user_agent, 'Opera') && stristr($_user_agent, 'Windows') )
|| ( stristr($_user_agent, 'Opera') && stristr($_user_agent, 'Linux') )
|| stristr($_user_agent, 'Gecko')
|| ( (stristr($_user_agent, 'MSIE 6') || stristr($_user_agent, 'MSIE 5') ) && !stristr($_user_agent, 'MIDP') && !stristr($_user_agent, 'Windows CE') && !stristr($_user_agent, 'Symbian') )
// This is a web browser. Setting the defaults
$this->_toLog('constructor', 'Web browser', LOG_INFO);
$this->browser_is_wap=false;
$this->capabilities['product_info']['brand_name'] = 'Generic Web browser';
$this->capabilities['product_info']['model_name'] = '1.0';
$this->capabilities['product_info']['is_wireless_device'] = false;
$this->capabilities['product_info']['device_claims_web_support'] = true;
if($this->ignoreBrowser){
// choosing not to waste time looking up desktop browsers
// TODO: Spend some time on this code and make it MUCH more robust.
// FIXME: I'm not sure that this even actually does anything :( - I think 'browser_is_wap'
// get's overwritten anyway. Hmmmm.... Also, I changed it from LOG_WARNING to LOG_NOTICE
if ($_check_accept == true) {
$this->_toLog('getDeviceCapabilitiesFromAgent', 'This browser does not support wml, wap, or xhtml', LOG_NOTICE);
// We can assume this is a mobile device since it accepts wml || wap || xhtml
$this->_toLog('getDeviceCapabilitiesFromAgent', 'searching for '. $_user_agent, LOG_INFO);
if ( trim($_user_agent) == '' || !$_user_agent ) {
// NO USER AGENT??? This is not a WAP device
$this->_toLog('getDeviceCapabilitiesFromAgent', 'No user agent', LOG_ERR);
$curr_device = $this->_UserAgentInDB($this->user_agent);
// the exact user agent was in the WURFL - Great!
//DEBUG: echo "Found exact UA in DB!<br />".print_r($curr_device,true)."<br />";
$this->_GetFullCapabilities($curr_device['deviceID']);
// in case you have desktop web browsers in your patch file
// It was suggested by MOLABIB on 22Dec2006 that the following line
// is incorrect and should be replaced:
// $this->browser_is_wap = $this->capabilities['browser_is_wap'];
// the following is it's replacement:
//$this->browser_is_wap = $this->capabilities['product_info']['is_wireless_device'];
//$this->brand = $this->capabilities['product_info']['brand_name'];
//$this->model = $this->capabilities['product_info']['model_name'];
// the user agent was NOT in the WURFL - try to dissect it
// Used to be $_ua_len = strlen($_ua) - 1 but that resulted in erroneous results since
// the full string could still match UAs longer than itself in the DB
// Thanks to Christian Aune Thomassen [christian.thomassen(at)waptheweb.no] for finding this bug!
// Searching in wurfl_agents
// The user_agent should not become shorter than 4 characters
$this->_toLog('getDeviceCapabilitiesFromAgent', 'Searching the DB ('. $this->devtable. ')', LOG_INFO);
// I request to set a short list of UA's among which I should search an unknown user agent
$_last_good_short_ua = array();
// track number of queries
$niceua = rtrim($this->_sqlPrep(substr($_ua, 0, $_min_len)),"'"). "%'";
$minquery = "SELECT COUNT(deviceID) AS num FROM ". $this->devtable. " WHERE user_agent LIKE $niceua";
$_short_wurfl_ua = array();
//DEBUG: echo "no devices match the UA down to the min chars in the DB<br />$minquery";
// no devices match the UA down to the min chars in the DB
// basically you're not going to get a match.
// look for acceptable generic ID
if(!$this->_getGenericID($_user_agent)){
// no generic ID found - assuming this is not a WAP device
while ( $_ua_len > $_min_len) {
//DEBUG: echo "--trying user agent length $_ua_len<br />";
$_short_wurfl_ua = array();
$_short_ua = substr($_ua, 0, $_ua_len);
// take the user agent and prep it (escapes chars and adds single quotes
// then remove the right most quote and put %' in it's place which will
// make it this MOT- look like this: 'MOT-%' - that will work for MySQL LIKE queries
$niceua = rtrim($this->_sqlPrep($_short_ua),"'"). "%'";
$res = mysql_query("SELECT user_agent FROM ". $this->devtable. " WHERE user_agent LIKE $niceua",$this->dbcon);
// load the $_short_wurfl_ua array with matching user agents
|