getHeaderLine('User-Agent'); // Detect platform & browser using robust fallback logic [$platform, $browser] = self::detectFromUserAgent($uaString); $data = [ 'ip' => $request->getIPAddress(), 'real_ip' => getRealClientIP(), 'platform' => $platform, 'browser' => $browser, 'method' => strtoupper($request->getMethod()), 'endpoint' => $request->uri->getPath(), 'getparams' => json_encode($request->uri->getSegments(), JSON_UNESCAPED_UNICODE), 'postparams' => self::sanitizePostForLog($request->getPost()), ]; return $data; } /** * Detect platform & browser from UA string (reliable fallback) */ private static function detectFromUserAgent(string $ua): array { $uaLower = strtolower($ua); // ========================= // PLATFORM DETECTION // ========================= $platform = 'Unknown'; if (str_contains($uaLower, 'windows nt 11') || str_contains($uaLower, 'windows 11')) { $platform = 'Windows 11'; } elseif (str_contains($uaLower, 'windows nt 10')) { $platform = 'Windows 10'; } elseif (str_contains($uaLower, 'windows nt 6.3')) { $platform = 'Windows 8.1'; } elseif (str_contains($uaLower, 'windows nt 6.2')) { $platform = 'Windows 8'; } elseif (str_contains($uaLower, 'windows nt 6.1')) { $platform = 'Windows 7'; } elseif (str_contains($uaLower, 'windows nt 6.0')) { $platform = 'Windows Vista'; } elseif (str_contains($uaLower, 'windows nt 5.1') || str_contains($uaLower, 'windows xp')) { $platform = 'Windows XP'; } elseif (str_contains($uaLower, 'android')) { $platform = 'Android'; } elseif (str_contains($uaLower, 'iphone')) { $platform = 'iOS (iPhone)'; } elseif (str_contains($uaLower, 'ipad')) { $platform = 'iOS (iPad)'; } elseif (str_contains($uaLower, 'ipod')) { $platform = 'iOS (iPod)'; } elseif (str_contains($uaLower, 'mac os') || str_contains($uaLower, 'macintosh')) { $platform = 'Mac OS'; } elseif (str_contains($uaLower, 'cros')) { $platform = 'Chrome OS'; } elseif (str_contains($uaLower, 'linux')) { $platform = 'Linux'; } elseif (str_contains($uaLower, 'freebsd')) { $platform = 'FreeBSD'; } elseif (str_contains($uaLower, 'openbsd')) { $platform = 'OpenBSD'; } elseif (str_contains($uaLower, 'netbsd')) { $platform = 'NetBSD'; } elseif (str_contains($uaLower, 'unix')) { $platform = 'Unix'; } elseif (str_contains($uaLower, 'symbian')) { $platform = 'Symbian'; } elseif (str_contains($uaLower, 'blackberry')) { $platform = 'BlackBerry'; } elseif (str_contains($uaLower, 'tizen')) { $platform = 'Tizen'; } elseif (str_contains($uaLower, 'webos')) { $platform = 'WebOS'; } elseif (str_contains($uaLower, 'kaios')) { $platform = 'KaiOS'; } elseif (str_contains($uaLower, 'harmonyos')) { $platform = 'HarmonyOS'; } elseif (str_contains($uaLower, 'watchos')) { $platform = 'watchOS'; } elseif (str_contains($uaLower, 'tv os') || str_contains($uaLower, 'tvos')) { $platform = 'tvOS'; } // ========================= // BROWSER / CLIENT DETECTION // ========================= $browser = 'Unknown'; // Bots & tools first if (preg_match('/googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot|sogou|exabot|facebot|ia_archiver/i', $ua)) { $browser = 'Search Bot'; } elseif (preg_match('/postman/i', $ua)) { $browser = 'Postman'; } elseif (preg_match('/insomnia/i', $ua)) { $browser = 'Insomnia'; } elseif (preg_match('/curl/i', $ua)) { $browser = 'curl'; } elseif (preg_match('/wget/i', $ua)) { $browser = 'wget'; } // Real browsers elseif (preg_match('/edg\/([\d\.]+)/i', $ua, $m)) { $browser = 'Edge ' . $m[1]; } elseif (preg_match('/opr\/([\d\.]+)/i', $ua, $m)) { $browser = 'Opera ' . $m[1]; } elseif (preg_match('/vivaldi\/([\d\.]+)/i', $ua, $m)) { $browser = 'Vivaldi ' . $m[1]; } elseif (preg_match('/brave\/([\d\.]+)/i', $ua, $m)) { $browser = 'Brave ' . $m[1]; } elseif (preg_match('/chrome\/([\d\.]+)/i', $ua, $m)) { $browser = 'Chrome ' . $m[1]; } elseif (preg_match('/firefox\/([\d\.]+)/i', $ua, $m)) { $browser = 'Firefox ' . $m[1]; } elseif (preg_match('/safari\/([\d\.]+)/i', $ua, $m)) { $browser = 'Safari ' . $m[1]; } elseif (preg_match('/msie\s([\d\.]+)/i', $ua, $m) || preg_match('/trident\/.*rv:([\d\.]+)/i', $ua, $m)) { $browser = 'Internet Explorer ' . $m[1]; } // In-app browsers elseif (preg_match('/fbav|fban/i', $ua)) { $browser = 'Facebook In-App Browser'; } elseif (preg_match('/instagram/i', $ua)) { $browser = 'Instagram In-App Browser'; } elseif (preg_match('/linkedinapp/i', $ua)) { $browser = 'LinkedIn In-App Browser'; } elseif (preg_match('/twitter/i', $ua)) { $browser = 'Twitter/X In-App Browser'; } return [$platform, $browser]; } /** * Remove sensitive fields before logging POST */ private static function sanitizePostForLog(array $post): string { if (empty($post)) { return json_encode([]); } $sensitiveKeys = [ 'password', 'pass', 'pwd', 'token', 'access_token', 'refresh_token', 'secret', 'api_key', 'authorization', 'otp', 'pin' ]; foreach ($post as $k => $v) { foreach ($sensitiveKeys as $sk) { if (stripos($k, $sk) !== false) { $post[$k] = '***MASKED***'; } } } return json_encode($post, JSON_UNESCAPED_UNICODE); } public static function add($payload) { return $payload['a'] + $payload['b']; } }