Passed
Push — main ( e55150...6ce6d7 )
by Will
02:49
created

apps::get()   F

Complexity

Conditions 36
Paths 1

Size

Total Lines 257
Code Lines 188

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 232
CRAP Score 36.0027

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 188
c 1
b 0
f 0
dl 0
loc 257
ccs 232
cts 235
cp 0.9872
rs 3.3333
cc 36
nc 1
nop 0
crap 36.0027

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 props from config
7
 */
8
class apps {
9
10
	/**
11
	 * Generates a configuration array for matching apps
12
	 * 
13
	 * @return props An array with keys representing the string to match, and a value of an array containing parsing and output settings
14
	 */
15 44
	public static function get() : array {
16 1
		$fn = [
17 1
			'appslash' => function (string $value, int $i, array $tokens, string $match) : array {
18 44
				if (\mb_stripos($value, 'AppleWebKit') !== 0 && !\str_contains($value, '://')) {
19 19
					$parts = \explode('/', $value, 4);
20 19
					$offset = isset($parts[2]) ? 1 : 0;
21 19
					$map = [
22 19
						'YaApp_iOS' => 'Yandex',
23 19
						'YaApp_Android' => 'Yandex',
24 19
						'YaSearchApp' => 'Yandex',
25 19
						'YaBrowser' => 'Yandex',
26 19
						'LinkedInApp' => 'LinkedIn',
27 19
						'[LinkedInApp]' => 'LinkedIn',
28 19
						'GoogleApp' => 'Google',
29 19
						'com.zhiliaoapp.musically' => 'TikTok',
30 19
						'com.google.android.apps.searchlite' => 'Google (Lite)',
31 19
						'com.google.android.googlequicksearchbox' => 'Google (Quick Search Box)',
32 19
						'com.google.photos' => 'Google Photos',
33 19
						'com.google.ios.youtube' => 'YouTube',
34 19
						'com.google.GoogleMobile' => 'Google',
35 19
						'AlohaBrowserApp' => 'Aloha'
36 19
					];
37 19
					$app = $parts[0 + $offset];
38 19
					if (\mb_stripos($app, \rtrim($match, '/')) !== false) { // check the match is before the slash
39 19
						return [
40 19
							'type' => 'human',
41 19
							'app' => $map[$app] ?? $app,
42 19
							'appversion' => $parts[1 + $offset] ?? null
43 19
						];
44
					}
45
				}
46 37
				return [];
47 1
			},
48 1
			'langslash' => function (string $value) : array {
49 6
				$parts = \explode('-', \str_replace('_', '-', \explode('/', $value, 2)[1]), 4);
50 6
				$suffix = $parts[2] ?? $parts[1] ?? null;
51 6
				return [
52 6
					'language' => \strtolower($parts[0]).($suffix !== null ? '-'.\strtoupper($suffix) : '')
53 6
				];
54 1
			}
55 1
		];
56 3
		return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('OcIdWebVie...any', $fn['appslash'])) returns the type array<string,hexydec\agentzero\props> which is incompatible with the documented return type hexydec\agentzero\props.
Loading history...
57 3
			'OcIdWebView' => new props('exact', function (string $value) : array {
58 1
				$data = [
59 1
					'app' => $value
60 1
				];
61 1
				return $data;
62 3
			}),
63 3
			'com.google.GoogleMobile/' => new props('start', function (string $value, int $i, array $tokens, string $match) use ($fn) : array {
64
				return \array_merge($fn['appslash']($value, $i, $tokens, $match), [
65
					'category' => 'mobile'
66
				]);
67 3
			}),
68 3
			'com.google.' => new props('start', $fn['appslash']),
69 3
			'Instagram' => new props('any', function (string $value, int $i, array $tokens) : array {
70 6
				$parts = \explode(' ', $value, 4);
71 6
				$data = [
72 6
					'type' => 'human',
73 6
					'app' => 'Instagram',
74 6
					'appversion' => $parts[1] ?? null,
75 6
					'platform' => empty($parts[2]) ? null : platforms::getPlatform($parts[2])
76 6
				];
77 6
				foreach (\array_slice($tokens, $i + 1) AS $key => $item) {
78 6
					$ipad = null;
79 6
					if (($ipados = \str_starts_with($item, 'iPadOS')) || \str_starts_with($item, 'iOS ')) {
80 2
						$data['kernel'] = 'Linux';
81 2
						$data['platform'] = 'iOS';
82 2
						$data['platformversion'] = \str_replace('_', '.', \mb_substr($item, $ipados ? 7 : 4));
83 2
						$data['density'] = \floatval(\mb_substr($item, 6));
84 6
					} elseif (($iphone = \str_starts_with($item, 'iPhone')) || ($ipad = \str_starts_with($item, 'iPad')) || \str_starts_with($item, 'iPod')) {
85 2
						$data['vendor'] = 'Apple';
86 2
						$data['category'] = $ipad ? 'tablet' : 'mobile';
87 2
						$data['device'] = $iphone ? 'iPhone' : ($ipad ? 'iPad' : 'iPod');
88 2
						$data['model'] = \str_replace(',', '.', \mb_substr($item, $iphone ? 6 : 4));
89 2
						$data['architecture'] = 'arm';
90 2
						$data['bits'] = 64;
91 6
					} elseif (\str_starts_with($item, 'scale=')) {
92 2
						$data['density'] = \floatval(\mb_substr($item, 6));
93 6
					} elseif (\str_ends_with($item, 'dpi')) {
94 5
						$data['dpi'] = \intval(\mb_substr($item, 0, -3));
95 6
					} elseif (\str_contains($item, 'x') && \strspn($item, '0123456789x') === \strlen($item)) {
96 6
						list($data['width'], $data['height']) = \array_map('intval', \explode('x', $item, 2));
97
98
						// get device when the UA string starts with "Instagram"
99 6
						if ($i === 0 && !isset($data['vendor'])) {
100 1
							$device = [
101 1
								\trim(\mb_strstr($tokens[$key + 2] ?? '', '/') ?: $tokens[$key + 2] ?? '', '/ '), // sometimes the vendor name has a / with the correct name after
102 1
								$tokens[$key + 3] ?? null
103 1
							];
104
105
							// remove duplicated name
106 1
							if ($device[1] !== null && \mb_stripos($device[1], $device[0]) === 0) {
107 1
								unset($device[0]);
108
							}
109 1
							$data = \array_merge($data, devices::getDevice(\implode(' ', \array_filter($device))));
110 1
							break;
111
						}
112
					}
113
				}
114 6
				return $data;
115 3
			}),
116 3
			'imoAndroid/' => new props('start', fn (string $value, int $i, array $tokens) : array => \array_merge(
117 3
				isset($tokens[$i + 3]) ? devices::getDevice($tokens[$i + 3]) : [],
118 3
				[
119 3
					'app' => 'imo',
120 3
					'appversion' => ($ver = \mb_strstr($value, '/')) === false ? $value : \ltrim($ver, '/'),
121 3
					'platform' => 'Android',
122 3
					'platformversion' => $tokens[$i + 1] ?? null,
123 3
					'cpu' => $tokens[$i + 11] ?? null,
124 3
					'vendor' => isset($tokens[$i + 4]) ? devices::getVendor($tokens[$i + 4]) : null
125 3
				]
126 3
			)),
127 3
			'GSA/' => new props('any', fn (string $value) : array => [
128 3
				'app' => 'Google',
129 3
				'appversion' => \mb_substr($value, 4)
130 3
			]),
131 3
			'DuckDuckGo/' => new props('start', $fn['appslash']),
132 3
			'FlipboardProxy/' => new props('start', $fn['appslash']),
133 3
			'Emacs/' => new props('start', $fn['appslash']),
134 3
			'AndroidDownloadManager/' => new props('start', $fn['appslash']),
135 3
			'Google-Read-Aloud' => new props('exact', [
136 3
				'type' => 'human',
137 3
				'app' => 'Google-Read-Aloud'
138 3
			]),
139 3
			'Zoom ' => new props('start', fn (string $value) : array => [
140 2
				'app' => 'Zoom',
141 2
				'appversion' => \mb_substr($value, 5)
142 2
			]),
143 3
			'OculusBrowser/' => new props('start', $fn['appslash']),
144 3
			'YaBrowser/' => new props('start', $fn['appslash']),
145 3
			'choqok/' => new props('start', $fn['appslash']),
146 3
			'PowerShell/' => new props('start', $fn['appslash']),
147 3
			'Quora ' => new props('start', fn (string $value) : array => [
148 1
				'type' => 'human',
149 1
				'app' => 'Quora',
150 1
				'appversion' => \explode(' ', $value, 3)[1]
151 1
			]),
152 3
			'AmazonKidsBrowser/' => new props('start', $fn['appslash']),
153 3
			'Viber/' => new props('start', $fn['appslash']),
154
155
			// special parser for Facebook app because it is completely different to any other
156 3
			'FBAN/' => new props('any', function (string $value) : array {
157 4
				$map = [
158 4
					'FBAN/MessengerLiteForiOS' => [
159 4
						'type' => 'human',
160 4
						'app' => 'Facebook Messenger',
161 4
						'platform' => 'iOS'
162 4
					],
163 4
					'FBAN/FB4A' => [
164 4
						'type' => 'human',
165 4
						'app' => 'Facebook',
166 4
						'platform' => 'Android'
167 4
					],
168 4
					'FBAN/FBIOS' => [
169 4
						'type' => 'human',
170 4
						'app' => 'Facebook',
171 4
						'platform' => 'iOS'
172 4
					],
173 4
					'FBAN/FB4FireTV' => [
174 4
						'type' => 'human',
175 4
						'category' => 'tv',
176 4
						'app' => 'Facebook',
177 4
						'platform' => 'Android'
178 4
					],
179 4
					'FBAN/MessengerDesktop' => [
180 4
						'type' => 'human',
181 4
						'category' => 'desktop',
182 4
						'app' => 'Facebook Messenger'
183 4
					],
184 4
					'FacebookCanvasDesktop FBAN/GamesWindowsDesktopApp' => [
185 4
						'type' => 'human',
186 4
						'platform' => 'Windows',
187 4
						'category' => 'desktop',
188 4
						'app' => 'Facebook Gamesroom'
189 4
					]
190 4
				];
191 4
				return $map[$value] ?? [
192 4
					'app' => 'Facebook',
193 4
					'type' => 'human'
194
				];
195 3
			}),
196 3
			'FB_IAB/' => new props('start', [
197 3
				'app' => 'Facebook'
198 3
			]),
199 3
			'FBAV/' => new props('start', fn (string $value) : array => [
200 9
				'appversion' => \mb_substr($value, 5)
201 9
			]),
202 3
			'FBMF/' => new props('start', fn (string $value) : array => [
203 2
				'vendor' => devices::getVendor(\mb_substr($value, 5))
204 2
			]),
205 3
			'FBDV/' => new props('start', fn (string $value) : array => devices::getDevice(\mb_substr($value, 5))),
206 3
			'FBMD/' => new props('start', fn (string $value) : array => [
207 3
				'model' => \mb_substr($value, 5)
208 3
			]),
209 3
			'FBLC/' => new props('start', $fn['langslash']),
210 3
			'FBDM/' => new props('start', function (string $value) : array {
211 1
				$data = [];
212 1
				foreach (\explode(',', \trim(\mb_substr($value, 5), '{}')) AS $item) {
213 1
					$parts = \explode('=', $item);
214 1
					if (!empty($parts[1])) {
215 1
						if (\is_numeric($parts[1])) {
216 1
							$parts[1] = \str_contains($parts[1], '.') ? \floatval($parts[1]) : \intval($parts[1]);
217
						}
218 1
						$data[$parts[0]] = $parts[1];
219
					}
220
				}
221 1
				return $data;
222 3
			}),
223 3
			'width=' => new props('start', fn (string $value) : array => [
224 2
				'width' => \intval(\mb_substr($value, 6))
225 2
			]),
226 3
			'height=' => new props('start', fn (string $value) : array => [
227 2
				'height' => \intval(\mb_substr($value, 7))
228 2
			]),
229 3
			'dpi=' => new props('start', fn (string $value) : array => [
230 1
				'dpi' => \mb_substr($value, 4)
231 1
			]),
232 3
			'FBSN/' => new props('start', fn (string $value) : array => [
233 3
				'platform' => \mb_substr($value, 5)
234 3
			]),
235 3
			'FBSV' => new props('start', fn (string $value) : array => [
236 3
				'platformversion' => \mb_substr($value, 5)
237 3
			]),
238 3
			'isDarkMode/' => new props('start', function (string $value) : array {
239 1
				$mode = \mb_substr($value, 11);
240 1
				return [
241 1
					'darkmode' => \in_array($mode, ['0', '1'], true) ? \boolval($mode) : null
242 1
				];
243 3
			}),
244 3
			'ByteFullLocale/' => new props('start', $fn['langslash']),
245 3
			'NetType/' => new props('start', fn (string $value) : array => [
246 3
				'nettype' => \mb_convert_case(\mb_substr($value, 8), MB_CASE_UPPER)
247 3
			]),
248
249
			// other
250 3
			'MAUI' => new props('start', fn (string $value) : array => [
251 2
				'type' => 'human',
252 2
				'app' => $value
253 2
			]),
254
255
			// TikTok
256 3
			'AppName/' => new props('start', fn(string $value) : array => [
257 3
				'app' => $value === 'AppName/musical_ly' ? 'TikTok' : \mb_substr($value, 8)
258 3
			]),
259 3
			'app_version/' => new props('start', fn(string $value) : array => [
260 3
				'appversion' => \mb_substr($value, 12)
261 3
			]),
262 3
			'AppVersion/' => new props('any', fn(string $value) : array => [
263 2
				'appversion' => \explode('/', $value, 2)[1]
264 2
			]),
265 3
			'musical_ly' => new props('start', fn(string $value) : array => [
266 2
				'app' => 'TikTok',
267 2
				'appversion' => \str_replace('_', '.', \mb_substr(\explode(' ', $value, 2)[0], 11))
268 2
			]),
269
				
270
			// generic
271 3
			'App' => new props('any', $fn['appslash']),
272 3
		];
273
	}
274
}