1*d5ef99ddSAndreas Gohr<?php 2*d5ef99ddSAndreas Gohr 3*d5ef99ddSAndreas Gohr/** 4*d5ef99ddSAndreas Gohr * Device Detector - The Universal Device Detection library for parsing User Agents 5*d5ef99ddSAndreas Gohr * 6*d5ef99ddSAndreas Gohr * @link https://matomo.org 7*d5ef99ddSAndreas Gohr * 8*d5ef99ddSAndreas Gohr * @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later 9*d5ef99ddSAndreas Gohr */ 10*d5ef99ddSAndreas Gohr 11*d5ef99ddSAndreas Gohrdeclare(strict_types=1); 12*d5ef99ddSAndreas Gohr 13*d5ef99ddSAndreas Gohrnamespace DeviceDetector\Parser; 14*d5ef99ddSAndreas Gohr 15*d5ef99ddSAndreas Gohruse DeviceDetector\ClientHints; 16*d5ef99ddSAndreas Gohr 17*d5ef99ddSAndreas Gohr/** 18*d5ef99ddSAndreas Gohr * Class OperatingSystem 19*d5ef99ddSAndreas Gohr * 20*d5ef99ddSAndreas Gohr * Parses the useragent for operating system information 21*d5ef99ddSAndreas Gohr * 22*d5ef99ddSAndreas Gohr * Detected operating systems can be found in self::$operatingSystems and /regexes/oss.yml 23*d5ef99ddSAndreas Gohr * This class also defined some operating system families and methods to get the family for a specific os 24*d5ef99ddSAndreas Gohr */ 25*d5ef99ddSAndreas Gohrclass OperatingSystem extends AbstractParser 26*d5ef99ddSAndreas Gohr{ 27*d5ef99ddSAndreas Gohr /** 28*d5ef99ddSAndreas Gohr * @var string 29*d5ef99ddSAndreas Gohr */ 30*d5ef99ddSAndreas Gohr protected $fixtureFile = 'regexes/oss.yml'; 31*d5ef99ddSAndreas Gohr 32*d5ef99ddSAndreas Gohr /** 33*d5ef99ddSAndreas Gohr * @var string 34*d5ef99ddSAndreas Gohr */ 35*d5ef99ddSAndreas Gohr protected $parserName = 'os'; 36*d5ef99ddSAndreas Gohr 37*d5ef99ddSAndreas Gohr /** 38*d5ef99ddSAndreas Gohr * Known operating systems mapped to their internal short codes 39*d5ef99ddSAndreas Gohr * 40*d5ef99ddSAndreas Gohr * @var array 41*d5ef99ddSAndreas Gohr */ 42*d5ef99ddSAndreas Gohr protected static $operatingSystems = [ 43*d5ef99ddSAndreas Gohr 'AIX' => 'AIX', 44*d5ef99ddSAndreas Gohr 'AND' => 'Android', 45*d5ef99ddSAndreas Gohr 'ADR' => 'Android TV', 46*d5ef99ddSAndreas Gohr 'ALP' => 'Alpine Linux', 47*d5ef99ddSAndreas Gohr 'AMZ' => 'Amazon Linux', 48*d5ef99ddSAndreas Gohr 'AMG' => 'AmigaOS', 49*d5ef99ddSAndreas Gohr 'ARM' => 'Armadillo OS', 50*d5ef99ddSAndreas Gohr 'ARO' => 'AROS', 51*d5ef99ddSAndreas Gohr 'ATV' => 'tvOS', 52*d5ef99ddSAndreas Gohr 'ARL' => 'Arch Linux', 53*d5ef99ddSAndreas Gohr 'AOS' => 'AOSC OS', 54*d5ef99ddSAndreas Gohr 'ASP' => 'ASPLinux', 55*d5ef99ddSAndreas Gohr 'AZU' => 'Azure Linux', 56*d5ef99ddSAndreas Gohr 'BTR' => 'BackTrack', 57*d5ef99ddSAndreas Gohr 'SBA' => 'Bada', 58*d5ef99ddSAndreas Gohr 'BYI' => 'Baidu Yi', 59*d5ef99ddSAndreas Gohr 'BEO' => 'BeOS', 60*d5ef99ddSAndreas Gohr 'BLB' => 'BlackBerry OS', 61*d5ef99ddSAndreas Gohr 'QNX' => 'BlackBerry Tablet OS', 62*d5ef99ddSAndreas Gohr 'PAN' => 'blackPanther OS', 63*d5ef99ddSAndreas Gohr 'BOS' => 'Bliss OS', 64*d5ef99ddSAndreas Gohr 'BMP' => 'Brew', 65*d5ef99ddSAndreas Gohr 'BSN' => 'BrightSignOS', 66*d5ef99ddSAndreas Gohr 'CAI' => 'Caixa Mágica', 67*d5ef99ddSAndreas Gohr 'CES' => 'CentOS', 68*d5ef99ddSAndreas Gohr 'CST' => 'CentOS Stream', 69*d5ef99ddSAndreas Gohr 'CLO' => 'Clear Linux OS', 70*d5ef99ddSAndreas Gohr 'CLR' => 'ClearOS Mobile', 71*d5ef99ddSAndreas Gohr 'COS' => 'Chrome OS', 72*d5ef99ddSAndreas Gohr 'CRS' => 'Chromium OS', 73*d5ef99ddSAndreas Gohr 'CHN' => 'China OS', 74*d5ef99ddSAndreas Gohr 'COL' => 'Coolita OS', 75*d5ef99ddSAndreas Gohr 'CYN' => 'CyanogenMod', 76*d5ef99ddSAndreas Gohr 'DEB' => 'Debian', 77*d5ef99ddSAndreas Gohr 'DEE' => 'Deepin', 78*d5ef99ddSAndreas Gohr 'DFB' => 'DragonFly', 79*d5ef99ddSAndreas Gohr 'DVK' => 'DVKBuntu', 80*d5ef99ddSAndreas Gohr 'ELE' => 'ElectroBSD', 81*d5ef99ddSAndreas Gohr 'EUL' => 'EulerOS', 82*d5ef99ddSAndreas Gohr 'FED' => 'Fedora', 83*d5ef99ddSAndreas Gohr 'FEN' => 'Fenix', 84*d5ef99ddSAndreas Gohr 'FOS' => 'Firefox OS', 85*d5ef99ddSAndreas Gohr 'FIR' => 'Fire OS', 86*d5ef99ddSAndreas Gohr 'FOR' => 'Foresight Linux', 87*d5ef99ddSAndreas Gohr 'FRE' => 'Freebox', 88*d5ef99ddSAndreas Gohr 'BSD' => 'FreeBSD', 89*d5ef99ddSAndreas Gohr 'FRI' => 'FRITZ!OS', 90*d5ef99ddSAndreas Gohr 'FYD' => 'FydeOS', 91*d5ef99ddSAndreas Gohr 'FUC' => 'Fuchsia', 92*d5ef99ddSAndreas Gohr 'GNT' => 'Gentoo', 93*d5ef99ddSAndreas Gohr 'GNX' => 'GENIX', 94*d5ef99ddSAndreas Gohr 'GEO' => 'GEOS', 95*d5ef99ddSAndreas Gohr 'GNS' => 'gNewSense', 96*d5ef99ddSAndreas Gohr 'GRI' => 'GridOS', 97*d5ef99ddSAndreas Gohr 'GTV' => 'Google TV', 98*d5ef99ddSAndreas Gohr 'HPX' => 'HP-UX', 99*d5ef99ddSAndreas Gohr 'HAI' => 'Haiku OS', 100*d5ef99ddSAndreas Gohr 'IPA' => 'iPadOS', 101*d5ef99ddSAndreas Gohr 'HAR' => 'HarmonyOS', 102*d5ef99ddSAndreas Gohr 'HAS' => 'HasCodingOS', 103*d5ef99ddSAndreas Gohr 'HEL' => 'HELIX OS', 104*d5ef99ddSAndreas Gohr 'IRI' => 'IRIX', 105*d5ef99ddSAndreas Gohr 'INF' => 'Inferno', 106*d5ef99ddSAndreas Gohr 'JME' => 'Java ME', 107*d5ef99ddSAndreas Gohr 'JOL' => 'Joli OS', 108*d5ef99ddSAndreas Gohr 'KOS' => 'KaiOS', 109*d5ef99ddSAndreas Gohr 'KAL' => 'Kali', 110*d5ef99ddSAndreas Gohr 'KAN' => 'Kanotix', 111*d5ef99ddSAndreas Gohr 'KIN' => 'KIN OS', 112*d5ef99ddSAndreas Gohr 'KNO' => 'Knoppix', 113*d5ef99ddSAndreas Gohr 'KTV' => 'KreaTV', 114*d5ef99ddSAndreas Gohr 'KBT' => 'Kubuntu', 115*d5ef99ddSAndreas Gohr 'LIN' => 'GNU/Linux', 116*d5ef99ddSAndreas Gohr 'LEA' => 'LeafOS', 117*d5ef99ddSAndreas Gohr 'LND' => 'LindowsOS', 118*d5ef99ddSAndreas Gohr 'LNS' => 'Linspire', 119*d5ef99ddSAndreas Gohr 'LEN' => 'Lineage OS', 120*d5ef99ddSAndreas Gohr 'LIR' => 'Liri OS', 121*d5ef99ddSAndreas Gohr 'LOO' => 'Loongnix', 122*d5ef99ddSAndreas Gohr 'LBT' => 'Lubuntu', 123*d5ef99ddSAndreas Gohr 'LOS' => 'Lumin OS', 124*d5ef99ddSAndreas Gohr 'LUN' => 'LuneOS', 125*d5ef99ddSAndreas Gohr 'VLN' => 'VectorLinux', 126*d5ef99ddSAndreas Gohr 'MAC' => 'Mac', 127*d5ef99ddSAndreas Gohr 'MAE' => 'Maemo', 128*d5ef99ddSAndreas Gohr 'MAG' => 'Mageia', 129*d5ef99ddSAndreas Gohr 'MDR' => 'Mandriva', 130*d5ef99ddSAndreas Gohr 'SMG' => 'MeeGo', 131*d5ef99ddSAndreas Gohr 'MET' => 'Meta Horizon', 132*d5ef99ddSAndreas Gohr 'MCD' => 'MocorDroid', 133*d5ef99ddSAndreas Gohr 'MON' => 'moonOS', 134*d5ef99ddSAndreas Gohr 'EZX' => 'Motorola EZX', 135*d5ef99ddSAndreas Gohr 'MIN' => 'Mint', 136*d5ef99ddSAndreas Gohr 'MLD' => 'MildWild', 137*d5ef99ddSAndreas Gohr 'MOR' => 'MorphOS', 138*d5ef99ddSAndreas Gohr 'NBS' => 'NetBSD', 139*d5ef99ddSAndreas Gohr 'MTK' => 'MTK / Nucleus', 140*d5ef99ddSAndreas Gohr 'MRE' => 'MRE', 141*d5ef99ddSAndreas Gohr 'NXT' => 'NeXTSTEP', 142*d5ef99ddSAndreas Gohr 'NWS' => 'NEWS-OS', 143*d5ef99ddSAndreas Gohr 'WII' => 'Nintendo', 144*d5ef99ddSAndreas Gohr 'NDS' => 'Nintendo Mobile', 145*d5ef99ddSAndreas Gohr 'NOV' => 'Nova', 146*d5ef99ddSAndreas Gohr 'OS2' => 'OS/2', 147*d5ef99ddSAndreas Gohr 'T64' => 'OSF1', 148*d5ef99ddSAndreas Gohr 'OBS' => 'OpenBSD', 149*d5ef99ddSAndreas Gohr 'OVS' => 'OpenVMS', 150*d5ef99ddSAndreas Gohr 'OVZ' => 'OpenVZ', 151*d5ef99ddSAndreas Gohr 'OWR' => 'OpenWrt', 152*d5ef99ddSAndreas Gohr 'OTV' => 'Opera TV', 153*d5ef99ddSAndreas Gohr 'ORA' => 'Oracle Linux', 154*d5ef99ddSAndreas Gohr 'ORD' => 'Ordissimo', 155*d5ef99ddSAndreas Gohr 'PAR' => 'Pardus', 156*d5ef99ddSAndreas Gohr 'PCL' => 'PCLinuxOS', 157*d5ef99ddSAndreas Gohr 'PIC' => 'PICO OS', 158*d5ef99ddSAndreas Gohr 'PLA' => 'Plasma Mobile', 159*d5ef99ddSAndreas Gohr 'PSP' => 'PlayStation Portable', 160*d5ef99ddSAndreas Gohr 'PS3' => 'PlayStation', 161*d5ef99ddSAndreas Gohr 'PVE' => 'Proxmox VE', 162*d5ef99ddSAndreas Gohr 'PUF' => 'Puffin OS', 163*d5ef99ddSAndreas Gohr 'PUR' => 'PureOS', 164*d5ef99ddSAndreas Gohr 'QTP' => 'Qtopia', 165*d5ef99ddSAndreas Gohr 'PIO' => 'Raspberry Pi OS', 166*d5ef99ddSAndreas Gohr 'RAS' => 'Raspbian', 167*d5ef99ddSAndreas Gohr 'RHT' => 'Red Hat', 168*d5ef99ddSAndreas Gohr 'RST' => 'Red Star', 169*d5ef99ddSAndreas Gohr 'RED' => 'RedOS', 170*d5ef99ddSAndreas Gohr 'REV' => 'Revenge OS', 171*d5ef99ddSAndreas Gohr 'RIS' => 'risingOS', 172*d5ef99ddSAndreas Gohr 'ROS' => 'RISC OS', 173*d5ef99ddSAndreas Gohr 'ROC' => 'Rocky Linux', 174*d5ef99ddSAndreas Gohr 'ROK' => 'Roku OS', 175*d5ef99ddSAndreas Gohr 'RSO' => 'Rosa', 176*d5ef99ddSAndreas Gohr 'ROU' => 'RouterOS', 177*d5ef99ddSAndreas Gohr 'REM' => 'Remix OS', 178*d5ef99ddSAndreas Gohr 'RRS' => 'Resurrection Remix OS', 179*d5ef99ddSAndreas Gohr 'REX' => 'REX', 180*d5ef99ddSAndreas Gohr 'RZD' => 'RazoDroiD', 181*d5ef99ddSAndreas Gohr 'RXT' => 'RTOS & Next', 182*d5ef99ddSAndreas Gohr 'SAB' => 'Sabayon', 183*d5ef99ddSAndreas Gohr 'SSE' => 'SUSE', 184*d5ef99ddSAndreas Gohr 'SAF' => 'Sailfish OS', 185*d5ef99ddSAndreas Gohr 'SCI' => 'Scientific Linux', 186*d5ef99ddSAndreas Gohr 'SEE' => 'SeewoOS', 187*d5ef99ddSAndreas Gohr 'SER' => 'SerenityOS', 188*d5ef99ddSAndreas Gohr 'SIR' => 'Sirin OS', 189*d5ef99ddSAndreas Gohr 'SLW' => 'Slackware', 190*d5ef99ddSAndreas Gohr 'SOS' => 'Solaris', 191*d5ef99ddSAndreas Gohr 'SBL' => 'Star-Blade OS', 192*d5ef99ddSAndreas Gohr 'SYL' => 'Syllable', 193*d5ef99ddSAndreas Gohr 'SYM' => 'Symbian', 194*d5ef99ddSAndreas Gohr 'SYS' => 'Symbian OS', 195*d5ef99ddSAndreas Gohr 'S40' => 'Symbian OS Series 40', 196*d5ef99ddSAndreas Gohr 'S60' => 'Symbian OS Series 60', 197*d5ef99ddSAndreas Gohr 'SY3' => 'Symbian^3', 198*d5ef99ddSAndreas Gohr 'TEN' => 'TencentOS', 199*d5ef99ddSAndreas Gohr 'TDX' => 'ThreadX', 200*d5ef99ddSAndreas Gohr 'TIZ' => 'Tizen', 201*d5ef99ddSAndreas Gohr 'TIV' => 'TiVo OS', 202*d5ef99ddSAndreas Gohr 'TOS' => 'TmaxOS', 203*d5ef99ddSAndreas Gohr 'TUR' => 'Turbolinux', 204*d5ef99ddSAndreas Gohr 'UBT' => 'Ubuntu', 205*d5ef99ddSAndreas Gohr 'ULT' => 'ULTRIX', 206*d5ef99ddSAndreas Gohr 'UOS' => 'UOS', 207*d5ef99ddSAndreas Gohr 'VID' => 'VIDAA', 208*d5ef99ddSAndreas Gohr 'VIZ' => 'ViziOS', 209*d5ef99ddSAndreas Gohr 'WAS' => 'watchOS', 210*d5ef99ddSAndreas Gohr 'WER' => 'Wear OS', 211*d5ef99ddSAndreas Gohr 'WTV' => 'WebTV', 212*d5ef99ddSAndreas Gohr 'WHS' => 'Whale OS', 213*d5ef99ddSAndreas Gohr 'WIN' => 'Windows', 214*d5ef99ddSAndreas Gohr 'WCE' => 'Windows CE', 215*d5ef99ddSAndreas Gohr 'WIO' => 'Windows IoT', 216*d5ef99ddSAndreas Gohr 'WMO' => 'Windows Mobile', 217*d5ef99ddSAndreas Gohr 'WPH' => 'Windows Phone', 218*d5ef99ddSAndreas Gohr 'WRT' => 'Windows RT', 219*d5ef99ddSAndreas Gohr 'WPO' => 'WoPhone', 220*d5ef99ddSAndreas Gohr 'XBX' => 'Xbox', 221*d5ef99ddSAndreas Gohr 'XBT' => 'Xubuntu', 222*d5ef99ddSAndreas Gohr 'YNS' => 'YunOS', 223*d5ef99ddSAndreas Gohr 'ZEN' => 'Zenwalk', 224*d5ef99ddSAndreas Gohr 'ZOR' => 'ZorinOS', 225*d5ef99ddSAndreas Gohr 'IOS' => 'iOS', 226*d5ef99ddSAndreas Gohr 'POS' => 'palmOS', 227*d5ef99ddSAndreas Gohr 'WEB' => 'Webian', 228*d5ef99ddSAndreas Gohr 'WOS' => 'webOS', 229*d5ef99ddSAndreas Gohr ]; 230*d5ef99ddSAndreas Gohr 231*d5ef99ddSAndreas Gohr /** 232*d5ef99ddSAndreas Gohr * Operating system families mapped to the short codes of the associated operating systems 233*d5ef99ddSAndreas Gohr * 234*d5ef99ddSAndreas Gohr * @var array 235*d5ef99ddSAndreas Gohr */ 236*d5ef99ddSAndreas Gohr protected static $osFamilies = [ 237*d5ef99ddSAndreas Gohr 'Android' => [ 238*d5ef99ddSAndreas Gohr 'AND', 'CYN', 'FIR', 'REM', 'RZD', 'MLD', 'MCD', 'YNS', 'GRI', 'HAR', 239*d5ef99ddSAndreas Gohr 'ADR', 'CLR', 'BOS', 'REV', 'LEN', 'SIR', 'RRS', 'WER', 'PIC', 'ARM', 240*d5ef99ddSAndreas Gohr 'HEL', 'BYI', 'RIS', 'PUF', 'LEA', 'MET', 241*d5ef99ddSAndreas Gohr ], 242*d5ef99ddSAndreas Gohr 'AmigaOS' => ['AMG', 'MOR', 'ARO'], 243*d5ef99ddSAndreas Gohr 'BlackBerry' => ['BLB', 'QNX'], 244*d5ef99ddSAndreas Gohr 'Brew' => ['BMP'], 245*d5ef99ddSAndreas Gohr 'BeOS' => ['BEO', 'HAI'], 246*d5ef99ddSAndreas Gohr 'Chrome OS' => ['COS', 'CRS', 'FYD', 'SEE'], 247*d5ef99ddSAndreas Gohr 'Firefox OS' => ['FOS', 'KOS'], 248*d5ef99ddSAndreas Gohr 'Gaming Console' => ['WII', 'PS3'], 249*d5ef99ddSAndreas Gohr 'Google TV' => ['GTV'], 250*d5ef99ddSAndreas Gohr 'IBM' => ['OS2'], 251*d5ef99ddSAndreas Gohr 'iOS' => ['IOS', 'ATV', 'WAS', 'IPA'], 252*d5ef99ddSAndreas Gohr 'RISC OS' => ['ROS'], 253*d5ef99ddSAndreas Gohr 'GNU/Linux' => [ 254*d5ef99ddSAndreas Gohr 'LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED', 255*d5ef99ddSAndreas Gohr 'RHT', 'VLN', 'MDR', 'GNT', 'SAB', 'SLW', 'SSE', 'CES', 'BTR', 'SAF', 256*d5ef99ddSAndreas Gohr 'ORD', 'TOS', 'RSO', 'DEE', 'FRE', 'MAG', 'FEN', 'CAI', 'PCL', 'HAS', 257*d5ef99ddSAndreas Gohr 'LOS', 'DVK', 'ROK', 'OWR', 'OTV', 'KTV', 'PUR', 'PLA', 'FUC', 'PAR', 258*d5ef99ddSAndreas Gohr 'FOR', 'MON', 'KAN', 'ZEN', 'LND', 'LNS', 'CHN', 'AMZ', 'TEN', 'CST', 259*d5ef99ddSAndreas Gohr 'NOV', 'ROU', 'ZOR', 'RED', 'KAL', 'ORA', 'VID', 'TIV', 'BSN', 'RAS', 260*d5ef99ddSAndreas Gohr 'UOS', 'PIO', 'FRI', 'LIR', 'WEB', 'SER', 'ASP', 'AOS', 'LOO', 'EUL', 261*d5ef99ddSAndreas Gohr 'SCI', 'ALP', 'CLO', 'ROC', 'OVZ', 'PVE', 'RST', 'EZX', 'GNS', 'JOL', 262*d5ef99ddSAndreas Gohr 'TUR', 'QTP', 'WPO', 'PAN', 'VIZ', 'AZU', 'COL', 263*d5ef99ddSAndreas Gohr ], 264*d5ef99ddSAndreas Gohr 'Mac' => ['MAC'], 265*d5ef99ddSAndreas Gohr 'Mobile Gaming Console' => ['PSP', 'NDS', 'XBX'], 266*d5ef99ddSAndreas Gohr 'OpenVMS' => ['OVS'], 267*d5ef99ddSAndreas Gohr 'Real-time OS' => ['MTK', 'TDX', 'MRE', 'JME', 'REX', 'RXT'], 268*d5ef99ddSAndreas Gohr 'Other Mobile' => ['WOS', 'POS', 'SBA', 'TIZ', 'SMG', 'MAE', 'LUN', 'GEO'], 269*d5ef99ddSAndreas Gohr 'Symbian' => ['SYM', 'SYS', 'SY3', 'S60', 'S40'], 270*d5ef99ddSAndreas Gohr 'Unix' => [ 271*d5ef99ddSAndreas Gohr 'SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64', 272*d5ef99ddSAndreas Gohr 'INF', 'ELE', 'GNX', 'ULT', 'NWS', 'NXT', 'SBL', 273*d5ef99ddSAndreas Gohr ], 274*d5ef99ddSAndreas Gohr 'WebTV' => ['WTV'], 275*d5ef99ddSAndreas Gohr 'Windows' => ['WIN'], 276*d5ef99ddSAndreas Gohr 'Windows Mobile' => ['WPH', 'WMO', 'WCE', 'WRT', 'WIO', 'KIN'], 277*d5ef99ddSAndreas Gohr 'Other Smart TV' => ['WHS'], 278*d5ef99ddSAndreas Gohr ]; 279*d5ef99ddSAndreas Gohr 280*d5ef99ddSAndreas Gohr /** 281*d5ef99ddSAndreas Gohr * Contains a list of mappings from OS names we use to known client hint values 282*d5ef99ddSAndreas Gohr * 283*d5ef99ddSAndreas Gohr * @var array<string, array<string>> 284*d5ef99ddSAndreas Gohr */ 285*d5ef99ddSAndreas Gohr protected static $clientHintMapping = [ 286*d5ef99ddSAndreas Gohr 'GNU/Linux' => ['Linux'], 287*d5ef99ddSAndreas Gohr 'Mac' => ['MacOS'], 288*d5ef99ddSAndreas Gohr ]; 289*d5ef99ddSAndreas Gohr 290*d5ef99ddSAndreas Gohr /** 291*d5ef99ddSAndreas Gohr * Operating system families that are known as desktop only 292*d5ef99ddSAndreas Gohr * 293*d5ef99ddSAndreas Gohr * @var array 294*d5ef99ddSAndreas Gohr */ 295*d5ef99ddSAndreas Gohr protected static $desktopOsArray = [ 296*d5ef99ddSAndreas Gohr 'AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS', 297*d5ef99ddSAndreas Gohr ]; 298*d5ef99ddSAndreas Gohr 299*d5ef99ddSAndreas Gohr /** 300*d5ef99ddSAndreas Gohr * Fire OS version mapping 301*d5ef99ddSAndreas Gohr * 302*d5ef99ddSAndreas Gohr * @var array 303*d5ef99ddSAndreas Gohr */ 304*d5ef99ddSAndreas Gohr private $fireOsVersionMapping = [ 305*d5ef99ddSAndreas Gohr '11' => '8', 306*d5ef99ddSAndreas Gohr '10' => '8', 307*d5ef99ddSAndreas Gohr '9' => '7', 308*d5ef99ddSAndreas Gohr '7' => '6', 309*d5ef99ddSAndreas Gohr '5' => '5', 310*d5ef99ddSAndreas Gohr '4.4.3' => '4.5.1', 311*d5ef99ddSAndreas Gohr '4.4.2' => '4', 312*d5ef99ddSAndreas Gohr '4.2.2' => '3', 313*d5ef99ddSAndreas Gohr '4.0.3' => '3', 314*d5ef99ddSAndreas Gohr '4.0.2' => '3', 315*d5ef99ddSAndreas Gohr '4' => '2', 316*d5ef99ddSAndreas Gohr '2' => '1', 317*d5ef99ddSAndreas Gohr ]; 318*d5ef99ddSAndreas Gohr 319*d5ef99ddSAndreas Gohr /** 320*d5ef99ddSAndreas Gohr * Lineage OS version mapping 321*d5ef99ddSAndreas Gohr * 322*d5ef99ddSAndreas Gohr * @var array 323*d5ef99ddSAndreas Gohr */ 324*d5ef99ddSAndreas Gohr private $lineageOsVersionMapping = [ 325*d5ef99ddSAndreas Gohr '15' => '22', 326*d5ef99ddSAndreas Gohr '14' => '21', 327*d5ef99ddSAndreas Gohr '13' => '20.0', 328*d5ef99ddSAndreas Gohr '12.1' => '19.1', 329*d5ef99ddSAndreas Gohr '12' => '19.0', 330*d5ef99ddSAndreas Gohr '11' => '18.0', 331*d5ef99ddSAndreas Gohr '10' => '17.0', 332*d5ef99ddSAndreas Gohr '9' => '16.0', 333*d5ef99ddSAndreas Gohr '8.1.0' => '15.1', 334*d5ef99ddSAndreas Gohr '8.0.0' => '15.0', 335*d5ef99ddSAndreas Gohr '7.1.2' => '14.1', 336*d5ef99ddSAndreas Gohr '7.1.1' => '14.1', 337*d5ef99ddSAndreas Gohr '7.0' => '14.0', 338*d5ef99ddSAndreas Gohr '6.0.1' => '13.0', 339*d5ef99ddSAndreas Gohr '6.0' => '13.0', 340*d5ef99ddSAndreas Gohr '5.1.1' => '12.1', 341*d5ef99ddSAndreas Gohr '5.0.2' => '12.0', 342*d5ef99ddSAndreas Gohr '5.0' => '12.0', 343*d5ef99ddSAndreas Gohr '4.4.4' => '11.0', 344*d5ef99ddSAndreas Gohr '4.3' => '10.2', 345*d5ef99ddSAndreas Gohr '4.2.2' => '10.1', 346*d5ef99ddSAndreas Gohr '4.0.4' => '9.1.0', 347*d5ef99ddSAndreas Gohr ]; 348*d5ef99ddSAndreas Gohr 349*d5ef99ddSAndreas Gohr /** 350*d5ef99ddSAndreas Gohr * Returns all available operating systems 351*d5ef99ddSAndreas Gohr * 352*d5ef99ddSAndreas Gohr * @return array 353*d5ef99ddSAndreas Gohr */ 354*d5ef99ddSAndreas Gohr public static function getAvailableOperatingSystems(): array 355*d5ef99ddSAndreas Gohr { 356*d5ef99ddSAndreas Gohr return self::$operatingSystems; 357*d5ef99ddSAndreas Gohr } 358*d5ef99ddSAndreas Gohr 359*d5ef99ddSAndreas Gohr /** 360*d5ef99ddSAndreas Gohr * Returns all available operating system families 361*d5ef99ddSAndreas Gohr * 362*d5ef99ddSAndreas Gohr * @return array 363*d5ef99ddSAndreas Gohr */ 364*d5ef99ddSAndreas Gohr public static function getAvailableOperatingSystemFamilies(): array 365*d5ef99ddSAndreas Gohr { 366*d5ef99ddSAndreas Gohr return self::$osFamilies; 367*d5ef99ddSAndreas Gohr } 368*d5ef99ddSAndreas Gohr 369*d5ef99ddSAndreas Gohr /** 370*d5ef99ddSAndreas Gohr * Returns the os name and shot name 371*d5ef99ddSAndreas Gohr * 372*d5ef99ddSAndreas Gohr * @param string $name 373*d5ef99ddSAndreas Gohr * 374*d5ef99ddSAndreas Gohr * @return array 375*d5ef99ddSAndreas Gohr */ 376*d5ef99ddSAndreas Gohr public static function getShortOsData(string $name): array 377*d5ef99ddSAndreas Gohr { 378*d5ef99ddSAndreas Gohr $short = 'UNK'; 379*d5ef99ddSAndreas Gohr 380*d5ef99ddSAndreas Gohr foreach (self::$operatingSystems as $osShort => $osName) { 381*d5ef99ddSAndreas Gohr if (\strtolower($name) !== \strtolower($osName)) { 382*d5ef99ddSAndreas Gohr continue; 383*d5ef99ddSAndreas Gohr } 384*d5ef99ddSAndreas Gohr 385*d5ef99ddSAndreas Gohr $name = $osName; 386*d5ef99ddSAndreas Gohr $short = $osShort; 387*d5ef99ddSAndreas Gohr 388*d5ef99ddSAndreas Gohr break; 389*d5ef99ddSAndreas Gohr } 390*d5ef99ddSAndreas Gohr 391*d5ef99ddSAndreas Gohr return \compact('short', 'name'); 392*d5ef99ddSAndreas Gohr } 393*d5ef99ddSAndreas Gohr 394*d5ef99ddSAndreas Gohr /** 395*d5ef99ddSAndreas Gohr * @inheritdoc 396*d5ef99ddSAndreas Gohr */ 397*d5ef99ddSAndreas Gohr public function parse(): ?array 398*d5ef99ddSAndreas Gohr { 399*d5ef99ddSAndreas Gohr $this->restoreUserAgentFromClientHints(); 400*d5ef99ddSAndreas Gohr 401*d5ef99ddSAndreas Gohr $osFromClientHints = $this->parseOsFromClientHints(); 402*d5ef99ddSAndreas Gohr $osFromUserAgent = $this->parseOsFromUserAgent(); 403*d5ef99ddSAndreas Gohr 404*d5ef99ddSAndreas Gohr if (!empty($osFromClientHints['name'])) { 405*d5ef99ddSAndreas Gohr $name = $osFromClientHints['name']; 406*d5ef99ddSAndreas Gohr $version = $osFromClientHints['version']; 407*d5ef99ddSAndreas Gohr 408*d5ef99ddSAndreas Gohr // use version from user agent if non was provided in client hints, but os family from useragent matches 409*d5ef99ddSAndreas Gohr if (empty($version) 410*d5ef99ddSAndreas Gohr && self::getOsFamily($name) === self::getOsFamily($osFromUserAgent['name']) 411*d5ef99ddSAndreas Gohr ) { 412*d5ef99ddSAndreas Gohr $version = $osFromUserAgent['version']; 413*d5ef99ddSAndreas Gohr } 414*d5ef99ddSAndreas Gohr 415*d5ef99ddSAndreas Gohr // On Windows, version 0.0.0 can be either 7, 8 or 8.1 416*d5ef99ddSAndreas Gohr if ('Windows' === $name && '0.0.0' === $version) { 417*d5ef99ddSAndreas Gohr $version = ('10' === $osFromUserAgent['version']) ? '' : $osFromUserAgent['version']; 418*d5ef99ddSAndreas Gohr } 419*d5ef99ddSAndreas Gohr 420*d5ef99ddSAndreas Gohr // If the OS name detected from client hints matches the OS family from user agent 421*d5ef99ddSAndreas Gohr // but the os name is another, we use the one from user agent, as it might be more detailed 422*d5ef99ddSAndreas Gohr if (self::getOsFamily($osFromUserAgent['name']) === $name && $osFromUserAgent['name'] !== $name) { 423*d5ef99ddSAndreas Gohr $name = $osFromUserAgent['name']; 424*d5ef99ddSAndreas Gohr 425*d5ef99ddSAndreas Gohr if ('LeafOS' === $name || 'HarmonyOS' === $name) { 426*d5ef99ddSAndreas Gohr $version = ''; 427*d5ef99ddSAndreas Gohr } 428*d5ef99ddSAndreas Gohr 429*d5ef99ddSAndreas Gohr if ('PICO OS' === $name) { 430*d5ef99ddSAndreas Gohr $version = $osFromUserAgent['version']; 431*d5ef99ddSAndreas Gohr } 432*d5ef99ddSAndreas Gohr 433*d5ef99ddSAndreas Gohr if ('Fire OS' === $name && !empty($osFromClientHints['version'])) { 434*d5ef99ddSAndreas Gohr $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0'); 435*d5ef99ddSAndreas Gohr 436*d5ef99ddSAndreas Gohr $version = $this->fireOsVersionMapping[$version] 437*d5ef99ddSAndreas Gohr ?? $this->fireOsVersionMapping[$majorVersion] ?? ''; 438*d5ef99ddSAndreas Gohr } 439*d5ef99ddSAndreas Gohr } 440*d5ef99ddSAndreas Gohr 441*d5ef99ddSAndreas Gohr $short = $osFromClientHints['short_name']; 442*d5ef99ddSAndreas Gohr 443*d5ef99ddSAndreas Gohr // Chrome OS is in some cases reported as Linux in client hints, we fix this only if the version matches 444*d5ef99ddSAndreas Gohr if ('GNU/Linux' === $name 445*d5ef99ddSAndreas Gohr && 'Chrome OS' === $osFromUserAgent['name'] 446*d5ef99ddSAndreas Gohr && $osFromClientHints['version'] === $osFromUserAgent['version'] 447*d5ef99ddSAndreas Gohr ) { 448*d5ef99ddSAndreas Gohr $name = $osFromUserAgent['name']; 449*d5ef99ddSAndreas Gohr $short = $osFromUserAgent['short_name']; 450*d5ef99ddSAndreas Gohr } 451*d5ef99ddSAndreas Gohr 452*d5ef99ddSAndreas Gohr // Chrome OS is in some cases reported as Android in client hints 453*d5ef99ddSAndreas Gohr if ('Android' === $name && 'Chrome OS' === $osFromUserAgent['name']) { 454*d5ef99ddSAndreas Gohr $name = $osFromUserAgent['name']; 455*d5ef99ddSAndreas Gohr $version = ''; 456*d5ef99ddSAndreas Gohr $short = $osFromUserAgent['short_name']; 457*d5ef99ddSAndreas Gohr } 458*d5ef99ddSAndreas Gohr 459*d5ef99ddSAndreas Gohr // Meta Horizon is reported as Linux in client hints 460*d5ef99ddSAndreas Gohr if ('GNU/Linux' === $name && 'Meta Horizon' === $osFromUserAgent['name']) { 461*d5ef99ddSAndreas Gohr $name = $osFromUserAgent['name']; 462*d5ef99ddSAndreas Gohr $short = $osFromUserAgent['short_name']; 463*d5ef99ddSAndreas Gohr } 464*d5ef99ddSAndreas Gohr } elseif (!empty($osFromUserAgent['name'])) { 465*d5ef99ddSAndreas Gohr $name = $osFromUserAgent['name']; 466*d5ef99ddSAndreas Gohr $version = $osFromUserAgent['version']; 467*d5ef99ddSAndreas Gohr $short = $osFromUserAgent['short_name']; 468*d5ef99ddSAndreas Gohr } else { 469*d5ef99ddSAndreas Gohr return []; 470*d5ef99ddSAndreas Gohr } 471*d5ef99ddSAndreas Gohr 472*d5ef99ddSAndreas Gohr $platform = $this->parsePlatform(); 473*d5ef99ddSAndreas Gohr $family = self::getOsFamily($short); 474*d5ef99ddSAndreas Gohr $androidApps = [ 475*d5ef99ddSAndreas Gohr 'com.hisense.odinbrowser', 'com.seraphic.openinet.pre', 'com.appssppa.idesktoppcbrowser', 476*d5ef99ddSAndreas Gohr 'every.browser.inc', 477*d5ef99ddSAndreas Gohr ]; 478*d5ef99ddSAndreas Gohr 479*d5ef99ddSAndreas Gohr if (null !== $this->clientHints) { 480*d5ef99ddSAndreas Gohr if (\in_array($this->clientHints->getApp(), $androidApps) && 'Android' !== $name) { 481*d5ef99ddSAndreas Gohr $name = 'Android'; 482*d5ef99ddSAndreas Gohr $family = 'Android'; 483*d5ef99ddSAndreas Gohr $short = 'ADR'; 484*d5ef99ddSAndreas Gohr $version = ''; 485*d5ef99ddSAndreas Gohr } 486*d5ef99ddSAndreas Gohr 487*d5ef99ddSAndreas Gohr if ('org.lineageos.jelly' === $this->clientHints->getApp() && 'Lineage OS' !== $name) { 488*d5ef99ddSAndreas Gohr $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0'); 489*d5ef99ddSAndreas Gohr 490*d5ef99ddSAndreas Gohr $name = 'Lineage OS'; 491*d5ef99ddSAndreas Gohr $family = 'Android'; 492*d5ef99ddSAndreas Gohr $short = 'LEN'; 493*d5ef99ddSAndreas Gohr $version = $this->lineageOsVersionMapping[$version] 494*d5ef99ddSAndreas Gohr ?? $this->lineageOsVersionMapping[$majorVersion] ?? ''; 495*d5ef99ddSAndreas Gohr } 496*d5ef99ddSAndreas Gohr 497*d5ef99ddSAndreas Gohr if ('org.mozilla.tv.firefox' === $this->clientHints->getApp() && 'Fire OS' !== $name) { 498*d5ef99ddSAndreas Gohr $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0'); 499*d5ef99ddSAndreas Gohr 500*d5ef99ddSAndreas Gohr $name = 'Fire OS'; 501*d5ef99ddSAndreas Gohr $family = 'Android'; 502*d5ef99ddSAndreas Gohr $short = 'FIR'; 503*d5ef99ddSAndreas Gohr $version = $this->fireOsVersionMapping[$version] ?? $this->fireOsVersionMapping[$majorVersion] ?? ''; 504*d5ef99ddSAndreas Gohr } 505*d5ef99ddSAndreas Gohr } 506*d5ef99ddSAndreas Gohr 507*d5ef99ddSAndreas Gohr $return = [ 508*d5ef99ddSAndreas Gohr 'name' => $name, 509*d5ef99ddSAndreas Gohr 'short_name' => $short, 510*d5ef99ddSAndreas Gohr 'version' => $version, 511*d5ef99ddSAndreas Gohr 'platform' => $platform, 512*d5ef99ddSAndreas Gohr 'family' => $family, 513*d5ef99ddSAndreas Gohr ]; 514*d5ef99ddSAndreas Gohr 515*d5ef99ddSAndreas Gohr if (\in_array($return['name'], self::$operatingSystems)) { 516*d5ef99ddSAndreas Gohr $return['short_name'] = \array_search($return['name'], self::$operatingSystems); 517*d5ef99ddSAndreas Gohr } 518*d5ef99ddSAndreas Gohr 519*d5ef99ddSAndreas Gohr return $return; 520*d5ef99ddSAndreas Gohr } 521*d5ef99ddSAndreas Gohr 522*d5ef99ddSAndreas Gohr /** 523*d5ef99ddSAndreas Gohr * Returns the operating system family for the given operating system 524*d5ef99ddSAndreas Gohr * 525*d5ef99ddSAndreas Gohr * @param string $osLabel name or short name 526*d5ef99ddSAndreas Gohr * 527*d5ef99ddSAndreas Gohr * @return string|null If null, "Unknown" 528*d5ef99ddSAndreas Gohr */ 529*d5ef99ddSAndreas Gohr public static function getOsFamily(string $osLabel): ?string 530*d5ef99ddSAndreas Gohr { 531*d5ef99ddSAndreas Gohr if (\in_array($osLabel, self::$operatingSystems)) { 532*d5ef99ddSAndreas Gohr $osLabel = \array_search($osLabel, self::$operatingSystems); 533*d5ef99ddSAndreas Gohr } 534*d5ef99ddSAndreas Gohr 535*d5ef99ddSAndreas Gohr foreach (self::$osFamilies as $family => $labels) { 536*d5ef99ddSAndreas Gohr if (\in_array($osLabel, $labels)) { 537*d5ef99ddSAndreas Gohr return (string) $family; 538*d5ef99ddSAndreas Gohr } 539*d5ef99ddSAndreas Gohr } 540*d5ef99ddSAndreas Gohr 541*d5ef99ddSAndreas Gohr return null; 542*d5ef99ddSAndreas Gohr } 543*d5ef99ddSAndreas Gohr 544*d5ef99ddSAndreas Gohr /** 545*d5ef99ddSAndreas Gohr * Returns true if OS is desktop 546*d5ef99ddSAndreas Gohr * 547*d5ef99ddSAndreas Gohr * @param string $osName OS short name 548*d5ef99ddSAndreas Gohr * 549*d5ef99ddSAndreas Gohr * @return bool 550*d5ef99ddSAndreas Gohr */ 551*d5ef99ddSAndreas Gohr public static function isDesktopOs(string $osName): bool 552*d5ef99ddSAndreas Gohr { 553*d5ef99ddSAndreas Gohr $osFamily = self::getOsFamily($osName); 554*d5ef99ddSAndreas Gohr 555*d5ef99ddSAndreas Gohr return \in_array($osFamily, self::$desktopOsArray); 556*d5ef99ddSAndreas Gohr } 557*d5ef99ddSAndreas Gohr 558*d5ef99ddSAndreas Gohr /** 559*d5ef99ddSAndreas Gohr * Returns the full name for the given short name 560*d5ef99ddSAndreas Gohr * 561*d5ef99ddSAndreas Gohr * @param string $os 562*d5ef99ddSAndreas Gohr * @param string|null $ver 563*d5ef99ddSAndreas Gohr * 564*d5ef99ddSAndreas Gohr * @return ?string 565*d5ef99ddSAndreas Gohr */ 566*d5ef99ddSAndreas Gohr public static function getNameFromId(string $os, ?string $ver = null): ?string 567*d5ef99ddSAndreas Gohr { 568*d5ef99ddSAndreas Gohr if (\array_key_exists($os, self::$operatingSystems)) { 569*d5ef99ddSAndreas Gohr $osFullName = self::$operatingSystems[$os]; 570*d5ef99ddSAndreas Gohr 571*d5ef99ddSAndreas Gohr return \trim($osFullName . ' ' . $ver); 572*d5ef99ddSAndreas Gohr } 573*d5ef99ddSAndreas Gohr 574*d5ef99ddSAndreas Gohr return null; 575*d5ef99ddSAndreas Gohr } 576*d5ef99ddSAndreas Gohr 577*d5ef99ddSAndreas Gohr /** 578*d5ef99ddSAndreas Gohr * Returns the OS that can be safely detected from client hints 579*d5ef99ddSAndreas Gohr * 580*d5ef99ddSAndreas Gohr * @return array 581*d5ef99ddSAndreas Gohr */ 582*d5ef99ddSAndreas Gohr protected function parseOsFromClientHints(): array 583*d5ef99ddSAndreas Gohr { 584*d5ef99ddSAndreas Gohr $name = $version = $short = ''; 585*d5ef99ddSAndreas Gohr 586*d5ef99ddSAndreas Gohr if ($this->clientHints instanceof ClientHints && $this->clientHints->getOperatingSystem()) { 587*d5ef99ddSAndreas Gohr $hintName = $this->applyClientHintMapping($this->clientHints->getOperatingSystem()); 588*d5ef99ddSAndreas Gohr 589*d5ef99ddSAndreas Gohr foreach (self::$operatingSystems as $osShort => $osName) { 590*d5ef99ddSAndreas Gohr if ($this->fuzzyCompare($hintName, $osName)) { 591*d5ef99ddSAndreas Gohr $name = $osName; 592*d5ef99ddSAndreas Gohr $short = $osShort; 593*d5ef99ddSAndreas Gohr 594*d5ef99ddSAndreas Gohr break; 595*d5ef99ddSAndreas Gohr } 596*d5ef99ddSAndreas Gohr } 597*d5ef99ddSAndreas Gohr 598*d5ef99ddSAndreas Gohr $version = $this->clientHints->getOperatingSystemVersion(); 599*d5ef99ddSAndreas Gohr 600*d5ef99ddSAndreas Gohr if ('Windows' === $name) { 601*d5ef99ddSAndreas Gohr $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0'); 602*d5ef99ddSAndreas Gohr $minorVersion = (int) (\explode('.', $version, 2)[1] ?? '0'); 603*d5ef99ddSAndreas Gohr 604*d5ef99ddSAndreas Gohr if (0 === $majorVersion) { 605*d5ef99ddSAndreas Gohr $minorVersionMapping = [1 => '7', 2 => '8', 3 => '8.1']; 606*d5ef99ddSAndreas Gohr $version = $minorVersionMapping[$minorVersion] ?? $version; 607*d5ef99ddSAndreas Gohr } elseif ($majorVersion > 0 && $majorVersion < 11) { 608*d5ef99ddSAndreas Gohr $version = '10'; 609*d5ef99ddSAndreas Gohr } elseif ($majorVersion > 10) { 610*d5ef99ddSAndreas Gohr $version = '11'; 611*d5ef99ddSAndreas Gohr } 612*d5ef99ddSAndreas Gohr } 613*d5ef99ddSAndreas Gohr 614*d5ef99ddSAndreas Gohr // On Windows, version 0.0.0 can be either 7, 8 or 8.1, so we return 0.0.0 615*d5ef99ddSAndreas Gohr if ('Windows' !== $name && '0.0.0' !== $version && 0 === (int) $version) { 616*d5ef99ddSAndreas Gohr $version = ''; 617*d5ef99ddSAndreas Gohr } 618*d5ef99ddSAndreas Gohr } 619*d5ef99ddSAndreas Gohr 620*d5ef99ddSAndreas Gohr return [ 621*d5ef99ddSAndreas Gohr 'name' => $name, 622*d5ef99ddSAndreas Gohr 'short_name' => $short, 623*d5ef99ddSAndreas Gohr 'version' => $this->buildVersion($version, []), 624*d5ef99ddSAndreas Gohr ]; 625*d5ef99ddSAndreas Gohr } 626*d5ef99ddSAndreas Gohr 627*d5ef99ddSAndreas Gohr /** 628*d5ef99ddSAndreas Gohr * Returns the OS that can be detected from useragent 629*d5ef99ddSAndreas Gohr * 630*d5ef99ddSAndreas Gohr * @return array 631*d5ef99ddSAndreas Gohr * 632*d5ef99ddSAndreas Gohr * @throws \Exception 633*d5ef99ddSAndreas Gohr */ 634*d5ef99ddSAndreas Gohr protected function parseOsFromUserAgent(): array 635*d5ef99ddSAndreas Gohr { 636*d5ef99ddSAndreas Gohr $osRegex = $matches = []; 637*d5ef99ddSAndreas Gohr $name = $version = $short = ''; 638*d5ef99ddSAndreas Gohr 639*d5ef99ddSAndreas Gohr foreach ($this->getRegexes() as $osRegex) { 640*d5ef99ddSAndreas Gohr $matches = $this->matchUserAgent($osRegex['regex']); 641*d5ef99ddSAndreas Gohr 642*d5ef99ddSAndreas Gohr if ($matches) { 643*d5ef99ddSAndreas Gohr break; 644*d5ef99ddSAndreas Gohr } 645*d5ef99ddSAndreas Gohr } 646*d5ef99ddSAndreas Gohr 647*d5ef99ddSAndreas Gohr if (!empty($matches)) { 648*d5ef99ddSAndreas Gohr $name = $this->buildByMatch($osRegex['name'], $matches); 649*d5ef99ddSAndreas Gohr ['name' => $name, 'short' => $short] = self::getShortOsData($name); 650*d5ef99ddSAndreas Gohr 651*d5ef99ddSAndreas Gohr $version = \array_key_exists('version', $osRegex) 652*d5ef99ddSAndreas Gohr ? $this->buildVersion((string) $osRegex['version'], $matches) 653*d5ef99ddSAndreas Gohr : ''; 654*d5ef99ddSAndreas Gohr 655*d5ef99ddSAndreas Gohr foreach ($osRegex['versions'] ?? [] as $regex) { 656*d5ef99ddSAndreas Gohr $matches = $this->matchUserAgent($regex['regex']); 657*d5ef99ddSAndreas Gohr 658*d5ef99ddSAndreas Gohr if (!$matches) { 659*d5ef99ddSAndreas Gohr continue; 660*d5ef99ddSAndreas Gohr } 661*d5ef99ddSAndreas Gohr 662*d5ef99ddSAndreas Gohr if (\array_key_exists('name', $regex)) { 663*d5ef99ddSAndreas Gohr $name = $this->buildByMatch($regex['name'], $matches); 664*d5ef99ddSAndreas Gohr ['name' => $name, 'short' => $short] = self::getShortOsData($name); 665*d5ef99ddSAndreas Gohr } 666*d5ef99ddSAndreas Gohr 667*d5ef99ddSAndreas Gohr if (\array_key_exists('version', $regex)) { 668*d5ef99ddSAndreas Gohr $version = $this->buildVersion((string) $regex['version'], $matches); 669*d5ef99ddSAndreas Gohr } 670*d5ef99ddSAndreas Gohr 671*d5ef99ddSAndreas Gohr break; 672*d5ef99ddSAndreas Gohr } 673*d5ef99ddSAndreas Gohr } 674*d5ef99ddSAndreas Gohr 675*d5ef99ddSAndreas Gohr return [ 676*d5ef99ddSAndreas Gohr 'name' => $name, 677*d5ef99ddSAndreas Gohr 'short_name' => $short, 678*d5ef99ddSAndreas Gohr 'version' => $version, 679*d5ef99ddSAndreas Gohr ]; 680*d5ef99ddSAndreas Gohr } 681*d5ef99ddSAndreas Gohr 682*d5ef99ddSAndreas Gohr /** 683*d5ef99ddSAndreas Gohr * Parse current UserAgent string for the operating system platform 684*d5ef99ddSAndreas Gohr * 685*d5ef99ddSAndreas Gohr * @return string 686*d5ef99ddSAndreas Gohr */ 687*d5ef99ddSAndreas Gohr protected function parsePlatform(): string 688*d5ef99ddSAndreas Gohr { 689*d5ef99ddSAndreas Gohr // Use architecture from client hints if available 690*d5ef99ddSAndreas Gohr if ($this->clientHints instanceof ClientHints && $this->clientHints->getArchitecture()) { 691*d5ef99ddSAndreas Gohr $arch = \strtolower($this->clientHints->getArchitecture()); 692*d5ef99ddSAndreas Gohr 693*d5ef99ddSAndreas Gohr if (false !== \strpos($arch, 'arm')) { 694*d5ef99ddSAndreas Gohr return 'ARM'; 695*d5ef99ddSAndreas Gohr } 696*d5ef99ddSAndreas Gohr 697*d5ef99ddSAndreas Gohr if (false !== \strpos($arch, 'loongarch64')) { 698*d5ef99ddSAndreas Gohr return 'LoongArch64'; 699*d5ef99ddSAndreas Gohr } 700*d5ef99ddSAndreas Gohr 701*d5ef99ddSAndreas Gohr if (false !== \strpos($arch, 'mips')) { 702*d5ef99ddSAndreas Gohr return 'MIPS'; 703*d5ef99ddSAndreas Gohr } 704*d5ef99ddSAndreas Gohr 705*d5ef99ddSAndreas Gohr if (false !== \strpos($arch, 'sh4')) { 706*d5ef99ddSAndreas Gohr return 'SuperH'; 707*d5ef99ddSAndreas Gohr } 708*d5ef99ddSAndreas Gohr 709*d5ef99ddSAndreas Gohr if (false !== \strpos($arch, 'sparc64')) { 710*d5ef99ddSAndreas Gohr return 'SPARC64'; 711*d5ef99ddSAndreas Gohr } 712*d5ef99ddSAndreas Gohr 713*d5ef99ddSAndreas Gohr if (false !== \strpos($arch, 'x64') 714*d5ef99ddSAndreas Gohr || (false !== \strpos($arch, 'x86') && '64' === $this->clientHints->getBitness()) 715*d5ef99ddSAndreas Gohr ) { 716*d5ef99ddSAndreas Gohr return 'x64'; 717*d5ef99ddSAndreas Gohr } 718*d5ef99ddSAndreas Gohr 719*d5ef99ddSAndreas Gohr if (false !== \strpos($arch, 'x86')) { 720*d5ef99ddSAndreas Gohr return 'x86'; 721*d5ef99ddSAndreas Gohr } 722*d5ef99ddSAndreas Gohr } 723*d5ef99ddSAndreas Gohr 724*d5ef99ddSAndreas Gohr if ($this->matchUserAgent('arm[ _;)ev]|.*arm$|.*arm64|aarch64|Apple ?TV|Watch ?OS|Watch1,[12]')) { 725*d5ef99ddSAndreas Gohr return 'ARM'; 726*d5ef99ddSAndreas Gohr } 727*d5ef99ddSAndreas Gohr 728*d5ef99ddSAndreas Gohr if ($this->matchUserAgent('loongarch64')) { 729*d5ef99ddSAndreas Gohr return 'LoongArch64'; 730*d5ef99ddSAndreas Gohr } 731*d5ef99ddSAndreas Gohr 732*d5ef99ddSAndreas Gohr if ($this->matchUserAgent('mips')) { 733*d5ef99ddSAndreas Gohr return 'MIPS'; 734*d5ef99ddSAndreas Gohr } 735*d5ef99ddSAndreas Gohr 736*d5ef99ddSAndreas Gohr if ($this->matchUserAgent('sh4')) { 737*d5ef99ddSAndreas Gohr return 'SuperH'; 738*d5ef99ddSAndreas Gohr } 739*d5ef99ddSAndreas Gohr 740*d5ef99ddSAndreas Gohr if ($this->matchUserAgent('sparc64')) { 741*d5ef99ddSAndreas Gohr return 'SPARC64'; 742*d5ef99ddSAndreas Gohr } 743*d5ef99ddSAndreas Gohr 744*d5ef99ddSAndreas Gohr if ($this->matchUserAgent('64-?bit|WOW64|(?:Intel)?x64|WINDOWS_64|win64|.*amd64|.*x86_?64')) { 745*d5ef99ddSAndreas Gohr return 'x64'; 746*d5ef99ddSAndreas Gohr } 747*d5ef99ddSAndreas Gohr 748*d5ef99ddSAndreas Gohr if ($this->matchUserAgent('.*32bit|.*win32|(?:i[0-9]|x)86|i86pc')) { 749*d5ef99ddSAndreas Gohr return 'x86'; 750*d5ef99ddSAndreas Gohr } 751*d5ef99ddSAndreas Gohr 752*d5ef99ddSAndreas Gohr return ''; 753*d5ef99ddSAndreas Gohr } 754*d5ef99ddSAndreas Gohr} 755