platforms   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 428
Duplicated Lines 0 %

Test Coverage

Coverage 99.75%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 332
c 3
b 0
f 0
dl 0
loc 428
ccs 394
cts 395
cp 0.9975
rs 8.64
wmc 47

2 Methods

Rating   Name   Duplication   Size   Complexity  
A getPlatform() 0 15 1
F get() 0 402 46

How to fix   Complexity   

Complex Class

Complex classes like platforms often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use platforms, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types = 1);
3
namespace hexydec\agentzero;
4
5
class platforms {
6
7
	/**
8
	 * Generates a configuration array for matching platforms
9
	 * 
10
	 * @return array<string,props> An array with keys representing the string to match, and values a props object defining how to generate the match and which properties to set
11
	 */
12 61
	public static function get() : array {
13 1
		$fn = [
14 1
			'platformlinux' => function (string $value) : array {
15 16
				if (!\str_starts_with($value, 'Red Hat/') && ($platform = \mb_strstr($value, ' ', true)) !== false) {
16 5
					$value = $platform;
17
				}
18 16
				$parts = \explode('/', $value, 2);
19 16
				if (!isset($parts[1])) {
20 14
					$parts = \explode('-', $value, 2);
21
				}
22 16
				if (!isset($parts[1])) {
23 12
					$parts = \explode(' ', $value, 2);
24
				}
25 16
				return [
26 16
					'type' => 'human',
27 16
					'category' => 'desktop',
28 16
					'kernel' => 'Linux',
29 16
					'platform' => self::getPlatform($parts[0]),
30 16
					'platformversion' => $parts[1] ?? null
31 16
				];
32 1
			},
33 1
			'platformwindows' => function (string $value) : array {
34 48
				$mapping = [
35 48
					'5.0' => '2000',
36 48
					'5.1' => 'XP',
37 48
					'5.2' => 'XP',
38 48
					'6.0' => 'Vista',
39 48
					'6.1' => '7',
40 48
					'6.2' => '8',
41 48
					'6.3' => '8.1',
42 48
					'10.0' => '10'
43 48
				];
44 48
				$version = null;
45 48
				foreach (['Windows NT ', 'Windows '] AS $item) {
46 48
					if (($pos = \mb_stripos($value, $item)) !== false) {
47 48
						$version = \explode(' ', \mb_substr($value, $pos + \mb_strlen($item)))[0];
48 48
						break;
49
					}
50
				}
51 48
				return [
52 48
					'type' => 'human',
53 48
					'category' => 'desktop',
54 48
					'kernel' => 'Windows NT',
55 48
					'platform' => 'Windows',
56 48
					'platformversion' => $mapping[$version] ?? $version
57 48
				];
58 1
			},
59 1
			'android' => function (string $value, int $i, array $tokens) : array {
60 42
				$os = \explode(' ', $value, 3);
61
62
				// skip language
63 42
				if (!empty($tokens[++$i]) && (\strlen($tokens[$i]) === 2 || (\strlen($tokens[$i]) === 5 && \mb_strpos($tokens[$i], '_') === 2))) {
64 2
					$i++;
65
				}
66
67
				// only where the token length is greater than 2, or it doesn't contain a /, or if it does it is Build/
68 42
				$device = empty($tokens[$i]) || \strlen($tokens[$i]) <= 2 || (\str_contains($tokens[$i], '/') && \mb_stripos($tokens[$i], 'Build/') === false) ? [] : devices::getDevice($tokens[$i]);
69 42
				return \array_merge($device, [
70 42
					'type' => 'human',
71 42
					'platform' => 'Android',
72 42
					'platformversion' => isset($os[1]) ? \explode('/', $os[1])[0] : null
73 42
				]);
74 1
			}
75 1
		];
76
77 49
		return [
78
79
			// platforms
80 49
			'PalmSource' => new props('start', fn (string $value) : array => [
81 3
				'type' => 'human',
82 3
				'category' => 'mobile',
83 3
				'platform' => 'PalmOS',
84 3
				'platformversion' => \mb_substr($value, 11) ?: null,
85 3
				'kernel' => 'AMX 68000',
86 3
				'architecture' => 'Arm',
87 3
				'bits' => 32,
88 3
			]),
89 49
			'PalmOS' => new props('start', fn (string $value) : array => [
90 2
				'type' => 'human',
91 2
				'category' => 'mobile',
92 2
				'platform' => 'PalmOS',
93 2
				'platformversion' => \mb_substr($value, 7) ?: null,
94 2
				'kernel' => 'AMX 68000',
95 2
				'architecture' => 'Arm',
96 2
				'bits' => 32,
97 2
			]),
98 49
			'Windows NT ' => new props('any', $fn['platformwindows']),
99 49
			'Windows Phone' => new props('start', function (string $value) : array {
100 3
				$version = \mb_substr($value, 14);
101 3
				return [
102 3
					'type' => 'human',
103 3
					'category' => 'mobile',
104 3
					'platform' => 'Windows Phone',
105 3
					'platformversion' => $version,
106 3
					'kernel' => \intval($version) >= 8 ? 'Windows NT' : 'Windows CE'
107 3
				];
108 49
			}),
109 49
			'Win98' => new props('start', function (string $value, int $i, array $tokens) : array {
110 2
				foreach ($tokens AS $item) {
111 2
					if (\str_starts_with($item, 'PalmSource')) {
112
						return [];
113
					}
114
				}
115 2
				return [
116 2
					'type' => 'human',
117 2
					'category' => 'desktop',
118 2
					'architecture' => 'x86',
119 2
					'bits' => 32,
120 2
					'kernel' => 'MS-DOS',
121 2
					'platform' => 'Windows',
122 2
					'platformversion' => '98'
123 2
				];
124 49
			}),
125 49
			'Win32' => new props('exact', [
126 49
				'type' => 'human',
127 49
				'category' => 'desktop',
128 49
				'architecture' => 'x86',
129 49
				'bits' => 32,
130 49
				'platform' => 'Windows'
131 49
			]),
132 49
			'Win64' => new props('exact', [
133 49
				'type' => 'human',
134 49
				'category' => 'desktop',
135 49
				'bits' => 64,
136 49
				'platform' => 'Windows'
137 49
			]),
138 49
			'WinNT' => new props('start', fn (string $value) : array => [
139 2
				'type' => 'human',
140 2
				'category' => 'desktop',
141 2
				'architecture' => 'x86',
142 2
				'bits' => 32,
143 2
				'kernel' => 'Windows NT',
144 2
				'platform' => 'Windows',
145 2
				'platformversion' => \mb_substr($value, 5)
146 2
			]),
147 49
			'Windows' => new props('any', function (string $value, int $i, array $tokens) use ($fn) : array {
148 49
				foreach ($tokens AS $item) {
149 49
					if (\str_starts_with($item, 'PalmSource')) {
150 2
						return [];
151
					}
152
				}
153 48
				return $fn['platformwindows']($value);
154 49
			}),
155 49
			'MacOS/' => new props('start', fn (string $value) : array => [
156 2
				'type' => 'human',
157 2
				'category' => 'desktop',
158 2
				'vendor' => 'Apple',
159 2
				'platform' => 'MacOS',
160 2
				'platformversion' => \mb_substr($value, 6),
161 2
				'bits' => 64
162 2
			]),
163 49
			'Mac OS X' => new props('any', function (string $value) : array {
164 31
				$version = \str_replace('_', '.', \mb_substr($value, \mb_stripos($value, 'Mac OS X') + 9));
165 31
				$register = $version && \intval(\explode('.', $version)[1] ?? 0) >= 6 ? 64 : null;
166 31
				return [
167 31
					'type' => 'human',
168 31
					'category' => 'desktop',
169 31
					'kernel' => 'Linux',
170 31
					'platform' => 'Mac OS X',
171 31
					'platformversion' => $version === '' ? null : $version,
172 31
					'bits' => $register
173 31
				];
174 49
			}),
175 49
			'AppleTV' => new props('exact', [
176 49
				'type' => 'human',
177 49
				'category' => 'tv',
178 49
				'device' => 'AppleTV',
179 49
				'platform' => 'tvOS',
180 49
				'bits' => 64
181 49
			]),
182 49
			'iOS/' => new props('start', fn (string $value) : array => [
183 2
				'type' => 'human',
184 2
				'category' => 'mobile',
185 2
				'platform' => 'iOS',
186 2
				'platformversion' => \mb_substr($value, 4)
187 2
			]),
188 49
			'iOS ' => new props('start', fn (string $value) : array => [
189 3
				'type' => 'human',
190 3
				'category' => 'mobile',
191 3
				'platform' => 'iOS',
192 3
				'platformversion' => \str_replace('_', '.', \mb_substr($value, 4))
193 3
			]),
194 49
			'iOS' => new props('exact', fn (string $value, int $i, array $tokens) : array => [
195 1
				'type' => 'human',
196 1
				'category' => 'mobile',
197 1
				'platform' => 'iOS',
198 1
				'platformversion' => $tokens[$i+1] ?? null
199 1
			]),
200 49
			'iPadOS' => new props('exact', fn (string $value, int $i, array $tokens) : array => [
201 1
				'type' => 'human',
202 1
				'category' => 'tablet',
203 1
				'vendor' => 'Apple',
204 1
				'device' => 'iPad',
205 1
				'platform' => 'iOS',
206 1
				'platformversion' => $tokens[$i+1] ?? null
207 1
			]),
208 49
			'CrOS' => new props('start', function (string $value) : array {
209 4
				$parts = \explode(' ', $value);
210 4
				return [
211 4
					'type' => 'human',
212 4
					'category' => 'desktop',
213 4
					'platform' => 'Chrome OS',
214 4
					'platformversion' => $parts[2] ?? null
215 4
				];
216 49
			}),
217 49
			'Kindle/' => new props('start', fn (string $value) : array => [
218 2
				'type' => 'human',
219 2
				'category' => 'ebook',
220 2
				'platform' => 'Kindle',
221 2
				'platformversion' => \mb_substr($value, 7)
222 2
			]),
223 49
			'Tizen' => new props('start', function (string $value) : array {
224 1
				$parts = \explode(' ', $value, 2);
225 1
				return [
226 1
					'type' => 'human',
227 1
					'category' => 'desktop',
228 1
					'kernel' => 'Linux',
229 1
					'platform' => 'Tizen',
230 1
					'platformversion' => $parts[1] ?? null
231 1
				];
232 49
			}),
233 49
			'KAIOS/' => new props('start', function (string $value, int $i, array $tokens) : array {
234
235
				// find device
236 1
				foreach ($tokens AS $i => $item) {
237 1
					if ($item === 'Mobile' && !\str_starts_with($tokens[$i++] ?? 'rv:', 'rv:')) {
238 1
						if (\str_contains($tokens[$i], '/')) {
239 1
							$parts = \explode('/', $tokens[$i]);
240
						} else {
241 1
							$parts = \explode(' ', \str_replace('_', ' ', $tokens[$i]), 2);
242
						}
243 1
						break;
244
					}
245
				}
246 1
				return [
247 1
					'type' => 'human',
248 1
					'category' => 'mobile',
249 1
					'kernel' => 'Linux',
250 1
					'architecture' => 'Arm',
251 1
					'platform' => 'KaiOS',
252 1
					'platformversion' => \mb_substr($value, 6),
253 1
					'vendor' => !empty($parts[0]) ? devices::getVendor($parts[0]) : null,
254 1
					'device' => $parts[1] ?? null,
255 1
					'build' => $parts[2] ?? null
256 1
				];
257 49
	}		),
258 49
			'Ubuntu' => new props('start', $fn['platformlinux']),
259 49
			'Kubuntu' => new props('start', $fn['platformlinux']),
260 49
			'Mint' => new props('start', $fn['platformlinux']),
261 49
			'SUSE' => new props('start', $fn['platformlinux']),
262 49
			'Red Hat/' => new props('start', $fn['platformlinux']),
263 49
			'Debian' => new props('start', $fn['platformlinux']),
264 49
			'Darwin' => new props('start', $fn['platformlinux']),
265 49
			'Fedora' => new props('start', $fn['platformlinux']),
266 49
			'CentOS' => new props('start', $fn['platformlinux']),
267 49
			'Rocky' => new props('start', $fn['platformlinux']),
268 49
			'Alma' => new props('start', $fn['platformlinux']),
269 49
			'Gentoo' => new props('start', $fn['platformlinux']),
270 49
			'Slackware' => new props('start', $fn['platformlinux']),
271 49
			'OpenSUSE' => new props('start', $fn['platformlinux']),
272 49
			'FreeBSD' => new props('start', $fn['platformlinux']),
273 49
			'OpenBSD' => new props('start', $fn['platformlinux']),
274 49
			'ArchLinux' => new props('exact', fn () : array => [
275 1
				'type' => 'human',
276 1
				'category' => 'desktop',
277 1
				'kernel' => 'Linux',
278 1
				'platform' => 'Arch',
279 1
			]),
280 49
			'Arch' => new props('exact', fn () : array => [
281 1
				'type' => 'human',
282 1
				'category' => 'desktop',
283 1
				'kernel' => 'Linux',
284 1
				'platform' => 'Arch',
285 1
			]),
286 49
			'Web0S' => new props('exact', $fn['platformlinux']),
287 49
			'webOSTV' => new props('exact', [
288 49
				'type' => 'human',
289 49
				'category' => 'tv',
290 49
				'kernel' => 'Linux',
291 49
				'platform' => 'WebOS'
292 49
			]),
293 49
			'WEBOS' => new props('start', fn (string $value) : array => [
294 2
				'type' => 'human',
295 2
				'category' => 'tv',
296 2
				'kernel' => 'Linux',
297 2
				'platform' => 'WebOS',
298 2
				'platformversion' => \mb_substr($value, 5)
299 2
			]),
300 49
			'Roku ' => new props('start', function (string $value, int $i, array $tokens) : array {
301 1
				$app = \str_contains($tokens[$i - 1] ?? '', '/') ? \explode('/', $tokens[$i - 1], 2) : null;
302 1
				return \array_merge(
303 1
					[
304 1
						'type' => 'human',
305 1
						'category' => 'tv',
306 1
						'kernel' => 'Linux',
307 1
						'platform' => 'Roku OS',
308 1
						'platformversion' => \mb_substr($value, 5),
309 1
						'app' => $app[0] ?? null,
310 1
						'appname' => $app[0] ?? null,
311 1
						'appversion' => $app[1] ?? null,
312 1
					],
313 1
					isset($tokens[$i + 2]) ? devices::getDevice($tokens[$i + 2]) : []
314 1
				);
315 49
			}),
316 49
			'SunOS' => new props('start', [
317 49
				'type' => 'human',
318 49
				'category' => 'desktop',
319 49
				'kernel' => 'unix',
320 49
				'platform' => 'Solaris',
321 49
			]),
322 49
			'AmigaOS' => new props('any', function (string $value) : array {
323 3
				if (($pos = \mb_stripos($value, 'AmigaOS')) !== false) {
324 3
					$value = \mb_substr($value, $pos);
325
				}
326 3
				$parts = \explode(' ', $value, 2);
327 3
				return [
328 3
					'type' => 'human',
329 3
					'category' => 'desktop',
330 3
					'kernel' => 'Exec',
331 3
					'platform' => 'AmigaOS',
332 3
					'platformversion' => isset($parts[1]) && \strspn($parts[1], '0123456789.-_') === \strlen($parts[1]) ? $parts[1] : null
333 3
				];
334 49
			}),
335 49
			'Fuchsia' => new props('exact', function (string $value, int $i, array $tokens) : array {
336 1
				$os = \explode(' ', $tokens[++$i], 2);
337 1
				return [
338 1
					'type' => 'human',
339 1
					'category' => 'mobile',
340 1
					'kernel' => 'Zircon',
341 1
					'platform' => 'Fuchsia',
342 1
					'platformversion' => isset($os[1]) && \strspn($os[1], '0123456789.-_', \strlen($os[0])) === \strlen($os[1]) ? $os[1] : null
343 1
				];
344 49
			}),
345 49
			'Maemo' => new props('exact', function (string $value, int $i, array $tokens) : array {
346 1
				$os = \explode(' ', $tokens[++$i], 2);
347 1
				return [
348 1
					'type' => 'human',
349 1
					'category' => 'mobile',
350 1
					'kernel' => 'Linux',
351 1
					'platform' => 'Maemo',
352 1
					'platformversion' => isset($os[1]) && \strspn($os[1], '0123456789.-_', \strlen($os[0])) === \strlen($os[1]) ? $os[1] : null
353 1
				];
354 49
			}),
355 49
			'J2ME/MIDP' => new props('exact', fn (string $value) : array => [
356 3
				'type' => 'human',
357 3
				'category' => 'mobile',
358 3
				'kernel' => 'Java VM',
359 3
				'platform' => $value
360 3
			]),
361 49
			'Haiku' => new props('any', function (string $value) : array {
362 3
				$parts = \explode('/', $value, 2);
363 3
				return [
364 3
					'type' => 'human',
365 3
					'category' => 'desktop',
366 3
					'kernel' => 'Haiku',
367 3
					'platform' => 'Haiku',
368 3
					'platformversion' => $parts[1] ?? null
369 3
				];
370 49
			}),
371 49
			'BeOS' => new props('start', function (string $value) : array {
372 1
				$parts = \explode('/', $value, 2);
373 1
				return [
374 1
					'type' => 'human',
375 1
					'category' => 'desktop',
376 1
					'kernel' => 'BeOS',
377 1
					'platform' => 'BeOS',
378 1
					'platformversion' => $parts[1] ?? null
379 1
				];
380 49
			}),
381 49
			'Android' => new props('exact', $fn['android']),
382 49
			'Android/' => new props('start', fn (string $value) : array => [
383 2
				'type' => 'human',
384 2
				'platform' => 'Android',
385 2
				'platformversion' => \explode('/', $value, 3)[1] ?: null
386 2
			]),
387 49
			'Android ' => new props('start', $fn['android']),
388 49
			'Android-' => new props('start', $fn['android']),
389 49
			'Linux' => new props('any', function (string $value, int $i, array $tokens) : array {
390 61
				return [
391 61
					'kernel' => 'Linux',
392 61
					'platform' => 'Linux',
393 61
					'platformversion' => \str_contains($tokens[$i + 1] ?? '', '.') && \strspn($tokens[$i + 1], '0123456789.') >= 3 ? \explode(' ', $tokens[$i + 1])[0] : null
394 61
				];
395 49
			}),
396 49
			'X11' => new props('exact', function (string $value, int $i, array $tokens) : array {
397 32
				$os = \explode(' ', $tokens[++$i] ?? '', 2);
398 32
				return [
399 32
					'category' => 'desktop',
400 32
					'kernel' => 'Linux',
401 32
					'platform' => $os[0] ?: 'X11',
402 32
					'platformversion' => isset($os[1]) && \strspn($os[1], '0123456789.-_', \strlen($os[0])) === \strlen($os[1]) ? $os[1] : null
403 32
				];
404 49
			}),
405 49
			'OS/2' => new props('exact', [
406 49
				'category' => 'desktop',
407 49
				'platform' => 'OS/2'
408 49
			]),
409 49
			'Version/' => new props('start', fn (string $value) : array => [
410 46
				'platformversion' => \mb_substr($value, 8)
411 46
			]),
412 49
			'platformVersion/' => new props('start', fn (string $value) : array => [
413 2
				'platformversion' => \mb_substr($value, 16)
414 2
			]),
415 49
		];
416
	}
417
418 24
	public static function getPlatform(string $value) : string {
419 24
		$map = [
420 24
			'webos' => 'WebOS',
421 24
			'web0s' => 'WebOS',
422 24
			'j2me/midp' => 'J2ME/MIDP',
423 24
			'centos' => 'CentOS',
424 24
			'suse' => 'SUSE',
425 24
			'freebsd' => 'FreeBSD',
426 24
			'openbsd' => 'OpenBSD',
427 24
			'netbsd' => 'NetBSD',
428 24
			'opensuse' => 'OpenSUSE',
429 24
			'iphone' => 'iOS'
430 24
		];
431 24
		$value = \mb_strtolower($value);
432 24
		return $map[$value] ?? \mb_convert_case($value, MB_CASE_TITLE);
433
	}
434
}