72 lines
2.3 KiB
PHP
72 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Devices;
|
|
|
|
/**
|
|
* "Chrome auf macOS", from a user-agent string.
|
|
*
|
|
* For the person reading their list of sessions, and for nothing else. It never
|
|
* decides whether a device is known — which is exactly why it is allowed to be
|
|
* this rough, and why it is recomputed on every sign-in: a browser upgrade
|
|
* quietly relabels the row instead of raising an alarm.
|
|
*
|
|
* Order matters throughout. Every Chromium browser also says "Chrome", Edge
|
|
* also says "Chrome" and "Safari", and Safari says "Safari" while Chrome says
|
|
* it too — so the specific names have to be tested before the generic ones.
|
|
*/
|
|
final class DeviceName
|
|
{
|
|
/** @var array<string, string> needle => label, most specific first */
|
|
private const BROWSERS = [
|
|
'Edg/' => 'Edge',
|
|
'OPR/' => 'Opera',
|
|
'Vivaldi' => 'Vivaldi',
|
|
'Brave' => 'Brave',
|
|
'SamsungBrowser' => 'Samsung Internet',
|
|
'Firefox' => 'Firefox',
|
|
'CriOS' => 'Chrome', // Chrome on iOS, which is Safari underneath
|
|
'FxiOS' => 'Firefox',
|
|
'Chrome' => 'Chrome',
|
|
'Safari' => 'Safari',
|
|
];
|
|
|
|
/** @var array<string, string> needle => label, most specific first */
|
|
private const PLATFORMS = [
|
|
'iPhone' => 'iPhone',
|
|
'iPad' => 'iPad',
|
|
'Android' => 'Android',
|
|
'Macintosh' => 'macOS',
|
|
'Mac OS X' => 'macOS',
|
|
'Windows' => 'Windows',
|
|
'CrOS' => 'ChromeOS',
|
|
'Linux' => 'Linux',
|
|
];
|
|
|
|
public static function from(string $userAgent): string
|
|
{
|
|
$browser = self::match($userAgent, self::BROWSERS);
|
|
$platform = self::match($userAgent, self::PLATFORMS);
|
|
|
|
if ($browser !== null && $platform !== null) {
|
|
return __('devices.name_on', ['browser' => $browser, 'platform' => $platform]);
|
|
}
|
|
|
|
// One of them is better than the shrug, and the shrug is better than a
|
|
// raw user-agent string in a list somebody is meant to recognise
|
|
// themselves in.
|
|
return $browser ?? $platform ?? __('devices.name_unknown');
|
|
}
|
|
|
|
/** @param array<string, string> $map */
|
|
private static function match(string $haystack, array $map): ?string
|
|
{
|
|
foreach ($map as $needle => $label) {
|
|
if (str_contains($haystack, $needle)) {
|
|
return $label;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|