/** * Builds the full capabilities array from the WURFL ID * @param String WURFL ID * @return void */ public function getFullCapabilities($deviceID) { if (is_null($deviceID)) { throw new Exception("Invalid Device ID: " . var_export($deviceID, true) . "\nMatcher: {$this->userAgentMatcher->matcherName()}\nUser Agent: " . $this->userAgent); exit(1); } // Now get all the devices in the fallback tree $fallbackIDs = array(); if ($deviceID != WurflConstants::$GENERIC && $this->db->db_implements_fallback) { $fallbackTree = $this->db->getDeviceFallBackTree($deviceID); $this->addTopLevelSettings($fallbackTree[0]); $fallbackTree = array_reverse($fallbackTree); foreach ($fallbackTree as $dev) { $fallbackIDs[] = $dev['id']; if (isset($dev['actual_device_root']) && $dev['actual_device_root']) { $this->matchData['actual_root_device'] = $dev['id']; } $this->addCapabilities($dev); } $this->matchData['fall_back_tree'] = implode(',', array_reverse($fallbackIDs)); } else { $fallbackTree = array(); $childDevice = $this->db->getDeviceFromID($deviceID); $fallbackTree[] = $childDevice; $fallbackIDs[] = $childDevice['id']; $currentDevice = $childDevice; $i = 0; /** * This loop starts with the best-matched device, and follows its fall_back until it reaches the GENERIC device * Lets use "tmobile_shadow_ver1" for an example: * * 'id' => 'tmobile_shadow_ver1', 'fall_back' => 'ms_mobile_browser_ver1' * 'id' => 'ms_mobile_browser_ver1', 'fall_back' => 'generic_xhtml' * 'id' => 'generic_xhtml', 'fall_back' => 'generic' * 'id' => 'generic', 'fall_back' => 'root' * * This fallback_tree in this example contains 4 elements in the order shown above. * */ while ($currentDevice['fall_back'] != "root") { $currentDevice = $this->db->getDeviceFromID($currentDevice['fall_back']); if (in_array($currentDevice['id'], $fallbackIDs)) { // 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. $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); throw new Exception("Killed script to prevent infinate loop. See log for details."); break; } if (!isset($currentDevice['fall_back']) || $currentDevice['fall_back'] == '') { $this->toLog("Empty fall_back detected. DeviceID: {$deviceID}, FallbackIDs: [" . implode(',', $fallbackIDs) . "]", LOG_ERR); throw new Exception("Empty fall_back detected. See log for details."); } $fallbackTree[] = $currentDevice; $fallbackIDs[] = $currentDevice['id']; $i++; if ($i > $this->maxDeviceDepth) { $this->toLog("Exceeded maxDeviceDepth while trying to build capabilities for device. DeviceID: {$deviceID}, FallbackIDs: [" . implode(',', $fallbackIDs) . "]", LOG_ERR); throw new Exception("Killed script to prevent infinate loop. See log for details."); break; } } $this->matchData['fall_back_tree'] = implode(',', $fallbackIDs); if ($fallbackTree[count($fallbackTree) - 1]['id'] != WurflConstants::$GENERIC) { // The device we are looking up cannot be traced back to the GENERIC device // and will likely not contain the correct capabilities $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); } /** * Merge the device capabilities from the parent (GENERIC) to the child (DeviceID) * We merge in this order because the GENERIC device contains all the properties that can be set * Then the next child modifies them, then the next child, and the next child, etc... */ while (count($fallbackTree) > 0) { $dev = array_pop($fallbackTree); // actual_root_device is the most accurate device in the fallback tree that is a "real" device, not a sub version or generic if (isset($dev['actual_device_root']) && $dev['actual_device_root']) { $this->matchData['actual_root_device'] = $dev['id']; } $this->addCapabilities($dev); } $this->addTopLevelSettings($childDevice); } }
/** * Returns the matching WURFL ID for a given User Agent * @return string WURFL ID */ protected function getDeviceIDFromRequestLoose() { $this->matcherHistory = array(); // Return generic UA if userAgent is empty if (strlen($this->httpRequest->user_agent) == 0) { $this->matchData['matcher'] = "none"; $this->matchData['match_type'] = "none"; $this->matchData['match'] = false; $this->setMatcherHistory(); if ($this->httpRequest->uaprof instanceof TeraWurflUserAgentProfile && $this->httpRequest->uaprof->containsValidUrl()) { return WurflConstants::GENERIC_MOBILE; } else { return WurflConstants::NO_MATCH; } } // Check for exact match if (TeraWurflConfig::$SIMPLE_DESKTOP_ENGINE_ENABLE && $this->httpRequest->user_agent == WurflConstants::SIMPLE_DESKTOP_UA) { // SimpleDesktop UA Matching avoids querying the database here $this->matchData['matcher'] = $this->userAgentMatcher->matcherName(); $this->matchData['match_type'] = "high_performance"; $this->matchData['match'] = true; $this->matcherHistory[] = $this->matchData['matcher'] . "(high_performance)"; $this->setMatcherHistory(); return WurflConstants::GENERIC_WEB_BROWSER; } else { $deviceID = $this->db->getDeviceFromUA($this->httpRequest->user_agent->normalized); } $this->matcherHistory[] = $this->userAgentMatcher->matcherName() . "(exact)"; if ($deviceID !== false) { $this->matchData['matcher'] = $this->userAgentMatcher->matcherName(); $this->matchData['match_type'] = "exact"; $this->matchData['match'] = true; $this->setMatcherHistory(); return $deviceID; } // Check for a conclusive match $deviceID = $this->userAgentMatcher->applyConclusiveMatch($this->httpRequest); $this->matcherHistory[] = $this->userAgentMatcher->matcherName() . "(conclusive)"; if ($deviceID != WurflConstants::NO_MATCH) { $this->matchData['matcher'] = $this->userAgentMatcher->matcherName(); $this->matchData['match_type'] = "conclusive"; $this->matchData['match'] = true; $this->setMatcherHistory(); return $deviceID; } /* // Check for Vodafone magic if ($this->userAgentMatcher->matcherName()!="VodafoneUserAgentMatcher" && $this->httpRequest->user_agent->contains("Vodafone")) { @require_once realpath(dirname(__FILE__).'/UserAgentMatchers/VodafoneUserAgentMatcher.php'); $vodafoneUserAgentMatcher = new VodafoneUserAgentMatcher($this); $this->matcherHistory[] = $vodafoneUserAgentMatcher->matcherName() . "(conclusive)"; $deviceID = $vodafoneUserAgentMatcher->applyConclusiveMatch($this->httpRequest); if ($deviceID != WurflConstants::NO_MATCH) { $this->matchData['matcher'] = $vodafoneUserAgentMatcher->matcherName(); $this->matchData['match_type'] = "conclusive"; $this->matchData['match'] = true; $this->setMatcherHistory(); return $deviceID; } } */ // Check for recovery match $deviceID = $this->userAgentMatcher->applyRecoveryMatch($this->httpRequest); $this->matcherHistory[] = $this->userAgentMatcher->matcherName() . "(recovery)"; if ($deviceID != WurflConstants::NO_MATCH) { $this->matchData['matcher'] = $this->userAgentMatcher->matcherName(); $this->matchData['match_type'] = "recovery"; $this->matchData['match'] = false; $this->setMatcherHistory(); return $deviceID; } // Check CatchAll if it's not already in use if ($this->userAgentMatcher->matcherName() != "CatchAllUserAgentMatcher") { $catchAllUserAgentMatcher = new CatchAllUserAgentMatcher($this); $this->matcherHistory[] = $catchAllUserAgentMatcher->matcherName() . "(recovery)"; $deviceID = $catchAllUserAgentMatcher->applyRecoveryMatch($this->httpRequest); if ($deviceID != WurflConstants::NO_MATCH) { // The CatchAll matcher is intelligent enough to determine the match properties $this->matchData['matcher'] = $catchAllUserAgentMatcher->matcher; $this->matchData['match_type'] = $catchAllUserAgentMatcher->match_type; $this->matchData['match'] = $catchAllUserAgentMatcher->match; $this->setMatcherHistory(); return $deviceID; } } // A matching device still hasn't been found - check HTTP ACCEPT headers if ($this->httpRequest->accept->length() > 0) { $this->matcherHistory[] = 'http_accept'; if ($this->httpRequest->accept->contains('application/vnd.wap.xhtml+xml')) { $this->matchData['matcher'] = 'http_accept'; $this->matchData['match_type'] = 'recovery'; // This isn't really a match, it's a suggestion $this->matchData['match'] = false; $this->setMatcherHistory(); return WurflConstants::GENERIC_MOBILE; } } $this->matchData['matcher'] = "none"; $this->matchData['match_type'] = "none"; $this->matchData['match'] = false; $this->setMatcherHistory(); if ($this->httpRequest->uaprof instanceof TeraWurflUserAgentProfile && $this->httpRequest->uaprof->containsValidUrl()) { return WurflConstants::GENERIC_MOBILE; } if ($this->httpRequest->isMobileBrowser()) { return WurflConstants::GENERIC_MOBILE; } if ($this->httpRequest->isSmartTV()) { return WurflConstants::GENERIC_SMARTTV; } return WurflConstants::GENERIC; }