CluPilotCloud/app/Services/Devices/DeviceName.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;
}
}