@param string $userAgent

@return string
191085
public function normalize($userAgent)
    {
        $model     = WebOSHandler::getWebOSModelVersion($userAgent);
        $osVersion = WebOSHandler::getWebOSVersion($userAgent);

        if ($model !== null && $osVersion !== null) {
            $prefix = $model . ' ' . $osVersion . WurflConstants::RIS_DELIMITER;

            return $prefix . $userAgent;
        }

        return $userAgent;
    }
259497
public function applyConclusiveMatch($userAgent)
    {
        if (preg_match('#^.*?HTC.+?[/ ;]#', $userAgent, $matches)) {
            // The length of the complete match (from the beginning) is the tolerance
            $tolerance = strlen($matches[0]);
        } else {
            $tolerance = strlen($userAgent);
        }

        return $this->getDeviceIDFromRIS($userAgent, $tolerance);
    }
251742
public function browser($userAgent = null)
    {
        // Get browser rules
        $rules = $this->mergeRules(
            static::$additionalBrowsers, // NEW
            static::$browsers
        );

        return $this->findDetectionRulesAgainstUA($rules, $userAgent);
    }
994628
public function getBrowser(string $userAgent) : array
    {
        // Init variables
        $browserId = $this->getBrowserId($userAgent);
        $browserParentId = $this->getBrowserParentId($userAgent);

        // Get all settings for the found browser
        //
        // It's best to use a recursive query here, but some linux distributions like CentOS and RHEL
        // use old sqlite library versions (like 3.6.20 or 3.7.17), so that we have to check the
        // version here and fall back to multiple single queries in case of older versions.
        if (version_compare($this->getSqliteVersion(), '3.8.3', '>=')) {
            $query  = 'SELECT t3.property_key, t4.property_value FROM (';
            $query .= 'WITH RECURSIVE browser_recursive(browser_id,browser_parent_id) AS (';
            $query .= 'SELECT browser_id, browser_parent_id FROM browser WHERE browser_id = :id';
            $query .= ' UNION ALL ';
            $query .= 'SELECT browser.browser_id, browser.browser_parent_id FROM browser, browser_recursive ';
            $query .= 'WHERE browser_recursive.browser_parent_id IS NOT NULL ';
            $query .= 'AND browser.browser_id = browser_recursive.browser_parent_id';
            $query .= ') ';
            $query .= 'SELECT MAX(t2.browser_id) AS browser_id, t2.property_key_id FROM browser_recursive t1 ';
            $query .= 'JOIN browser_property t2 ON t2.browser_id = t1.browser_id ';
            $query .= 'GROUP BY t2.property_key_id';
            $query .= ') t1 ';
            $query .= 'JOIN browser_property t2 ON t2.browser_id = t1.browser_id ';
            $query .= 'AND t2.property_key_id = t1.property_key_id ';
            $query .= 'JOIN browser_property_key t3 ON t3.property_key_id = t2.property_key_id ';
            $query .= 'JOIN browser_property_value t4 ON t4.property_value_id = t2.property_value_id';
            $query .= ' UNION ALL ';
            $query .= 'SELECT \'browser_name_pattern\' AS property_key, browser_pattern AS property_value ';
            $query .= 'FROM browser WHERE browser_id = :id';
            $query .= ' UNION ALL ';
            $query .= 'SELECT \'Parent\' AS property_key, browser_pattern AS property_value ';
            $query .= 'FROM browser WHERE browser_id = :parent';
            $statement = $this->getAdapter()->prepare($query);
        } else {
            $query = 'SELECT browser_id, browser_parent_id FROM browser WHERE browser_id = :id';
            $statement = $this->getAdapter()->prepare($query);
            $browserIds = [];
            $lastBrowserId = $browserId;
            while ($lastBrowserId !== null) {
                $result = $statement->execute(['id' => $lastBrowserId]);

                $lastBrowserId = null;
                if (count($result) > 0) {
                    $browserIds[] = (int)$result[0]['browser_id'];
                    if ($result[0]['browser_parent_id'] !== null) {
                        $lastBrowserId = (int)$result[0]['browser_parent_id'];
                    }
                }
            }

            $query  = 'SELECT t3.property_key, t4.property_value FROM (';
            $query .= 'SELECT MAX(browser_id) AS browser_id, property_key_id ';
            $query .= 'FROM browser_property WHERE browser_id IN (' . implode(', ', $browserIds) . ') ';
            $query .= 'GROUP BY property_key_id';
            $query .= ') t1 ';
            $query .= 'JOIN browser_property t2 ON t2.browser_id = t1.browser_id ';
            $query .= 'AND t2.property_key_id = t1.property_key_id ';
            $query .= 'JOIN browser_property_key t3 ON t3.property_key_id = t2.property_key_id ';
            $query .= 'JOIN browser_property_value t4 ON t4.property_value_id = t2.property_value_id';
            $query .= ' UNION ALL ';
            $query .= 'SELECT \'browser_name_pattern\' AS property_key, browser_pattern AS property_value ';
            $query .= 'FROM browser WHERE browser_id = :id';
            $query .= ' UNION ALL ';
            $query .= 'SELECT \'Parent\' AS property_key, browser_pattern AS property_value ';
            $query .= 'FROM browser WHERE browser_id = :parent';
            $statement = $this->getAdapter()->prepare($query);
        }

        $properties = [];
        foreach ($statement->execute(['id' => $browserId, 'parent' => $browserParentId]) as $row) {
            $properties[$row['property_key']] = $row['property_value'];
        }

        // Set regular expression properties
        $propertyFilter = $this->getPropertyFilter();
        if (!$propertyFilter->isFiltered('browser_name_regex')) {
            $properties['browser_name_regex'] = $this->getRegExpForPattern($properties['browser_name_pattern']);
        }
        if ($propertyFilter->isFiltered('browser_name_pattern')) {
            unset($properties['browser_name_pattern']);
        }
        if ($propertyFilter->isFiltered('Parent')) {
            unset($properties['Parent']);
        }

        // IMPORTANT: Reset browserId and browserParentId for next call
        $this->browserId = null;
        $this->browserParentId = null;

        // The settings are in random order, so sort them
        return $this->sortProperties($properties);
    }
546520
public function applyConclusiveMatch($userAgent)
    {
        $tolerance = Utils::toleranceToRisDelimeter($userAgent);
        if ($tolerance !== false) {
            return $this->getDeviceIDFromRIS($userAgent, $tolerance);
        }

        $operaMiniIndex = Utils::indexOfOrLength($userAgent, 'Opera Mini');

        if ($operaMiniIndex !== false) {
            // Match up to the first '.' after 'Opera Mini'
            $tolerance = strpos($userAgent, '.', $operaMiniIndex);

            if ($tolerance !== false) {
                // +1 to match just after the '.'
                return $this->getDeviceIDFromRIS($userAgent, $tolerance + 1);
            }
        }

        $tolerance = Utils::firstSlash($userAgent);

        return $this->getDeviceIDFromRIS($userAgent, $tolerance);
    }
1066503
public function applyConclusiveMatch($userAgent)
    {
        $tolerance = Utils::toleranceToRisDelimeter($userAgent);
        if ($tolerance !== false) {
            return $this->getDeviceIDFromRIS($userAgent, $tolerance);
        }

        $tolerance = Utils::firstSlash($userAgent);

        return $this->getDeviceIDFromRIS($userAgent, $tolerance);
    }
213548
public static function clientInfo($useragent = '') {
		if (!$useragent) {
			$useragent = self::getIndpEnv('HTTP_USER_AGENT');
		}

		$bInfo = array();
			// Which browser?
		if (strpos($useragent, 'Konqueror') !== FALSE) {
			$bInfo['BROWSER'] = 'konqu';
		} elseif (strpos($useragent, 'Opera') !== FALSE) {
			$bInfo['BROWSER'] = 'opera';
		} elseif (strpos($useragent, 'MSIE') !== FALSE) {
			$bInfo['BROWSER'] = 'msie';
		} elseif (strpos($useragent, 'Mozilla') !== FALSE) {
			$bInfo['BROWSER'] = 'net';
		} elseif (strpos($useragent, 'Flash') !== FALSE) {
			$bInfo['BROWSER'] = 'flash';
		}
		if ($bInfo['BROWSER']) {
				// Browser version
			switch ($bInfo['BROWSER']) {
				case 'net':
					$bInfo['VERSION'] = doubleval(substr($useragent, 8));
					if (strpos($useragent, 'Netscape6/') !== FALSE) {
						$bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape6/'), 10));
					} // Will we ever know if this was a typo or intention...?! :-(
					if (strpos($useragent, 'Netscape/6') !== FALSE) {
						$bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape/6'), 10));
					}
					if (strpos($useragent, 'Netscape/7') !== FALSE) {
						$bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape/7'), 9));
					}
					break;
				case 'msie':
					$tmp = strstr($useragent, 'MSIE');
					$bInfo['VERSION'] = doubleval(preg_replace('/^[^0-9]*/', '', substr($tmp, 4)));
					break;
				case 'opera':
					$tmp = strstr($useragent, 'Opera');
					$bInfo['VERSION'] = doubleval(preg_replace('/^[^0-9]*/', '', substr($tmp, 5)));
					break;
				case 'konqu':
					$tmp = strstr($useragent, 'Konqueror/');
					$bInfo['VERSION'] = doubleval(substr($tmp, 10));
					break;
			}
			// Client system
			if (strpos($useragent, 'Win') !== FALSE) {
				$bInfo['SYSTEM'] = 'win';
			} elseif (strpos($useragent, 'Mac') !== FALSE) {
				$bInfo['SYSTEM'] = 'mac';
			} elseif (strpos($useragent, 'Linux') !== FALSE || strpos($useragent, 'X11') !== FALSE || strpos($useragent, 'SGI') !== FALSE || strpos($useragent, ' SunOS ') !== FALSE || strpos($useragent, ' HP-UX ') !== FALSE) {
				$bInfo['SYSTEM'] = 'unix';
			}
		}
		// Is TRUE if the browser supports css to format forms, especially the width
		$bInfo['FORMSTYLE'] = ($bInfo['BROWSER'] == 'msie' || ($bInfo['BROWSER'] == 'net' && $bInfo['VERSION'] >= 5) || $bInfo['BROWSER'] == 'opera' || $bInfo['BROWSER'] == 'konqu');

		return $bInfo;
	}
164770
public static function getSafariVersion($userAgent)
    {
        $search = 'Version/';
        $idx    = strpos($userAgent, $search);

        if ($idx === false) {
            return null;
        }

        $idx += strlen($search);
        $endIdx = strpos($userAgent, '.', $idx);

        if ($endIdx === false) {
            return null;
        }

        return substr($userAgent, $idx, $endIdx - $idx);
    }
968430
public static function getHTCMacModel($userAgent)
    {
        if (preg_match('/(HTC[^;\)]+)/', $userAgent, $matches)) {
            $model = preg_replace('#[ _\-/]#', '~', $matches[1]);

            return $model;
        }

        return null;
    }
528926
public function setUserAgent($userAgent) {
		$this->userAgent = $userAgent;
		$this->removeHeader(self::HEADER_KEY_USER_AGENT);
		$this->addHeader(self::HEADER_KEY_USER_AGENT, $userAgent);
		return $this;
	}
1055128
public static function getInstance($userAgent)
    {
        if (array_key_exists($userAgent, self::$deviceDetectorInstances)) {
            return self::$deviceDetectorInstances[$userAgent];
        }

        $deviceDetector = new DeviceDetector($userAgent);
        $deviceDetector->discardBotInformation();
        $deviceDetector->setCache(new DeviceDetectorCache(86400));
        $deviceDetector->parse();

        self::$deviceDetectorInstances[$userAgent] = $deviceDetector;

        return $deviceDetector;
    }
772925
public function normalize($userAgent)
    {
        $ucbVersion = UcwebU3Handler::getUcBrowserVersion($userAgent);

        if ($ucbVersion === null) {
            return $userAgent;
        }

        //Android U2K Mobile + Tablet
        if (Utils::checkIfContains($userAgent, 'Adr ')) {
            $model   = UcwebU3Handler::getUcAndroidModel($userAgent, false);
            $version = UcwebU3Handler::getUcAndroidVersion($userAgent, false);

            if ($model !== null && $version !== null) {
                $prefix = $version . ' U2Android ' . $ucbVersion . ' ' . $model . WurflConstants::RIS_DELIMITER;

                return $prefix . $userAgent;
            }
        } elseif (Utils::checkIfContains($userAgent, 'iPh OS')) {
            //iPhone U2K
            if (preg_match('/iPh OS (\d)_?(\d)?[ _\d]?.+; iPh(\d), ?(\d)\) U2/', $userAgent, $matches)) {
                $version = $matches[1] . '.' . $matches[2];
                $model   = $matches[3] . '.' . $matches[4];
                $prefix  = $version . ' U2iPhone ' . $ucbVersion . ' ' . $model . WurflConstants::RIS_DELIMITER;

                return $prefix . $userAgent;
            }
        } elseif (Utils::checkIfContains($userAgent, 'wds')) {
            //WP7&8 U2K
            //Add spaces and normalize
            $userAgent = preg_replace('|;(?! )|', '; ', $userAgent);
            if (preg_match(
                '/^UCWEB.+; wds (\d+)\.([\d]+);.+; ([ A-Za-z0-9_-]+); ([ A-Za-z0-9_-]+)\) U2/',
                $userAgent,
                $matches
            )) {
                $version = $matches[1] . '.' . $matches[2];
                $model   = $matches[3] . '.' . $matches[4];
                //Standard normalization stuff from WP matcher
                $model  = str_replace('_blocked', '', $model);
                $model  = preg_replace('/(NOKIA.RM-.+?)_.*/', '$1', $model, 1);
                $prefix = $version . ' U2WindowsPhone ' . $ucbVersion . ' ' . $model . WurflConstants::RIS_DELIMITER;

                return $prefix . $userAgent;
            }
        } elseif (Utils::checkIfContains($userAgent, 'Symbian')) {
            //Symbian U2K
            if (preg_match('/^UCWEB.+; S60 V(\d); .+; (.+)\) U2/', $userAgent, $matches)) {
                $version = 'S60 V' . $matches[1];
                $model   = $matches[2];
                $prefix  = $version . ' U2Symbian ' . $ucbVersion . ' ' . $model . WurflConstants::RIS_DELIMITER;

                return $prefix . $userAgent;
            }
        } elseif (Utils::checkIfContains($userAgent, 'Java')) {
            //Java U2K - check results for regex
            if (preg_match('/^UCWEB[^\(]+\(Java; .+; (.+)\) U2/', $userAgent, $matches)) {
                $version = 'Java';
                $model   = $matches[1];
                $prefix  = $version . ' U2JavaApp ' . $ucbVersion . ' ' . $model . WurflConstants::RIS_DELIMITER;

                return $prefix . $userAgent;
            }
        }

        return $userAgent;
    }
690399
public function applyConclusiveMatch($userAgent)
    {
        $match = $this->lookForMatchingUserAgent($userAgent);

        if (!empty($match)) {
            return $this->userAgentsWithDeviceID[$match];
        }

        return WurflConstants::NO_MATCH;
    }
648462
public function applyExactMatch($userAgent)
    {
        if (array_key_exists($userAgent, $this->userAgentsWithDeviceID)) {
            return $this->userAgentsWithDeviceID[$userAgent];
        }

        return WurflConstants::NO_MATCH;
    }
770866
public function applyConclusiveMatch($userAgent)
    {
        // Example Windows 8 RT MSIE 10 UA:
        // Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0; Touch)
        //                                                        ^ RIS Tolerance
        //Example Windows 8.1 RT MSIE 11 UA
        //Mozilla/5.0 (Windows NT 6.3; ARM; Trident/7.0; Touch; rv:11.0) like Gecko
        //																    	  ^ RIS Tolerance

        if (Utils::checkIfContainsAll($userAgent, array('like Gecko'))) {
            //Use this logic for MSIE 11 and above
            $search = ' Gecko';
            $idx    = strpos($userAgent, $search);
            if ($idx !== false) {
                // Match to the end of the search string
                return $this->getDeviceIDFromRIS($userAgent, $idx + strlen($search));
            }
        } else {
            $search = ' ARM;';
            $idx    = strpos($userAgent, $search);
            if ($idx !== false) {
                // Match to the end of the search string
                return $this->getDeviceIDFromRIS($userAgent, $idx + strlen($search));
            }
        }

        return WurflConstants::NO_MATCH;
    }
1008399
public function applyConclusiveMatch($userAgent)
    {
        $operaIndex = strpos($userAgent, 'Opera');
        $tolerance  = Utils::indexOfOrLength($userAgent, '.', $operaIndex);

        return $this->getDeviceIDFromRIS($userAgent, $tolerance);
    }
1304018
public function applyRecoveryCatchAllMatch($userAgent)
    {
        if (WURFL_Handlers_Utils::isDesktopBrowserHeavyDutyAnalysis($userAgent)) {
            return WURFL_Constants::GENERIC_WEB_BROWSER;
        }

        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'CoreMedia')) {
            return 'apple_iphone_coremedia_ver1';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'Windows CE')) {
            return 'generic_ms_mobile';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'UP.Browser/7.2')) {
            return 'opwv_v72_generic';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'UP.Browser/7')) {
            return 'opwv_v7_generic';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'UP.Browser/6.2')) {
            return 'opwv_v62_generic';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'UP.Browser/6')) {
            return 'opwv_v6_generic';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'UP.Browser/5')) {
            return 'upgui_generic';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'UP.Browser/4')) {
            return 'uptext_generic';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'UP.Browser/3')) {
            return 'uptext_generic';
        }
        // Series 60
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'Series60')) {
            return 'nokia_generic_series60';
        }
        // Access/Net Front
        if (WURFL_Handlers_Utils::checkIfContainsAnyOf($userAgent, array('NetFront/3.0', 'ACS-NF/3.0'))) {
            return 'generic_netfront_ver3';
        }
        if (WURFL_Handlers_Utils::checkIfContainsAnyOf($userAgent, array('NetFront/3.1', 'ACS-NF/3.1'))) {
            return 'generic_netfront_ver3_1';
        }
        if (WURFL_Handlers_Utils::checkIfContainsAnyOf($userAgent, array('NetFront/3.2', 'ACS-NF/3.2'))) {
            return 'generic_netfront_ver3_2';
        }
        if (WURFL_Handlers_Utils::checkIfContainsAnyOf($userAgent, array('NetFront/3.3', 'ACS-NF/3.3'))) {
            return 'generic_netfront_ver3_3';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'NetFront/3.4')) {
            return 'generic_netfront_ver3_4';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'NetFront/3.5')) {
            return 'generic_netfront_ver3_5';
        }
        if (WURFL_Handlers_Utils::checkIfContains($userAgent, 'NetFront/4.0')) {
            return 'generic_netfront_ver4_0';
        }
        // Contains Mozilla/, but not at the beginning of the UA
        // ie: MOTORAZR V8/R601_G_80.41.17R Mozilla/4.0 (compatible; MSIE 6.0 Linux; MOTORAZR V88.50) Profile/MIDP-2.0 Configuration/CLDC-1.1 Opera 8.50[zh]
        if (strpos($userAgent, 'Mozilla/') > 0) {
            return WURFL_Constants::GENERIC_XHTML;
        }
        if (WURFL_Handlers_Utils::checkIfContainsAnyOf($userAgent, array('Obigo', 'AU-MIC/2', 'AU-MIC-', 'AU-OBIGO/', 'Teleca Q03B1'))) {
            return WURFL_Constants::GENERIC_XHTML;
        }
        // DoCoMo
        if (WURFL_Handlers_Utils::checkIfStartsWithAnyOf($userAgent, array('DoCoMo', 'KDDI'))) {
            return 'docomo_generic_jap_ver1';
        }

        if (WURFL_Handlers_Utils::isMobileBrowser($userAgent)) {
            return WURFL_Constants::GENERIC_MOBILE;
        }
        return WURFL_Constants::GENERIC;
    }