Passed
Push — main ( 03a93c...274053 )
by Will
13:21
created

devices::get()   F

Complexity

Conditions 23
Paths 1

Size

Total Lines 428
Code Lines 330

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 403
CRAP Score 23.0482

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 330
c 1
b 0
f 0
dl 0
loc 428
ccs 403
cts 422
cp 0.955
rs 3.3333
cc 23
nc 1
nop 0
crap 23.0482

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types = 1);
3
namespace hexydec\agentzero;
4
5
/**
6
 * @phpstan-import-type MatchConfig from config
7
 */
8
class devices {
9
10
	/**
11
	 * Generates a configuration array for matching devices
12
	 * 
13
	 * @return MatchConfig An array with keys representing the string to match, and a value of an array containing parsing and output settings
0 ignored issues
show
Bug introduced by
The type hexydec\agentzero\MatchConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
	 */
15 78
	public static function get() : array {
16 3
		$fn = [
17 3
			'ios' => function (string $value, int $i, array $tokens) : array {
18 15
				$version = null;
19 15
				$model = null;
20 15
				foreach ($tokens AS $item) {
21 15
					if (\str_starts_with($item, 'Mobile/')) {
22 15
						$model = \mb_substr($item, 7);
23 15
					} elseif (\str_starts_with($item, 'CPU iPhone OS ')) {
24 14
						$version = \str_replace('_', '.', \mb_substr($item, 14, \mb_strpos($item, ' ', 14) - 14));
25 15
					} elseif (\str_starts_with($item, 'CPU OS ')) {
26 2
						$version = \str_replace('_', '.', \mb_substr($item, 7, \mb_strpos($item, ' ', 7) - 7));
27
					}
28
				}
29 15
				return [
30 15
					'type' => 'human',
31 15
					'category' => $value === 'iPad' ? 'tablet' : 'mobile',
32 15
					'architecture' => 'arm',
33 15
					'bits' => $value === 'iPod' ? 32 : 64,
34 15
					'kernel' => 'Linux',
35 15
					'platform' => 'iOS',
36 15
					'platformversion' => $version,
37 15
					'vendor' => 'Apple',
38 15
					'device' => $value,
39 15
					'model' => $model
40 15
				];
41 3
			},
42 3
			'xbox' => fn (string $value) : array => [
43 3
				'type' => 'human',
44 3
				'category' => 'console',
45 3
				'vendor' => 'Microsoft',
46 3
				'device' => 'Xbox',
47 3
				'model' => ($model = \mb_substr($value, 5)) === '' ? null : $model
48 3
			],
49 3
			'playstation' => function (string $value) : array {
50 1
				$parts = \explode(' ', $value);
51 1
				if (\str_contains($parts[1], '/')) {
52 1
					list($parts[1], $parts[2]) = \explode('/', $parts[1]);
53
				}
54 1
				$platform = [
55 1
					4 => 'Orbis OS',
56 1
					5 => 'FreeBSD'
57 1
				];
58 1
				return [
59 1
					'device' => $parts[0],
60 1
					'model' => $parts[1] ?? null,
61 1
					'kernel' => 'Linux',
62 1
					'platform' => $platform[\intval($parts[1])] ?? null,
63 1
					'platformversion' => $parts[2] ?? null,
64 1
					'type' => 'human',
65 1
					'category' => 'console',
66 1
					'vendor' => 'Sony',
67 1
					'processor' => 'AMD',
68 1
					'architecture' => 'x86',
69 1
					'bits' => 64
70 1
				];
71 3
			},
72 3
			'firetablet' => function (string $value) : ?array {
73 2
				$model = \explode(' ', $value)[0];
74 2
				if (\ctype_alpha($model) && \mb_strlen($model) <= 7) {
75 2
					return [
76 2
						'type' => 'human',
77 2
						'category' => 'tablet',
78 2
						'vendor' => 'Amazon',
79 2
						'device' => 'Fire Tablet',
80 2
						'model' => $model
81 2
					];
82
				}
83
				return null;
84 3
			}
85 3
		];
86 25
		return [
87 25
			'iPhone' => [
88 25
				'match' => 'exact',
89 25
				'categories' => $fn['ios']
90 25
			],
91 25
			'iPad' => [
92 25
				'match' => 'exact',
93 25
				'categories' => $fn['ios']
94 25
			],
95 25
			'iPod' => [
96 25
				'match' => 'exact',
97 25
				'categories' => $fn['ios']
98 25
			],
99 25
			'iPod touch' => [
100 25
				'match' => 'exact',
101 25
				'categories' => $fn['ios']
102 25
			],
103 25
			'Macintosh' => [
104 25
				'match' => 'exact',
105 25
				'categories' => [
106 25
					'vendor' => 'Apple',
107 25
					'device' => 'Macintosh'
108 25
				]
109 25
			],
110 25
			'Quest' => [
111 25
				'match' => 'start',
112 25
				'categories' => fn (string $value) : array => [
113 3
					'vendor' => 'Oculus',
114 3
					'device' => 'Quest',
115 3
					'model' => ($model = \mb_substr($value, 6)) === '' ? null : $model,
116 3
					'type' => 'human',
117 3
					'category' => 'vr'
118 3
				]
119 25
			],
120 25
			'Pacific' => [
121 25
				'match' => 'start',
122 25
				'categories' => [
123 25
					'vendor' => 'Oculus',
124 25
					'device' => 'Go',
125 25
					'type' => 'human',
126 25
					'category' => 'vr'
127 25
				]
128 25
			],
129 25
			'Nintendo' => [
130 25
				'match' => 'start',
131 25
				'categories' => fn (string $value) : array => [
132 3
					'type' => 'human',
133 3
					'category' => 'console',
134 3
					'vendor' => 'Nintendo',
135 3
					'device' => $value === 'Nintendo WiiU' ? 'Wii U' : \mb_substr($value, 9),
136 3
					'architecture' => \str_ends_with($value, 'U') ? 'PowerPC' : null
137 3
				]
138 25
			],
139 25
			'Xbox Series S' => [
140 25
				'match' => 'exact',
141 25
				'categories' => $fn['xbox']
142 25
			],
143 25
			'Xbox Series X' => [
144 25
				'match' => 'exact',
145 25
				'categories' => $fn['xbox']
146 25
			],
147 25
			'Xbox One' => [
148 25
				'match' => 'exact',
149 25
				'categories' => $fn['xbox']
150 25
			],
151 25
			'Xbox 360' => [
152 25
				'match' => 'exact',
153 25
				'categories' => $fn['xbox']
154 25
			],
155 25
			'Xbox' => [
156 25
				'match' => 'exact',
157 25
				'categories' => $fn['xbox']
158 25
			],
159 25
			'Playstation 4' => [
160 25
				'match' => 'start',
161 25
				'categories' => $fn['playstation']
162 25
			],
163 25
			'Playstation 5' => [
164 25
				'match' => 'start',
165 25
				'categories' => $fn['playstation']
166 25
			],
167 25
			'SHIELD Android TV' => [
168 25
				'match' => 'start',
169 25
				'categories' => [
170 25
					'type' => 'human',
171 25
					'category' => 'console',
172 25
					'vendor' => 'NVIDIA',
173 25
					'device' => 'Shield'
174 25
				]
175 25
			],
176 25
			'CrKey/' => [
177 25
				'match' => 'start',
178 25
				'categories' => fn (string $value) : array => [
179 2
					'type' => 'human',
180 2
					'category' => 'tv',
181 2
					'app' => 'Chromecast',
182 2
					'appversion' => \explode(',', \mb_substr($value, 6), 2)[0]
183 2
				]
184 25
			],
185 25
			'ChromeBook' => [
186 25
				'match' => 'any',
187 25
				'categories' => [
188 25
					'type' => 'human',
189 25
					'category' => 'desktop'
190 25
				]
191 25
			],
192 25
			'GoogleTV' => [
193 25
				'match' => 'exact',
194 25
				'categories' => [
195 25
					'type' => 'human',
196 25
					'category' => 'tv',
197 25
					'device' => 'GoogleTV'
198 25
				]
199 25
			],
200 25
			'CriKey/' => [
201 25
				'match' => 'start',
202 25
				'categories' => fn (string $value) : array => [
203
					'type' => 'human',
204
					'category' => 'tv',
205
					'device' => 'Chromecast',
206
					'vendor' => 'Google',
207
					'platformversion' => \mb_substr($value, 7)
208
				]
209 25
			],
210 25
			'Apple/' => [
211 25
				'match' => 'start',
212 25
				'categories' => function (string $value) : array {
213 1
					$value = \mb_substr($value, 6);
214 1
					$split = \strcspn($value, '0123456789');
215 1
					$device = \mb_substr($value, 0, $split);
216 1
					return [
217 1
						'type' => 'human',
218 1
						'category' => $device === 'iPad' ? 'tablet' : 'mobile',
219 1
						'vendor' => 'Apple',
220 1
						'device' => $device,
221 1
						'model' => \mb_substr($value, $split) ?: null
222 1
					];
223 25
				}
224 25
			],
225 25
			'iPhone/' => [
226 25
				'match' => 'start',
227 25
				'categories' => fn (string $value) : array => [
228
					'platform' => 'iOS',
229
					'platformversion' => \mb_substr($value, 7),
230
					'vendor' => 'Apple',
231
					'device' => 'iPhone'
232
				]
233 25
			],
234 25
			'hw/iPhone' => [
235 25
				'match' => 'start',
236 25
				'categories' => fn (string $value) : array => [
237
					'platform' => 'iOS',
238
					'vendor' => 'Apple',
239
					'device' => 'iPhone',
240
					'model' => \str_replace('_', '.', \mb_substr($value, 9))
241
				]
242 25
			],
243 25
			'KF' => [
244 25
				'match' => 'start',
245 25
				'categories' => $fn['firetablet']
246 25
			],
247 25
			'AFT' => [
248 25
				'match' => 'start',
249 25
				'categories' => fn (string $value) : array => [
250 1
					'type' => 'human',
251 1
					'category' => 'tv',
252 1
					'vendor' => 'Amazon',
253 1
					'device' => 'Fire TV',
254 1
					'model' => $value
255 1
				]
256 25
			],
257 25
			'Roku/' => [
258 25
				'match' => 'start',
259 25
				'categories' => fn (string $value, int $i, array $tokens) : array => [
260 1
					'type' => 'human',
261 1
					'category' => 'tv',
262 1
					'kernel' => 'Linux',
263 1
					'platform' => 'Roku OS',
264 1
					'platformversion' => \mb_substr($value, 5),
265 1
					'vendor' => 'Roku',
266 1
					'device' => 'Roku',
267 1
					'build' => $tokens[++$i] ?? null
268 1
				]
269 25
			],
270 25
			'AmigaOneX' => [
271 25
				'match' => 'start',
272 25
				'categories' => fn (string $value) : array => [
273 1
					'type' => 'human',
274 1
					'category' => 'desktop',
275 1
					'vendor' => 'A-Eon Technology',
276 1
					'device' => 'AmigaOne',
277 1
					'model' => \mb_substr($value, 8)
278 1
				]
279 25
			],
280 25
			'googleweblight' => [
281 25
				'match' => 'exact',
282 25
				'categories' => [
283 25
					'proxy' => 'googleweblight'
284 25
				]
285 25
			],
286 25
			'SAMSUNG-' => [
287 25
				'match' => 'start',
288 25
				'categories' => function (string $value) : array {
289 2
					$parts = \explode('/', $value, 2);
290 2
					return [
291 2
						'type' => 'human',
292 2
						'category' => 'mobile',
293 2
						'vendor' => 'Samsung',
294 2
						'model' => \mb_substr($parts[0], 8),
295 2
						'build' => $parts[1] ?? null,
296 2
					];
297 25
				}
298 25
			],
299 25
			'Samsung' => [
300 25
				'match' => 'start',
301 12
				'categories' => fn (string $value) : ?array => \str_starts_with($value, 'SamsungBrowser') ? null : [
302 12
					'vendor' => 'Samsung'
303 12
				]
304 25
			],
305 25
			'SM-' => [
306 25
				'match' => 'start',
307 25
				'categories' => function (string $value) : ?array {
308 8
					$parts = \explode('.', \explode(' ', $value)[0]);
309 8
					return [
310 8
						'vendor' => 'Samsung',
311 8
						'model' => $parts[0],
312 8
						'build' => $parts[1] ?? null
313 8
					];
314 25
				}
315 25
			],
316 25
			'Acer' => [
317 25
				'match' => 'start',
318 25
				'categories' => [
319 25
					'vendor' => 'Acer'
320 25
				]
321 25
			],
322 25
			'SonyEricsson' => [
323 25
				'match' => 'start',
324 25
				'categories' => function (string $value) : array {
325 1
					$parts = \explode('/', $value, 2);
326 1
					return [
327 1
						'type' => 'human',
328 1
						'category' => 'mobile',
329 1
						'vendor' => 'Sony Ericsson',
330 1
						'model' => \mb_substr($parts[0], 12),
331 1
						'build' => $parts[1] ?? null
332 1
					];
333 25
				}
334 25
			],
335 25
			'LGE' => [
336 25
				'match' => 'exact',
337 25
				'categories' => function (string $value, int $i, array $tokens) : array {
338 1
					$device = $tokens[++$i] ?? null;
339 1
					$platformversion = empty($tokens[++$i]) ? null : \mb_substr(\explode(' ', $tokens[$i])[0], 5);
340 1
					$build = $tokens[++$i] ?? null;
341 1
					return [
342 1
						'type' => 'human',
343 1
						'category' => 'tv',
344 1
						'model' => $device,
345 1
						'build' => $build,
346 1
						'platformversion' => $platformversion,
347 1
						'vendor' => 'LG'
348 1
					];
349 25
				}
350 25
			],
351 25
			'NOKIA' => [
352 25
				'match' => 'start',
353 25
				'categories' => function (string $value) : array {
354 3
					return \array_merge(devices::getDevice($value), [
355 3
						'type' => 'human',
356 3
						'category' => 'mobile',
357 3
						'vendor' => 'Nokia',
358 3
					]);
359 25
				}
360 25
			],
361 25
			'Lumia' => [
362 25
				'match' => 'start',
363 25
				'categories' => fn (string $value) : array => \array_merge(devices::getDevice($value), [
364 25
					'type' => 'human',
365 25
					'category' => 'mobile',
366 25
					'vendor' => 'Nokia'
367 25
				])
368 25
			],
369 25
			'BRAVIA' => [
370 25
				'match' => 'start',
371 25
				'categories' => [
372 25
					'type' => 'human',
373 25
					'category' => 'tv',
374 25
					'vendor' => 'Sony',
375 25
					'device' => 'Bravia'
376 25
				]
377 25
			],
378 25
			'TECNO' => [
379 25
				'match' => 'start',
380 25
				'categories' => fn (string $value) : array =>  [
381 2
					'type' => 'human',
382 2
					'category' => 'mobile',
383 2
					'vendor' => 'Tecno',
384 2
					'model' => \explode(' ', \str_replace('-', ' ', $value), 2)[1] ?? null
385 2
				]
386 25
			],
387 25
			'ThinkPad' => [
388 25
				'match' => 'start',
389 25
				'categories' => function (string $value, int $i, array $tokens) : array {
390 1
					if (\mb_strpos($tokens[++$i] ?? '', 'Build/') === 0) {
391 1
						$device = \explode('_', \mb_substr($tokens[$i], 6));
392
					}
393 1
					return [
394 1
						'type' => 'human',
395 1
						'vendor' => 'Lenovo',
396 1
						'device' => $device[0] ?? null,
397 1
						'model' => $device[1] ?? null,
398 1
						'build' => $device[2] ?? null
399 1
					];
400 25
				}
401 25
			],
402 25
			'BlackBerry' => [
403 25
				'match' => 'start',
404 25
				'categories' => function (string $value) : array {
405 1
					$parts = \explode('/', $value);
406 1
					return [
407 1
						'type' => 'human',
408 1
						'category' => 'mobile',
409 1
						'vendor' => 'Blackberry',
410 1
						'device' => \mb_substr($parts[0], 10) ?: null,
411 1
						'platform' => 'Blackberry OS',
412 1
						'platformversion' => $parts[1] ?? null
413 1
					];
414 25
				}
415 25
			],
416 25
			'Model/' => [
417 25
				'match' => 'start',
418 25
				'categories' => fn (string $value) : array => [
419
					'model' => \mb_substr($value, 6)
420
				]
421 25
			],
422 25
			'Build/' => [
423 25
				'match' => 'any',
424 25
				'categories' => fn (string $value) : array => self::getDevice($value)
425 25
			],
426 25
			'x' => [
427 25
				'match' => 'any',
428 25
				'categories' => function (string $value) : ?array {
429 78
					$parts = \explode('x', $value);
430 78
					if (!isset($parts[2]) && \is_numeric($parts[0]) && \is_numeric($parts[1])) {
431 7
						return [
432 7
							'width' => \intval($parts[0]),
433 7
							'height' => \intval($parts[1])
434 7
						];
435
					}
436 78
					return null;
437 25
				}
438 25
			],
439 25
			'MB' => [
440 25
				'match' => 'end',
441 25
				'categories' => fn (string $value) : array => [
442 1
					'ram' => \intval(\mb_substr($value, 0, -2))
443 1
				]
444 25
			]
445 25
		];
446
	}
447
448
	/**
449
	 * Extracts device information from a token
450
	 * 
451
	 * @param string $value A token expected to contain device information
452
	 * @return array<string,string|null> An array containing the extracted devices information
453
	 */
454 31
	public static function getDevice(string $value) : array {
455 31
		foreach (['Mobile', 'Tablet', 'Safari', 'AppleWebKit', 'Linux', 'rv:'] AS $item) {
456 31
			if (\mb_stripos($value, $item) === 0) {
457 5
				return [];
458
			}
459
		}
460 29
		$parts = \explode('Build/', \str_ireplace('build/', 'Build/', $value), 2);
0 ignored issues
show
Bug introduced by
It seems like str_ireplace('build/', 'Build/', $value) can also be of type array; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

460
		$parts = \explode('Build/', /** @scrutinizer ignore-type */ \str_ireplace('build/', 'Build/', $value), 2);
Loading history...
461 29
		$parts[0] = \trim($parts[0]);
462 29
		$build = $parts[1] ?? null;
463 29
		$vendors = [
464 29
			'Samsung' => 'Samsung',
465 29
			'OnePlus' => 'OnePlus',
466 29
			'Oppo' => 'Oppo',
467 29
			'CPH' =>'OnePlus',
468 29
			'KB' => 'OnePlus',
469 29
			'Pixel' => 'Google',
470 29
			'SM-' => 'Samsung',
471 29
			'LM-' => 'LG',
472 29
			'LG' => 'LG',
473 29
			'RealMe' => 'RealMe',
474 29
			'RMX' => 'RealMe',
475 29
			'HTC' => 'HTC',
476 29
			'Nexus' => 'Google',
477 29
			'Redmi' => 'Redmi', // must be above 'MI '
478 29
			'MI ' => 'Xiaomi',
479 29
			'HM ' => 'Xiaomi',
480 29
			'Xiaomi' => 'Xiaomi',
481 29
			'Huawei' => 'Huawei',
482 29
			'Honor' => 'Honor',
483 29
			'Motorola' => 'Motorola',
484 29
			'moto' => 'Motorola',
485 29
			'Intel' => 'Intel',
486 29
			'SonyEricsson' => 'Sony Ericsson',
487 29
			'Tecno' => 'Tecno',
488 29
			'Vivo' => 'Vivo',
489 29
			'Huawei' => 'Huawei',
490 29
			'Oppo' => 'Oppo',
491 29
			'Asus' => 'Asus',
492 29
			'Acer' => 'Acer',
493 29
			'Alcatel' => 'Alcatel',
494 29
			'Infinix' => 'Infinix',
495 29
			'Poco' => 'Poco',
496 29
			'Cubot' => 'Cubot',
497 29
			'Nokia' => 'Nokia'
498 29
		];
499
500
		// find vendor
501 29
		$vendor = null;
502 29
		foreach ($vendors AS $key => $item) {
503 29
			if (($pos = \mb_stripos($value, $key)) !== false) {
504 21
				$vendor = self::getVendor($item);
505
506
				// remove vendor name
507 21
				if ($pos === 0 && ($key === $item || $key === 'SonyEricsson')) {
508 13
					$parts[0] = \trim(\mb_substr($parts[0], \mb_strlen($key)), ' -_/');
509
				}
510 21
				break;
511
			}
512
		}
513 29
		$model = \explode(' ', $parts[0], 2);
514 29
		$device = $model[0] !== '' && \ctype_alpha($model[0]) ? \ucfirst($model[0]) : null; // device name if only letters
515 29
		$model = $device === null ? \implode(' ', $model) : ($model[1] ?? null); // reconstruct remainder of device name
516
517
		// remove everything after a slash
518 29
		if ($build === null && \str_contains($model ?? '', '/')) {
519 1
			$model = \mb_strstr($model, '/', true);
0 ignored issues
show
Bug introduced by
It seems like $model can also be of type null; however, parameter $haystack of mb_strstr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

519
			$model = \mb_strstr(/** @scrutinizer ignore-type */ $model, '/', true);
Loading history...
520
		}
521
522
		// special case for SMART TV
523 29
		if (\strcasecmp($device.$model, 'smarttv') === 0) {
524 1
			$device = 'Smart TV';
525 1
			$model = null;
526
		}
527
		// var_dump($value, $parts, $device, $model);
528 29
		return [
529 29
			'vendor' => $vendor,
530 29
			'device' => $device,
531 29
			'model' => $model ? \ucwords($model) : null,
532 29
			'build' => $build
533 29
		];
534
	}
535
536 21
	public static function getVendor(string $value) : string {
537 21
		$map = [
538 21
			'oneplus' => 'OnePlus',
539 21
			'lg' => 'LG',
540 21
			'lge' => 'LG',
541 21
			'realme' => 'RealMe',
542 21
			'htc' => 'HTC',
543 21
			'sonyericsson' => 'Sony Ericsson',
544 21
			'tcl' => 'TCL',
545 21
			'zte' => 'ZTE',
546 21
			'hmd' => 'HMD',
547 21
			'lt' => 'LT'
548 21
		];
549 21
		$value = \mb_strtolower($value);
550 21
		return $map[$value] ?? \mb_convert_case($value, MB_CASE_TITLE);
551
	}
552
}