Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
78 / 78 |
|
100.00% |
20 / 20 |
CRAP | |
100.00% |
1 / 1 |
UserAgent | |
100.00% |
78 / 78 |
|
100.00% |
20 / 20 |
44 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
parse | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
compileData | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
setPlatform | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
setBrowser | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
3 | |||
setMobile | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
setRobot | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
getAsString | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getBrowser | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getBrowserVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMobile | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPlatform | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRobot | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isBrowser | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
isMobile | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
isRobot | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
getType | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getName | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
jsonSerialize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php declare(strict_types=1); |
2 | /* |
3 | * This file is part of Aplus Framework HTTP Library. |
4 | * |
5 | * (c) Natan Felles <natanfelles@gmail.com> |
6 | * |
7 | * For the full copyright and license information, please view the LICENSE |
8 | * file that was distributed with this source code. |
9 | */ |
10 | namespace Framework\HTTP; |
11 | |
12 | use JetBrains\PhpStorm\Deprecated; |
13 | use JetBrains\PhpStorm\Pure; |
14 | |
15 | /** |
16 | * Class UserAgent. |
17 | * |
18 | * @package http |
19 | */ |
20 | class UserAgent implements \JsonSerializable, \Stringable |
21 | { |
22 | protected ?string $agent = null; |
23 | protected ?string $browser = null; |
24 | protected ?string $browserVersion = null; |
25 | protected ?string $mobile = null; |
26 | protected ?string $platform = null; |
27 | protected ?string $robot = null; |
28 | protected bool $isBrowser = false; |
29 | protected bool $isMobile = false; |
30 | protected bool $isRobot = false; |
31 | /** |
32 | * @var array<string,array<string,string>> |
33 | */ |
34 | protected static array $config = [ |
35 | 'platforms' => [ |
36 | 'windows nt 10.0' => 'Windows 10', |
37 | 'windows nt 6.3' => 'Windows 8.1', |
38 | 'windows nt 6.2' => 'Windows 8', |
39 | 'windows nt 6.1' => 'Windows 7', |
40 | 'windows nt 6.0' => 'Windows Vista', |
41 | 'windows nt 5.2' => 'Windows 2003', |
42 | 'windows nt 5.1' => 'Windows XP', |
43 | 'windows nt 5.0' => 'Windows 2000', |
44 | 'windows nt 4.0' => 'Windows NT 4.0', |
45 | 'winnt4.0' => 'Windows NT 4.0', |
46 | 'winnt 4.0' => 'Windows NT', |
47 | 'winnt' => 'Windows NT', |
48 | 'windows 98' => 'Windows 98', |
49 | 'win98' => 'Windows 98', |
50 | 'windows 95' => 'Windows 95', |
51 | 'win95' => 'Windows 95', |
52 | 'windows phone' => 'Windows Phone', |
53 | 'windows' => 'Unknown Windows OS', |
54 | 'android' => 'Android', |
55 | 'blackberry' => 'BlackBerry', |
56 | 'iphone' => 'iOS', |
57 | 'ipad' => 'iOS', |
58 | 'ipod' => 'iOS', |
59 | 'os x' => 'Mac OS X', |
60 | 'ppc mac' => 'Power PC Mac', |
61 | 'freebsd' => 'FreeBSD', |
62 | 'ppc' => 'Macintosh', |
63 | 'ubuntu' => 'Ubuntu', |
64 | 'debian' => 'Debian', |
65 | 'fedora' => 'Fedora', |
66 | 'linux' => 'Linux', |
67 | 'sunos' => 'Sun Solaris', |
68 | 'beos' => 'BeOS', |
69 | 'apachebench' => 'ApacheBench', |
70 | 'aix' => 'AIX', |
71 | 'irix' => 'Irix', |
72 | 'osf' => 'DEC OSF', |
73 | 'hp-ux' => 'HP-UX', |
74 | 'netbsd' => 'NetBSD', |
75 | 'bsdi' => 'BSDi', |
76 | 'openbsd' => 'OpenBSD', |
77 | 'gnu' => 'GNU/Linux', |
78 | 'unix' => 'Unknown Unix OS', |
79 | 'symbian' => 'Symbian OS', |
80 | ], |
81 | // The order of this array should NOT be changed. Many browsers return |
82 | // multiple browser types so we want to identify the sub-type first. |
83 | 'browsers' => [ |
84 | 'curl' => 'Curl', |
85 | 'PostmanRuntime' => 'Postman', |
86 | 'OPR' => 'Opera', |
87 | 'Flock' => 'Flock', |
88 | 'Edge' => 'Spartan', |
89 | 'Edg' => 'Edge', |
90 | 'EdgA' => 'Edge', |
91 | 'Chrome' => 'Chrome', |
92 | // Opera 10+ always reports Opera/9.80 and appends |
93 | // Version/<real version> to the user agent string |
94 | 'Opera.*?Version' => 'Opera', |
95 | 'Opera' => 'Opera', |
96 | 'MSIE' => 'Internet Explorer', |
97 | 'Internet Explorer' => 'Internet Explorer', |
98 | 'Trident.* rv' => 'Internet Explorer', |
99 | 'Shiira' => 'Shiira', |
100 | 'Firefox' => 'Firefox', |
101 | 'Chimera' => 'Chimera', |
102 | 'Phoenix' => 'Phoenix', |
103 | 'Firebird' => 'Firebird', |
104 | 'Camino' => 'Camino', |
105 | 'Netscape' => 'Netscape', |
106 | 'OmniWeb' => 'OmniWeb', |
107 | 'Safari' => 'Safari', |
108 | 'Mozilla' => 'Mozilla', |
109 | 'Konqueror' => 'Konqueror', |
110 | 'icab' => 'iCab', |
111 | 'Lynx' => 'Lynx', |
112 | 'Links' => 'Links', |
113 | 'hotjava' => 'HotJava', |
114 | 'amaya' => 'Amaya', |
115 | 'IBrowse' => 'IBrowse', |
116 | 'Maxthon' => 'Maxthon', |
117 | 'Ubuntu' => 'Ubuntu Web Browser', |
118 | 'Vivaldi' => 'Vivaldi', |
119 | ], |
120 | 'mobiles' => [ |
121 | 'mobileexplorer' => 'Mobile Explorer', |
122 | 'palmsource' => 'Palm', |
123 | 'palmscape' => 'Palmscape', |
124 | // Phones and Manufacturers |
125 | 'motorola' => 'Motorola', |
126 | 'nokia' => 'Nokia', |
127 | 'palm' => 'Palm', |
128 | 'iphone' => 'Apple iPhone', |
129 | 'ipad' => 'iPad', |
130 | 'ipod' => 'Apple iPod Touch', |
131 | 'sony' => 'Sony Ericsson', |
132 | 'ericsson' => 'Sony Ericsson', |
133 | 'blackberry' => 'BlackBerry', |
134 | 'cocoon' => 'O2 Cocoon', |
135 | 'blazer' => 'Treo', |
136 | 'lg' => 'LG', |
137 | 'amoi' => 'Amoi', |
138 | 'xda' => 'XDA', |
139 | 'mda' => 'MDA', |
140 | 'vario' => 'Vario', |
141 | 'htc' => 'HTC', |
142 | 'samsung' => 'Samsung', |
143 | 'sharp' => 'Sharp', |
144 | 'sie-' => 'Siemens', |
145 | 'alcatel' => 'Alcatel', |
146 | 'benq' => 'BenQ', |
147 | 'ipaq' => 'HP iPaq', |
148 | 'mot-' => 'Motorola', |
149 | 'playstation portable' => 'PlayStation Portable', |
150 | 'playstation 3' => 'PlayStation 3', |
151 | 'playstation vita' => 'PlayStation Vita', |
152 | 'hiptop' => 'Danger Hiptop', |
153 | 'nec-' => 'NEC', |
154 | 'panasonic' => 'Panasonic', |
155 | 'philips' => 'Philips', |
156 | 'sagem' => 'Sagem', |
157 | 'sanyo' => 'Sanyo', |
158 | 'spv' => 'SPV', |
159 | 'zte' => 'ZTE', |
160 | 'sendo' => 'Sendo', |
161 | 'nintendo dsi' => 'Nintendo DSi', |
162 | 'nintendo ds' => 'Nintendo DS', |
163 | 'nintendo 3ds' => 'Nintendo 3DS', |
164 | 'wii' => 'Nintendo Wii', |
165 | 'open web' => 'Open Web', |
166 | 'openweb' => 'OpenWeb', |
167 | // Operating Systems |
168 | 'android' => 'Android', |
169 | 'symbian' => 'Symbian', |
170 | 'SymbianOS' => 'SymbianOS', |
171 | 'elaine' => 'Palm', |
172 | 'series60' => 'Symbian S60', |
173 | 'windows ce' => 'Windows CE', |
174 | // Browsers |
175 | 'obigo' => 'Obigo', |
176 | 'netfront' => 'Netfront Browser', |
177 | 'openwave' => 'Openwave Browser', |
178 | 'mobilexplorer' => 'Mobile Explorer', |
179 | 'operamini' => 'Opera Mini', |
180 | 'opera mini' => 'Opera Mini', |
181 | 'opera mobi' => 'Opera Mobile', |
182 | 'fennec' => 'Firefox Mobile', |
183 | // Other |
184 | 'digital paths' => 'Digital Paths', |
185 | 'avantgo' => 'AvantGo', |
186 | 'xiino' => 'Xiino', |
187 | 'novarra' => 'Novarra Transcoder', |
188 | 'vodafone' => 'Vodafone', |
189 | 'docomo' => 'NTT DoCoMo', |
190 | 'o2' => 'O2', |
191 | // Fallback |
192 | 'mobile' => 'Generic Mobile', |
193 | 'wireless' => 'Generic Mobile', |
194 | 'j2me' => 'Generic Mobile', |
195 | 'midp' => 'Generic Mobile', |
196 | 'cldc' => 'Generic Mobile', |
197 | 'up.link' => 'Generic Mobile', |
198 | 'up.browser' => 'Generic Mobile', |
199 | 'smartphone' => 'Generic Mobile', |
200 | 'cellphone' => 'Generic Mobile', |
201 | ], |
202 | 'robots' => [ |
203 | 'googlebot' => 'Googlebot', |
204 | 'msnbot' => 'MSNBot', |
205 | 'baiduspider' => 'Baiduspider', |
206 | 'bingbot' => 'Bing', |
207 | 'slurp' => 'Inktomi Slurp', |
208 | 'yahoo' => 'Yahoo', |
209 | 'ask jeeves' => 'Ask Jeeves', |
210 | 'fastcrawler' => 'FastCrawler', |
211 | 'infoseek' => 'InfoSeek Robot 1.0', |
212 | 'lycos' => 'Lycos', |
213 | 'yandex' => 'YandexBot', |
214 | 'mediapartners-google' => 'MediaPartners Google', |
215 | 'CRAZYWEBCRAWLER' => 'Crazy Webcrawler', |
216 | 'adsbot-google' => 'AdsBot Google', |
217 | 'feedfetcher-google' => 'Feedfetcher Google', |
218 | 'curious george' => 'Curious George', |
219 | 'ia_archiver' => 'Alexa Crawler', |
220 | 'MJ12bot' => 'Majestic-12', |
221 | 'Uptimebot' => 'Uptimebot', |
222 | ], |
223 | ]; |
224 | |
225 | /** |
226 | * UserAgent constructor. |
227 | * |
228 | * @param string $userAgent User-Agent string |
229 | */ |
230 | public function __construct(string $userAgent) |
231 | { |
232 | $this->parse($userAgent); |
233 | } |
234 | |
235 | #[Pure] |
236 | public function __toString() : string |
237 | { |
238 | return $this->toString(); |
239 | } |
240 | |
241 | /** |
242 | * @param string $string |
243 | * |
244 | * @return static |
245 | */ |
246 | protected function parse(string $string) : static |
247 | { |
248 | $this->isBrowser = false; |
249 | $this->isRobot = false; |
250 | $this->isMobile = false; |
251 | $this->browser = null; |
252 | $this->browserVersion = null; |
253 | $this->mobile = null; |
254 | $this->robot = null; |
255 | $this->agent = $string; |
256 | $this->compileData(); |
257 | return $this; |
258 | } |
259 | |
260 | protected function compileData() : void |
261 | { |
262 | $this->setPlatform(); |
263 | foreach (['setRobot', 'setBrowser', 'setMobile'] as $function) { |
264 | if ($this->{$function}()) { |
265 | break; |
266 | } |
267 | } |
268 | } |
269 | |
270 | protected function setPlatform() : bool |
271 | { |
272 | foreach (static::$config['platforms'] as $key => $val) { |
273 | if (\preg_match('#' . \preg_quote($key, '#') . '#i', $this->agent)) { |
274 | $this->platform = $val; |
275 | return true; |
276 | } |
277 | } |
278 | return false; |
279 | } |
280 | |
281 | protected function setBrowser() : bool |
282 | { |
283 | foreach (static::$config['browsers'] as $key => $val) { |
284 | if (\preg_match( |
285 | '#' . \preg_quote($key, '#') . '.*?([0-9\.]+)#i', |
286 | $this->agent, |
287 | $match |
288 | )) { |
289 | $this->isBrowser = true; |
290 | $this->browserVersion = $match[1]; |
291 | $this->browser = $val; |
292 | $this->setMobile(); |
293 | return true; |
294 | } |
295 | } |
296 | return false; |
297 | } |
298 | |
299 | protected function setMobile() : bool |
300 | { |
301 | foreach (static::$config['mobiles'] as $key => $val) { |
302 | if (\stripos($this->agent, $key) !== false) { |
303 | $this->isMobile = true; |
304 | $this->mobile = $val; |
305 | return true; |
306 | } |
307 | } |
308 | return false; |
309 | } |
310 | |
311 | protected function setRobot() : bool |
312 | { |
313 | foreach (static::$config['robots'] as $key => $val) { |
314 | if (\preg_match('#' . \preg_quote($key, '#') . '#i', $this->agent)) { |
315 | $this->isRobot = true; |
316 | $this->robot = $val; |
317 | $this->setMobile(); |
318 | return true; |
319 | } |
320 | } |
321 | return false; |
322 | } |
323 | |
324 | /** |
325 | * @return string |
326 | * |
327 | * @deprecated Use {@see UserAgent::toString()} |
328 | * |
329 | * @codeCoverageIgnore |
330 | */ |
331 | #[Deprecated( |
332 | reason: 'since HTTP Library version 5.3, use toString() instead', |
333 | replacement: '%class%->toString()' |
334 | )] |
335 | public function getAsString() : string |
336 | { |
337 | \trigger_error( |
338 | 'Method ' . __METHOD__ . ' is deprecated', |
339 | \E_USER_DEPRECATED |
340 | ); |
341 | return $this->toString(); |
342 | } |
343 | |
344 | /** |
345 | * Get the User-Agent as string. |
346 | * |
347 | * @since 5.3 |
348 | * |
349 | * @return string |
350 | */ |
351 | #[Pure] |
352 | public function toString() : string |
353 | { |
354 | return $this->agent; |
355 | } |
356 | |
357 | /** |
358 | * Gets the Browser name. |
359 | * |
360 | * @return string|null |
361 | */ |
362 | #[Pure] |
363 | public function getBrowser() : ?string |
364 | { |
365 | return $this->browser; |
366 | } |
367 | |
368 | /** |
369 | * Gets the Browser Version. |
370 | * |
371 | * @return string|null |
372 | */ |
373 | #[Pure] |
374 | public function getBrowserVersion() : ?string |
375 | { |
376 | return $this->browserVersion; |
377 | } |
378 | |
379 | /** |
380 | * Gets the Mobile device. |
381 | * |
382 | * @return string|null |
383 | */ |
384 | #[Pure] |
385 | public function getMobile() : ?string |
386 | { |
387 | return $this->mobile; |
388 | } |
389 | |
390 | /** |
391 | * Gets the OS Platform. |
392 | * |
393 | * @return string|null |
394 | */ |
395 | #[Pure] |
396 | public function getPlatform() : ?string |
397 | { |
398 | return $this->platform; |
399 | } |
400 | |
401 | /** |
402 | * Gets the Robot name. |
403 | * |
404 | * @return string|null |
405 | */ |
406 | #[Pure] |
407 | public function getRobot() : ?string |
408 | { |
409 | return $this->robot; |
410 | } |
411 | |
412 | /** |
413 | * Is Browser. |
414 | * |
415 | * @param string|null $key |
416 | * |
417 | * @return bool |
418 | */ |
419 | #[Pure] |
420 | public function isBrowser(string $key = null) : bool |
421 | { |
422 | if ($key === null || $this->isBrowser === false) { |
423 | return $this->isBrowser; |
424 | } |
425 | $config = static::$config['browsers'] ?? []; |
426 | return isset($config[$key]) |
427 | && $this->browser === $config[$key]; |
428 | } |
429 | |
430 | /** |
431 | * Is Mobile. |
432 | * |
433 | * @param string|null $key |
434 | * |
435 | * @return bool |
436 | */ |
437 | #[Pure] |
438 | public function isMobile(string $key = null) : bool |
439 | { |
440 | if ($key === null || $this->isMobile === false) { |
441 | return $this->isMobile; |
442 | } |
443 | $config = static::$config['mobiles'] ?? []; |
444 | return isset($config[$key]) |
445 | && $this->mobile === $config[$key]; |
446 | } |
447 | |
448 | /** |
449 | * Is Robot. |
450 | * |
451 | * @param string|null $key |
452 | * |
453 | * @return bool |
454 | */ |
455 | #[Pure] |
456 | public function isRobot(string $key = null) : bool |
457 | { |
458 | if ($key === null || $this->isRobot === false) { |
459 | return $this->isRobot; |
460 | } |
461 | $config = static::$config['robots'] ?? []; |
462 | return isset($config[$key]) |
463 | && $this->robot === $config[$key]; |
464 | } |
465 | |
466 | #[Pure] |
467 | public function getType() : string |
468 | { |
469 | if ($this->isBrowser()) { |
470 | return 'Browser'; |
471 | } |
472 | if ($this->isRobot()) { |
473 | return 'Robot'; |
474 | } |
475 | return 'Unknown'; |
476 | } |
477 | |
478 | #[Pure] |
479 | public function getName() : string |
480 | { |
481 | if ($this->isBrowser()) { |
482 | return $this->getBrowser(); |
483 | } |
484 | if ($this->isRobot()) { |
485 | return $this->getRobot(); |
486 | } |
487 | return 'Unknown'; |
488 | } |
489 | |
490 | #[Pure] |
491 | public function jsonSerialize() : string |
492 | { |
493 | return $this->toString(); |
494 | } |
495 | } |