Passed
Push — main ( bcdb57...ad9cc7 )
by Will
03:05
created

devices::get()   F

Complexity

Conditions 27
Paths 1

Size

Total Lines 338
Code Lines 277

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 326
CRAP Score 27

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 27
eloc 277
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 338
ccs 326
cts 327
cp 0.9969
crap 27
rs 3.3333

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
class devices {
6
7
	/**
8
	 * Generates a configuration array for matching devices
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 85
	public static function get() : array {
13 3
		$fn = [
14 3
			'ios' => function (string $value, int $i, array $tokens) : array {
15 16
				$version = null;
16 16
				$model = null;
17
18
				// device name with slash like iPhone/16.5
19 16
				$parts = \explode('/', $value, 3);
20 16
				if (isset($parts[1])) {
21 1
					$value = $parts[0];
22 1
					$version = $parts[1];
23
				}
24
25
				// look at other tokens to gather info
26 16
				foreach ($tokens AS $item) {
27 16
					if (\str_starts_with($item, 'Mobile/')) {
28 16
						$model = \mb_substr($item, 7);
29 16
					} elseif (\str_starts_with($item, 'CPU iPhone OS ')) {
30 15
						$version = \str_replace('_', '.', \mb_substr($item, 14, \mb_strpos($item, ' ', 14) - 14));
31 16
					} elseif (\str_starts_with($item, 'CPU OS ')) {
32 2
						$version = \str_replace('_', '.', \mb_substr($item, 7, \mb_strpos($item, ' ', 7) - 7));
33
					}
34
				}
35 16
				return [
36 16
					'type' => 'human',
37 16
					'category' => $value === 'iPad' ? 'tablet' : 'mobile',
38 16
					'architecture' => 'arm',
39 16
					'bits' => $value === 'iPod' ? 32 : 64,
40 16
					'kernel' => 'Linux',
41 16
					'platform' => 'iOS',
42 16
					'platformversion' => $version,
43 16
					'vendor' => 'Apple',
44 16
					'device' => \mb_stripos($value, 'iPhone') === 0 ? 'iPhone' : $value,
45 16
					'model' => $model
46 16
				];
47 3
			},
48 3
			'xbox' => fn (string $value) : array => [
49 3
				'type' => 'human',
50 3
				'category' => 'console',
51 3
				'vendor' => 'Microsoft',
52 3
				'device' => 'Xbox',
53 3
				'model' => ($model = \mb_substr($value, 5)) === '' ? null : $model
54 3
			],
55 3
			'playstation' => function (string $value) : array {
56 1
				$parts = \explode(' ', $value);
57 1
				if (\str_contains($parts[1], '/')) {
58 1
					list($parts[1], $parts[2]) = \explode('/', $parts[1]);
59
				}
60 1
				$platform = [
61 1
					'4' => 'Orbis OS',
62 1
					'5' => 'FreeBSD'
63 1
				];
64 1
				return [
65 1
					'device' => 'PlayStation',
66 1
					'model' => $parts[1] ?? null,
67 1
					'kernel' => 'Linux',
68 1
					'platform' => $platform[$parts[1]] ?? null,
69 1
					'platformversion' => $parts[2] ?? null,
70 1
					'type' => 'human',
71 1
					'category' => 'console',
72 1
					'vendor' => 'Sony',
73 1
					'processor' => 'AMD',
74 1
					'architecture' => 'x86',
75 1
					'bits' => 64
76 1
				];
77 3
			},
78 3
			'firetablet' => function (string $value) : ?array {
79 3
				$model = \explode(' ', $value)[0];
80 3
				if (\ctype_alpha($model) && \mb_strlen($model) <= 7) {
81 3
					return [
82 3
						'type' => 'human',
83 3
						'category' => 'tablet',
84 3
						'vendor' => 'Amazon',
85 3
						'device' => 'Fire Tablet',
86 3
						'model' => $model
87 3
					];
88
				}
89
				return null;
90 3
			}
91 3
		];
92 31
		return [
93 31
			'iPhone' => new props('start', $fn['ios']),
94 31
			'iPad' => new props('exact', $fn['ios']),
95 31
			'iPod' => new props('exact', $fn['ios']),
96 31
			'iPod touch' => new props('exact', $fn['ios']),
97 31
			'Macintosh' => new props('exact', [
98 31
				'vendor' => 'Apple',
99 31
				'device' => 'Macintosh'
100 31
			]),
101 31
			'Quest' => new props('start', fn (string $value) : array => [
102 4
				'vendor' => 'Oculus',
103 4
				'device' => 'Quest',
104 4
				'model' => ($model = \mb_substr($value, 6)) === '' ? null : $model,
105 4
				'type' => 'human',
106 4
				'category' => 'vr'
107 4
			]),
108 31
			'Pacific' => new props('start', [
109 31
				'vendor' => 'Oculus',
110 31
				'device' => 'Go',
111 31
				'type' => 'human',
112 31
				'category' => 'vr'
113 31
			]),
114 31
			'Nintendo' => new props('start', fn (string $value) : array => [
115 4
				'type' => 'human',
116 4
				'category' => 'console',
117 4
				'vendor' => 'Nintendo',
118 4
				'device' => $value === 'Nintendo WiiU' ? 'Wii U' : \mb_substr($value, 9),
119 4
				'architecture' => \str_ends_with($value, 'U') ? 'PowerPC' : null
120 4
			]),
121 31
			'Xbox Series S' => new props('exact', $fn['xbox']),
122 31
			'Xbox Series X' => new props('exact', $fn['xbox']),
123 31
			'Xbox One' => new props('exact', $fn['xbox']),
124 31
			'Xbox 360' => new props('exact', $fn['xbox']),
125 31
			'Xbox' => new props('exact', $fn['xbox']),
126 31
			'Playstation 4' => new props('start', $fn['playstation']),
127 31
			'Playstation 5' => new props('start', $fn['playstation']),
128 31
			'Playstation Vita' => new props('start', fn (string $value) : array => [
129 2
				'type' => 'human',
130 2
				'category' => 'console',
131 2
				'vendor' => 'Sony',
132 2
				'device' => 'PlayStation',
133 2
				'model' => 'Vita',
134 2
				'architecture' => 'arm',
135 2
				'processor' => 'MediaTek',
136 2
				'cpu' => 'Cortex-A9 MPCore',
137 2
				'bits' => 32,
138 2
				'width' => 960,
139 2
				'height' => 544,
140 2
				'dpi' => 220,
141 2
				'ram' => 512,
142 2
				'kernel' => 'Linux',
143 2
				'platform' => 'PlayStation Vita System Software',
144 2
				'platformversion' => \mb_substr($value, 17)
145 2
			]),
146 31
			'Playstation Portable' => new props('start', fn (string $value, int $i, array $tokens) : array => [
147 2
				'type' => 'human',
148 2
				'category' => 'console',
149 2
				'vendor' => 'Sony',
150 2
				'device' => 'PlayStation',
151 2
				'model' => 'PSP',
152 2
				'architecture' => 'arm',
153 2
				'cpu' => 'MIPS R4000',
154 2
				'cpuclock' => 333,
155 2
				'bits' => 64,
156 2
				'width' => 480,
157 2
				'height' => 272,
158 2
				'ram' => 64,
159 2
				'kernel' => 'Linux',
160 2
				'platform' => 'PlayStation Portable System Software',
161 2
				'platformversion' => $tokens[++$i],
162 2
				'browser' => 'NetFront',
163 2
				'engine' => 'WebKit'
164 2
			]),
165 31
			'SHIELD Android TV' => new props('start', [
166 31
				'type' => 'human',
167 31
				'category' => 'console',
168 31
				'vendor' => 'NVIDIA',
169 31
				'device' => 'Shield'
170 31
			]),
171 31
			'CrKey/' => new props('start', fn (string $value) : array => [
172 3
				'type' => 'human',
173 3
				'category' => 'tv',
174 3
				'app' => 'Chromecast',
175 3
				'appname' => 'CrKey',
176 3
				'appversion' => \explode(',', \mb_substr($value, 6), 2)[0]
177 3
			]),
178 31
			'ChromeBook' => new props('any', [
179 31
				'type' => 'human',
180 31
				'category' => 'desktop'
181 31
			]),
182 31
			'GoogleTV' => new props('exact', [
183 31
				'type' => 'human',
184 31
				'category' => 'tv',
185 31
				'device' => 'GoogleTV'
186 31
			]),
187 31
			'CriKey/' => new props('start', fn (string $value) : array => [
188 1
				'type' => 'human',
189 1
				'category' => 'tv',
190 1
				'device' => 'Chromecast',
191 1
				'vendor' => 'Google',
192 1
				'platformversion' => \mb_substr($value, 7)
193 1
			]),
194 31
			'Apple/' => new props('start', function (string $value) : array {
195 1
				$value = \mb_substr($value, 6);
196 1
				$split = \strcspn($value, '0123456789');
197 1
				$device = \mb_substr($value, 0, $split);
198 1
				return [
199 1
					'type' => 'human',
200 1
					'category' => $device === 'iPad' ? 'tablet' : 'mobile',
201 1
					'vendor' => 'Apple',
202 1
					'device' => $device,
203 1
					'model' => \mb_substr($value, $split) ?: null
204 1
				];
205 31
			}),
206 31
			'hw/iPhone' => new props('start', fn (string $value) : array => [
207 1
				'platform' => 'iOS',
208 1
				'vendor' => 'Apple',
209 1
				'device' => 'iPhone',
210 1
				'model' => \str_replace('_', '.', \mb_substr($value, 9))
211 1
			]),
212 31
			'KF' => new props('start', $fn['firetablet']),
213 31
			'AFT' => new props('start', fn (string $value) : array => [
214 2
				'type' => 'human',
215 2
				'category' => 'tv',
216 2
				'vendor' => 'Amazon',
217 2
				'device' => 'Fire TV',
218 2
				'model' => $value
219 2
			]),
220 31
			'Roku/' => new props('start', fn (string $value, int $i, array $tokens) : array => [
221 2
				'type' => 'human',
222 2
				'category' => 'tv',
223 2
				'kernel' => 'Linux',
224 2
				'platform' => 'Roku OS',
225 2
				'platformversion' => \mb_substr($value, 5),
226 2
				'vendor' => 'Roku',
227 2
				'device' => 'Roku',
228 2
				'build' => $tokens[++$i] ?? null
229 2
			]),
230 31
			'AmigaOneX' => new props('start', fn (string $value) : array => [
231 2
				'type' => 'human',
232 2
				'category' => 'desktop',
233 2
				'vendor' => 'A-Eon Technology',
234 2
				'device' => 'AmigaOne',
235 2
				'model' => \mb_substr($value, 8)
236 2
			]),
237 31
			'googleweblight' => new props('exact', [
238 31
				'proxy' => 'googleweblight'
239 31
			]),
240 31
			'SAMSUNG-' => new props('start', function (string $value) : array {
241 2
				$parts = \explode('/', $value, 2);
242 2
				return [
243 2
					'type' => 'human',
244 2
					'category' => 'mobile',
245 2
					'vendor' => 'Samsung',
246 2
					'model' => \mb_substr($parts[0], 8),
247 2
					'build' => $parts[1] ?? null,
248 2
				];
249 31
			}),
250 12
			'Samsung' => new props('start', fn (string $value) : ?array => \str_starts_with($value, 'SamsungBrowser') ? null : [
251 12
				'vendor' => 'Samsung'
252 12
			]),
253 31
			'SM-' => new props('start', function (string $value) : array {
254 9
				$parts = \explode('.', \explode(' ', $value)[0]);
255 9
				return [
256 9
					'vendor' => 'Samsung',
257 9
					'model' => $parts[0],
258 9
					'build' => $parts[1] ?? null
259 9
				];
260 31
			}),
261 31
			'Acer' => new props('start', [
262 31
				'vendor' => 'Acer'
263 31
			]),
264 31
			'SonyEricsson' => new props('start', function (string $value) : array {
265 1
				$parts = \explode('/', $value, 2);
266 1
				return [
267 1
					'type' => 'human',
268 1
					'category' => 'mobile',
269 1
					'vendor' => 'Sony Ericsson',
270 1
					'model' => \mb_substr($parts[0], 12),
271 1
					'build' => $parts[1] ?? null
272 1
				];
273 31
			}),
274 31
			'LGE' => new props('exact', function (string $value, int $i, array $tokens) : array {
275 1
				$device = $tokens[++$i] ?? null;
276 1
				$platformversion = empty($tokens[++$i]) ? null : \mb_substr(\explode(' ', $tokens[$i])[0], 5);
277 1
				$build = $tokens[++$i] ?? null;
278 1
				return [
279 1
					'type' => 'human',
280 1
					'category' => 'tv',
281 1
					'model' => $device,
282 1
					'build' => $build,
283 1
					'platformversion' => $platformversion,
284 1
					'vendor' => 'LG'
285 1
				];
286 31
			}),
287 31
			'NOKIA' => new props('start', function (string $value) : array {
288 3
				return \array_merge(devices::getDevice($value), [
289 3
					'type' => 'human',
290 3
					'category' => 'mobile',
291 3
					'vendor' => 'Nokia',
292 3
				]);
293 31
			}),
294 31
			'Lumia' => new props('start', fn (string $value) : array => \array_merge(devices::getDevice($value), [
295 31
				'type' => 'human',
296 31
				'category' => 'mobile',
297 31
				'vendor' => 'Nokia'
298 31
			])),
299 31
			'BRAVIA' => new props('start', [
300 31
				'type' => 'human',
301 31
				'category' => 'tv',
302 31
				'vendor' => 'Sony',
303 31
				'device' => 'Bravia'
304 31
			]),
305 31
			'TECNO' => new props('start', fn (string $value) : array =>  [
306 3
				'type' => 'human',
307 3
				'category' => 'mobile',
308 3
				'vendor' => 'Tecno',
309 3
				'model' => \explode(' ', \str_replace('-', ' ', $value), 2)[1] ?? null
310 3
			]),
311 31
			'ThinkPad' => new props('start', function (string $value, int $i, array $tokens) : array {
312 1
				if (\mb_strpos($tokens[++$i] ?? '', 'Build/') === 0) {
313 1
					$device = \explode('_', \mb_substr($tokens[$i], 6));
314
				}
315 1
				return [
316 1
					'type' => 'human',
317 1
					'vendor' => 'Lenovo',
318 1
					'device' => $device[0] ?? null,
319 1
					'model' => $device[1] ?? null,
320 1
					'build' => $device[2] ?? null
321 1
				];
322 31
			}),
323 31
			'BlackBerry' => new props('start', function (string $value) : array {
324 1
				$parts = \explode('/', $value);
325 1
				return [
326 1
					'type' => 'human',
327 1
					'category' => 'mobile',
328 1
					'vendor' => 'Blackberry',
329 1
					'device' => \mb_substr($parts[0], 10) ?: null,
330 1
					'platform' => 'Blackberry OS',
331 1
					'platformversion' => $parts[1] ?? null
332 1
				];
333 31
			}),
334 31
			'Model/' => new props('start', fn (string $value) : array => [
335 1
				'model' => \mb_substr($value, 6)
336 1
			]),
337 31
			'Build/' => new props('any', fn (string $value) : array => self::getDevice($value)),
338 31
			'x' => new props('any', function (string $value) : ?array {
339 85
				$parts = \explode('x', $value);
340 85
				if (!isset($parts[2]) && \is_numeric($parts[0]) && \is_numeric($parts[1]) && !empty($parts[0]) && !empty($parts[1])) {
341 9
					return [
342 9
						'width' => \intval($parts[0]),
343 9
						'height' => \intval($parts[1])
344 9
					];
345
				}
346 85
				return null;
347 31
			}),
348 31
			'MB' => new props('end', fn (string $value) : array => [
349 1
				'ram' => \intval(\mb_substr($value, 0, -2))
350 1
			])
351 31
		];
352
	}
353
354
	/**
355
	 * Extracts device information from a token
356
	 * 
357
	 * @param string $value A token expected to contain device information
358
	 * @return array<string,string|null> An array containing the extracted devices information
359
	 */
360 38
	public static function getDevice(string $value) : array {
361 38
		foreach (['Mobile', 'Tablet', 'Safari', 'AppleWebKit', 'Linux', 'rv:'] AS $item) {
362 38
			if (\mb_stripos($value, $item) === 0) {
363 6
				return [];
364
			}
365
		}
366 35
		$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

366
		$parts = \explode('Build/', /** @scrutinizer ignore-type */ \str_ireplace('build/', 'Build/', $value), 2);
Loading history...
367 35
		$parts[0] = \trim($parts[0]);
368 35
		$build = $parts[1] ?? null;
369 35
		$vendors = [
370 35
			'Samsung' => 'Samsung',
371 35
			'OnePlus' => 'OnePlus',
372 35
			'CPH' =>'OnePlus',
373 35
			'KB' => 'OnePlus',
374 35
			'Pixel' => 'Google',
375 35
			'SM-' => 'Samsung',
376 35
			'LM-' => 'LG',
377 35
			'LG' => 'LG',
378 35
			'RealMe' => 'RealMe',
379 35
			'RMX' => 'RealMe',
380 35
			'HTC' => 'HTC',
381 35
			'Nexus' => 'Google',
382 35
			'Redmi' => 'Redmi', // must be above 'MI '
383 35
			'MI ' => 'Xiaomi',
384 35
			'HM ' => 'Xiaomi',
385 35
			'Xiaomi' => 'Xiaomi',
386 35
			'Huawei' => 'Huawei',
387 35
			'Honor' => 'Honor',
388 35
			'Motorola' => 'Motorola',
389 35
			'moto' => 'Motorola',
390 35
			'Intel' => 'Intel',
391 35
			'SonyEricsson' => 'Sony Ericsson',
392 35
			'Tecno' => 'Tecno',
393 35
			'Vivo' => 'Vivo',
394 35
			'Oppo' => 'Oppo',
395 35
			'Asus' => 'Asus',
396 35
			'Acer' => 'Acer',
397 35
			'Alcatel' => 'Alcatel',
398 35
			'Infinix' => 'Infinix',
399 35
			'Poco' => 'Poco',
400 35
			'Cubot' => 'Cubot',
401 35
			'Nokia' => 'Nokia'
402 35
		];
403
404
		// find vendor
405 35
		$vendor = null;
406 35
		foreach ($vendors AS $key => $item) {
407 35
			if (($pos = \mb_stripos($value, $key)) !== false) {
408 26
				$vendor = self::getVendor($item);
409
410
				// remove vendor name
411 26
				if ($pos === 0 && ($key === $item || $key === 'SonyEricsson')) {
412 15
					$parts[0] = \trim(\mb_substr($parts[0], \mb_strlen($key)), ' -_/');
413
				}
414 26
				break;
415
			}
416
		}
417 35
		$model = \explode(' ', $parts[0], 2);
418 35
		$device = $model[0] !== '' && \ctype_alpha($model[0]) ? \ucfirst($model[0]) : null; // device name if only letters
419 35
		$model = $device === null ? \implode(' ', $model) : ($model[1] ?? null); // reconstruct remainder of device name
420
421
		// remove everything after a slash
422 35
		if ($build === null && \str_contains($model ?? '', '/')) {
423 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

423
			$model = \mb_strstr(/** @scrutinizer ignore-type */ $model, '/', true);
Loading history...
424
		}
425
426
		// special case for SMART TV
427 35
		if (\strcasecmp($device.$model, 'smarttv') === 0) {
428 1
			$device = 'Smart TV';
429 1
			$model = null;
430
		}
431
		// var_dump($value, $parts, $device, $model);
432 35
		return [
433 35
			'vendor' => $vendor,
434 35
			'device' => $device,
435 35
			'model' => $model ? \ucwords($model) : null,
436 35
			'build' => $build
437 35
		];
438
	}
439
440 27
	public static function getVendor(string $value) : string {
441 27
		$map = [
442 27
			'oneplus' => 'OnePlus',
443 27
			'lg' => 'LG',
444 27
			'lge' => 'LG',
445 27
			'realme' => 'RealMe',
446 27
			'htc' => 'HTC',
447 27
			'sonyericsson' => 'Sony Ericsson',
448 27
			'tcl' => 'TCL',
449 27
			'zte' => 'ZTE',
450 27
			'hmd' => 'HMD',
451 27
			'lt' => 'LT'
452 27
		];
453 27
		$value = \mb_strtolower($value);
454 27
		return $map[$value] ?? \mb_convert_case($value, MB_CASE_TITLE);
455
	}
456
}