TeraWurfl
[ class tree: TeraWurfl ] [ index: TeraWurfl ] [ all elements ]

Source for file TeraWurfl.php

Documentation is available at TeraWurfl.php

  1. <?php
  2. /**
  3.  * Tera_WURFL - PHP MySQL driven WURFL
  4.  * 
  5.  * Tera-WURFL was written by Steve Kamerman, and is based on the
  6.  * Java WURFL Evolution package by Luca Passani and WURFL PHP Tools by Andrea Trassati.
  7.  * This version uses a MySQL database to store the entire WURFL file, multiple patch
  8.  * files, and a persistent caching mechanism to provide extreme performance increases.
  9.  * 
  10.  * @package TeraWurfl
  11.  * @author Steve Kamerman <stevekamerman AT gmail.com>
  12.  * @version Stable 2.1.3 $Date: 2010/09/18 15:43:21
  13.  * @license http://www.mozilla.org/MPL/ MPL Vesion 1.1
  14.  */
  15. /**#@+
  16.  * Include required files
  17.  */
  18. require_once realpath(dirname(__FILE__).'/TeraWurflConfig.php');
  19. require_once realpath(dirname(__FILE__).'/DatabaseConnectors/TeraWurflDatabase.php');
  20. require_once realpath(dirname(__FILE__).'/TeraWurflLoader.php');
  21. require_once realpath(dirname(__FILE__).'/UserAgentFactory.php');
  22. require_once realpath(dirname(__FILE__).'/UserAgentUtils.php');
  23. require_once realpath(dirname(__FILE__).'/WurflConstants.php');
  24. require_once realpath(dirname(__FILE__).'/WurflSupport.php');
  25. require_once realpath(dirname(__FILE__).'/UserAgentMatchers/UserAgentMatcher.php');
  26. /**#@-*/
  27. /**
  28.  * The main Tera-WURFL Class, provides all end-user methods and properties for interacting
  29.  * with Tera-WURFL
  30.  * 
  31.  * @package TeraWurfl
  32.  */
  33. class TeraWurfl{
  34.     
  35.     public static $SETTING_WURFL_VERSION 'wurfl_version';
  36.     public static $SETTING_WURFL_DATE 'wurfl_date';
  37.     public static $SETTING_LOADED_DATE 'loaded_date';
  38.     public static $SETTING_PATCHES_LOADED 'patches_loaded';
  39.     /**
  40.      * Array of errors that were encountered while processing the request
  41.      * @var Array 
  42.      */
  43.     public $errors;
  44.     /**
  45.      * Array of WURFL capabilities of the requested device
  46.      * @var Array 
  47.      */
  48.     public $capabilities;
  49.     /**
  50.      * Database connector to be used, must extend TeraWurflDatabase.  All database functions are performed
  51.      * in the database connector through its methods and properties.
  52.      * @see TeraWurflDatabase
  53.      * @see TeraWurflDatabase_MySQL5
  54.      * @var TeraWurflDatabase 
  55.      */
  56.     public $db = false;
  57.     /**
  58.      * The directory that TeraWurfl.php is in
  59.      * @var String 
  60.      */
  61.     public $rootdir;
  62.     /**
  63.      * The user agent that is being evaluated
  64.      * @var String 
  65.      */
  66.     public $userAgent
  67.     /**
  68.      * The HTTP Accept header that is being evaluated
  69.      * @var String 
  70.      */
  71.     public $httpAccept;
  72.     /**
  73.      * The UserAgentMatcher that is currently in use
  74.      * @var UserAgentMatcher 
  75.      */
  76.     public $userAgentMatcher;
  77.     /**
  78.      * Was the evaluated device found in the cache
  79.      * @var Bool 
  80.      */
  81.     public $foundInCache;
  82.     
  83.     /**
  84.      * The installed branch of Tera-WURFL
  85.      * @var String 
  86.      */
  87.     public $release_branch = "Stable";
  88.     /**
  89.      * The installed version of Tera-WURFL
  90.      * @var String 
  91.      */
  92.     public $release_version = "2.1.3";
  93.     /**
  94.      * The required version of PHP for this release
  95.      * @var String 
  96.      */
  97.     public static $required_php_version "5.0.0";
  98.     
  99.     /**
  100.      * Lookup start time
  101.      * @var int 
  102.      */
  103.     protected $lookup_start;
  104.     /**
  105.      * Lookup end time
  106.      * @var int 
  107.      */
  108.     protected $lookup_end;
  109.     /**
  110.      * The array key that is returned as a WURFL capability group in the capabilities
  111.      * array that stored Tera-WURFL specific information about the request
  112.      * @var String 
  113.      */
  114.     protected $matchDataKey = "tera_wurfl";
  115.     /**
  116.      * The Tera-WURFL specific data that is added to the capabilities array
  117.      * @var array 
  118.      */
  119.     protected $matchData;
  120.     /**
  121.      * Array of UserAgentMatchers and match attempt types that the API used to find a matching device
  122.      * @var Array 
  123.      */
  124.     protected $matcherHistory;
  125.     /*
  126.      * This keeps the device fallback lookup from running away.
  127.      * The deepest device I've seen is sonyericsson_z520a_subr3c at 15
  128.      */
  129.     protected $maxDeviceDepth = 40;
  130.     
  131.     // Constructor
  132.     public function __construct(){
  133.         $this->errors = array();
  134.         $this->capabilities = array();
  135.         $this->matcherHistory = array();
  136.         $this->rootdir = dirname(__FILE__).'/';
  137.         $dbconnector 'TeraWurflDatabase_'.TeraWurflConfig::$DB_CONNECTOR;
  138.         if($this->db === false$this->db = new $dbconnector;
  139.         if(!$this->db->connect()){
  140.             //throw new Exception("Cannot connect to database: ".$this->db->getLastError());
  141.             return false;
  142.         }
  143.     }
  144.     
  145.     /**
  146.      * Returns the matching WURFL ID for a given User Agent
  147.      * @return String WURFL ID
  148.      */
  149.     protected function getDeviceIDFromUALoose(){
  150.         $this->matcherHistory = array();
  151.         // Return generic UA if userAgent is empty
  152.         if(strlen($this->userAgent)==0){
  153.             $this->matchData['matcher'"none"
  154.             $this->matchData['match_type'"none";
  155.             $this->matchData['match'false;
  156.             $this->setMatcherHistory();
  157.             return WurflConstants::$GENERIC;
  158.         }
  159.         
  160.         // Check for exact match
  161.         if(TeraWurflConfig::$SIMPLE_DESKTOP_ENGINE_ENABLE && $this->userAgent == WurflConstants::$SIMPLE_DESKTOP_UA){
  162.             // SimpleDesktop UA Matching avoids querying the database here
  163.             $deviceID WurflConstants::$GENERIC_WEB_BROWSER;
  164.         }else{
  165.             $deviceID $this->db->getDeviceFromUA($this->userAgent);
  166.         }
  167.         $this->matcherHistory[$this->userAgentMatcher->matcherName("(exact)";
  168.         if($deviceID !== false){
  169.             $this->matchData['matcher'$this->userAgentMatcher->matcherName();
  170.             $this->matchData['match_type'"exact";
  171.             $this->matchData['match'true;
  172.             $this->setMatcherHistory();
  173.             return $deviceID;
  174.         }
  175.         // Check for a conclusive match
  176.         $deviceID $this->userAgentMatcher->applyConclusiveMatch($this->userAgent);
  177.         $this->matcherHistory[$this->userAgentMatcher->matcherName("(conclusive)";
  178.         if($deviceID != WurflConstants::$GENERIC){
  179.             $this->matchData['matcher'$this->userAgentMatcher->matcherName();
  180.             $this->matchData['match_type'"conclusive";
  181.             $this->matchData['match'true;
  182.             $this->setMatcherHistory();
  183.             return $deviceID;
  184.         }
  185.         // Check for Vodafone magic
  186.         if($this->userAgentMatcher->matcherName()!="VodafoneUserAgentMatcher" && UserAgentMatcher::contains($this->userAgent,"Vodafone")){
  187.             @require_once realpath(dirname(__FILE__).'/UserAgentMatchers/VodafoneUserAgentMatcher.php');
  188.             $vodafoneUserAgentMatcher new VodafoneUserAgentMatcher($this);
  189.             $this->matcherHistory[$vodafoneUserAgentMatcher->matcherName("(conclusive)";
  190.             $deviceID $vodafoneUserAgentMatcher->applyConclusiveMatch($this->userAgent);
  191.             if($deviceID != WurflConstants::$GENERIC){
  192.                 $this->matchData['matcher'$vodafoneUserAgentMatcher->matcherName();
  193.                 $this->matchData['match_type'"conclusive";
  194.                 $this->matchData['match'true;
  195.                 $this->setMatcherHistory();
  196.                 return $deviceID;
  197.             }
  198.         }
  199.         // Check for recovery match
  200.         $deviceID $this->userAgentMatcher->applyRecoveryMatch($this->userAgent);
  201.         $this->matcherHistory[$this->userAgentMatcher->matcherName("(recovery)";
  202.         if($deviceID != WurflConstants::$GENERIC){
  203.             $this->matchData['matcher'$this->userAgentMatcher->matcherName();
  204.             $this->matchData['match_type'"recovery";
  205.             $this->matchData['match'false;
  206.             $this->setMatcherHistory();
  207.             return $deviceID;
  208.         }
  209.         // Check CatchAll if it's not already in use
  210.         if($this->userAgentMatcher->matcherName()!="CatchAllUserAgentMatcher"){
  211.             $catchAllUserAgentMatcher new CatchAllUserAgentMatcher($this);
  212.             $this->matcherHistory[$catchAllUserAgentMatcher->matcherName("(recovery)";
  213.             $deviceID $catchAllUserAgentMatcher->applyRecoveryMatch($this->userAgent);
  214.             if($deviceID != WurflConstants::$GENERIC){
  215.                 // The CatchAll matcher is intelligent enough to determine the match properties
  216.                 $this->matchData['matcher'$catchAllUserAgentMatcher->matcher;
  217.                 $this->matchData['match_type'$catchAllUserAgentMatcher->match_type;
  218.                 $this->matchData['match'$catchAllUserAgentMatcher->match;
  219.                 $this->setMatcherHistory();
  220.                 return $deviceID;
  221.             }
  222.         }
  223.         
  224.         // A matching device still hasn't been found - check HTTP ACCEPT headers
  225.         if(strlen($this->httpAccept0){
  226.             $this->matcherHistory["http_accept";
  227.             if(UserAgentMatcher::contains($this->httpAccept,array(
  228.                 WurflConstants::$ACCEPT_HEADER_VND_WAP_XHTML_XML,
  229.                 WurflConstants::$ACCEPT_HEADER_XHTML_XML,
  230.                 WurflConstants::$ACCEPT_HEADER_TEXT_HTML
  231.               ))){
  232.                 $this->matchData['matcher'"http_accept";
  233.                 $this->matchData['match_type'"recovery";
  234.                 // This isn't really a match, it's a suggestion
  235.                 $this->matchData['match'false;
  236.                 $this->setMatcherHistory();
  237.                 return WurflConstants::$GENERIC_XHTML;
  238.             }
  239.         }
  240.         $this->matchData['matcher'"none";
  241.         $this->matchData['match_type'"none";
  242.         $this->matchData['match'false;
  243.         $this->setMatcherHistory();
  244.         
  245.         if(UserAgentUtils::isMobileBrowser($this->userAgent)) return WurflConstants::$GENERIC_XHTML;
  246.         return WurflConstants::$GENERIC_WEB_BROWSER;
  247.     }
  248.     /**
  249.      * Detects the capabilities from a given request object ($_SERVER)
  250.      * @param Array Request object ($_SERVER contains this data)
  251.      * @return Bool Match
  252.      */
  253.     public function getDeviceCapabilitiesFromRequest($server){
  254.         if(!isset($server))$server $_SERVER;
  255.         return $this->getDeviceCapabilitiesFromAgent(WurflSupport::getUserAgent($server),WurflSupport::getAcceptHeader($server));
  256.     }
  257.     /**
  258.      * Detects the capabilities of a device from a given user agent and optionally, the HTTP Accept Headers
  259.      * @param String HTTP User Agent
  260.      * @param String HTTP Accept Header
  261.      * @return Bool matching device was found
  262.      */
  263.     public function getDeviceCapabilitiesFromAgent($userAgent=null,$httpAccept=null){
  264.         $this->db->numQueries 0;
  265.         $this->matchData = array(
  266.             "num_queries" => 0,
  267.             "actual_root_device" => '',
  268.             "match_type" => '',
  269.             "matcher" => '',
  270.             "match"    => false,
  271.             "lookup_time" => 0,
  272.             "fall_back_tree" => ''
  273.         );
  274.         $this->lookup_start = microtime(true);
  275.         $this->foundInCache = false;
  276.         $this->capabilities = array();
  277.         // Define User Agent
  278.         $this->userAgent = (is_null($userAgent))WurflSupport::getUserAgent()$userAgent;
  279.         if(strlen($this->userAgent255$this->userAgent = substr($this->userAgent,0,255);
  280.         // Use the ultra high performance SimpleDesktopMatcher if enabled
  281.         if(TeraWurflConfig::$SIMPLE_DESKTOP_ENGINE_ENABLE){
  282.             require_once realpath(dirname(__FILE__).'/UserAgentMatchers/SimpleDesktopUserAgentMatcher.php');
  283.             if(SimpleDesktopUserAgentMatcher::isDesktopBrowser($userAgent)) $this->userAgent = WurflConstants::$SIMPLE_DESKTOP_UA;
  284.         }
  285.         // Define HTTP ACCEPT header.  Default: DO NOT use HTTP_ACCEPT headers
  286.         //$this->httpAccept= (is_null($httpAccept))? WurflSupport::getAcceptHeader(): $httpAccept;
  287.         $this->userAgent = UserAgentUtils::cleanUserAgent($this->userAgent);
  288.         // Check cache for device
  289.         if(TeraWurflConfig::$CACHE_ENABLE){
  290.             $cacheData $this->db->getDeviceFromCache($this->userAgent);
  291.             // Found in cache
  292.             if($cacheData !== false){
  293.                 $this->capabilities = $cacheData;
  294.                 $this->foundInCache = true;
  295.                 $deviceID $cacheData['id'];
  296.             }
  297.         }
  298.         if(!$this->foundInCache){
  299.             require_once realpath(dirname(__FILE__).'/UserAgentMatchers/SimpleDesktopUserAgentMatcher.php');
  300.             // Find appropriate user agent matcher
  301.             $this->userAgentMatcher = UserAgentFactory::createUserAgentMatcher($this,$this->userAgent);
  302.             // Find the best matching WURFL ID
  303.             $deviceID $this->getDeviceIDFromUALoose($userAgent);
  304.             // Get the capabilities of this device and all its ancestors
  305.             $this->getFullCapabilities($deviceID);
  306.             // Now add in the Tera-WURFL results array
  307.             $this->lookup_end = microtime(true);
  308.             $this->matchData['num_queries'$this->db->numQueries;
  309.             $this->matchData['lookup_time'$this->lookup_end - $this->lookup_start;
  310.             // Add the match data to the capabilities array so it gets cached
  311.             $this->addCapabilities(array($this->matchDataKey => $this->matchData));
  312.         }
  313.         if(TeraWurflConfig::$CACHE_ENABLE==true && !$this->foundInCache){
  314.             // Since this device was not cached, cache it now.
  315.             $this->db->saveDeviceInCache($this->userAgent,$this->capabilities);
  316.         }
  317.         return $this->capabilities[$this->matchDataKey]['match'];
  318.     }
  319.     /**
  320.      * Builds the full capabilities array from the WURFL ID
  321.      * @param String WURFL ID
  322.      * @return void 
  323.      */
  324.     public function getFullCapabilities($deviceID){
  325.         if(is_null($deviceID)){
  326.             throw new Exception("Invalid Device ID: ".var_export($deviceID,true)."\nMatcher: {$this->userAgentMatcher->matcherName()}\nUser Agent: ".$this->userAgent);
  327.             exit(1);
  328.         }
  329.         // Now get all the devices in the fallback tree
  330.         $fallbackIDs array();
  331.         if($deviceID != WurflConstants::$GENERIC && $this->db->db_implements_fallback){
  332.             $fallbackTree $this->db->getDeviceFallBackTree($deviceID);
  333.             $this->addTopLevelSettings($fallbackTree[0]);
  334.             $fallbackTree array_reverse($fallbackTree);
  335.             foreach($fallbackTree as $dev){
  336.                 $fallbackIDs[$dev['id'];
  337.                 if(isset($dev['actual_device_root']&& $dev['actual_device_root'])$this->matchData['actual_root_device'$dev['id'];
  338.                 $this->addCapabilities($dev);
  339.             }
  340.             $this->matchData['fall_back_tree'implode(',',array_reverse($fallbackIDs));
  341.         }else{
  342.             $fallbackTree array();
  343.             $childDevice $this->db->getDeviceFromID($deviceID);
  344.             $fallbackTree[$childDevice;
  345.             $fallbackIDs[$childDevice['id'];
  346.             $currentDevice $childDevice;
  347.             $i=0;
  348.             /**
  349.              * This loop starts with the best-matched device, and follows its fall_back until it reaches the GENERIC device
  350.              * Lets use "tmobile_shadow_ver1" for an example:
  351.              * 
  352.              * 'id' => 'tmobile_shadow_ver1', 'fall_back' => 'ms_mobile_browser_ver1'
  353.              * 'id' => 'ms_mobile_browser_ver1', 'fall_back' => 'generic_xhtml'
  354.              * 'id' => 'generic_xhtml', 'fall_back' => 'generic'
  355.              * 'id' => 'generic', 'fall_back' => 'root'
  356.              * 
  357.              * This fallback_tree in this example contains 4 elements in the order shown above.
  358.              * 
  359.              */
  360.             while($currentDevice['fall_back'!= "root"){
  361.                 $currentDevice $this->db->getDeviceFromID($currentDevice['fall_back']);
  362.                 if(in_array($currentDevice['id'],$fallbackIDs)){
  363.                     // The device we just looked up is already in the list, which means that
  364.                     // we are going to enter an infinate loop if we don't break from it.
  365.                     $this->toLog("The device we just looked up is already in the list, which means that we are going to enter an infinate loop if we don't break from it. DeviceID: $deviceID, FallbackIDs: [".implode(',',$fallbackIDs)."]",LOG_ERR);
  366.                     throw new Exception("Killed script to prevent infinate loop.  See log for details.");
  367.                     break;
  368.                 }
  369.                 if(!isset($currentDevice['fall_back']|| $currentDevice['fall_back'== ''){
  370.                     $this->toLog("Empty fall_back detected. DeviceID: $deviceID, FallbackIDs: [".implode(',',$fallbackIDs)."]",LOG_ERR);
  371.                     throw new Exception("Empty fall_back detected.  See log for details.");
  372.                 }
  373.                 $fallbackTree[$currentDevice;
  374.                 $fallbackIDs[$currentDevice['id'];
  375.                 $i++;
  376.                 if($i $this->maxDeviceDepth){
  377.                     $this->toLog("Exceeded maxDeviceDepth while trying to build capabilities for device. DeviceID: $deviceID, FallbackIDs: [".implode(',',$fallbackIDs)."]",LOG_ERR);
  378.                     throw new Exception("Killed script to prevent infinate loop.  See log for details.");
  379.                     break;
  380.                 }
  381.             }
  382.             $this->matchData['fall_back_tree'implode(',',$fallbackIDs);
  383.             if($fallbackTree[count($fallbackTree)-1]['id'!= WurflConstants::$GENERIC){
  384.                 // The device we are looking up cannot be traced back to the GENERIC device
  385.                 // and will likely not contain the correct capabilities
  386.                 $this->toLog("The device we are looking up cannot be traced back to the GENERIC device and will likely not contain the correct capabilities. DeviceID: $deviceID, FallbackIDs: [".implode(',',$fallbackIDs)."]",LOG_ERR);
  387.             }
  388.             /**
  389.              * Merge the device capabilities from the parent (GENERIC) to the child (DeviceID)
  390.              * We merge in this order because the GENERIC device contains all the properties that can be set
  391.              * Then the next child modifies them, then the next child, and the next child, etc... 
  392.              */
  393.             while(count($fallbackTree)>0){
  394.                 $dev array_pop($fallbackTree);
  395.                 // actual_root_device is the most accurate device in the fallback tree that is a "real" device, not a sub version or generic
  396.                 if(isset($dev['actual_device_root']&& $dev['actual_device_root'])$this->matchData['actual_root_device'$dev['id'];
  397.                 $this->addCapabilities($dev);
  398.             }
  399.             $this->addTopLevelSettings($childDevice);
  400.         }
  401.     }
  402.     /**
  403.      * Returns the value of the requested capability for the detected device
  404.      * @param String Capability name (e.g. "is_wireless_device")
  405.      * @return Mixed Capability value
  406.      */
  407.     public function getDeviceCapability($capability{
  408.         // TODO: Optimize function, one method is to flatten the capabilities array, or create a group=>cap index
  409.         $this->toLog('Searching for '.$capability.' as a capability'LOG_INFO);
  410.         foreach $this->capabilities as $group {
  411.             if !is_array($group) ) {
  412.                 continue;
  413.             }
  414.             while list($key$value)=each($group) ) {
  415.                 if ($key==$capability{
  416.                     $this->toLog('I found it, value is '.$valueLOG_INFO);
  417.                     return $value;
  418.                 }
  419.             }
  420.         }
  421.         $this->toLog('I could not find the requested capability ('.$capability.'), returning NULL'LOG_WARNING);
  422.         // since 1.5.2, I can't return "false" because that is a valid value.  Now I return NULL, use is_null() to check
  423.         return null;
  424.     }
  425.     /**
  426.      * Returns the value of the given setting name
  427.      * @param String Setting value
  428.      */
  429.     public function getSetting($key){
  430.         return $this->db->getSetting($key);
  431.     }
  432.     public function fullTableName(){
  433.         return TeraWurflConfig::$TABLE_PREFIX.'_'.$this->userAgentMatcher->tableSuffix();
  434.     }
  435.     /**
  436.      * Log an error in the Tera-WURFL log file
  437.      * @see TeraWurflConfig
  438.      * @param String The error message text
  439.      * @param Int The log level / severity of the error
  440.      * @param String The function or code that was being run when the error occured
  441.      * @return void 
  442.      */
  443.     public function toLog($text$requestedLogLevel=LOG_NOTICE$func="Tera-WURFL"){
  444.         if($requestedLogLevel == LOG_ERR$this->errors[$text;
  445.         if (TeraWurflConfig::$LOG_LEVEL == || ($requestedLogLevel-1>= TeraWurflConfig::$LOG_LEVEL {
  446.             return;
  447.         }
  448.         if $requestedLogLevel == LOG_ERR {
  449.             $warn_banner 'ERROR: ';
  450.         else if $requestedLogLevel == LOG_WARNING {
  451.             $warn_banner 'WARNING: ';
  452.         else {
  453.             $warn_banner '';
  454.         }
  455.         $_textToLog date('r')." [".php_uname('n')." ".getmypid()."]"."[$func".$warn_banner $text;
  456.         $logfile $this->rootdir.TeraWurflConfig::$DATADIR.TeraWurflConfig::$LOG_FILE;
  457.         if(!is_writeable($logfile)){
  458.             throw new Exception("Tera-WURFL Error: cannot write to log file ($logfile)");
  459.         }
  460.         $_logFP fopen($logfile"a+");
  461.         fputs($_logFP$_textToLog."\n");
  462.         fclose($_logFP);
  463.     }
  464.     /**
  465.      * Adds the top level properties to the capabilities array, like id and user_agent
  466.      * @param Array New properties to be added
  467.      * @return void 
  468.      */
  469.     public function addTopLevelSettings(Array $newCapabilities){
  470.         foreach($newCapabilities as $key => $val){
  471.             if(is_array($val))continue;
  472.             $this->capabilities[$key$val;
  473.         }
  474.     }
  475.     /**
  476.      * Add new capabilities to the capabilities array
  477.      * @param Array Capabilities that are to be added
  478.      * @return void 
  479.      */
  480.     public function addCapabilities(Array $newCapabilities){
  481.         self::mergeCapabilities($this->capabilities,$newCapabilities);
  482.     
  483.     /**
  484.      * Combines the MatcherHistory array into a string and stores it in the matchData
  485.      * @return void 
  486.      */
  487.     protected function setMatcherHistory(){
  488.         $this->matchData['matcher_history'implode(',',$this->matcherHistory);
  489.     }
  490.     /**
  491.      * Merges given $addedDevice array onto $baseDevice array
  492.      * @param Array Main capabilities array
  493.      * @param Array New capabilities array
  494.      * @return void 
  495.      */
  496.     public static function mergeCapabilities(Array &$baseDeviceArray $addedDevice){
  497.         if(count($baseDevice== 0){
  498.             // Base device is empty
  499.             $baseDevice $addedDevice;
  500.             return;
  501.         }
  502.         foreach($addedDevice as $levOneKey => $levOneVal){
  503.             // Check if the base device has defined this value yet
  504.             if(!is_array($levOneVal)){
  505.                 // This is top level setting, not a capability
  506.                 continue;
  507.             }else{
  508.                 if(!array_key_exists($levOneKey,$baseDevice))$baseDevice[$levOneKey]=array();
  509.                 // This is an array value, merge the contents
  510.                 foreach($levOneVal as $levTwoKey => $levTwoVal){
  511.                     // This is just a scalar value, apply it
  512.                     $baseDevice[$levOneKey][$levTwoKey$levTwoVal;
  513.                     continue;
  514.                 }
  515.             }
  516.         }
  517.     }
  518.     /**
  519.      * Get the absolute path to the data directory on the filesystem
  520.      * @return String Absolute path to data directory
  521.      */
  522.     public static function absoluteDataDir(){
  523.         return dirname(__FILE__).'/'.TeraWurflConfig::$DATADIR;
  524.     }
  525. }

Documentation generated on Sun, 19 Sep 2010 00:15:56 +0000 by phpDocumentor 1.4.3