Passed
Push — main ( 50dae2...0a2d45 )
by Will
12:58
created

apps   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 324
Duplicated Lines 0 %

Test Coverage

Coverage 99.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 34
eloc 226
c 1
b 0
f 0
dl 0
loc 324
ccs 295
cts 297
cp 0.9933
rs 9.68

1 Method

Rating   Name   Duplication   Size   Complexity  
F get() 0 317 34
1
<?php
2
declare(strict_types = 1);
3
namespace hexydec\agentzero;
4
5
/**
6
 * @phpstan-import-type MatchConfig from config
7
 */
8
class apps {
9
10
	/**
11
	 * Generates a configuration array for matching apps
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 40
	public static function get() : array {
16 1
		$fn = [
17 1
			'appslash' => function (string $value, int $i, array $tokens, string $match) : ?array {
18 40
				if (\mb_stripos($value, 'AppleWebKit') !== 0 && !\str_contains($value, '://')) {
19 14
					$parts = \explode('/', $value, 4);
20 14
					$offset = isset($parts[2]) ? 1 : 0;
21 14
					$map = [
22 14
						'YaApp_iOS' => 'Yandex',
23 14
						'YaApp_Android' => 'Yandex',
24 14
						'YaSearchApp' => 'Yandex',
25 14
						'LinkedInApp' => 'LinkedIn',
26 14
						'[LinkedInApp]' => 'LinkedIn',
27 14
						'GoogleApp' => 'Google',
28 14
						'com.zhiliaoapp.musically' => 'TikTok',
29 14
						'com.google.android.apps.searchlite' => 'Google (Lite)',
30 14
						'com.google.photos' => 'Google Photos',
31 14
						'com.google.ios.youtube' => 'YouTube',
32 14
						'AlohaBrowserApp' => 'Aloha'
33 14
					];
34 14
					$app = $parts[0 + $offset];
35 14
					if (\mb_stripos($app, \rtrim($match, '/')) !== false) { // check the match is before the slash
36 13
						return [
37 13
							'app' => $map[$app] ?? $app,
38 13
							'appversion' => $parts[1 + $offset] ?? null
39 13
						];
40
					}
41
				}
42 38
				return null;
43 1
			},
44 1
			'langslash' => function (string $value) : array {
45 6
				$parts = \explode('-', \str_replace('_', '-', \explode('/', $value, 2)[1]), 4);
46 6
				$suffix = $parts[2] ?? $parts[1] ?? null;
47 6
				return [
48 6
					'language' => \strtolower($parts[0]).($suffix !== null ? '-'.\strtoupper($suffix) : '')
49 6
				];
50 1
			}
51 1
		];
52 1
		return [
53 1
			'OcIdWebView' => [
54 1
				'match' => 'exact',
55 1
				'categories' => function (string $value, int $i, array $tokens) : array {
56 1
					$data = [
57 1
						'app' => $value
58 1
					];
59 1
					if (!empty($tokens[$i+1]) && ($json = \json_decode($tokens[$i+1], true)) !== null) {
60 1
						$data['appversion'] = $json['appVersion'] ?? null;
61 1
						$data['darkmode'] = isset($json['isDarkTheme']) ? \boolval($json['isDarkTheme']) : null;
62
					}
63 1
					return $data;
64 1
				}
65 1
			],
66 1
			'com.google.android.apps.' => [
67 1
				'match' => 'any',
68 1
				'categories' => $fn['appslash']
69 1
			],
70 1
			'Instagram' => [
71 1
				'match' => 'any',
72 1
				'categories' => function (string $value, int $i, array $tokens) : array {
73 5
					$parts = \explode(' ', $value, 4);
74 5
					$data = [
75 5
						'type' => 'human',
76 5
						'app' => 'Instagram',
77 5
						'appversion' => $parts[1] ?? null,
78 5
						'platform' => empty($parts[2]) ? null : platforms::getPlatform($parts[2])
79 5
					];
80 5
					foreach (\array_slice($tokens, $i + 1) AS $key => $item) {
81 5
						if (($ipados = \str_starts_with($item, 'iPadOS')) || \str_starts_with($item, 'iOS ')) {
82 2
							$data['kernel'] = 'Linux';
83 2
							$data['platform'] = 'iOS';
84 2
							$data['platformversion'] = \str_replace('_', '.', \mb_substr($item, $ipados ? 7 : 4));
85 2
							$data['density'] = \floatval(\mb_substr($item, 6));
86 5
						} elseif (($iphone = \str_starts_with($item, 'iPhone')) || ($ipad = \str_starts_with($item, 'iPad')) || \str_starts_with($item, 'iPod')) {
87 2
							$data['vendor'] = 'Apple';
88 2
							$data['category'] = empty($ipad) ? 'mobile' : 'tablet';
89 2
							$data['device'] = $iphone ? 'iPhone' : ($ipad ? 'iPad' : 'iPod');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ipad does not seem to be defined for all execution paths leading up to this point.
Loading history...
90 2
							$data['model'] = \str_replace(',', '.', \mb_substr($item, $iphone ? 6 : 4));
91 2
							$data['architecture'] = 'arm';
92 2
							$data['bits'] = 64;
93 5
						} elseif (\str_starts_with($item, 'scale=')) {
94 2
							$data['density'] = \floatval(\mb_substr($item, 6));
95 5
						} elseif (\str_ends_with($item, 'dpi')) {
96 4
							$data['dpi'] = \intval(\mb_substr($item, 0, -3));
97 5
						} elseif (\str_contains($item, 'x') && \strspn($item, '0123456789x') === \strlen($item)) {
98 5
							list($data['width'], $data['height']) = \array_map('intval', \explode('x', $item, 2));
99
100
							// get device when the UA string starts with "Instagram"
101 5
							if ($i === 0 && !isset($data['vendor'])) {
102 1
								$device = [
103 1
									\trim(\mb_strstr($tokens[$key + 2] ?? '', '/') ?: $tokens[$key + 2] ?? '', '/ '), // sometimes the vendor name has a / with the correct name after
104 1
									$tokens[$key + 3] ?? null
105 1
								];
106 1
								$data = \array_merge($data, devices::getDevice(\implode(' ', \array_filter($device))));
107 1
								break;
108
							}
109
						}
110
					}
111 5
					return $data;
112 1
				}
113 1
			],
114 1
			'GSA/' => [
115 1
				'match' => 'any',
116 1
				'categories' => fn (string $value) : array => [
117 3
					'app' => 'Google',
118 3
					'appversion' => \mb_substr($value, 4)
119 3
				]
120 1
			],
121 1
			'DuckDuckGo/' => [
122 1
				'match' => 'start',
123 1
				'categories' => $fn['appslash']
124 1
			],
125 1
			'FlipboardProxy/' => [
126 1
				'match' => 'start',
127 1
				'categories' => $fn['appslash']
128 1
			],
129 1
			'AndroidDownloadManager/' => [
130 1
				'match' => 'start',
131 1
				'categories' => $fn['appslash']
132 1
			],
133 1
			'Google-Read-Aloud' => [
134 1
				'match' => 'exact',
135 1
				'categories' => [
136 1
					'type' => 'human',
137 1
					'app' => 'Google-Read-Aloud'
138 1
				]
139 1
			],
140 1
			'Zoom ' => [
141 1
				'match' => 'start',
142 1
				'categories' => fn (string $value) : array => [
143 1
					'app' => 'Zoom',
144 1
					'appversion' => \mb_substr($value, 5)
145 1
				]
146 1
			],
147
148
			// special parser for Facebook app because it is completely different to any other
149 1
			'FBAN/' => [
150 1
				'match' => 'any',
151 1
				'categories' => function (string $value) : array {
152 4
					$map = [
153 4
						'FBAN/MessengerLiteForiOS' => [
154 4
							'type' => 'human',
155 4
							'app' => 'Facebook Messenger',
156 4
							'platform' => 'iOS'
157 4
						],
158 4
						'FBAN/FB4A' => [
159 4
							'type' => 'human',
160 4
							'app' => 'Facebook',
161 4
							'platform' => 'Android'
162 4
						],
163 4
						'FBAN/FBIOS' => [
164 4
							'type' => 'human',
165 4
							'app' => 'Facebook',
166 4
							'platform' => 'iOS'
167 4
						],
168 4
						'FBAN/FB4FireTV' => [
169 4
							'type' => 'human',
170 4
							'category' => 'tv',
171 4
							'app' => 'Facebook',
172 4
							'platform' => 'Android'
173 4
						],
174 4
						'FBAN/MessengerDesktop' => [
175 4
							'type' => 'human',
176 4
							'category' => 'desktop',
177 4
							'app' => 'Facebook Messenger'
178 4
						],
179 4
						'FacebookCanvasDesktop FBAN/GamesWindowsDesktopApp' => [
180 4
							'type' => 'human',
181 4
							'platform' => 'Windows',
182 4
							'category' => 'desktop',
183 4
							'app' => 'Facebook Gamesroom'
184 4
						]
185 4
					];
186 4
					return $map[$value] ?? [
187 4
						'app' => 'Facebook',
188 4
						'type' => 'human'
189
					];
190 1
				}
191 1
			],
192 1
			'FB_IAB/' => [
193 1
				'match' => 'start',
194 1
				'categories' => [
195 1
					'app' => 'Facebook'
196 1
				]
197 1
			],
198 1
			'FBAV/' => [
199 1
				'match' => 'start',
200 1
				'categories' => fn (string $value) : array => [
201 8
					'appversion' => \mb_substr($value, 5)
202 8
				]
203 1
			],
204 1
			'FBMF/' => [
205 1
				'match' => 'start',
206 1
				'categories' => fn (string $value) : array => [
207 1
					'vendor' => devices::getVendor(\mb_substr($value, 5))
208 1
				]
209 1
			],
210 1
			'FBDV/' => [
211 1
				'match' => 'start',
212 1
				'categories' => fn (string $value) : array => [
213 2
					'device' => \mb_substr($value, 5)
214 2
				]
215 1
			],
216 1
			'FBMD/' => [
217 1
				'match' => 'start',
218 1
				'categories' => fn (string $value) : array => [
219 2
					'model' => \mb_substr($value, 5)
220 2
				]
221 1
			],
222 1
			'FBLC/' => [
223 1
				'match' => 'start',
224 1
				'categories' => $fn['langslash']
225 1
			],
226 1
			'FBDM/' => [
227 1
				'match' => 'start',
228 1
				'categories' => function (string $value) : array {
229 1
					$data = [];
230 1
					foreach (\explode(',', \trim(\mb_substr($value, 5), '{}')) AS $item) {
231 1
						$parts = \explode('=', $item);
232 1
						if (!empty($parts[1])) {
233 1
							if (\is_numeric($parts[1])) {
234 1
								$parts[1] = \str_contains($parts[1], '.') ? \floatval($parts[1]) : \intval($parts[1]);
235
							}
236 1
							$data[$parts[0]] = $parts[1];
237
						}
238
					}
239 1
					return $data;
240 1
				}
241 1
			],
242 1
			'width=' => [
243 1
				'match' => 'start',
244 1
				'categories' => fn (string $value) : array => [
245 1
					'width' => \intval(\mb_substr($value, 6))
246 1
				]
247 1
			],
248 1
			'height=' => [
249 1
				'match' => 'start',
250 1
				'categories' => fn (string $value) : array => [
251 1
					'height' => \intval(\mb_substr($value, 7))
252 1
				]
253 1
			],
254 1
			'dpi=' => [
255 1
				'match' => 'start',
256 1
				'categories' => fn (string $value) : array => [
257
					'dpi' => \mb_substr($value, 4)
258
				]
259 1
			],
260 1
			'FBSN/' => [
261 1
				'match' => 'start',
262 1
				'categories' => fn (string $value) : array => [
263 2
					'platform' => \mb_substr($value, 5)
264 2
				]
265 1
			],
266 1
			'FBSV' => [
267 1
				'match' => 'start',
268 1
				'categories' => fn (string $value) : array => [
269 2
					'platformversion' => \mb_substr($value, 5)
270 2
				]
271 1
			],
272 1
			'isDarkMode/' => [
273 1
				'match' => 'start',
274 1
				'categories' => function (string $value) : array {
275 1
					$mode = \mb_substr($value, 11);
276 1
					return [
277 1
						'darkmode' => \in_array($mode, ['0', '1'], true) ? \boolval($mode) : null
278 1
					];
279 1
				}
280 1
			],
281 1
			'ByteFullLocale/' => [
282 1
				'match' => 'start',
283 1
				'categories' => $fn['langslash']
284 1
			],
285 1
			'NetType/' => [
286 1
				'match' => 'start',
287 1
				'categories' => fn (string $value) : array => [
288 2
					'nettype' => \mb_convert_case(\mb_substr($value, 8), MB_CASE_UPPER)
289 2
				]
290 1
			],
291
292
			// other
293 1
			'MAUI' => [
294 1
				'match' => 'start',
295 1
				'categories' => fn (string $value) : array => [
296 1
					'type' => 'human',
297 1
					'app' => $value
298 1
				]
299 1
			],
300
301
			// TikTok
302 1
			'AppName/' => [
303 1
				'match' => 'start',
304 1
				'categories' => fn(string $value) : array => [
305 2
					'app' => $value === 'AppName/musical_ly' ? 'TikTok' : \mb_substr($value, 8)
306 2
				]
307 1
			],
308 1
			'app_version/' => [
309 1
				'match' => 'start',
310 1
				'categories' => fn(string $value) : array => [
311 2
					'appversion' => \mb_substr($value, 12)
312 2
				]
313 1
			],
314 1
			'AppVersion/' => [
315 1
				'match' => 'any',
316 1
				'categories' => fn(string $value) : array => [
317 1
					'appversion' => \explode('/', $value, 2)[1]
318 1
				]
319 1
			],
320 1
			'musical_ly' => [
321 1
				'match' => 'start',
322 1
				'categories' => fn(string $value) : array => [
323 1
					'app' => 'TikTok',
324 1
					'appversion' => \str_replace('_', '.', \mb_substr(\explode(' ', $value, 2)[0], 11))
325 1
				]
326 1
			],
327
				
328
			// generic
329 1
			'App' => [
330 1
				'match' => 'any',
331 1
				'categories' => $fn['appslash']
332 1
			],
333 1
		];
334
	}
335
}