fix: statisc
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
/**
|
||||
* Lightweight UA → OS / browser fields for page-visit grouping.
|
||||
*/
|
||||
final class UserAgentParser
|
||||
{
|
||||
/**
|
||||
* @return array{os: string, os_version: string, browser: string, browser_version: string}
|
||||
*/
|
||||
public static function parse(?string $ua): array
|
||||
{
|
||||
$ua = trim((string) $ua);
|
||||
if ($ua === '') {
|
||||
return [
|
||||
'os' => 'Unknown',
|
||||
'os_version' => '',
|
||||
'browser' => 'Unknown',
|
||||
'browser_version' => '',
|
||||
];
|
||||
}
|
||||
|
||||
[$os, $osVersion] = self::parseOs($ua);
|
||||
[$browser, $browserVersion] = self::parseBrowser($ua);
|
||||
|
||||
return [
|
||||
'os' => $os,
|
||||
'os_version' => $osVersion,
|
||||
'browser' => $browser,
|
||||
'browser_version' => $browserVersion,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: string, 1: string}
|
||||
*/
|
||||
private static function parseOs(string $ua): array
|
||||
{
|
||||
if (preg_match('/iPhone OS (\d+)[._](\d+)(?:[._](\d+))?/i', $ua, $m)
|
||||
|| preg_match('/CPU OS (\d+)[._](\d+)(?:[._](\d+))?.*like Mac OS X/i', $ua, $m)) {
|
||||
$ver = $m[1].'.'.$m[2].(isset($m[3]) && $m[3] !== '' ? '.'.$m[3] : '');
|
||||
|
||||
return ['iOS', $ver];
|
||||
}
|
||||
if (preg_match('/iPad.*OS (\d+)[._](\d+)/i', $ua, $m)) {
|
||||
return ['iPadOS', $m[1].'.'.$m[2]];
|
||||
}
|
||||
if (preg_match('/Android (\d+(?:\.\d+){0,2})/i', $ua, $m)) {
|
||||
return ['Android', $m[1]];
|
||||
}
|
||||
if (preg_match('/Windows NT (\d+\.\d+)/i', $ua, $m)) {
|
||||
$map = [
|
||||
'10.0' => '10/11',
|
||||
'6.3' => '8.1',
|
||||
'6.2' => '8',
|
||||
'6.1' => '7',
|
||||
];
|
||||
|
||||
return ['Windows', $map[$m[1]] ?? $m[1]];
|
||||
}
|
||||
if (preg_match('/Mac OS X (\d+)[._](\d+)(?:[._](\d+))?/i', $ua, $m)) {
|
||||
$ver = $m[1].'.'.$m[2].(isset($m[3]) && $m[3] !== '' ? '.'.$m[3] : '');
|
||||
|
||||
return ['macOS', $ver];
|
||||
}
|
||||
if (preg_match('/Linux/i', $ua)) {
|
||||
return ['Linux', ''];
|
||||
}
|
||||
|
||||
return ['Unknown', ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: string, 1: string}
|
||||
*/
|
||||
private static function parseBrowser(string $ua): array
|
||||
{
|
||||
// Order matters: more specific tokens first (iOS Chrome/Firefox/Edge before Safari).
|
||||
$rules = [
|
||||
['CriOS', 'Chrome'],
|
||||
['Chrome', 'Chrome'],
|
||||
['EdgiOS', 'Edge'],
|
||||
['Edg/', 'Edge'],
|
||||
['FxiOS', 'Firefox'],
|
||||
['Firefox', 'Firefox'],
|
||||
['OPT/', 'Opera'],
|
||||
['OPiOS', 'Opera'],
|
||||
['Opera', 'Opera'],
|
||||
['SamsungBrowser', 'Samsung'],
|
||||
['UCBrowser', 'UC'],
|
||||
['Version/', 'Safari'], // Safari reports Version/x before Safari/
|
||||
['Safari', 'Safari'],
|
||||
];
|
||||
|
||||
foreach ($rules as [$token, $name]) {
|
||||
if ($token === 'Version/' && ! preg_match('/Safari/i', $ua)) {
|
||||
continue;
|
||||
}
|
||||
if ($token === 'Safari' && preg_match('/CriOS|FxiOS|EdgiOS|Chrome|Android/i', $ua)) {
|
||||
continue;
|
||||
}
|
||||
$pattern = $token === 'Version/' || str_ends_with($token, '/')
|
||||
? '/'.preg_quote(rtrim($token, '/'), '/').'\/(\d+(?:\.\d+){0,2})/i'
|
||||
: '/'.preg_quote($token, '/').'[\/\s]?(\d+(?:\.\d+){0,2})?/i';
|
||||
if (preg_match($pattern, $ua, $m)) {
|
||||
return [$name, $m[1] ?? ''];
|
||||
}
|
||||
}
|
||||
|
||||
return ['Unknown', ''];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user