devices   B
last analyzed

Complexity

Total Complexity 50

Size/Duplication

Total Lines 519
Duplicated Lines 0 %

Test Coverage

Coverage 99.79%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 405
c 2
b 0
f 0
dl 0
loc 519
ccs 473
cts 474
cp 0.9979
rs 8.4
wmc 50

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getVendor() 0 15 1
F get() 0 401 34
F getDevice() 0 83 15

How to fix   Complexity   

Complex Class

Complex classes like devices 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 devices, and based on these observations, apply Extract Interface, too.

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 91
	public static function get() : array {
13 3
		$fn = [
14 3
			'ios' => function (string $value, int $i, array $tokens) : array {
15 19
				$version = null;
16 19
				$model = null;
17
18
				// device name with slash like iPhone/16.5
19 19
				$parts = \explode('/', $value, 3);
20 19
				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 19
				foreach ($tokens AS $item) {
27 19
					if (\str_starts_with($item, 'Mobile/')) {
28 19
						$model = \mb_substr($item, 7);
29 19
					} elseif (\str_starts_with($item, 'CPU iPhone OS ')) {
30 18
						$version = \str_replace('_', '.', \mb_substr($item, 14, \mb_strpos($item, ' ', 14) - 14));
31 19
					} elseif (\str_starts_with($item, 'CPU OS ')) {
32 2
						$version = \str_replace('_', '.', \mb_substr($item, 7, \mb_strpos($item, ' ', 7) - 7));
33
					}
34
				}
35 19
				return [
36 19
					'type' => 'human',
37 19
					'category' => $value === 'iPad' ? 'tablet' : 'mobile',
38 19
					'architecture' => 'Arm',
39 19
					'bits' => $value === 'iPod' ? 32 : 64,
40 19
					'kernel' => 'Linux',
41 19
					'platform' => 'iOS',
42 19
					'platformversion' => $version,
43 19
					'vendor' => 'Apple',
44 19
					'device' => \mb_stripos($value, 'iPhone') === 0 ? 'iPhone' : $value,
45 19
					'model' => $model
46 19
				];
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 34
		return [
93 34
			'iPhone' => new props('start', $fn['ios']),
94 34
			'iPad' => new props('exact', $fn['ios']),
95 34
			'iPod' => new props('exact', $fn['ios']),
96 34
			'iPod touch' => new props('exact', $fn['ios']),
97 34
			'Macintosh' => new props('exact', [
98 34
				'vendor' => 'Apple',
99 34
				'device' => 'Macintosh'
100 34
			]),
101 34
			'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 34
			'Pacific' => new props('start', [
109 34
				'vendor' => 'Oculus',
110 34
				'device' => 'Go',
111 34
				'type' => 'human',
112 34
				'category' => 'vr'
113 34
			]),
114 34
			'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 34
			'Xbox Series S' => new props('exact', $fn['xbox']),
122 34
			'Xbox Series X' => new props('exact', $fn['xbox']),
123 34
			'Xbox One' => new props('exact', $fn['xbox']),
124 34
			'Xbox 360' => new props('exact', $fn['xbox']),
125 34
			'Xbox' => new props('exact', $fn['xbox']),
126 34
			'Playstation 3' => new props('start', $fn['playstation']),
127 34
			'Playstation 4' => new props('start', $fn['playstation']),
128 34
			'Playstation 5' => new props('start', $fn['playstation']),
129 34
			'Playstation Vita' => new props('start', fn (string $value) : array => [
130 2
				'type' => 'human',
131 2
				'category' => 'console',
132 2
				'vendor' => 'Sony',
133 2
				'device' => 'PlayStation',
134 2
				'model' => 'Vita',
135 2
				'architecture' => 'Arm',
136 2
				'processor' => 'MediaTek',
137 2
				'cpu' => 'Cortex-A9 MPCore',
138 2
				'bits' => 32,
139 2
				'width' => 960,
140 2
				'height' => 544,
141 2
				'dpi' => 220,
142 2
				'ram' => 512,
143 2
				'kernel' => 'Linux',
144 2
				'platform' => 'PlayStation Vita System Software',
145 2
				'platformversion' => \mb_substr($value, 17)
146 2
			]),
147 34
			'Playstation Portable' => new props('start', fn (string $value, int $i, array $tokens) : array => [
148 2
				'type' => 'human',
149 2
				'category' => 'console',
150 2
				'vendor' => 'Sony',
151 2
				'device' => 'PlayStation',
152 2
				'model' => 'PSP',
153 2
				'architecture' => 'Arm',
154 2
				'cpu' => 'MIPS R4000',
155 2
				'cpuclock' => 333,
156 2
				'bits' => 64,
157 2
				'width' => 480,
158 2
				'height' => 272,
159 2
				'ram' => 64,
160 2
				'kernel' => 'Linux',
161 2
				'platform' => 'PlayStation Portable System Software',
162 2
				'platformversion' => $tokens[++$i],
163 2
				'browser' => 'NetFront',
164 2
				'engine' => 'WebKit'
165 2
			]),
166 34
			'SHIELD Android TV' => new props('start', [
167 34
				'type' => 'human',
168 34
				'category' => 'console',
169 34
				'vendor' => 'NVIDIA',
170 34
				'device' => 'Shield'
171 34
			]),
172 34
			'CrKey/' => new props('start', fn (string $value) : array => [
173 3
				'type' => 'human',
174 3
				'category' => 'tv',
175 3
				'app' => 'Chromecast',
176 3
				'appname' => 'CrKey',
177 3
				'appversion' => \explode(',', \mb_substr($value, 6), 2)[0]
178 3
			]),
179 34
			'ChromeBook' => new props('any', [
180 34
				'type' => 'human',
181 34
				'category' => 'desktop'
182 34
			]),
183 34
			'GoogleTV' => new props('exact', [
184 34
				'type' => 'human',
185 34
				'category' => 'tv',
186 34
				'device' => 'GoogleTV'
187 34
			]),
188 34
			'CriKey/' => new props('start', fn (string $value) : array => [
189 1
				'type' => 'human',
190 1
				'category' => 'tv',
191 1
				'device' => 'Chromecast',
192 1
				'vendor' => 'Google',
193 1
				'platformversion' => \mb_substr($value, 7)
194 1
			]),
195 34
			'Apple/' => new props('start', function (string $value) : array {
196 1
				$value = \mb_substr($value, 6);
197 1
				$split = \strcspn($value, '0123456789');
198 1
				$device = \mb_substr($value, 0, $split);
199 1
				return [
200 1
					'type' => 'human',
201 1
					'category' => $device === 'iPad' ? 'tablet' : 'mobile',
202 1
					'vendor' => 'Apple',
203 1
					'device' => $device,
204 1
					'model' => \mb_substr($value, $split) ?: null
205 1
				];
206 34
			}),
207 34
			'hw/iPhone' => new props('start', fn (string $value) : array => [
208 1
				'platform' => 'iOS',
209 1
				'vendor' => 'Apple',
210 1
				'device' => 'iPhone',
211 1
				'model' => \str_replace('_', '.', \mb_substr($value, 9))
212 1
			]),
213 34
			'KF' => new props('start', $fn['firetablet']),
214 34
			'AFT' => new props('start', fn (string $value) : array => [
215 2
				'type' => 'human',
216 2
				'category' => 'tv',
217 2
				'vendor' => 'Amazon',
218 2
				'device' => 'Fire TV',
219 2
				'model' => $value
220 2
			]),
221 34
			'Roku/' => new props('start', fn (string $value, int $i, array $tokens) : array => [
222 2
				'type' => 'human',
223 2
				'category' => 'tv',
224 2
				'kernel' => 'Linux',
225 2
				'platform' => 'Roku OS',
226 2
				'platformversion' => $tokens[++$i] ?? null,
227 2
				'vendor' => 'Roku',
228 2
				'device' => \mb_substr($value, 5)
229 2
			]),
230 34
			'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 34
			'googleweblight' => new props('exact', [
238 34
				'proxy' => 'googleweblight'
239 34
			]),
240 34
			'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 34
			}),
250 12
			'Samsung' => new props('start', fn (string $value) : ?array => \str_starts_with($value, 'SamsungBrowser') ? null : [
251 12
				'vendor' => 'Samsung'
252 12
			]),
253 34
			'SM-' => new props('start', function (string $value) : array {
254 10
				$parts = \explode('.', \explode(' ', $value)[0]);
255 10
				return [
256 10
					'vendor' => 'Samsung',
257 10
					'model' => $parts[0],
258 10
					'build' => $parts[1] ?? null
259 10
				];
260 34
			}),
261 34
			'Acer' => new props('start', [
262 34
				'vendor' => 'Acer'
263 34
			]),
264 34
			'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 34
			}),
274 34
			'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 34
			}),
287 34
			'LG-' => new props('start', function (string $value) : array {
288 1
				$parts = \explode('/', \mb_substr($value, 3), 3);
289 1
				return [
290 1
					'type' => 'human',
291 1
					'category' => 'mobile',
292 1
					'vendor' => 'LG',
293 1
					'model' => \explode(' ', $parts[0])[0],
294 1
					'build' => $parts[1] ?? null
295 1
				];
296 34
			}),
297 34
			'NOKIA' => new props('start', fn (string $value) : array => \array_merge(devices::getDevice($value), [
298 34
				'type' => 'human',
299 34
				'category' => 'mobile',
300 34
				'vendor' => 'Nokia',
301 34
			])),
302 34
			'Lumia' => new props('start', fn (string $value) : array => \array_merge(devices::getDevice($value), [
303 34
				'type' => 'human',
304 34
				'category' => 'mobile',
305 34
				'vendor' => 'Nokia'
306 34
			])),
307 34
			'BRAVIA' => new props('start', [
308 34
				'type' => 'human',
309 34
				'category' => 'tv',
310 34
				'vendor' => 'Sony',
311 34
				'device' => 'Bravia'
312 34
			]),
313 34
			'TECNO' => new props('start', fn (string $value) : array => [
314 3
				'type' => 'human',
315 3
				'category' => 'mobile',
316 3
				'vendor' => 'Tecno',
317 3
				'model' => \explode(' ', \str_replace('-', ' ', $value), 2)[1] ?? null
318 3
			]),
319 34
			'Xiaomi' => new props('start', function (string $value, int $i, array $tokens) : array {
320 5
				return [
321 5
					'type' => 'human',
322 5
					'vendor' => 'Xiaomi',
323 5
					'device' => \mb_stripos($value, 'Poco') !== false ? 'Poco' : null,
324 5
					'model' => \mb_stripos($value, 'Xiaomi-Mi') !== 0 ? (\explode(' ', $value)[1] ?? null) : null,
325 5
					'platformversion' => \is_numeric($tokens[$i+1] ?? null) ? $tokens[$i+1] : null
326 5
				];
327 34
			}),
328 34
			'ThinkPad' => new props('start', function (string $value, int $i, array $tokens) : array {
329 1
				if (\mb_strpos($tokens[++$i] ?? '', 'Build/') === 0) {
330 1
					$device = \explode('_', \mb_substr($tokens[$i], 6));
331
				}
332 1
				return [
333 1
					'type' => 'human',
334 1
					'vendor' => 'Lenovo',
335 1
					'device' => $device[0] ?? null,
336 1
					'model' => $device[1] ?? null,
337 1
					'build' => $device[2] ?? null
338 1
				];
339 34
			}),
340 34
			'BlackBerry' => new props('start', function (string $value) : array {
341 1
				$parts = \explode('/', $value);
342 1
				return [
343 1
					'type' => 'human',
344 1
					'category' => 'mobile',
345 1
					'vendor' => 'Blackberry',
346 1
					'device' => \mb_substr($parts[0], 10) ?: null,
347 1
					'platform' => 'Blackberry OS',
348 1
					'platformversion' => $parts[1] ?? null
349 1
				];
350 34
			}),
351 34
			'CUBOT' => new props('exact', fn () : array => [
352 2
				'type' => 'human',
353 2
				'vendor' => 'Cubot'
354 2
			]),
355 34
			'TCL ' => new props('start', fn (string $value) : array => [
356 2
				'type' => 'human',
357 2
				'category' => 'tv',
358 2
				'vendor' => 'TCL',
359 2
				'model' => \mb_substr($value, 4)
360 2
			]),
361 34
			'deviceName/' => new props('start', fn (string $value) : array => self::getDevice(\mb_substr($value, 11))),
362 34
			'deviceModel/' => new props('start', fn (string $value) : array => [
363 2
				'model' => \mb_substr($value, 12)
364 2
			]),
365 34
			'Model/' => new props('start', fn (string $value) : array => [
366 1
				'model' => \mb_substr($value, 6)
367 1
			]),
368 34
			'Build/' => new props('any', fn (string $value) : array => self::getDevice($value)),
369 34
			'width=' => new props('start', fn (string $value) : array => [
370 2
				'width' => \intval(\mb_substr($value, 6))
371 2
			]),
372 34
			'height=' => new props('start', fn (string $value) : array => [
373 2
				'height' => \intval(\mb_substr($value, 7))
374 2
			]),
375 34
			'dpi=' => new props('start', fn (string $value) : array => [
376 1
				'dpi' => \mb_substr($value, 4)
377 1
			]),
378 34
			'NetType/' => new props('start', function (string $value) : array {
379 4
				$type = \mb_convert_case(\mb_substr($value, 8), MB_CASE_UPPER);
380 4
				return [
381 4
					'nettype' => \in_array($type, ['WF', 'WIFI'], true) ? 'WiFi' : $type
382 4
				];
383 34
			}),
384 34
			'netWorkType/' => new props('start', function (string $value) : array {
385 1
				$type = \mb_convert_case(\mb_substr($value, 12), MB_CASE_UPPER);
386 1
				return [
387 1
					'nettype' => \in_array($type, ['WF', 'WIFI'], true) ? 'WiFi' : $type
388 1
				];
389 34
			}),
390 34
			'2G' => new props('exact', ['nettype' => '2g']),
391 34
			'3G' => new props('exact', ['nettype' => '3g']),
392 34
			'4G' => new props('exact', ['nettype' => '4g']),
393 34
			'4.5G' => new props('exact', ['nettype' => '4g']),
394 34
			'4.5G+' => new props('exact', ['nettype' => '4g']),
395 34
			'5G' => new props('exact', ['nettype' => '5g']),
396 34
			'x' => new props('any', function (string $value) : ?array {
397 91
				if (\str_contains($value, '@')) {
398 2
					$dpi = \explode('@', $value);
399 2
					$value = $dpi[0];
400
				}
401 91
				$parts = \explode('x', $value);
402 91
				if (!isset($parts[2]) && \is_numeric($parts[0]) && \is_numeric($parts[1]) && !empty($parts[0]) && !empty($parts[1])) {
403 11
					return [
404 11
						'width' => \intval($parts[0]),
405 11
						'height' => \intval($parts[1]),
406 11
						'density' => isset($dpi[1]) ? \floatval(\rtrim($dpi[1], 'x')) : null
407 11
					];
408
				}
409 91
				return null;
410 34
			}),
411 34
			'MB' => new props('end', fn (string $value) : array => [
412 2
				'ram' => \intval(\mb_substr($value, 0, -2))
413 2
			])
414 34
		];
415
	}
416
417
	/**
418
	 * Extracts device information from a token
419
	 * 
420
	 * @param string $value A token expected to contain device information
421
	 * @return array<string,string|null> An array containing the extracted devices information
422
	 */
423 43
	public static function getDevice(string $value) : array {
424 43
		foreach (['Mobile', 'Tablet', 'Safari', 'AppleWebKit', 'Linux', 'rv:'] AS $item) {
425 43
			if (\mb_stripos($value, $item) === 0) {
426 8
				return [];
427
			}
428
		}
429 38
		$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

429
		$parts = \explode('Build/', /** @scrutinizer ignore-type */ \str_ireplace('build/', 'Build/', $value), 2);
Loading history...
430 38
		$parts[0] = \trim($parts[0]);
431 38
		$build = $parts[1] ?? null;
432 38
		$vendors = [
433 38
			'Samsung' => 'Samsung',
434 38
			'OnePlus' => 'OnePlus',
435 38
			'CPH' =>'OnePlus',
436 38
			'KB' => 'OnePlus',
437 38
			'Pixel' => 'Google',
438 38
			'SM-' => 'Samsung',
439 38
			'LM-' => 'LG',
440 38
			'LG' => 'LG',
441 38
			'LG-' => 'LG',
442 38
			'RealMe' => 'RealMe',
443 38
			'RMX' => 'RealMe',
444 38
			'HTC' => 'HTC',
445 38
			'Nexus' => 'Google',
446 38
			'Redmi' => 'Redmi', // must be above 'MI '
447 38
			'MI ' => 'Xiaomi',
448 38
			'HM ' => 'Xiaomi',
449 38
			'Xiaomi' => 'Xiaomi',
450 38
			'Huawei' => 'Huawei',
451 38
			'Honor' => 'Honor',
452 38
			'Motorola' => 'Motorola',
453 38
			'moto' => 'Motorola',
454 38
			'Intel' => 'Intel',
455 38
			'SonyEricsson' => 'Sony Ericsson',
456 38
			'Tecno' => 'Tecno',
457 38
			'Vivo' => 'Vivo',
458 38
			'Oppo' => 'Oppo',
459 38
			'Asus' => 'Asus',
460 38
			'Acer' => 'Acer',
461 38
			'Alcatel' => 'Alcatel',
462 38
			'Infinix' => 'Infinix',
463 38
			'Poco' => 'Poco',
464 38
			'Cubot' => 'Cubot',
465 38
			'Kingkong' => 'Cubot',
466 38
			'Nokia' => 'Nokia',
467 38
			'WR' => 'Westinghouse',
468 38
			'HKP' => 'HKPro',
469 38
			'Roku' => 'Roku',
470 38
			'TCL' => 'TCL'
471 38
		];
472
473
		// find vendor
474 38
		$vendor = null;
475 38
		foreach ($vendors AS $key => $item) {
476 38
			if (($pos = \mb_stripos($value, $key)) !== false) {
477 29
				$vendor = self::getVendor($item);
478
479
				// remove vendor name
480 29
				if ($pos === 0 && ($key === $item || $key === 'SonyEricsson')) {
481 17
					$parts[0] = \trim(\mb_substr($parts[0], \mb_strlen($key)), ' -_/');
482
				}
483 29
				break;
484
			}
485
		}
486 38
		$model = \explode(' ', \str_replace('_', ' ', $parts[0]), 2);
487 38
		$device = $model[0] !== '' && \ctype_alpha($model[0]) ? \ucfirst($model[0]) : null; // device name if only letters
488 38
		$model = $device === null ? \implode(' ', $model) : ($model[1] ?? null); // reconstruct remainder of device name
489
490
		// remove everything after a slash
491 38
		if ($build === null && \str_contains($model ?? '', '/')) {
492 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

492
			$model = \mb_strstr(/** @scrutinizer ignore-type */ $model, '/', true);
Loading history...
493
		}
494
495
		// special case for SMART TV
496 38
		if (\strcasecmp($device.$model, 'smarttv') === 0) {
497 1
			$device = 'Smart TV';
498 1
			$model = null;
499
		}
500
		// var_dump($value, $parts, $device, $model);
501 38
		return [
502 38
			'vendor' => $vendor,
503 38
			'device' => $device,
504 38
			'model' => $model ? \ucwords($model) : null,
505 38
			'build' => $build
506 38
		];
507
	}
508
509 30
	public static function getVendor(string $value) : string {
510 30
		$map = [
511 30
			'oneplus' => 'OnePlus',
512 30
			'lg' => 'LG',
513 30
			'lge' => 'LG',
514 30
			'realme' => 'RealMe',
515 30
			'htc' => 'HTC',
516 30
			'sonyericsson' => 'Sony Ericsson',
517 30
			'tcl' => 'TCL',
518 30
			'zte' => 'ZTE',
519 30
			'hmd' => 'HMD',
520 30
			'lt' => 'LT'
521 30
		];
522 30
		$value = \mb_strtolower($value);
523 30
		return $map[$value] ?? \mb_convert_case($value, MB_CASE_TITLE);
524
	}
525
}