PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.2.0
Independent Analytics – WordPress Analytics Plugin v2.2.0
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 2 years ago Device 2 years ago AbstractBotParser.php 2 years ago AbstractParser.php 2 years ago Bot.php 2 years ago OperatingSystem.php 2 years ago VendorFragment.php 2 years ago
OperatingSystem.php
313 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 IAWP_SCOPED\DeviceDetector\Parser;
12
13 use IAWP_SCOPED\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', 'AMZ' => 'Amazon Linux', 'AMG' => 'AmigaOS', 'ATV' => 'tvOS', 'ARL' => 'Arch Linux', 'BTR' => 'BackTrack', 'SBA' => 'Bada', 'BEO' => 'BeOS', 'BLB' => 'BlackBerry OS', 'QNX' => 'BlackBerry Tablet OS', 'BOS' => 'Bliss OS', 'BMP' => 'Brew', 'CAI' => 'Caixa Mágica', 'CES' => 'CentOS', 'CST' => 'CentOS Stream', 'CLR' => 'ClearOS Mobile', 'COS' => 'Chrome OS', 'CRS' => 'Chromium OS', 'CHN' => 'China OS', 'CYN' => 'CyanogenMod', 'DEB' => 'Debian', 'DEE' => 'Deepin', 'DFB' => 'DragonFly', 'DVK' => 'DVKBuntu', 'FED' => 'Fedora', 'FEN' => 'Fenix', 'FOS' => 'Firefox OS', 'FIR' => 'Fire OS', 'FOR' => 'Foresight Linux', 'FRE' => 'Freebox', 'BSD' => 'FreeBSD', 'FYD' => 'FydeOS', 'FUC' => 'Fuchsia', 'GNT' => 'Gentoo', 'GRI' => 'GridOS', 'GTV' => 'Google TV', 'HPX' => 'HP-UX', 'HAI' => 'Haiku OS', 'IPA' => 'iPadOS', 'HAR' => 'HarmonyOS', 'HAS' => 'HasCodingOS', 'IRI' => 'IRIX', 'INF' => 'Inferno', 'JME' => 'Java ME', 'KOS' => 'KaiOS', 'KAL' => 'Kali', 'KAN' => 'Kanotix', 'KNO' => 'Knoppix', 'KTV' => 'KreaTV', 'KBT' => 'Kubuntu', 'LIN' => 'GNU/Linux', 'LND' => 'LindowsOS', 'LNS' => 'Linspire', 'LEN' => 'Lineage OS', 'LBT' => 'Lubuntu', 'LOS' => 'Lumin OS', 'LUN' => 'LuneOS', 'VLN' => 'VectorLinux', 'MAC' => 'Mac', 'MAE' => 'Maemo', 'MAG' => 'Mageia', 'MDR' => 'Mandriva', 'SMG' => 'MeeGo', 'MCD' => 'MocorDroid', 'MON' => 'moonOS', 'MIN' => 'Mint', 'MLD' => 'MildWild', 'MOR' => 'MorphOS', 'NBS' => 'NetBSD', 'MTK' => 'MTK / Nucleus', 'MRE' => 'MRE', 'WII' => 'Nintendo', 'NDS' => 'Nintendo Mobile', 'NOV' => 'Nova', 'OS2' => 'OS/2', 'T64' => 'OSF1', 'OBS' => 'OpenBSD', 'OWR' => 'OpenWrt', 'OTV' => 'Opera TV', 'ORA' => 'Oracle Linux', 'ORD' => 'Ordissimo', 'PAR' => 'Pardus', 'PCL' => 'PCLinuxOS', 'PIC' => 'PICO OS', 'PLA' => 'Plasma Mobile', 'PSP' => 'PlayStation Portable', 'PS3' => 'PlayStation', 'PUR' => 'PureOS', 'RHT' => 'Red Hat', 'RED' => 'RedOS', 'REV' => 'Revenge OS', 'ROS' => 'RISC OS', 'ROK' => 'Roku OS', 'RSO' => 'Rosa', 'ROU' => 'RouterOS', 'REM' => 'Remix OS', 'RRS' => 'Resurrection Remix OS', 'REX' => 'REX', 'RZD' => 'RazoDroiD', 'SAB' => 'Sabayon', 'SSE' => 'SUSE', 'SAF' => 'Sailfish OS', 'SEE' => 'SeewoOS', 'SIR' => 'Sirin OS', 'SLW' => 'Slackware', 'SOS' => 'Solaris', 'SYL' => 'Syllable', 'SYM' => 'Symbian', 'SYS' => 'Symbian OS', 'S40' => 'Symbian OS Series 40', 'S60' => 'Symbian OS Series 60', 'SY3' => 'Symbian^3', 'TEN' => 'TencentOS', 'TDX' => 'ThreadX', 'TIZ' => 'Tizen', 'TIV' => 'TiVo OS', 'TOS' => 'TmaxOS', 'UBT' => 'Ubuntu', 'VID' => 'VIDAA', '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', 'XBX' => 'Xbox', 'XBT' => 'Xubuntu', 'YNS' => 'YunOS', 'ZEN' => 'Zenwalk', 'ZOR' => 'ZorinOS', 'IOS' => 'iOS', 'POS' => 'palmOS', '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'], 'AmigaOS' => ['AMG', 'MOR'], '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'], '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'], 'Mac' => ['MAC'], 'Mobile Gaming Console' => ['PSP', 'NDS', 'XBX'], 'Real-time OS' => ['MTK', 'TDX', 'MRE', 'JME', 'REX'], 'Other Mobile' => ['WOS', 'POS', 'SBA', 'TIZ', 'SMG', 'MAE', 'LUN'], 'Symbian' => ['SYM', 'SYS', 'SY3', 'S60', 'S40'], 'Unix' => ['SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64', 'INF'], 'WebTV' => ['WTV'], 'Windows' => ['WIN'], 'Windows Mobile' => ['WPH', 'WMO', 'WCE', 'WRT', 'WIO'], 'Other Smart TV' => ['WHS']];
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', 'Chromium OS'];
57 /**
58 * Returns all available operating systems
59 *
60 * @return array
61 */
62 public static function getAvailableOperatingSystems() : array
63 {
64 return self::$operatingSystems;
65 }
66 /**
67 * Returns all available operating system families
68 *
69 * @return array
70 */
71 public static function getAvailableOperatingSystemFamilies() : array
72 {
73 return self::$osFamilies;
74 }
75 /**
76 * Returns the os name and shot name
77 *
78 * @param string $name
79 *
80 * @return array
81 */
82 public static function getShortOsData(string $name) : array
83 {
84 $short = 'UNK';
85 foreach (self::$operatingSystems as $osShort => $osName) {
86 if (\strtolower($name) !== \strtolower($osName)) {
87 continue;
88 }
89 $name = $osName;
90 $short = $osShort;
91 break;
92 }
93 return \compact('short', 'name');
94 }
95 /**
96 * @inheritdoc
97 */
98 public function parse() : ?array
99 {
100 $osFromClientHints = $this->parseOsFromClientHints();
101 $osFromUserAgent = $this->parseOsFromUserAgent();
102 if (!empty($osFromClientHints['name'])) {
103 $name = $osFromClientHints['name'];
104 $version = $osFromClientHints['version'];
105 // use version from user agent if non was provided in client hints, but os family from useragent matches
106 if (empty($version) && self::getOsFamily($name) === self::getOsFamily($osFromUserAgent['name'])) {
107 $version = $osFromUserAgent['version'];
108 }
109 // If the OS name detected from client hints matches the OS family from user agent
110 // but the os name is another, we use the one from user agent, as it might be more detailed
111 if (self::getOsFamily($osFromUserAgent['name']) === $name && $osFromUserAgent['name'] !== $name) {
112 $name = $osFromUserAgent['name'];
113 if ('HarmonyOS' === $name) {
114 $version = '';
115 }
116 if ('Fire OS' === $osFromUserAgent['name']) {
117 $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
118 $fireOsVersionMapping = ['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'];
119 $version = $fireOsVersionMapping[$version] ?? $fireOsVersionMapping[$majorVersion] ?? $version;
120 }
121 }
122 $short = $osFromClientHints['short_name'];
123 // Chrome OS is in some cases reported as Linux in client hints, we fix this only if the version matches
124 if ('GNU/Linux' === $name && 'Chrome OS' === $osFromUserAgent['name'] && $osFromClientHints['version'] === $osFromUserAgent['version']) {
125 $name = $osFromUserAgent['name'];
126 $short = $osFromUserAgent['short_name'];
127 }
128 } elseif (!empty($osFromUserAgent['name'])) {
129 $name = $osFromUserAgent['name'];
130 $version = $osFromUserAgent['version'];
131 $short = $osFromUserAgent['short_name'];
132 } else {
133 return [];
134 }
135 $platform = $this->parsePlatform();
136 $family = self::getOsFamily($short);
137 $androidApps = ['com.hisense.odinbrowser', 'com.seraphic.openinet.pre', 'com.appssppa.idesktoppcbrowser', 'every.browser.inc'];
138 if (null !== $this->clientHints) {
139 if (\in_array($this->clientHints->getApp(), $androidApps) && 'Android' !== $name) {
140 $name = 'Android';
141 $family = 'Android';
142 $short = 'ADR';
143 $version = '';
144 }
145 }
146 $return = ['name' => $name, 'short_name' => $short, 'version' => $version, 'platform' => $platform, 'family' => $family];
147 if (\in_array($return['name'], self::$operatingSystems)) {
148 $return['short_name'] = \array_search($return['name'], self::$operatingSystems);
149 }
150 return $return;
151 }
152 /**
153 * Returns the operating system family for the given operating system
154 *
155 * @param string $osLabel name or short name
156 *
157 * @return string|null If null, "Unknown"
158 */
159 public static function getOsFamily(string $osLabel) : ?string
160 {
161 if (\in_array($osLabel, self::$operatingSystems)) {
162 $osLabel = \array_search($osLabel, self::$operatingSystems);
163 }
164 foreach (self::$osFamilies as $family => $labels) {
165 if (\in_array($osLabel, $labels)) {
166 return (string) $family;
167 }
168 }
169 return null;
170 }
171 /**
172 * Returns true if OS is desktop
173 *
174 * @param string $osName OS short name
175 *
176 * @return bool
177 */
178 public static function isDesktopOs(string $osName) : bool
179 {
180 $osFamily = self::getOsFamily($osName);
181 return \in_array($osFamily, self::$desktopOsArray);
182 }
183 /**
184 * Returns the full name for the given short name
185 *
186 * @param string $os
187 * @param string|null $ver
188 *
189 * @return ?string
190 */
191 public static function getNameFromId(string $os, ?string $ver = null) : ?string
192 {
193 if (\array_key_exists($os, self::$operatingSystems)) {
194 $osFullName = self::$operatingSystems[$os];
195 return \trim($osFullName . ' ' . $ver);
196 }
197 return null;
198 }
199 /**
200 * Returns the OS that can be safely detected from client hints
201 *
202 * @return array
203 */
204 protected function parseOsFromClientHints() : array
205 {
206 $name = $version = $short = '';
207 if ($this->clientHints instanceof ClientHints && $this->clientHints->getOperatingSystem()) {
208 $hintName = $this->applyClientHintMapping($this->clientHints->getOperatingSystem());
209 foreach (self::$operatingSystems as $osShort => $osName) {
210 if ($this->fuzzyCompare($hintName, $osName)) {
211 $name = $osName;
212 $short = $osShort;
213 break;
214 }
215 }
216 $version = $this->clientHints->getOperatingSystemVersion();
217 if ('Windows' === $name) {
218 $majorVersion = (int) (\explode('.', $version, 1)[0] ?? '0');
219 if ($majorVersion > 0 && $majorVersion < 11) {
220 $version = '10';
221 } elseif ($majorVersion > 10) {
222 $version = '11';
223 }
224 }
225 if (0 === (int) $version) {
226 $version = '';
227 }
228 }
229 return ['name' => $name, 'short_name' => $short, 'version' => $this->buildVersion($version, [])];
230 }
231 /**
232 * Returns the OS that can be detected from useragent
233 *
234 * @return array
235 *
236 * @throws \Exception
237 */
238 protected function parseOsFromUserAgent() : array
239 {
240 $osRegex = $matches = [];
241 $name = $version = $short = '';
242 foreach ($this->getRegexes() as $osRegex) {
243 $matches = $this->matchUserAgent($osRegex['regex']);
244 if ($matches) {
245 break;
246 }
247 }
248 if (!empty($matches)) {
249 $name = $this->buildByMatch($osRegex['name'], $matches);
250 ['name' => $name, 'short' => $short] = self::getShortOsData($name);
251 $version = \array_key_exists('version', $osRegex) ? $this->buildVersion((string) $osRegex['version'], $matches) : '';
252 foreach ($osRegex['versions'] ?? [] as $regex) {
253 $matches = $this->matchUserAgent($regex['regex']);
254 if (!$matches) {
255 continue;
256 }
257 if (\array_key_exists('name', $regex)) {
258 $name = $this->buildByMatch($regex['name'], $matches);
259 ['name' => $name, 'short' => $short] = self::getShortOsData($name);
260 }
261 if (\array_key_exists('version', $regex)) {
262 $version = $this->buildVersion((string) $regex['version'], $matches);
263 }
264 break;
265 }
266 }
267 return ['name' => $name, 'short_name' => $short, 'version' => $version];
268 }
269 /**
270 * Parse current UserAgent string for the operating system platform
271 *
272 * @return string
273 */
274 protected function parsePlatform() : string
275 {
276 // Use architecture from client hints if available
277 if ($this->clientHints instanceof ClientHints && $this->clientHints->getArchitecture()) {
278 $arch = \strtolower($this->clientHints->getArchitecture());
279 if (\false !== \strpos($arch, 'arm')) {
280 return 'ARM';
281 }
282 if (\false !== \strpos($arch, 'mips')) {
283 return 'MIPS';
284 }
285 if (\false !== \strpos($arch, 'sh4')) {
286 return 'SuperH';
287 }
288 if (\false !== \strpos($arch, 'x64') || \false !== \strpos($arch, 'x86') && '64' === $this->clientHints->getBitness()) {
289 return 'x64';
290 }
291 if (\false !== \strpos($arch, 'x86')) {
292 return 'x86';
293 }
294 }
295 if ($this->matchUserAgent('arm|aarch64|Apple ?TV|Watch ?OS|Watch1,[12]')) {
296 return 'ARM';
297 }
298 if ($this->matchUserAgent('mips')) {
299 return 'MIPS';
300 }
301 if ($this->matchUserAgent('sh4')) {
302 return 'SuperH';
303 }
304 if ($this->matchUserAgent('64-?bit|WOW64|(?:Intel)?x64|WINDOWS_64|win64|amd64|x86_?64')) {
305 return 'x64';
306 }
307 if ($this->matchUserAgent('.+32bit|.+win32|(?:i[0-9]|x)86|i86pc')) {
308 return 'x86';
309 }
310 return '';
311 }
312 }
313