@param string $userAgent

@return string
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;
    }
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;
    }
834627
public static function toleranceToRisDelimeter($userAgent)
    {
        $tolerance = strpos($userAgent, WurflConstants::RIS_DELIMITER);
        if ($tolerance === false) {
            return false;
        }

        // Push the tolerance to the *end* of the RIS Delimiter
        return $tolerance + strlen(WurflConstants::RIS_DELIMITER);
    }
488282
protected function filterBrowserNames(array $userAgent)
    {
        // IE11 hasn't 'MSIE' in its user agent string
        if (empty($userAgent['browser_name']) && $userAgent['browser_engine'] === 'trident' && strpos($userAgent['string'], 'rv:')) {
            $userAgent['browser_name'] = 'msie';
            $userAgent['browser_version'] = preg_replace('|.+rv:([0-9]+(?:\.[0-9]+)+).+|', '$1', $userAgent['string']);

            return $userAgent;
        }

        return $userAgent;
    }
127303
public function platform($userAgent = null)
    {
        // Get platform rules
        $rules = $this->mergeRules(
            static::$operatingSystems,
            static::$additionalOperatingSystems // NEW
        );

        return $this->findDetectionRulesAgainstUA($rules, $userAgent);
    }
302623
public function parse(string $useragent): array
    {
        if (preg_match('/new-sogou-spider|zollard|socialradarbot|microsoft office protocol discovery|powermarks|archivebot|marketwirebot|microsoft-cryptoapi|pad-bot|james bot|winhttp|jobboerse|<|>|online-versicherungsportal\.info|versicherungssuchmaschine\.net|microsearch|microsoft data access|microsoft url control|infegyatlas|msie or firefox mutant|semantic-visions\.com crawler|labs\.topsy\.com\/butterfly|dolphin http client|google wireless transcoder|commoncrawler|ipodder|tripadvisor|nokia wap gateway|outclicksbot/i', $useragent)) {
            return $this->load('unknown', 'unknown', $useragent);
        }

        if (!preg_match('/freebsd|raspbian/i', $useragent)
            && preg_match('/darwin|cfnetwork/i', $useragent)
        ) {
            return $this->darwinParser->parse($useragent);
        }

        if ($this->mobileDevice->isMobile($useragent)) {
            return $this->mobileParser->parse($useragent);
        }

        if ($this->tvDevice->isTvDevice($useragent)) {
            return $this->tvParser->parse($useragent);
        }

        if ($this->desktopDevice->isDesktopDevice($useragent)) {
            return $this->desktopParser->parse($useragent);
        }

        return $this->load('unknown', 'unknown', $useragent);
    }
1084232
public static function isRobot($userAgent) {
		if (self::$_is_robot !== null) return self::$_is_robot;
		self::$_is_robot = false;
		$userAgent = strtolower($userAgent);
		foreach (self::$robots as $key) {
			if (strpos($userAgent, $key) !== false) {
				self::$_is_robot = true;
				break;
			}
		}
		return self::$_is_robot;
	}
423656
public function normalize($userAgent)
    {
        // Repair Opera user agents using fake version 9.80
        // Normalize: Opera/9.80 (X11; Linux x86_64; U; sv) Presto/2.9.168 Version/11.50
        // Into: Opera/11.50 (X11; Linux x86_64; U; sv) Presto/2.9.168 Version/11.50
        if (Utils::checkIfStartsWith($userAgent, 'Opera/9.80')) {
            if (preg_match('#Version/(\d+\.\d+)#', $userAgent, $matches)) {
                $userAgent = str_replace('Opera/9.80', 'Opera/' . $matches[1], $userAgent);
            }

            //Match to the '.' in the Opera version number
            return $userAgent;
        }
        //Normalize Opera v15 and above UAs, that say OPR, into 'Opera/version UA' format used above
        if (preg_match('#OPR/(\d+\.\d+)#', $userAgent, $matches)) {
            $prefix    = 'Opera/' . $matches[1] . ' ';
            $userAgent = $prefix . $userAgent;
        }

        return $userAgent;
    }
211163
public function normalize($userAgent)
    {
        //If there are no spaces in a UA and more than 2 plus symbols, the UA is likely affected by IIS style logging issues
        if (substr_count($userAgent, ' ') === 0 and substr_count($userAgent, '+') > 2) {
            $userAgent = str_replace('+', ' ', $userAgent);
        }

        return $userAgent;
    }
835831
protected static function getBrowserData($userAgent)
    {
        $matches = [];

        if (strpos($userAgent, 'Opera') !== false) {
            $browserName = 'Opera';
            preg_match_all('/Version\/([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } elseif (strpos($userAgent, 'OPR/') !== false) {
            $browserName = 'Opera';
            preg_match_all('/OPR\/([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } elseif (strpos($userAgent, 'Edge') !== false) {
            $browserName = 'Edge';
            preg_match_all('/Edge\/([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } elseif (strpos($userAgent, 'Chrome') !== false) {
            $browserName = 'Chrome';
            preg_match_all('/Chrome\/([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } elseif (strpos($userAgent, 'Safari') !== false) {
            $browserName = 'Safari';
            preg_match_all('/Version\/([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } elseif (strpos($userAgent, 'Firefox') !== false) {
            $browserName = 'Firefox';
            preg_match_all('/Firefox\/([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } elseif (strpos($userAgent, 'MSIE') !== false) {
            $browserName = 'IE';
            preg_match_all('/MSIE ([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } elseif (strpos($userAgent, 'Trident/7') !== false) {
            $browserName = 'IE';
            preg_match_all('/rv:([\d]+)/', $_SERVER['HTTP_USER_AGENT'], $matches);
        } else {
            $browserName = 'unknown';
        }

        $version = isset($matches[1]) ? $matches[1][0] : 'unknown';

        return ['name' => $browserName, 'version' => $version];
    }
390757
public function normalize($userAgent)
    {
        // Starts with 'JUC' or 'Mozilla/5.0(Linux;U;Android'
        if (strpos($userAgent, 'JUC') === 0 || strpos($userAgent, 'Mozilla/5.0(Linux;U;Android') === 0) {
            $userAgent = preg_replace('/^(JUC \(Linux; U;)(?= \d)/', '$1 Android', $userAgent);
            $userAgent = preg_replace('/(Android|JUC|[;\)])(?=[\w|\(])/', '$1 ', $userAgent);
        }

        return $userAgent;
    }
1328688
public static function isMobileBrowser($userAgent)
    {
        $userAgent_lower = strtolower($userAgent);
        if (self::$isMobileBrowser === null) {
            if (self::isDesktopBrowser($userAgent)) {
                self::$isMobileBrowser = false;
            } elseif (self::checkIfContainsAnyOf($userAgent_lower, self::$mobileBrowsers)) {
                self::$isMobileBrowser = true;
            } elseif (self::regexContains($userAgent, '/[^\d]\d{3}x\d{3}/')) {
                self::$isMobileBrowser = true;
            } else {
                self::$isMobileBrowser = false;
            }
        }

        return self::$isMobileBrowser;
    }
691389
public static function toleranceToRisDelimeter($userAgent)
    {
        $tolerance = strpos($userAgent, WURFL_Constants::RIS_DELIMITER);
        if ($tolerance === false) {
            return false;
        }
        // Push the tolerance to the *end* of the RIS Delimiter
        return $tolerance + strlen(WURFL_Constants::RIS_DELIMITER);
    }
427679
public function applyConclusiveMatch($userAgent)
    {
        $tolerance = Utils::toleranceToRisDelimeter($userAgent);
        if ($tolerance !== false) {
            return $this->getDeviceIDFromRIS($userAgent, $tolerance);
        }

        return WurflConstants::NO_MATCH;
    }
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;
    }
1247376
public function normalize($userAgent)
    {
        if (Utils::checkIfContainsAnyOf($userAgent, array('WPDesktop', 'ZuneWP7'))
            || Utils::checkIfContainsAll($userAgent, array('Mozilla/5.0 (Windows NT ', ' ARM;', ' Edge/'))
        ) {
            $model   = WindowsPhoneHandler::getWindowsPhoneDesktopModel($userAgent);
            $version = WindowsPhoneHandler::getWindowsPhoneDesktopVersion($userAgent);
        } elseif (Utils::checkIfStartsWithAnyOf($userAgent, array('Windows Phone Ad Client', 'WindowsPhoneAdClient'))) {
            $model   = WindowsPhoneHandler::getWindowsPhoneAdClientModel($userAgent);
            $version = WindowsPhoneHandler::getWindowsPhoneVersion($userAgent);
        } elseif (Utils::checkIfContains($userAgent, 'NativeHost')) {
            return $userAgent;
        } else {
            $model   = WindowsPhoneHandler::getWindowsPhoneModel($userAgent);
            $version = WindowsPhoneHandler::getWindowsPhoneVersion($userAgent);
        }

        if ($model !== null && $version !== null) {
            // 'WP' is for Windows Phone
            $prefix = 'WP' . $version . ' ' . $model . WurflConstants::RIS_DELIMITER;

            return $prefix . $userAgent;
        }

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

            return $model;
        }

        return null;
    }