PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.15.5
Independent Analytics – WordPress Analytics Plugin v2.15.5
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / vendor / matomo / device-detector / Parser / OperatingSystem.php
independent-analytics / vendor / matomo / device-detector / Parser Last commit date
Client 1 month ago Device 1 month ago AbstractBotParser.php 2 years ago AbstractParser.php 1 month ago Bot.php 3 months ago OperatingSystem.php 1 month ago VendorFragment.php 2 years ago
OperatingSystem.php
379 lines
1 <?php
2
3 /**
4 * Device Detector - The Universal Device Detection library for parsing User Agents
5 *
6 * @link https://matomo.org
7 *
8 * @license http://www.gnu.org/licenses/lgpl.html LGPL v3 or later
9 */
10 declare (strict_types=1);
11 namespace IAWPSCOPED\DeviceDetector\Parser;
12
13 use IAWPSCOPED\DeviceDetector\ClientHints;
14 /**
15 * Class OperatingSystem
16 *
17 * Parses the useragent for operating system information
18 *
19 * Detected operating systems can be found in self::$operatingSystems and /regexes/oss.yml
20 * This class also defined some operating system families and methods to get the family for a specific os
21 * @internal
22 */
23 class OperatingSystem extends AbstractParser
24 {
25 /**
26 * @var string
27 */
28 protected $fixtureFile = 'regexes/oss.yml';
29 /**
30 * @var string
31 */
32 protected $parserName = 'os';
33 /**
34 * Known operating systems mapped to their internal short codes
35 *
36 * @var array
37 */
38 protected static $operatingSystems = ['AIX' => 'AIX', 'AND' => 'Android', 'ADR' => 'Android TV', 'ALP' => 'Alpine Linux', 'AMZ' => 'Amazon Linux', 'AMG' => 'AmigaOS', 'ARM' => 'Armadillo OS', 'ARO' => 'AROS', 'ATV' => 'tvOS', 'ARC' => 'ArcaOS', 'ARL' => 'Arch Linux', 'AOS' => 'AOSC OS', 'ASP' => 'ASPLinux', 'AZU' => 'Azure Linux', 'BTR' => 'BackTrack', 'SBA' => 'Bada', 'BYI' => 'Baidu Yi', 'BEO' => 'BeOS', 'BLB' => 'BlackBerry OS', 'QNX' => 'BlackBerry Tablet OS', 'PAN' => 'blackPanther OS', 'BOS' => 'Bliss OS', 'BMP' => 'Brew', 'BSN' => 'BrightSignOS', 'BS1' => 'BSD', 'CAI' => 'Caixa Mágica', 'CES' => 'CentOS', 'CST' => 'CentOS Stream', 'CLO' => 'Clear Linux OS', 'CLR' => 'ClearOS Mobile', 'COS' => 'Chrome OS', 'CRS' => 'Chromium OS', 'CHN' => 'China OS', 'CON' => 'Contiki', 'COL' => 'Coolita OS', 'CYN' => 'CyanogenMod', 'DEB' => 'Debian', 'DEE' => 'Deepin', 'DFB' => 'DragonFly', 'DVK' => 'DVKBuntu', 'ELE' => 'ElectroBSD', 'ELM' => 'elementary OS', 'EUL' => 'EulerOS', 'FED' => 'Fedora', 'FEN' => 'Fenix', 'FOS' => 'Firefox OS', 'FIR' => 'Fire OS', 'FOR' => 'Foresight Linux', 'FRE' => 'Freebox', 'BSD' => 'FreeBSD', 'FRI' => 'FRITZ!OS', 'FYD' => 'FydeOS', 'FUC' => 'Fuchsia', 'GNT' => 'Gentoo', 'GNX' => 'GENIX', 'GEO' => 'GEOS', 'GHO' => 'GhostBSD', 'GNS' => 'gNewSense', 'GRI' => 'GridOS', 'GTV' => 'Google TV', 'HPX' => 'HP-UX', 'HAI' => 'Haiku OS', 'IPA' => 'iPadOS', 'HAR' => 'HarmonyOS', 'HNX' => 'HarmonyOS NEXT', 'HAS' => 'HasCodingOS', 'HEL' => 'HELIX OS', 'IRI' => 'IRIX', 'INF' => 'Inferno', 'JME' => 'Java ME', 'JOL' => 'Joli OS', 'KOS' => 'KaiOS', 'KAL' => 'Kali', 'KAN' => 'Kanotix', 'KIN' => 'KIN OS', 'KOL' => 'KolibriOS', 'KNO' => 'Knoppix', 'KTV' => 'KreaTV', 'KBT' => 'Kubuntu', 'LIN' => 'GNU/Linux', 'LEA' => 'LeafOS', 'LND' => 'LindowsOS', 'LNS' => 'Linspire', 'LEN' => 'Lineage OS', 'LPU' => 'Linpus', 'LIR' => 'Liri OS', 'LOO' => 'Loongnix', 'LBT' => 'Lubuntu', 'LOS' => 'Lumin OS', 'LUN' => 'LuneOS', 'VLN' => 'VectorLinux', 'MAC' => 'Mac', 'MAE' => 'Maemo', 'MAG' => 'Mageia', 'MDR' => 'Mandriva', 'SMG' => 'MeeGo', 'MET' => 'Meta Horizon', 'MNX' => 'MINIX', 'MOS' => 'Mocor OS', 'MCD' => 'MocorDroid', 'MON' => 'moonOS', 'EZX' => 'Motorola EZX', 'MIN' => 'Mint', 'MLD' => 'MildWild', 'MOR' => 'MorphOS', 'NBS' => 'NetBSD', 'MTK' => 'MTK / Nucleus', 'MRE' => 'MRE', 'NXT' => 'NeXTSTEP', 'NWS' => 'NEWS-OS', 'WII' => 'Nintendo', 'NDS' => 'Nintendo Mobile', 'NOV' => 'Nova', 'NTX' => 'NuttX', 'OS2' => 'OS/2', 'T64' => 'OSF1', 'OSS' => 'openSUSE', 'OBS' => 'OpenBSD', 'OHS' => 'OpenHarmony', 'OVS' => 'OpenVMS', 'OVZ' => 'OpenVZ', 'OWR' => 'OpenWrt', 'OTV' => 'Opera TV', 'ORA' => 'Oracle Linux', 'ORD' => 'Ordissimo', 'ORS' => 'Orsay', 'PAR' => 'Pardus', 'PCL' => 'PCLinuxOS', 'PIC' => 'PICO OS', 'PLN' => 'Plan 9', 'PLA' => 'Plasma Mobile', 'PSP' => 'PlayStation Portable', 'PS3' => 'PlayStation', 'PVE' => 'Proxmox VE', 'PUF' => 'Puffin OS', 'PUR' => 'PureOS', 'QTP' => 'Qtopia', 'PIO' => 'Raspberry Pi OS', 'RAS' => 'Raspbian', 'RHT' => 'Red Hat', 'RST' => 'Red Star', 'RED' => 'RedOS', 'REV' => 'Revenge OS', 'RIS' => 'risingOS', 'ROS' => 'RISC OS', 'ROC' => 'Rocky Linux', 'ROK' => 'Roku OS', 'RSO' => 'Rosa', 'ROU' => 'RouterOS', 'REM' => 'Remix OS', 'RRS' => 'Resurrection Remix OS', 'REX' => 'REX', 'RZD' => 'RazoDroiD', 'RXT' => 'RTOS & Next', 'SAB' => 'Sabayon', 'SSE' => 'SUSE', 'SAF' => 'Sailfish OS', 'SCI' => 'Scientific Linux', 'SEE' => 'SeewoOS', 'SER' => 'SerenityOS', 'SIR' => 'Sirin OS', 'SLW' => 'Slackware', 'SMA' => 'Smartisan OS', 'SOS' => 'Solaris', 'SBL' => 'Star-Blade OS', 'SYL' => 'Syllable', 'SYM' => 'Symbian', 'SYS' => 'Symbian OS', 'S40' => 'Symbian OS Series 40', 'S60' => 'Symbian OS Series 60', 'SY3' => 'Symbian^3', 'TEN' => 'TencentOS', 'TDX' => 'ThreadX', 'TIT' => 'Titan OS', 'TIZ' => 'Tizen', 'TIV' => 'TiVo OS', 'TOS' => 'TmaxOS', 'TUR' => 'Turbolinux', 'UBT' => 'Ubuntu', 'ULT' => 'ULTRIX', 'UOS' => 'UOS', 'VID' => 'VIDAA', 'VIZ' => 'ViziOS', 'WAS' => 'watchOS', 'WER' => 'Wear OS', 'WTV' => 'WebTV', 'WHS' => 'Whale OS', 'WIN' => 'Windows', 'WCE' => 'Windows CE', 'WIO' => 'Windows IoT', 'WMO' => 'Windows Mobile', 'WPH' => 'Windows Phone', 'WRT' => 'Windows RT', 'WPO' => 'WoPhone', 'XBX' => 'Xbox', 'XBT' => 'Xubuntu', 'YNS' => 'YunOS', 'ZEN' => 'Zenwalk', 'ZOR' => 'ZorinOS', 'IOS' => 'iOS', 'POS' => 'palmOS', 'WEB' => 'Webian', 'WOS' => 'webOS'];
39 /**
40 * Operating system families mapped to the short codes of the associated operating systems
41 *
42 * @var array
43 */
44 protected static $osFamilies = ['Android' => ['AND', 'CYN', 'FIR', 'REM', 'RZD', 'MLD', 'MCD', 'YNS', 'GRI', 'HAR', 'ADR', 'CLR', 'BOS', 'REV', 'LEN', 'SIR', 'RRS', 'WER', 'PIC', 'ARM', 'HEL', 'BYI', 'RIS', 'PUF', 'LEA', 'MET', 'SMA'], 'AmigaOS' => ['AMG', 'MOR', 'ARO'], 'BlackBerry' => ['BLB', 'QNX'], 'Brew' => ['BMP'], 'BeOS' => ['BEO', 'HAI'], 'Chrome OS' => ['COS', 'CRS', 'FYD', 'SEE'], 'Firefox OS' => ['FOS', 'KOS'], 'Gaming Console' => ['WII', 'PS3'], 'Google TV' => ['GTV'], 'IBM' => ['OS2', 'ARC'], 'iOS' => ['IOS', 'ATV', 'WAS', 'IPA'], 'RISC OS' => ['ROS'], 'GNU/Linux' => ['LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED', 'RHT', 'VLN', 'MDR', 'GNT', 'SAB', 'SLW', 'SSE', 'CES', 'BTR', 'SAF', 'ORD', 'TOS', 'RSO', 'DEE', 'FRE', 'MAG', 'FEN', 'CAI', 'PCL', 'HAS', 'LOS', 'DVK', 'ROK', 'OWR', 'OTV', 'KTV', 'PUR', 'PLA', 'FUC', 'PAR', 'FOR', 'MON', 'KAN', 'ZEN', 'LND', 'LNS', 'CHN', 'AMZ', 'TEN', 'CST', 'NOV', 'ROU', 'ZOR', 'RED', 'KAL', 'ORA', 'VID', 'TIV', 'BSN', 'RAS', 'UOS', 'PIO', 'FRI', 'LIR', 'WEB', 'SER', 'ASP', 'AOS', 'LOO', 'EUL', 'SCI', 'ALP', 'CLO', 'ROC', 'OVZ', 'PVE', 'RST', 'EZX', 'GNS', 'JOL', 'TUR', 'QTP', 'WPO', 'PAN', 'VIZ', 'AZU', 'COL', 'OSS', 'ELM', 'LPU'], 'Mac' => ['MAC'], 'Mobile Gaming Console' => ['PSP', 'NDS', 'XBX'], 'OpenHarmony' => ['OHS', 'HNX'], 'OpenVMS' => ['OVS'], 'Real-time OS' => ['MTK', 'TDX', 'MRE', 'JME', 'REX', 'RXT', 'KOL', 'MOS', 'NTX'], 'Other Mobile' => ['WOS', 'POS', 'SBA', 'TIZ', 'SMG', 'MAE', 'LUN', 'GEO', 'CON'], 'Symbian' => ['SYM', 'SYS', 'SY3', 'S60', 'S40'], 'Unix' => ['SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64', 'INF', 'ELE', 'GNX', 'ULT', 'NWS', 'NXT', 'SBL', 'BS1', 'GHO', 'PLN', 'MNX'], 'WebTV' => ['WTV'], 'Windows' => ['WIN'], 'Windows Mobile' => ['WPH', 'WMO', 'WCE', 'WRT', 'WIO', 'KIN'], 'Other Smart TV' => ['WHS', 'TIT', 'ORS']];
45 /**
46 * Contains a list of mappings from OS names we use to known client hint values
47 *
48 * @var array<string, array<string>>
49 */
50 protected static $clientHintMapping = ['GNU/Linux' => ['Linux'], 'Mac' => ['MacOS']];
51 /**
52 * Operating system families that are known as desktop only
53 *
54 * @var array
55 */
56 protected static $desktopOsArray = ['AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS', 'OpenVMS'];
57 /**
58 * Fire OS version mapping
59 *
60 * @var array
61 */
62 private $fireOsVersionMapping = ['14' => '14', '13' => '14', '12' => '14', '11' => '8', '10' => '8', '9' => '7', '7' => '6', '5' => '5', '4.4.3' => '4.5.1', '4.4.2' => '4', '4.2.2' => '3', '4.0.3' => '3', '4.0.2' => '3', '4' => '2', '2' => '1'];
63 /**
64 * Lineage OS version mapping
65 *
66 * @var array
67 */
68 private $lineageOsVersionMapping = ['16' => '23', '15' => '22', '14' => '21', '13' => '20.0', '12.1' => '19.1', '12' => '19.0', '11' => '18.0', '10' => '17.0', '9' => '16.0', '8.1.0' => '15.1', '8.0.0' => '15.0', '7.1.2' => '14.1', '7.1.1' => '14.1', '7.0' => '14.0', '6.0.1' => '13.0', '6.0' => '13.0', '5.1.1' => '12.1', '5.0.2' => '12.0', '5.0' => '12.0', '4.4.4' => '11.0', '4.3' => '10.2', '4.2.2' => '10.1', '4.0.4' => '9.1.0'];
69 /**
70 * Returns all available operating systems
71 *
72 * @return array
73 */
74 public static function getAvailableOperatingSystems() : array
75 {
76 return self::$operatingSystems;
77 }
78 /**
79 * Returns all available operating system families
80 *
81 * @return array
82 */
83 public static function getAvailableOperatingSystemFamilies() : array
84 {
85 return self::$osFamilies;
86 }
87 /**
88 * Returns the os name and shot name
89 *
90 * @param string $name
91 *
92 * @return array
93 */
94 public static function getShortOsData(string $name) : array
95 {
96 $short = 'UNK';
97 foreach (self::$operatingSystems as $osShort => $osName) {
98 if (\strtolower($name) !== \strtolower($osName)) {
99 continue;
100 }
101 $name = $osName;
102 $short = $osShort;
103 break;
104 }
105 return \compact('short', 'name');
106 }
107 /**
108 * @inheritdoc
109 */
110 public function parse() : ?array
111 {
112 $this->restoreUserAgentFromClientHints();
113 $osFromClientHints = $this->parseOsFromClientHints();
114 $osFromUserAgent = $this->parseOsFromUserAgent();
115 if (!empty($osFromClientHints['name'])) {
116 $name = $osFromClientHints['name'];
117 $version = $osFromClientHints['version'];
118 // use version from user agent if non was provided in client hints, but os family from useragent matches
119 if (empty($version) && self::getOsFamily($name) === self::getOsFamily($osFromUserAgent['name'])) {
120 $version = $osFromUserAgent['version'];
121 }
122 // On Windows, version 0.0.0 can be either 7, 8 or 8.1
123 if ('Windows' === $name && '0.0.0' === $version) {
124 $version = '10' === $osFromUserAgent['version'] ? '' : $osFromUserAgent['version'];
125 }
126 // If the OS name detected from client hints matches the OS family from user agent
127 // but the os name is another, we use the one from user agent, as it might be more detailed
128 if ($osFromUserAgent['name'] !== $name && self::getOsFamily($osFromUserAgent['name']) === $name) {
129 $name = $osFromUserAgent['name'];
130 if ('LeafOS' === $name || 'HarmonyOS' === $name) {
131 $version = '';
132 }
133 if ('PICO OS' === $name) {
134 $version = $osFromUserAgent['version'];
135 }
136 if ('Fire OS' === $name && !empty($osFromClientHints['version'])) {
137 $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
138 $version = $this->fireOsVersionMapping[$version] ?? $this->fireOsVersionMapping[$majorVersion] ?? '';
139 }
140 }
141 $short = $osFromClientHints['short_name'];
142 // Chrome OS is in some cases reported as Linux in client hints, we fix this only if the version matches
143 if ('GNU/Linux' === $name && 'Chrome OS' === $osFromUserAgent['name'] && $osFromClientHints['version'] === $osFromUserAgent['version']) {
144 $name = $osFromUserAgent['name'];
145 $short = $osFromUserAgent['short_name'];
146 }
147 // Chrome OS is in some cases reported as Android in client hints
148 if ('Android' === $name && 'Chrome OS' === $osFromUserAgent['name']) {
149 $name = $osFromUserAgent['name'];
150 $version = '';
151 $short = $osFromUserAgent['short_name'];
152 }
153 // Meta Horizon is reported as Linux in client hints
154 if ('GNU/Linux' === $name && 'Meta Horizon' === $osFromUserAgent['name']) {
155 $name = $osFromUserAgent['name'];
156 $short = $osFromUserAgent['short_name'];
157 }
158 } elseif (!empty($osFromUserAgent['name'])) {
159 $name = $osFromUserAgent['name'];
160 $version = $osFromUserAgent['version'];
161 $short = $osFromUserAgent['short_name'];
162 } else {
163 return [];
164 }
165 $platform = $this->parsePlatform();
166 $family = self::getOsFamily($short);
167 $androidApps = ['com.hisense.odinbrowser', 'com.seraphic.openinet.pre', 'com.appssppa.idesktoppcbrowser', 'every.browser.inc'];
168 if (null !== $this->clientHints) {
169 if ('Android' !== $name && \in_array($this->clientHints->getApp(), $androidApps)) {
170 $name = 'Android';
171 $family = 'Android';
172 $short = 'ADR';
173 $version = '';
174 }
175 if ('Lineage OS' !== $name && 'org.lineageos.jelly' === $this->clientHints->getApp()) {
176 $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
177 $name = 'Lineage OS';
178 $family = 'Android';
179 $short = 'LEN';
180 $version = $this->lineageOsVersionMapping[$version] ?? $this->lineageOsVersionMapping[$majorVersion] ?? '';
181 }
182 if ('Fire OS' !== $name && 'org.mozilla.tv.firefox' === $this->clientHints->getApp()) {
183 $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
184 $name = 'Fire OS';
185 $family = 'Android';
186 $short = 'FIR';
187 $version = $this->fireOsVersionMapping[$version] ?? $this->fireOsVersionMapping[$majorVersion] ?? '';
188 }
189 }
190 if ('' !== $name && !\in_array($name, self::$operatingSystems)) {
191 // This Exception should never be thrown. If so, a defined operating system is missing in $operatingSystems
192 throw new \Exception(\sprintf("The operating system '%s' should be listed in operatingSystems array. Tried to parse user agent: %s", $name, $this->userAgent));
193 // @codeCoverageIgnore
194 }
195 $return = ['name' => $name, 'short_name' => $short, 'version' => $version, 'platform' => $platform, 'family' => $family];
196 if (\in_array($return['name'], self::$operatingSystems, \true)) {
197 $return['short_name'] = \array_search($return['name'], self::$operatingSystems, \true);
198 }
199 return $return;
200 }
201 /**
202 * Returns the operating system family for the given operating system
203 *
204 * @param string $osLabel name or short name
205 *
206 * @return string|null If null, "Unknown"
207 */
208 public static function getOsFamily(string $osLabel) : ?string
209 {
210 if (\in_array($osLabel, self::$operatingSystems, \true)) {
211 $osLabel = (string) \array_search($osLabel, self::$operatingSystems, \true);
212 }
213 foreach (self::$osFamilies as $family => $labels) {
214 if (\in_array($osLabel, $labels, \true)) {
215 return $family;
216 }
217 }
218 return null;
219 }
220 /**
221 * Returns true if OS is desktop
222 *
223 * @param string $osName OS short name
224 *
225 * @return bool
226 */
227 public static function isDesktopOs(string $osName) : bool
228 {
229 $osFamily = self::getOsFamily($osName);
230 return \in_array($osFamily, self::$desktopOsArray, \true);
231 }
232 /**
233 * Returns the full name for the given short name
234 *
235 * @param string $os
236 * @param string|null $ver
237 *
238 * @return ?string
239 */
240 public static function getNameFromId(string $os, ?string $ver = null) : ?string
241 {
242 if (\array_key_exists($os, self::$operatingSystems)) {
243 $osFullName = self::$operatingSystems[$os];
244 return \trim($osFullName . ' ' . $ver);
245 }
246 return null;
247 }
248 /**
249 * Returns the OS that can be safely detected from client hints
250 *
251 * @return array
252 */
253 protected function parseOsFromClientHints() : array
254 {
255 $name = $version = $short = '';
256 if ($this->clientHints instanceof ClientHints && $this->clientHints->getOperatingSystem()) {
257 $hintName = $this->applyClientHintMapping($this->clientHints->getOperatingSystem());
258 foreach (self::$operatingSystems as $osShort => $osName) {
259 if ($this->fuzzyCompare($hintName, $osName)) {
260 $name = $osName;
261 $short = $osShort;
262 break;
263 }
264 }
265 $version = $this->clientHints->getOperatingSystemVersion();
266 if ('Windows' === $name) {
267 $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
268 $minorVersion = (int) (\explode('.', $version, 2)[1] ?? '0');
269 if (0 === $majorVersion) {
270 $minorVersionMapping = [1 => '7', 2 => '8', 3 => '8.1'];
271 $version = $minorVersionMapping[$minorVersion] ?? $version;
272 } elseif ($majorVersion > 0 && $majorVersion < 11) {
273 $version = '10';
274 } elseif ($majorVersion > 10) {
275 $version = '11';
276 }
277 }
278 // On Windows, version 0.0.0 can be either 7, 8 or 8.1, so we return 0.0.0
279 if ('Windows' !== $name && '0.0.0' !== $version && 0 === (int) $version) {
280 $version = '';
281 }
282 }
283 return ['name' => $name, 'short_name' => $short, 'version' => $this->buildVersion($version, [])];
284 }
285 /**
286 * Returns the OS that can be detected from useragent
287 *
288 * @return array
289 */
290 protected function parseOsFromUserAgent() : array
291 {
292 $osRegex = $matches = [];
293 $name = $version = $short = '';
294 foreach ($this->getRegexes() as $osRegex) {
295 $matches = $this->matchUserAgent($osRegex['regex']);
296 if ($matches) {
297 break;
298 }
299 }
300 if (!empty($matches)) {
301 $name = $this->buildByMatch($osRegex['name'], $matches);
302 ['name' => $name, 'short' => $short] = self::getShortOsData($name);
303 $version = \array_key_exists('version', $osRegex) ? $this->buildVersion((string) $osRegex['version'], $matches) : '';
304 foreach ($osRegex['versions'] ?? [] as $regex) {
305 $matches = $this->matchUserAgent($regex['regex']);
306 if (!$matches) {
307 continue;
308 }
309 if (\array_key_exists('name', $regex)) {
310 $name = $this->buildByMatch($regex['name'], $matches);
311 ['name' => $name, 'short' => $short] = self::getShortOsData($name);
312 }
313 if (\array_key_exists('version', $regex)) {
314 $version = $this->buildVersion((string) $regex['version'], $matches);
315 }
316 break;
317 }
318 }
319 return ['name' => $name, 'short_name' => $short, 'version' => $version];
320 }
321 /**
322 * Parse current UserAgent string for the operating system platform
323 *
324 * @return string
325 *
326 * @throws \Exception
327 */
328 protected function parsePlatform() : string
329 {
330 // Use architecture from client hints if available
331 if ($this->clientHints instanceof ClientHints && $this->clientHints->getArchitecture()) {
332 $arch = \strtolower($this->clientHints->getArchitecture());
333 if (\false !== \strpos($arch, 'arm')) {
334 return 'ARM';
335 }
336 if (\false !== \strpos($arch, 'loongarch64')) {
337 return 'LoongArch64';
338 }
339 if (\false !== \strpos($arch, 'mips')) {
340 return 'MIPS';
341 }
342 if (\false !== \strpos($arch, 'sh4')) {
343 return 'SuperH';
344 }
345 if (\false !== \strpos($arch, 'sparc64')) {
346 return 'SPARC64';
347 }
348 if (\false !== \strpos($arch, 'x64') || \false !== \strpos($arch, 'x86') && '64' === $this->clientHints->getBitness()) {
349 return 'x64';
350 }
351 if (\false !== \strpos($arch, 'x86')) {
352 return 'x86';
353 }
354 }
355 if ($this->matchUserAgent('arm[ _;)ev]|.*arm$|.*arm64|aarch64|Apple ?TV|Watch ?OS|Watch1,[12]')) {
356 return 'ARM';
357 }
358 if ($this->matchUserAgent('loongarch64')) {
359 return 'LoongArch64';
360 }
361 if ($this->matchUserAgent('mips')) {
362 return 'MIPS';
363 }
364 if ($this->matchUserAgent('sh4')) {
365 return 'SuperH';
366 }
367 if ($this->matchUserAgent('sparc64')) {
368 return 'SPARC64';
369 }
370 if ($this->matchUserAgent('64-?bit|WOW64|(?:Intel)?x64|WINDOWS_64|win64|IRIX;?64|.*amd64|.*x86_?64')) {
371 return 'x64';
372 }
373 if ($this->matchUserAgent('.*32bit|.*win32|(?:i[0-9]|x)86|i86pc')) {
374 return 'x86';
375 }
376 return '';
377 }
378 }
379