createUnknownUserAgent(''); } // Check cache first $cacheKey = 'useragent:' . md5($normalized); if ($this->cache) { $cached = $this->cache->get($cacheKey); if ($cached instanceof ParsedUserAgent) { return $cached; } } // Parse components $browser = $this->parseBrowser($normalized); $browserVersion = $this->parseBrowserVersion($normalized, $browser); $platform = $this->parsePlatform($normalized); $platformVersion = $this->parsePlatformVersion($normalized, $platform); $engine = $this->parseEngine($normalized, $browser); $engineVersion = $this->parseEngineVersion($normalized, $engine); $isMobile = PlatformPatterns::isMobile($normalized) || $platform->isMobile(); $isBot = BrowserPatterns::isBot($normalized); $isModern = $this->determineModernBrowser($browser, $browserVersion, $isBot); $parsedUserAgent = new ParsedUserAgent( raw: $normalized, browser: $browser, browserVersion: $browserVersion, platform: $platform, platformVersion: $platformVersion, engine: $engine, engineVersion: $engineVersion, isMobile: $isMobile, isBot: $isBot, isModern: $isModern ); // Cache result if ($this->cache) { $this->cache->set($cacheKey, $parsedUserAgent, 3600); // Cache for 1 hour } return $parsedUserAgent; } /** * Parse browser type and version */ private function parseBrowser(string $userAgent): BrowserType { foreach (BrowserPatterns::getPatterns() as $pattern) { if (preg_match($pattern['pattern'], $userAgent)) { return $pattern['browser']; } } // Try fallback patterns foreach (BrowserPatterns::getFallbackPatterns() as $fallback) { if (preg_match($fallback['pattern'], $userAgent)) { return $fallback['browser']; } } return BrowserType::UNKNOWN; } /** * Parse browser version */ private function parseBrowserVersion(string $userAgent, BrowserType $browser): string { // Find matching pattern for this browser foreach (BrowserPatterns::getPatterns() as $pattern) { if ($pattern['browser'] === $browser && preg_match($pattern['versionPattern'], $userAgent, $matches)) { return $matches[1] ?? 'Unknown'; } } return 'Unknown'; } /** * Parse platform/operating system */ private function parsePlatform(string $userAgent): PlatformType { foreach (PlatformPatterns::getPatterns() as $pattern) { if (preg_match($pattern['pattern'], $userAgent)) { return $pattern['platform']; } } return PlatformType::UNKNOWN; } /** * Parse platform version */ private function parsePlatformVersion(string $userAgent, PlatformType $platform): string { foreach (PlatformPatterns::getPatterns() as $pattern) { if ($pattern['platform'] === $platform && ! empty($pattern['versionPattern']) && preg_match($pattern['versionPattern'], $userAgent, $matches)) { $version = $matches[1] ?? 'Unknown'; // Format version based on platform return match ($platform) { PlatformType::WINDOWS => PlatformPatterns::formatWindowsVersion($version), PlatformType::MACOS, PlatformType::IOS => PlatformPatterns::formatAppleVersion($version), default => $version }; } } return 'Unknown'; } /** * Parse browser engine */ private function parseEngine(string $userAgent, BrowserType $browser): EngineType { // Try pattern-based detection first foreach (EnginePatterns::getPatterns() as $pattern) { if (preg_match($pattern['pattern'], $userAgent)) { return $pattern['engine']; } } // Fallback to browser-based mapping $engineMap = EnginePatterns::getBrowserEngineMap(); return $engineMap[$browser->value] ?? EngineType::UNKNOWN; } /** * Parse engine version */ private function parseEngineVersion(string $userAgent, EngineType $engine): string { foreach (EnginePatterns::getPatterns() as $pattern) { if ($pattern['engine'] === $engine && preg_match($pattern['versionPattern'], $userAgent, $matches)) { $version = $matches[1] ?? 'Unknown'; // Special formatting for Gecko if ($engine === EngineType::GECKO) { return EnginePatterns::formatGeckoVersion($version); } return $version; } } return 'Unknown'; } /** * Determine if browser is considered modern */ private function determineModernBrowser(BrowserType $browser, string $version, bool $isBot): bool { if ($isBot || $version === 'Unknown') { return false; } if (! $browser->isModern()) { return false; } $threshold = $browser->getModernVersionThreshold(); return version_compare($version, $threshold, '>='); } /** * Create unknown ParsedUserAgent object */ private function createUnknownUserAgent(string $raw): ParsedUserAgent { return new ParsedUserAgent( raw: $raw, browser: BrowserType::UNKNOWN, browserVersion: 'Unknown', platform: PlatformType::UNKNOWN, platformVersion: 'Unknown', engine: EngineType::UNKNOWN, engineVersion: 'Unknown', isMobile: false, isBot: false, isModern: false ); } /** * Clear parser cache */ public function clearCache(): void { if ($this->cache) { // Clear all useragent cache entries // This would need cache implementation that supports key patterns // For now, this is a placeholder } } /** * Get parser statistics (for debugging/monitoring) * @return array */ public function getStats(): array { return [ 'cacheEnabled' => $this->cache !== null, 'supportedBrowsers' => count(BrowserType::cases()), 'supportedPlatforms' => count(PlatformType::cases()), 'supportedEngines' => count(EngineType::cases()), ]; } }