PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-device-detection/src/class-user-agent-info.php +1024 -225 13.2.4 → 16.3 View file →
@@ -4,8 +4,9 @@
4 4 *
5 5 * @package automattic/jetpack-device-detection
6 6 *
7 7 * We don't want to rename public members.
8 + *
8 9 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
9 10 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
10 11 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
11 12 * @phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
@@ -62,22 +63,55 @@
62 63 * Stores the device platform name
63 64 *
64 65 * @var null|string
65 66 */
66 - private $platform = null;
67 - const PLATFORM_WINDOWS = 'windows';
68 - const PLATFORM_IPHONE = 'iphone';
69 - const PLATFORM_IPOD = 'ipod';
70 - const PLATFORM_IPAD = 'ipad';
71 - const PLATFORM_BLACKBERRY = 'blackberry';
72 - const PLATFORM_BLACKBERRY_10 = 'blackberry_10';
73 - const PLATFORM_SYMBIAN = 'symbian_series60';
74 - const PLATFORM_SYMBIAN_S40 = 'symbian_series40';
75 - const PLATFORM_J2ME_MIDP = 'j2me_midp';
76 - const PLATFORM_ANDROID = 'android';
77 - const PLATFORM_ANDROID_TABLET = 'android_tablet';
78 - const PLATFORM_FIREFOX_OS = 'firefoxOS';
67 + private $platform = null;
68 + const PLATFORM_WINDOWS = 'windows';
69 + const PLATFORM_IPHONE = 'iphone';
70 + const PLATFORM_IPOD = 'ipod';
71 + const PLATFORM_IPAD = 'ipad';
72 + const PLATFORM_BLACKBERRY = 'blackberry';
73 + const PLATFORM_BLACKBERRY_10 = 'blackberry_10';
74 + const PLATFORM_SYMBIAN = 'symbian_series60';
75 + const PLATFORM_SYMBIAN_S40 = 'symbian_series40';
76 + const PLATFORM_J2ME_MIDP = 'j2me_midp';
77 + const PLATFORM_ANDROID = 'android';
78 + const PLATFORM_ANDROID_TABLET = 'android_tablet';
79 + const PLATFORM_FIREFOX_OS = 'firefoxOS';
80 + const PLATFORM_DESKTOP_LINUX = 'linux';
81 + const PLATFORM_DESKTOP_MAC = 'mac';
82 + const PLATFORM_DESKTOP_WINDOWS = 'windows';
83 + const PLATFORM_DESKTOP_CHROME = 'chrome';
84 + const BROWSER_CHROME = 'chrome';
85 + const BROWSER_FIREFOX = 'firefox';
86 + const BROWSER_SAFARI = 'safari';
87 + const BROWSER_EDGE = 'edge';
88 + const BROWSER_OPERA = 'opera';
89 + const BROWSER_IE = 'ie';
90 + const BROWSER_SAMSUNG = 'samsung';
91 + const BROWSER_UC = 'uc';
92 + const BROWSER_YANDEX = 'yandex';
93 + const BROWSER_VIVALDI = 'vivaldi';
94 + const BROWSER_MIUI = 'miui';
95 + const BROWSER_SILK = 'silk';
96 + const OTHER = 'other';
79 97
98 + const BROWSER_DISPLAY_NAME_MAP = array(
99 + self::BROWSER_CHROME => 'Chrome',
100 + self::BROWSER_FIREFOX => 'Firefox',
101 + self::BROWSER_SAFARI => 'Safari',
102 + self::BROWSER_EDGE => 'Edge',
103 + self::BROWSER_OPERA => 'Opera',
104 + self::BROWSER_IE => 'Internet Explorer',
105 + self::BROWSER_SAMSUNG => 'Samsung Browser',
106 + self::BROWSER_UC => 'UC Browser',
107 + self::BROWSER_YANDEX => 'Yandex Browser',
108 + self::BROWSER_VIVALDI => 'Vivaldi',
109 + self::BROWSER_MIUI => 'MIUI Browser',
110 + self::BROWSER_SILK => 'Amazon Silk',
111 + self::OTHER => 'Other',
112 + );
113 +
80 114 /**
81 115 * A list of dumb-phone user agent parts.
82 116 *
83 117 * @var array
@@ -139,9 +173,9 @@
139 173 public function __construct( $ua = '' ) {
140 174 if ( $ua ) {
141 175 $this->useragent = $ua;
142 176 } elseif ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
143 - $this->useragent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This class is all about validating.
177 + $this->useragent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This class is all about validating.
144 178 }
145 179 }
146 180
147 181 /**
@@ -229,9 +263,9 @@
229 263 * @return string|bool Name of the platform, false otherwise.
230 264 */
231 265 public function get_platform() {
232 266 if ( isset( $this->platform ) ) {
233 - return $this->platform;
267 + return $this->platform;
234 268 }
235 269
236 270 if ( empty( $this->useragent ) ) {
237 271 return false;
@@ -236,19 +270,20 @@
236 270 if ( empty( $this->useragent ) ) {
237 271 return false;
238 272 }
239 273
240 - if ( strpos( $this->useragent, 'windows phone' ) !== false ) {
241 - $this->platform = self::PLATFORM_WINDOWS;
242 - } elseif ( strpos( $this->useragent, 'windows ce' ) !== false ) {
274 + $ua = strtolower( $this->useragent );
275 + if ( strpos( $ua, 'windows phone' ) !== false ) {
243 276 $this->platform = self::PLATFORM_WINDOWS;
244 - } elseif ( strpos( $this->useragent, 'ipad' ) !== false ) {
277 + } elseif ( strpos( $ua, 'windows ce' ) !== false ) {
278 + $this->platform = self::PLATFORM_WINDOWS;
279 + } elseif ( strpos( $ua, 'ipad' ) !== false ) {
245 280 $this->platform = self::PLATFORM_IPAD;
246 - } elseif ( strpos( $this->useragent, 'ipod' ) !== false ) {
281 + } elseif ( strpos( $ua, 'ipod' ) !== false ) {
247 282 $this->platform = self::PLATFORM_IPOD;
248 - } elseif ( strpos( $this->useragent, 'iphone' ) !== false ) {
283 + } elseif ( strpos( $ua, 'iphone' ) !== false ) {
249 284 $this->platform = self::PLATFORM_IPHONE;
250 - } elseif ( strpos( $this->useragent, 'android' ) !== false ) {
285 + } elseif ( strpos( $ua, 'android' ) !== false ) {
251 286 if ( static::is_android_tablet() ) {
252 287 $this->platform = self::PLATFORM_ANDROID_TABLET;
253 288 } else {
254 289 $this->platform = self::PLATFORM_ANDROID;
@@ -256,9 +291,9 @@
256 291 } elseif ( static::is_kindle_fire() ) {
257 292 $this->platform = self::PLATFORM_ANDROID_TABLET;
258 293 } elseif ( static::is_blackberry_10() ) {
259 294 $this->platform = self::PLATFORM_BLACKBERRY_10;
260 - } elseif ( strpos( $this->useragent, 'blackberry' ) !== false ) {
295 + } elseif ( strpos( $ua, 'blackberry' ) !== false ) {
261 296 $this->platform = self::PLATFORM_BLACKBERRY;
262 297 } elseif ( static::is_blackberry_tablet() ) {
263 298 $this->platform = self::PLATFORM_BLACKBERRY;
264 299 } elseif ( static::is_symbian_platform() ) {
@@ -276,8 +311,85 @@
276 311 return $this->platform;
277 312 }
278 313
279 314 /**
315 + * Returns the platform for desktops
316 + *
317 + * @return string
318 + */
319 + public function get_desktop_platform() {
320 + $ua = $this->useragent;
321 + if ( empty( $ua ) ) {
322 + return false;
323 + }
324 + $platform = self::OTHER;
325 +
326 + if ( static::is_linux_desktop( $ua ) ) {
327 + $platform = self::PLATFORM_DESKTOP_LINUX;
328 + } elseif ( static::is_mac_desktop( $ua ) ) {
329 + $platform = self::PLATFORM_DESKTOP_MAC;
330 + } elseif ( static::is_windows_desktop( $ua ) ) {
331 + $platform = self::PLATFORM_DESKTOP_WINDOWS;
332 + } elseif ( static::is_chrome_desktop( $ua ) ) {
333 + $platform = self::PLATFORM_DESKTOP_CHROME;
334 + }
335 + return $platform;
336 + }
337 +
338 + /**
339 + * A simple pattern matching method for extracting the browser from the user agent.
340 + *
341 + * @return string
342 + */
343 + public function get_browser() {
344 + $ua = $this->useragent;
345 + if ( empty( $ua ) ) {
346 + return self::OTHER;
347 + }
348 +
349 + // Check for browsers based on Chromium BEFORE checking for Chrome itself,
350 + // as they all include "Chrome" in their user agent string.
351 + // Order matters - most specific checks first!
352 +
353 + if ( static::is_samsung_browser( $ua ) ) {
354 + return self::BROWSER_SAMSUNG;
355 + } elseif ( static::is_yandex_browser( $ua ) ) {
356 + return self::BROWSER_YANDEX;
357 + } elseif ( static::is_vivaldi_browser( $ua ) ) {
358 + return self::BROWSER_VIVALDI;
359 + } elseif ( static::is_uc_browser( $ua ) ) {
360 + return self::BROWSER_UC;
361 + } elseif ( static::is_miui_browser( $ua ) ) {
362 + return self::BROWSER_MIUI;
363 + } elseif ( static::is_silk_browser( $ua ) ) {
364 + return self::BROWSER_SILK;
365 + } elseif ( static::is_opera_mini( $ua ) || static::is_opera_mobile( $ua ) || static::is_opera_desktop( $ua ) || static::is_opera_mini_dumb( $ua ) ) {
366 + return self::BROWSER_OPERA;
367 + } elseif ( static::is_edge_browser( $ua ) ) {
368 + return self::BROWSER_EDGE;
369 + } elseif ( static::is_chrome_desktop( $ua ) || self::is_chrome_for_iOS( $ua ) ) {
370 + return self::BROWSER_CHROME;
371 + } elseif ( static::is_safari_browser( $ua ) ) {
372 + return self::BROWSER_SAFARI;
373 + } elseif ( static::is_firefox_mobile( $ua ) || static::is_firefox_desktop( $ua ) ) {
374 + return self::BROWSER_FIREFOX;
375 + } elseif ( static::is_ie_browser( $ua ) ) {
376 + return self::BROWSER_IE;
377 + }
378 + return self::OTHER;
379 + }
380 +
381 + /**
382 + * Get the display name of the browser.
383 + *
384 + * @return string
385 + */
386 + public function get_browser_display_name() {
387 + $browser = $this->get_browser();
388 + return self::BROWSER_DISPLAY_NAME_MAP[ $browser ] ?? $browser;
389 + }
390 +
391 + /**
280 392 * This method detects for UA which can display iPhone-optimized web content.
281 393 * Includes iPhone, iPod Touch, Android, WebOS, Fennec (Firefox mobile), etc.
282 394 */
283 395 public function isTierIphone() {
@@ -419,15 +531,16 @@
419 531 *
420 532 * @return bool
421 533 */
422 534 public function is_tablet() {
535 + $ua = $this->useragent;
423 536 return ( 0 // Never true, but makes it easier to manage our list of tablet conditions.
424 - || self::is_ipad()
425 - || self::is_android_tablet()
426 - || self::is_blackberry_tablet()
427 - || self::is_kindle_fire()
428 - || self::is_MaemoTablet()
429 - || self::is_TouchPad()
537 + || self::is_ipad( $ua )
538 + || self::is_android_tablet( $ua )
539 + || self::is_blackberry_tablet( $ua )
540 + || self::is_kindle_fire( $ua )
541 + || self::is_MaemoTablet( $ua )
542 + || self::is_TouchPad( $ua )
430 543 );
431 544 }
432 545
433 546 /**
@@ -452,8 +565,25 @@
452 565 }
453 566 }
454 567
455 568 /**
569 + * Retrieves the user agent from the server if not provided.
570 + *
571 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
572 + * @return string|false The user agent string or false if not available.
573 + */
574 + private static function maybe_get_user_agent_from_server( $user_agent = null ) {
575 + if ( null !== $user_agent ) {
576 + return $user_agent;
577 + }
578 +
579 + if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
580 + return false;
581 + }
582 + return wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
583 + }
584 +
585 + /**
456 586 * Detects if the current UA is iPhone Mobile Safari or another iPhone or iPod Touch Browser.
457 587 *
458 588 * They type can check for any iPhone, an iPhone using Safari, or an iPhone using something other than Safari.
459 589 *
@@ -460,16 +590,19 @@
460 590 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPhone browser),
461 591 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
462 592 * Otherwise those browsers will be 'catched' by the iphone string.
463 593 *
464 - * @param string $type Type of iPhone detection.
594 + * @param string $type Type of iPhone detection.
595 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
465 596 */
466 - public static function is_iphone_or_ipod( $type = 'iphone-any' ) {
467 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
597 + public static function is_iphone_or_ipod( $type = 'iphone-any', $user_agent = null ) {
598 +
599 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
600 + if ( empty( $user_agent ) ) {
468 601 return false;
469 602 }
470 603
471 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
604 + $ua = strtolower( wp_unslash( $user_agent ) );
472 605 $is_iphone = ( strpos( $ua, 'iphone' ) !== false ) || ( strpos( $ua, 'ipod' ) !== false );
473 606 $is_safari = ( false !== strpos( $ua, 'safari' ) );
474 607
475 608 if ( 'iphone-safari' === $type ) {
@@ -485,19 +618,22 @@
485 618 * Detects if the current UA is Chrome for iOS
486 619 *
487 620 * The User-Agent string in Chrome for iOS is the same as the Mobile Safari User-Agent, with CriOS/<ChromeRevision> instead of Version/<VersionNum>.
488 621 * - Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3
622 + *
623 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
489 624 */
490 - public static function is_chrome_for_iOS() {
491 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
625 + public static function is_chrome_for_iOS( $user_agent = null ) {
626 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
627 + if ( empty( $user_agent ) ) {
492 628 return false;
493 629 }
494 630
495 - if ( self::is_iphone_or_ipod( 'iphone-safari' ) === false ) {
631 + if ( self::is_iphone_or_ipod( 'iphone-safari', $user_agent ) === false ) {
496 632 return false;
497 633 }
498 634
499 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
635 + $ua = strtolower( wp_unslash( $user_agent ) );
500 636
501 637 if ( strpos( $ua, 'crios/' ) !== false ) {
502 638 return true;
503 639 } else {
@@ -509,15 +645,18 @@
509 645 * Detects if the current UA is Twitter for iPhone
510 646 *
511 647 * Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_5 like Mac OS X; nb-no) AppleWebKit/533.17.9 (KHTML, like Gecko) Mobile/8L1 Twitter for iPhone
512 648 * Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9B206 Twitter for iPhone
649 + *
650 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
513 651 */
514 - public static function is_twitter_for_iphone() {
515 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
652 + public static function is_twitter_for_iphone( $user_agent = null ) {
653 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
654 + if ( empty( $user_agent ) ) {
516 655 return false;
517 656 }
518 657
519 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
658 + $ua = strtolower( wp_unslash( $user_agent ) );
520 659
521 660 if ( strpos( $ua, 'ipad' ) !== false ) {
522 661 return false;
523 662 }
@@ -533,15 +672,18 @@
533 672 * Detects if the current UA is Twitter for iPad
534 673 *
535 674 * Old version 4.X - Mozilla/5.0 (iPad; U; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Mobile/8L1 Twitter for iPad
536 675 * Ver 5.0 or Higher - Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9B206 Twitter for iPhone
676 + *
677 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
537 678 */
538 - public static function is_twitter_for_ipad() {
539 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
679 + public static function is_twitter_for_ipad( $user_agent = null ) {
680 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
681 + if ( empty( $user_agent ) ) {
540 682 return false;
541 683 }
542 684
543 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
685 + $ua = strtolower( wp_unslash( $user_agent ) );
544 686
545 687 if ( strpos( $ua, 'twitter for ipad' ) !== false ) {
546 688 return true;
547 689 } elseif ( strpos( $ua, 'ipad' ) !== false && strpos( $ua, 'twitter for iphone' ) !== false ) {
@@ -555,15 +697,18 @@
555 697 * Detects if the current UA is Facebook for iPhone
556 698 * - Facebook 4020.0 (iPhone; iPhone OS 5.0.1; fr_FR)
557 699 * - Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_0 like Mac OS X; en_US) AppleWebKit (KHTML, like Gecko) Mobile [FBAN/FBForIPhone;FBAV/4.0.2;FBBV/4020.0;FBDV/iPhone3,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/5.0;FBSS/2; FBCR/O2;FBID/phone;FBLC/en_US;FBSF/2.0]
558 700 * - Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9B206 [FBAN/FBIOS;FBAV/5.0;FBBV/47423;FBDV/iPhone3,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/5.1.1;FBSS/2; FBCR/3ITA;FBID/phone;FBLC/en_US]
701 + *
702 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
559 703 */
560 - public static function is_facebook_for_iphone() {
561 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
704 + public static function is_facebook_for_iphone( $user_agent = null ) {
705 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
706 + if ( empty( $user_agent ) ) {
562 707 return false;
563 708 }
564 709
565 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
710 + $ua = strtolower( wp_unslash( $user_agent ) );
566 711
567 712 if ( false === strpos( $ua, 'iphone' ) ) {
568 713 return false;
569 714 }
@@ -583,15 +728,18 @@
583 728 * Detects if the current UA is Facebook for iPad
584 729 * - Facebook 4020.0 (iPad; iPhone OS 5.0.1; en_US)
585 730 * - Mozilla/5.0 (iPad; U; CPU iPhone OS 5_0 like Mac OS X; en_US) AppleWebKit (KHTML, like Gecko) Mobile [FBAN/FBForIPhone;FBAV/4.0.2;FBBV/4020.0;FBDV/iPad2,1;FBMD/iPad;FBSN/iPhone OS;FBSV/5.0;FBSS/1; FBCR/;FBID/tablet;FBLC/en_US;FBSF/1.0]
586 731 * - Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10A403 [FBAN/FBIOS;FBAV/5.0;FBBV/47423;FBDV/iPad2,1;FBMD/iPad;FBSN/iPhone OS;FBSV/6.0;FBSS/1; FBCR/;FBID/tablet;FBLC/en_US]
732 + *
733 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
587 734 */
588 - public static function is_facebook_for_ipad() {
589 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
735 + public static function is_facebook_for_ipad( $user_agent = null ) {
736 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
737 + if ( empty( $user_agent ) ) {
590 738 return false;
591 739 }
592 740
593 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
741 + $ua = strtolower( wp_unslash( $user_agent ) );
594 742
595 743 if ( false === strpos( $ua, 'ipad' ) ) {
596 744 return false;
597 745 }
@@ -604,15 +752,18 @@
604 752 }
605 753
606 754 /**
607 755 * Detects if the current UA is WordPress for iOS
756 + *
757 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
608 758 */
609 - public static function is_wordpress_for_ios() {
610 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
759 + public static function is_wordpress_for_ios( $user_agent = null ) {
760 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
761 + if ( empty( $user_agent ) ) {
611 762 return false;
612 763 }
613 764
614 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
765 + $ua = strtolower( wp_unslash( $user_agent ) );
615 766 if ( false !== strpos( $ua, 'wp-iphone' ) ) {
616 767 return true;
617 768 } else {
618 769 return false;
@@ -626,17 +777,18 @@
626 777 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPad browser),
627 778 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
628 779 * Otherwise those browsers will be 'catched' by the ipad string.
629 780 *
630 - * @param string $type iPad type.
781 + * @param string $type iPad type.
782 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
631 783 */
632 - public static function is_ipad( $type = 'ipad-any' ) {
633 -
634 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
784 + public static function is_ipad( $type = 'ipad-any', $user_agent = null ) {
785 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
786 + if ( empty( $user_agent ) ) {
635 787 return false;
636 788 }
637 789
638 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
790 + $ua = strtolower( wp_unslash( $user_agent ) );
639 791
640 792 $is_ipad = ( false !== strpos( $ua, 'ipad' ) );
641 793 $is_safari = ( false !== strpos( $ua, 'safari' ) );
642 794
@@ -654,16 +806,18 @@
654 806 *
655 807 * See http://www.useragentstring.com/pages/Fennec/
656 808 * Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.1.1) Gecko/20110415 Firefox/4.0.2pre Fennec/4.0.1
657 809 * Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1b2pre) Gecko/20081015 Fennec/1.0a1
810 + *
811 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
658 812 */
659 - public static function is_firefox_mobile() {
660 -
661 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
813 + public static function is_firefox_mobile( $user_agent = null ) {
814 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
815 + if ( empty( $user_agent ) ) {
662 816 return false;
663 817 }
664 818
665 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
819 + $ua = strtolower( wp_unslash( $user_agent ) );
666 820
667 821 if ( strpos( $ua, 'fennec' ) !== false ) {
668 822 return true;
669 823 } else {
@@ -676,16 +830,18 @@
676 830 *
677 831 * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
678 832 * Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion
679 833 * The platform section will include 'Mobile' for phones and 'Tablet' for tablets.
834 + *
835 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
680 836 */
681 - public static function is_firefox_desktop() {
682 -
683 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
837 + public static function is_firefox_desktop( $user_agent = null ) {
838 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
839 + if ( empty( $user_agent ) ) {
684 840 return false;
685 841 }
686 842
687 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
843 + $ua = strtolower( wp_unslash( $user_agent ) );
688 844
689 845 if ( false !== strpos( $ua, 'firefox' ) && false === strpos( $ua, 'mobile' ) && false === strpos( $ua, 'tablet' ) ) {
690 846 return true;
691 847 } else {
@@ -696,16 +852,18 @@
696 852 /**
697 853 * Detects if the current browser is FirefoxOS Native browser
698 854 *
699 855 * Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0
856 + *
857 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
700 858 */
701 - public static function is_firefox_os() {
702 -
703 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
859 + public static function is_firefox_os( $user_agent = null ) {
860 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
861 + if ( empty( $user_agent ) ) {
704 862 return false;
705 863 }
706 864
707 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
865 + $ua = strtolower( wp_unslash( $user_agent ) );
708 866
709 867 if ( strpos( $ua, 'mozilla' ) !== false && strpos( $ua, 'mobile' ) !== false && strpos( $ua, 'gecko' ) !== false && strpos( $ua, 'firefox' ) !== false ) {
710 868 return true;
711 869 } else {
@@ -713,20 +871,75 @@
713 871 }
714 872 }
715 873
716 874 /**
875 + * Detect Safari browser
876 + *
877 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
878 + */
879 + public static function is_safari_browser( $user_agent = null ) {
880 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
881 + if ( empty( $user_agent ) ) {
882 + return false;
883 + }
884 +
885 + if ( false === strpos( wp_unslash( $user_agent ), 'Safari' ) ) {
886 + return false;
887 + }
888 + return true;
889 + }
890 +
891 + /**
892 + * Detect Edge browser
893 + *
894 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
895 + */
896 + public static function is_edge_browser( $user_agent = null ) {
897 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
898 + if ( empty( $user_agent ) ) {
899 + return false;
900 + }
901 +
902 + $ua = wp_unslash( $user_agent );
903 +
904 + // Check for both legacy Edge ("Edge/") and modern Chromium-based Edge ("Edg/")
905 + if ( false === strpos( $ua, 'Edge' ) && false === strpos( $ua, 'Edg/' ) ) {
906 + return false;
907 + }
908 + return true;
909 + }
910 +
911 + /**
912 + * Detect IE browser
913 + *
914 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
915 + */
916 + public static function is_ie_browser( $user_agent = null ) {
917 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
918 + if ( empty( $user_agent ) ) {
919 + return false;
920 + }
921 +
922 + $ua = wp_unslash( $user_agent );
923 + return strpos( $ua, 'MSIE' ) !== false || strpos( $ua, 'Trident/7' ) !== false;
924 + }
925 +
926 + /**
717 927 * Detect modern Opera desktop
718 928 *
719 929 * Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 OPR/74.0.3911.203
720 930 *
721 931 * Looking for "OPR/" specifically.
932 + *
933 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
722 934 */
723 - public static function is_opera_desktop() {
724 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
935 + public static function is_opera_desktop( $user_agent = null ) {
936 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
937 + if ( empty( $user_agent ) ) {
725 938 return false;
726 939 }
727 940
728 - if ( false === strpos( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), 'OPR/' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
941 + if ( false === strpos( wp_unslash( $user_agent ), 'OPR/' ) ) {
729 942 return false;
730 943 }
731 944
732 945 return true;
@@ -742,15 +955,18 @@
742 955 * The rendering engine is on Opera's server.)
743 956 *
744 957 * Opera/9.80 (Windows NT 6.1; Opera Mobi/14316; U; en) Presto/2.7.81 Version/11.00"
745 958 * Opera/9.50 (Nintendo DSi; Opera/507; U; en-US)
959 + *
960 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
746 961 */
747 - public static function is_opera_mobile() {
748 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
962 + public static function is_opera_mobile( $user_agent = null ) {
963 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
964 + if ( empty( $user_agent ) ) {
749 965 return false;
750 966 }
751 967
752 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
968 + $ua = strtolower( wp_unslash( $user_agent ) );
753 969
754 970 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mobi' ) !== false ) {
755 971 return true;
756 972 } elseif ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'nintendo dsi' ) !== false ) {
@@ -769,15 +985,18 @@
769 985 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
770 986 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
771 987 * Opera/9.80 (Series 60; Opera Mini/5.1.22783/23.334; U; en) Presto/2.5.25 Version/10.54
772 988 * Opera/9.80 (BlackBerry; Opera Mini/5.1.22303/22.387; U; en) Presto/2.5.25 Version/10.54
989 + *
990 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
773 991 */
774 - public static function is_opera_mini() {
775 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
992 + public static function is_opera_mini( $user_agent = null ) {
993 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
994 + if ( empty( $user_agent ) ) {
776 995 return false;
777 996 }
778 997
779 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
998 + $ua = strtolower( wp_unslash( $user_agent ) );
780 999
781 1000 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mini' ) !== false ) {
782 1001 return true;
783 1002 } else {
@@ -787,16 +1006,20 @@
787 1006
788 1007 /**
789 1008 * Detects if the current browser is Opera Mini, but not on a smart device OS(Android, iOS, etc)
790 1009 * Used to send users on dumb devices to m.wor
1010 + *
1011 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
791 1012 */
792 - public static function is_opera_mini_dumb() {
793 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1013 + public static function is_opera_mini_dumb( $user_agent = null ) {
1014 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1015 + if ( empty( $user_agent ) ) {
794 1016 return false;
795 1017 }
796 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
797 1018
798 - if ( self::is_opera_mini() ) {
1019 + $ua = strtolower( wp_unslash( $user_agent ) );
1020 +
1021 + if ( self::is_opera_mini( $user_agent ) ) {
799 1022 if ( strpos( $ua, 'android' ) !== false || strpos( $ua, 'iphone' ) !== false || strpos( $ua, 'ipod' ) !== false
800 1023 || strpos( $ua, 'ipad' ) !== false || strpos( $ua, 'blackberry' ) !== false ) {
801 1024 return false;
802 1025 } else {
@@ -807,21 +1030,132 @@
807 1030 }
808 1031 }
809 1032
810 1033 /**
1034 + * Detects if the current browser is Samsung Internet for Android.
1035 + *
1036 + * Samsung Internet is the default browser on Samsung devices.
1037 + * User agent contains: SamsungBrowser
1038 + *
1039 + * @param string|null $user_agent Optional user agent string.
1040 + * @return bool
1041 + */
1042 + public static function is_samsung_browser( $user_agent = null ) {
1043 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1044 + if ( empty( $user_agent ) ) {
1045 + return false;
1046 + }
1047 +
1048 + return false !== stripos( $user_agent, 'SamsungBrowser' );
1049 + }
1050 +
1051 + /**
1052 + * Detects if the current browser is UC Browser.
1053 + *
1054 + * UC Browser is popular in Asia and emerging markets.
1055 + * User agent contains: UCBrowser or UCWEB
1056 + *
1057 + * @param string|null $user_agent Optional user agent string.
1058 + * @return bool
1059 + */
1060 + public static function is_uc_browser( $user_agent = null ) {
1061 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1062 + if ( empty( $user_agent ) ) {
1063 + return false;
1064 + }
1065 +
1066 + return false !== stripos( $user_agent, 'UCBrowser' ) || false !== stripos( $user_agent, 'UCWEB' );
1067 + }
1068 +
1069 + /**
1070 + * Detects if the current browser is Yandex Browser.
1071 + *
1072 + * Yandex Browser is popular in Russia and CIS countries.
1073 + * User agent contains: YaBrowser
1074 + *
1075 + * @param string|null $user_agent Optional user agent string.
1076 + * @return bool
1077 + */
1078 + public static function is_yandex_browser( $user_agent = null ) {
1079 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1080 + if ( empty( $user_agent ) ) {
1081 + return false;
1082 + }
1083 +
1084 + return false !== stripos( $user_agent, 'YaBrowser' );
1085 + }
1086 +
1087 + /**
1088 + * Detects if the current browser is Vivaldi.
1089 + *
1090 + * Vivaldi is a feature-rich browser for power users.
1091 + * User agent contains: Vivaldi
1092 + *
1093 + * @param string|null $user_agent Optional user agent string.
1094 + * @return bool
1095 + */
1096 + public static function is_vivaldi_browser( $user_agent = null ) {
1097 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1098 + if ( empty( $user_agent ) ) {
1099 + return false;
1100 + }
1101 +
1102 + return false !== stripos( $user_agent, 'Vivaldi' );
1103 + }
1104 +
1105 + /**
1106 + * Detects if the current browser is MIUI Browser.
1107 + *
1108 + * MIUI Browser is the default browser on Xiaomi devices.
1109 + * User agent contains: MiuiBrowser or XiaoMi
1110 + *
1111 + * @param string|null $user_agent Optional user agent string.
1112 + * @return bool
1113 + */
1114 + public static function is_miui_browser( $user_agent = null ) {
1115 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1116 + if ( empty( $user_agent ) ) {
1117 + return false;
1118 + }
1119 +
1120 + return false !== stripos( $user_agent, 'MiuiBrowser' ) || false !== stripos( $user_agent, 'XiaoMi' );
1121 + }
1122 +
1123 + /**
1124 + * Detects if the current browser is Amazon Silk.
1125 + *
1126 + * Amazon Silk is the browser on Kindle Fire and Echo devices.
1127 + * User agent contains: Silk or Silk-Accelerated
1128 + *
1129 + * @param string|null $user_agent Optional user agent string.
1130 + * @return bool
1131 + */
1132 + public static function is_silk_browser( $user_agent = null ) {
1133 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1134 + if ( empty( $user_agent ) ) {
1135 + return false;
1136 + }
1137 +
1138 + return false !== stripos( $user_agent, 'Silk' );
1139 + }
1140 +
1141 + /**
811 1142 * Detects if the current browser is a Windows Phone 7 device.
812 1143 * ex: Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; LG; GW910)
1144 + *
1145 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
813 1146 */
814 - public static function is_WindowsPhone7() {
815 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1147 + public static function is_WindowsPhone7( $user_agent = null ) {
1148 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1149 + if ( empty( $user_agent ) ) {
816 1150 return false;
817 1151 }
818 1152
819 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1153 + $ua = strtolower( wp_unslash( $user_agent ) );
820 1154
821 1155 if ( false === strpos( $ua, 'windows phone os 7' ) ) {
822 1156 return false;
823 - } elseif ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1157 + } elseif ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
824 1158 return false;
825 1159 } else {
826 1160 return true;
827 1161 }
@@ -829,15 +1163,18 @@
829 1163
830 1164 /**
831 1165 * Detects if the current browser is a Windows Phone 8 device.
832 1166 * ex: Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; ARM; Touch; IEMobile/10.0; <Manufacturer>; <Device> [;<Operator>])
1167 + *
1168 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
833 1169 */
834 - public static function is_windows_phone_8() {
835 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1170 + public static function is_windows_phone_8( $user_agent = null ) {
1171 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1172 + if ( empty( $user_agent ) ) {
836 1173 return false;
837 1174 }
838 1175
839 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1176 + $ua = strtolower( wp_unslash( $user_agent ) );
840 1177 if ( strpos( $ua, 'windows phone 8' ) === false ) {
841 1178 return false;
842 1179 } else {
843 1180 return true;
@@ -848,19 +1185,22 @@
848 1185 * Detects if the current browser is on a Palm device running the new WebOS. This EXCLUDES TouchPad.
849 1186 *
850 1187 * Ex1: Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.1
851 1188 * Ex2: Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pixi/1.1
1189 + *
1190 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
852 1191 */
853 - public static function is_PalmWebOS() {
854 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1192 + public static function is_PalmWebOS( $user_agent = null ) {
1193 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1194 + if ( empty( $user_agent ) ) {
855 1195 return false;
856 1196 }
857 1197
858 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1198 + $ua = strtolower( wp_unslash( $user_agent ) );
859 1199
860 1200 if ( false === strpos( $ua, 'webos' ) ) {
861 1201 return false;
862 - } elseif ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1202 + } elseif ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
863 1203 return false;
864 1204 } else {
865 1205 return true;
866 1206 }
@@ -870,17 +1210,20 @@
870 1210 * Detects if the current browser is the HP TouchPad default browser. This excludes phones wt WebOS.
871 1211 *
872 1212 * TouchPad Emulator: Mozilla/5.0 (hp-desktop; Linux; hpwOS/2.0; U; it-IT) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 Desktop/1.0
873 1213 * TouchPad: Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0
1214 + *
1215 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
874 1216 */
875 - public static function is_TouchPad() {
876 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1217 + public static function is_TouchPad( $user_agent = null ) {
1218 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1219 + if ( empty( $user_agent ) ) {
877 1220 return false;
878 1221 }
879 1222
880 - $http_user_agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1223 + $http_user_agent = strtolower( wp_unslash( $user_agent ) );
881 1224 if ( false !== strpos( $http_user_agent, 'hp-tablet' ) || false !== strpos( $http_user_agent, 'hpwos' ) || false !== strpos( $http_user_agent, 'touchpad' ) ) {
882 - if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1225 + if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
883 1226 return false;
884 1227 } else {
885 1228 return true;
886 1229 }
@@ -896,17 +1239,19 @@
896 1239 *
897 1240 * 7.0 Browser (Nokia 5800 XpressMusic (v21.0.025)) : Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Nokia5800d-1/21.0.025; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413
898 1241 *
899 1242 * Browser 7.1 (Nokia N97 (v12.0.024)) : Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/12.0.024; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.12344
1243 + *
1244 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
900 1245 */
901 - public static function is_S60_OSSBrowser() {
902 -
903 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1246 + public static function is_S60_OSSBrowser( $user_agent = null ) {
1247 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1248 + if ( empty( $user_agent ) ) {
904 1249 return false;
905 1250 }
906 1251
907 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
908 - if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1252 + $agent = strtolower( wp_unslash( $user_agent ) );
1253 + if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
909 1254 return false;
910 1255 }
911 1256
912 1257 $pos_webkit = strpos( $agent, 'webkit' );
@@ -927,16 +1272,18 @@
927 1272 }
928 1273
929 1274 /**
930 1275 * Detects if the device platform is the Symbian Series 60.
1276 + *
1277 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
931 1278 */
932 - public static function is_symbian_platform() {
933 -
934 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1279 + public static function is_symbian_platform( $user_agent = null ) {
1280 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1281 + if ( empty( $user_agent ) ) {
935 1282 return false;
936 1283 }
937 1284
938 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1285 + $agent = strtolower( wp_unslash( $user_agent ) );
939 1286
940 1287 $pos_webkit = strpos( $agent, 'webkit' );
941 1288 if ( false !== $pos_webkit ) {
942 1289 // First, test for WebKit, then make sure it's either Symbian or S60.
@@ -961,16 +1308,18 @@
961 1308 /**
962 1309 * Detects if the device platform is the Symbian Series 40.
963 1310 * Nokia Browser for Series 40 is a proxy based browser, previously known as Ovi Browser.
964 1311 * This browser will report 'NokiaBrowser' in the header, however some older version will also report 'OviBrowser'.
1312 + *
1313 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
965 1314 */
966 - public static function is_symbian_s40_platform() {
967 -
968 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1315 + public static function is_symbian_s40_platform( $user_agent = null ) {
1316 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1317 + if ( empty( $user_agent ) ) {
969 1318 return false;
970 1319 }
971 1320
972 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1321 + $agent = strtolower( wp_unslash( $user_agent ) );
973 1322
974 1323 if ( strpos( $agent, 'series40' ) !== false ) {
975 1324 if ( strpos( $agent, 'nokia' ) !== false || strpos( $agent, 'ovibrowser' ) !== false || strpos( $agent, 'nokiabrowser' ) !== false ) {
976 1325 return true;
@@ -982,17 +1331,19 @@
982 1331
983 1332 /**
984 1333 * Returns if the device belongs to J2ME capable family.
985 1334 *
1335 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1336 + *
986 1337 * @return bool
987 1338 */
988 - public static function is_J2ME_platform() {
989 -
990 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1339 + public static function is_J2ME_platform( $user_agent = null ) {
1340 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1341 + if ( empty( $user_agent ) ) {
991 1342 return false;
992 1343 }
993 1344
994 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1345 + $agent = strtolower( wp_unslash( $user_agent ) );
995 1346
996 1347 if ( strpos( $agent, 'j2me/midp' ) !== false ) {
997 1348 return true;
998 1349 } elseif ( strpos( $agent, 'midp' ) !== false && strpos( $agent, 'cldc' ) ) {
@@ -1002,16 +1353,18 @@
1002 1353 }
1003 1354
1004 1355 /**
1005 1356 * Detects if the current UA is on one of the Maemo-based Nokia Internet Tablets.
1357 + *
1358 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1006 1359 */
1007 - public static function is_MaemoTablet() {
1008 -
1009 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1360 + public static function is_MaemoTablet( $user_agent = null ) {
1361 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1362 + if ( empty( $user_agent ) ) {
1010 1363 return false;
1011 1364 }
1012 1365
1013 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1366 + $agent = strtolower( wp_unslash( $user_agent ) );
1014 1367
1015 1368 $pos_maemo = strpos( $agent, 'maemo' );
1016 1369 if ( false === $pos_maemo ) {
1017 1370 return false;
@@ -1018,9 +1371,9 @@
1018 1371 }
1019 1372
1020 1373 // Must be Linux + Tablet, or else it could be something else.
1021 1374 if ( strpos( $agent, 'tablet' ) !== false && strpos( $agent, 'linux' ) !== false ) {
1022 - if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1375 + if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1023 1376 return false;
1024 1377 } else {
1025 1378 return true;
1026 1379 }
@@ -1030,20 +1383,22 @@
1030 1383 }
1031 1384
1032 1385 /**
1033 1386 * Detects if the current UA is a MeeGo device (Nokia Smartphone).
1387 + *
1388 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1034 1389 */
1035 - public static function is_MeeGo() {
1036 -
1037 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1390 + public static function is_MeeGo( $user_agent = null ) {
1391 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1392 + if ( empty( $user_agent ) ) {
1038 1393 return false;
1039 1394 }
1040 1395
1041 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1396 + $ua = strtolower( wp_unslash( $user_agent ) );
1042 1397
1043 1398 if ( false === strpos( $ua, 'meego' ) ) {
1044 1399 return false;
1045 - } elseif ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1400 + } elseif ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1046 1401 return false;
1047 1402 } else {
1048 1403 return true;
1049 1404 }
@@ -1050,16 +1405,18 @@
1050 1405 }
1051 1406
1052 1407 /**
1053 1408 * The is_webkit() method can be used to check the User Agent for an webkit generic browser.
1409 + *
1410 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1054 1411 */
1055 - public static function is_webkit() {
1056 -
1057 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1412 + public static function is_webkit( $user_agent = null ) {
1413 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1414 + if ( empty( $user_agent ) ) {
1058 1415 return false;
1059 1416 }
1060 1417
1061 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1418 + $agent = strtolower( wp_unslash( $user_agent ) );
1062 1419
1063 1420 $pos_webkit = strpos( $agent, 'webkit' );
1064 1421
1065 1422 if ( false !== $pos_webkit ) {
@@ -1071,19 +1428,22 @@
1071 1428
1072 1429 /**
1073 1430 * Detects if the current browser is the Native Android browser.
1074 1431 *
1432 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1433 + *
1075 1434 * @return boolean true if the browser is Android otherwise false
1076 1435 */
1077 - public static function is_android() {
1078 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1436 + public static function is_android( $user_agent = null ) {
1437 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1438 + if ( empty( $user_agent ) ) {
1079 1439 return false;
1080 1440 }
1081 1441
1082 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1442 + $agent = strtolower( wp_unslash( $user_agent ) );
1083 1443 $pos_android = strpos( $agent, 'android' );
1084 1444 if ( false !== $pos_android ) {
1085 - if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1445 + if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1086 1446 return false;
1087 1447 } else {
1088 1448 return true;
1089 1449 }
@@ -1095,16 +1455,19 @@
1095 1455 /**
1096 1456 * Detects if the current browser is the Native Android Tablet browser.
1097 1457 * Assumes 'Android' should be in the user agent, but not 'mobile'
1098 1458 *
1459 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1460 + *
1099 1461 * @return boolean true if the browser is Android and not 'mobile' otherwise false
1100 1462 */
1101 - public static function is_android_tablet() {
1102 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1463 + public static function is_android_tablet( $user_agent = null ) {
1464 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1465 + if ( empty( $user_agent ) ) {
1103 1466 return false;
1104 1467 }
1105 1468
1106 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1469 + $agent = strtolower( wp_unslash( $user_agent ) );
1107 1470
1108 1471 $pos_android = strpos( $agent, 'android' );
1109 1472 $pos_mobile = strpos( $agent, 'mobile' );
1110 1473 $post_android_app = strpos( $agent, 'wp-android' );
@@ -1109,9 +1472,9 @@
1109 1472 $pos_mobile = strpos( $agent, 'mobile' );
1110 1473 $post_android_app = strpos( $agent, 'wp-android' );
1111 1474
1112 1475 if ( false !== $pos_android && false === $pos_mobile && false === $post_android_app ) {
1113 - if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1476 + if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1114 1477 return false;
1115 1478 } else {
1116 1479 return true;
1117 1480 }
@@ -1125,16 +1488,19 @@
1125 1488 *
1126 1489 * Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-84) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true
1127 1490 * Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-84) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=false
1128 1491 *
1492 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1493 + *
1129 1494 * @return boolean true if the browser is Kindle Fire Native browser otherwise false
1130 1495 */
1131 - public static function is_kindle_fire() {
1132 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1496 + public static function is_kindle_fire( $user_agent = null ) {
1497 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1498 + if ( empty( $user_agent ) ) {
1133 1499 return false;
1134 1500 }
1135 1501
1136 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1502 + $agent = strtolower( wp_unslash( $user_agent ) );
1137 1503 $pos_silk = strpos( $agent, 'silk/' );
1138 1504 $pos_silk_acc = strpos( $agent, 'silk-accelerated=' );
1139 1505 if ( false !== $pos_silk && false !== $pos_silk_acc ) {
1140 1506 return true;
@@ -1147,17 +1513,21 @@
1147 1513 * Detects if the current browser is the Kindle Touch Native browser
1148 1514 *
1149 1515 * Mozilla/5.0 (X11; U; Linux armv7l like Android; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/533.2+ Kindle/3.0+
1150 1516 *
1517 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1518 + *
1151 1519 * @return boolean true if the browser is Kindle monochrome Native browser otherwise false
1152 1520 */
1153 - public static function is_kindle_touch() {
1154 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1521 + public static function is_kindle_touch( $user_agent = null ) {
1522 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1523 + if ( empty( $user_agent ) ) {
1155 1524 return false;
1156 1525 }
1157 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1526 +
1527 + $agent = strtolower( wp_unslash( $user_agent ) );
1158 1528 $pos_kindle_touch = strpos( $agent, 'kindle/3.0+' );
1159 - if ( false !== $pos_kindle_touch && false === self::is_kindle_fire() ) {
1529 + if ( false !== $pos_kindle_touch && false === self::is_kindle_fire( $user_agent ) ) {
1160 1530 return true;
1161 1531 } else {
1162 1532 return false;
1163 1533 }
@@ -1164,15 +1534,18 @@
1164 1534 }
1165 1535
1166 1536 /**
1167 1537 * Detect if user agent is the WordPress.com Windows 8 app (used ONLY on the custom oauth stylesheet)
1538 + *
1539 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1168 1540 */
1169 - public static function is_windows8_auth() {
1170 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1541 + public static function is_windows8_auth( $user_agent = null ) {
1542 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1543 + if ( empty( $user_agent ) ) {
1171 1544 return false;
1172 1545 }
1173 1546
1174 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1547 + $agent = strtolower( wp_unslash( $user_agent ) );
1175 1548 $pos = strpos( $agent, 'msauthhost' );
1176 1549 if ( false !== $pos ) {
1177 1550 return true;
1178 1551 } else {
@@ -1181,15 +1554,18 @@
1181 1554 }
1182 1555
1183 1556 /**
1184 1557 * Detect if user agent is the WordPress.com Windows 8 app.
1558 + *
1559 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1185 1560 */
1186 - public static function is_wordpress_for_win8() {
1187 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1561 + public static function is_wordpress_for_win8( $user_agent = null ) {
1562 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1563 + if ( empty( $user_agent ) ) {
1188 1564 return false;
1189 1565 }
1190 1566
1191 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1567 + $agent = strtolower( wp_unslash( $user_agent ) );
1192 1568 $pos = strpos( $agent, 'wp-windows8' );
1193 1569 if ( false !== $pos ) {
1194 1570 return true;
1195 1571 } else {
@@ -1198,15 +1574,18 @@
1198 1574 }
1199 1575
1200 1576 /**
1201 1577 * Detect if user agent is the WordPress.com Desktop app.
1578 + *
1579 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1202 1580 */
1203 - public static function is_wordpress_desktop_app() {
1204 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1581 + public static function is_wordpress_desktop_app( $user_agent = null ) {
1582 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1583 + if ( empty( $user_agent ) ) {
1205 1584 return false;
1206 1585 }
1207 1586
1208 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1587 + $agent = strtolower( wp_unslash( $user_agent ) );
1209 1588 $pos = strpos( $agent, 'WordPressDesktop' );
1210 1589 if ( false !== $pos ) {
1211 1590 return true;
1212 1591 } else {
@@ -1217,16 +1596,18 @@
1217 1596 /**
1218 1597 * The is_blackberry_tablet() method can be used to check the User Agent for a RIM blackberry tablet.
1219 1598 * The user agent of the BlackBerry® Tablet OS follows a format similar to the following:
1220 1599 * Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+
1600 + *
1601 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1221 1602 */
1222 - public static function is_blackberry_tablet() {
1223 -
1224 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1603 + public static function is_blackberry_tablet( $user_agent = null ) {
1604 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1605 + if ( empty( $user_agent ) ) {
1225 1606 return false;
1226 1607 }
1227 1608
1228 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1609 + $agent = strtolower( wp_unslash( $user_agent ) );
1229 1610 $pos_playbook = stripos( $agent, 'PlayBook' );
1230 1611 $pos_rim_tablet = stripos( $agent, 'RIM Tablet' );
1231 1612
1232 1613 if ( ( false === $pos_playbook ) || ( false === $pos_rim_tablet ) ) {
@@ -1238,19 +1619,22 @@
1238 1619
1239 1620 /**
1240 1621 * The is_blackbeberry() method can be used to check the User Agent for a blackberry device.
1241 1622 * Note that opera mini on BB matches this rule.
1623 + *
1624 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1242 1625 */
1243 - public static function is_blackbeberry() {
1244 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1626 + public static function is_blackbeberry( $user_agent = null ) {
1627 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1628 + if ( empty( $user_agent ) ) {
1245 1629 return false;
1246 1630 }
1247 1631
1248 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1632 + $agent = strtolower( wp_unslash( $user_agent ) );
1249 1633
1250 1634 $pos_blackberry = strpos( $agent, 'blackberry' );
1251 1635 if ( false !== $pos_blackberry ) {
1252 - if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
1636 + if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1253 1637 return false;
1254 1638 } else {
1255 1639 return true;
1256 1640 }
@@ -1260,18 +1644,98 @@
1260 1644 }
1261 1645
1262 1646 /**
1263 1647 * The is_blackberry_10() method can be used to check the User Agent for a BlackBerry 10 device.
1648 + *
1649 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1264 1650 */
1265 - public static function is_blackberry_10() {
1266 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1651 + public static function is_blackberry_10( $user_agent = null ) {
1652 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1653 + if ( empty( $user_agent ) ) {
1267 1654 return false;
1268 1655 }
1269 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1656 +
1657 + $agent = strtolower( wp_unslash( $user_agent ) );
1270 1658 return ( strpos( $agent, 'bb10' ) !== false ) && ( strpos( $agent, 'mobile' ) !== false );
1271 1659 }
1272 1660
1273 1661 /**
1662 + * Determines whether a desktop platform is Linux OS
1663 + *
1664 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1665 + *
1666 + * @return bool
1667 + */
1668 + public static function is_linux_desktop( $user_agent = null ) {
1669 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1670 + if ( empty( $user_agent ) ) {
1671 + return false;
1672 + }
1673 +
1674 + if ( ! preg_match( '/linux/i', wp_unslash( $user_agent ) ) ) {
1675 + return false;
1676 + }
1677 + return true;
1678 + }
1679 +
1680 + /**
1681 + * Determines whether a desktop platform is Mac OS
1682 + *
1683 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1684 + *
1685 + * @return bool
1686 + */
1687 + public static function is_mac_desktop( $user_agent = null ) {
1688 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1689 + if ( empty( $user_agent ) ) {
1690 + return false;
1691 + }
1692 +
1693 + if ( ! preg_match( '/macintosh|mac os x/i', wp_unslash( $user_agent ) ) ) {
1694 + return false;
1695 + }
1696 + return true;
1697 + }
1698 +
1699 + /**
1700 + * Determines whether a desktop platform is Windows OS
1701 + *
1702 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1703 + *
1704 + * @return bool
1705 + */
1706 + public static function is_windows_desktop( $user_agent = null ) {
1707 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1708 + if ( empty( $user_agent ) ) {
1709 + return false;
1710 + }
1711 +
1712 + if ( ! preg_match( '/windows|win32/i', wp_unslash( $user_agent ) ) ) {
1713 + return false;
1714 + }
1715 + return true;
1716 + }
1717 +
1718 + /**
1719 + * Determines whether a desktop platform is Chrome OS
1720 + *
1721 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1722 + *
1723 + * @return bool
1724 + */
1725 + public static function is_chrome_desktop( $user_agent = null ) {
1726 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1727 + if ( empty( $user_agent ) ) {
1728 + return false;
1729 + }
1730 +
1731 + if ( ! preg_match( '/chrome/i', wp_unslash( $user_agent ) ) ) {
1732 + return false;
1733 + }
1734 + return true;
1735 + }
1736 +
1737 + /**
1274 1738 * Retrieve the blackberry OS version.
1275 1739 *
1276 1740 * Return strings are from the following list:
1277 1741 * - blackberry-10
@@ -1282,22 +1746,24 @@
1282 1746 * - blackberry-4.7
1283 1747 * - blackberry-4.6
1284 1748 * - blackberry-4.5
1285 1749 *
1750 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1751 + *
1286 1752 * @return string Version of the BB OS.
1287 1753 * If version is not found, get_blackbeberry_OS_version will return boolean false.
1288 1754 */
1289 - public static function get_blackbeberry_OS_version() {
1290 -
1291 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1755 + public static function get_blackbeberry_OS_version( $user_agent = null ) {
1756 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1757 + if ( empty( $user_agent ) ) {
1292 1758 return false;
1293 1759 }
1294 1760
1295 - if ( self::is_blackberry_10() ) {
1761 + if ( self::is_blackberry_10( $user_agent ) ) {
1296 1762 return 'blackberry-10';
1297 1763 }
1298 1764
1299 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1765 + $agent = strtolower( wp_unslash( $user_agent ) );
1300 1766
1301 1767 $pos_blackberry = stripos( $agent, 'blackberry' );
1302 1768 if ( false === $pos_blackberry ) {
1303 1769 // Not a blackberry device.
@@ -1316,9 +1782,9 @@
1316 1782 return 'blackberry-torch'; // Match the torch first edition. the 2nd edition should use the OS7 and doesn't need any special rule.
1317 1783 } elseif ( preg_match( '#Version\/([\d\.]+)#i', $agent, $matches ) ) { // Detecting the BB OS version for devices running OS 6.0 or higher.
1318 1784 $version = $matches[1];
1319 1785 $version_num = explode( '.', $version );
1320 - if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
1786 + if ( count( $version_num ) <= 1 ) {
1321 1787 return 'blackberry-6'; // not a BB device that match our rule.
1322 1788 } else {
1323 1789 return 'blackberry-' . $version_num[0];
1324 1790 }
@@ -1337,9 +1803,9 @@
1337 1803 }
1338 1804
1339 1805 $version_num = explode( '.', $version );
1340 1806
1341 - if ( is_array( $version_num ) === false || count( $version_num ) <= 1 ) {
1807 + if ( count( $version_num ) <= 1 ) {
1342 1808 return false;
1343 1809 }
1344 1810
1345 1811 $version_num_major = (int) $version_num[0];
@@ -1367,20 +1833,22 @@
1367 1833 * - blackberry-5
1368 1834 * - blackberry-4.7
1369 1835 * - blackberry-4.6
1370 1836 *
1837 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1838 + *
1371 1839 * @return string Type of the BB browser.
1372 1840 * If browser's version is not found, detect_blackbeberry_browser_version will return boolean false.
1373 1841 */
1374 - public static function detect_blackberry_browser_version() {
1375 -
1376 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1842 + public static function detect_blackberry_browser_version( $user_agent = null ) {
1843 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1844 + if ( empty( $user_agent ) ) {
1377 1845 return false;
1378 1846 }
1379 1847
1380 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1848 + $agent = strtolower( wp_unslash( $user_agent ) );
1381 1849
1382 - if ( self::is_blackberry_10() ) {
1850 + if ( self::is_blackberry_10( $user_agent ) ) {
1383 1851 return 'blackberry-10';
1384 1852 }
1385 1853
1386 1854 $pos_blackberry = strpos( $agent, 'blackberry' );
@@ -1399,9 +1867,9 @@
1399 1867 }
1400 1868
1401 1869 $version_num = explode( '.', $matches[1] );
1402 1870
1403 - if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
1871 + if ( count( $version_num ) <= 1 ) {
1404 1872 return false;
1405 1873 }
1406 1874
1407 1875 $version_num_major = (int) $version_num[0];
@@ -1422,17 +1890,19 @@
1422 1890
1423 1891 /**
1424 1892 * Checks if a visitor is coming from one of the WordPress mobile apps.
1425 1893 *
1894 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1895 + *
1426 1896 * @return bool
1427 1897 */
1428 - public static function is_mobile_app() {
1429 -
1430 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1898 + public static function is_mobile_app( $user_agent = null ) {
1899 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1900 + if ( empty( $user_agent ) ) {
1431 1901 return false;
1432 1902 }
1433 1903
1434 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1904 + $agent = strtolower( wp_unslash( $user_agent ) );
1435 1905
1436 1906 if ( isset( $_SERVER['X_USER_AGENT'] ) && preg_match( '|wp-webos|', $_SERVER['X_USER_AGENT'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
1437 1907 return true; // Wp4webos 1.1 or higher.
1438 1908 }
@@ -1456,15 +1926,18 @@
1456 1926 * Detects if the current browser is Nintendo 3DS handheld.
1457 1927 *
1458 1928 * Example: Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7498.US
1459 1929 * can differ in language, version and region
1930 + *
1931 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1460 1932 */
1461 - public static function is_Nintendo_3DS() {
1462 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1933 + public static function is_Nintendo_3DS( $user_agent = null ) {
1934 + $user_agent = self::maybe_get_user_agent_from_server( $user_agent );
1935 + if ( empty( $user_agent ) ) {
1463 1936 return false;
1464 1937 }
1465 1938
1466 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1939 + $ua = strtolower( wp_unslash( $user_agent ) );
1467 1940 if ( strpos( $ua, 'nintendo 3ds' ) !== false ) {
1468 1941 return true;
1469 1942 }
1470 1943 return false;
@@ -1472,22 +1945,34 @@
1472 1945
1473 1946 /**
1474 1947 * Was the current request made by a known bot?
1475 1948 *
1949 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1950 + *
1476 1951 * @return boolean
1477 1952 */
1478 - public static function is_bot() {
1953 + public static function is_bot( $user_agent = null ) {
1479 1954 static $is_bot = null;
1480 1955
1481 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1482 - return false;
1956 + if ( null === $user_agent ) {
1957 + if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1958 + return false;
1959 + }
1960 + $user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1961 +
1962 + // Use cached result only when using the default $_SERVER['HTTP_USER_AGENT'].
1963 + if ( $is_bot === null ) {
1964 + $is_bot = self::is_bot_user_agent( $user_agent );
1965 + }
1966 + return $is_bot;
1483 1967 }
1484 1968
1485 - if ( $is_bot === null ) {
1486 - $is_bot = self::is_bot_user_agent( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1969 + if ( empty( $user_agent ) ) {
1970 + return false;
1487 1971 }
1488 1972
1489 - return $is_bot;
1973 + // Don't use cache when a custom user agent is provided.
1974 + return self::is_bot_user_agent( $user_agent );
1490 1975 }
1491 1976
1492 1977 /**
1493 1978 * Is the given user-agent a known bot?
@@ -1502,87 +1987,401 @@
1502 1987 if ( empty( $ua ) ) {
1503 1988 return false;
1504 1989 }
1505 1990
1991 + // Some sourced via
1992 + // https://github.com/ua-parser/uap-core/blob/432e95f6767cc8bab4c20c255784cd6f7e93bc15/regexes.yaml#L151
1506 1993 $bot_agents = array(
1994 + // Microsoft/Bing https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0
1995 + 'bingbot', // Bing/Copilot
1996 + 'adidxbot', // Bing Ads
1997 + 'bingpreview', // Generates page snapshots for Bing
1998 + 'bingvideopreview', // Generates previews of videos for Bing
1999 + 'microsoft',
2000 +
2001 + // Google https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers
2002 + 'adsbot-google',
2003 + 'appengine-google',
2004 + 'feedfetcher-google',
2005 + 'mediapartners-google',
2006 + 'storebot-google', // https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#google-storebot
2007 + 'google sketchup',
2008 + 'google-cloudbertexbot', // https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#google-cloudvertexbot
2009 + 'google-extended', // Gemini https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#google-extended
2010 + 'google-inspectiontool', // https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers
2011 + 'google-safety;', // https://www.google.com/bot.html
2012 + 'googlebot-mobile',
2013 + 'googlebot', // and googlebot-[image,video,news,] https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#googlebot
2014 + 'googleother', // and googleother-[video,image] https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#googleother
2015 +
2016 + // OpenAI https://platform.openai.com/docs/bots
2017 + 'gptbot', // Crawler
2018 + 'chatgpt-user', // ChatGPT on behalf of user
2019 + 'oai-searchbot', // ChatGPT search features
2020 +
2021 + // Anthropic
2022 + 'claudebot', // chat citation fetch https://support.anthropic.com/en/articles/8896518
2023 + 'claude-web', // web-focused crawl https://darkvisitors.com/agents/claude-web
2024 + 'anthropic-ai', // bulk model training https://darkvisitors.com/agents/anthropic-ai
2025 +
2026 + // Perplexity
2027 + 'perplexitybot', // index builder https://docs.perplexity.ai/guides/bots
2028 + 'perplexity-user', // human-triggered visit https://docs.perplexity.ai/guides/bots
2029 +
2030 + // Pinterest https://help.pinterest.com/en/business/article/pinterestbot
2031 + 'pinterest/0.2',
2032 + 'pinterestbot',
2033 +
2034 + // Meta https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/
2035 + 'facebookbot', // AI data scraper https://darkvisitors.com/agents/facebookbot
2036 + 'facebookexternalhit', // shares https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify
2037 + 'facebookcatalog', // shares https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify
2038 + 'meta-webindexer', // Meta AI search indexer https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#meta-webindexer
2039 + 'meta-externalads', // web crawler improving ads https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#meta-externalads
2040 + 'meta-externalagent', // training AI models https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify-2
2041 + 'meta-externalfetcher', // user-initiated fetches, may skip robots.txt https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify-3
2042 +
2043 + // Semrush https://www.semrush.com/bot/
2044 + 'semrushbot',
2045 + 'siteauditbot',
2046 +
2047 + // Other bots (alphabetized list)
2048 + '123metaspider-bot',
2049 + '1470.net crawler',
2050 + '50.nu',
2051 + '8bo crawler bot',
2052 + 'aboundex',
2053 + 'ahrefsbot',
2054 + 'ai2bot', // AI2 crawler for LLMm training https://allenai.org/crawler
1507 2055 'alexa',
1508 2056 'altavista',
2057 + 'amazonbot', // https://developer.amazon.com/amazonbot
2058 + 'applebot', // https://support.apple.com/en-ca/HT204683
2059 + 'arcgis hub indexer',
2060 + 'archive.org_bot', // http://archive.org/details/archive.org_bot
2061 + 'archiver',
1509 2062 'ask jeeves',
1510 2063 'attentio',
1511 2064 'baiduspider',
1512 - 'bingbot',
2065 + 'blexbot',
2066 + 'blitzbot',
2067 + 'blogbridge',
2068 + 'bloglovin',
2069 + 'bne.es_bot', // https://www.bne.es/es/colecciones/archivo-web-espanola/aviso-webmasters
2070 + 'boardreader blog indexer',
2071 + 'boardreader favicon fetcher',
2072 + 'boitho.com-dc',
2073 + 'botseer',
2074 + 'bubing',
2075 + 'bytespider', // ByteDance (owner of TikTok) to train LLMs for Doubao https://darkvisitors.com/agents/bytespider
2076 + 'catchpoint',
2077 + 'ccbot', // CommonCrawl non-profit https://commoncrawl.org/ccbot
2078 + 'charlotte',
2079 + 'checklinks',
1513 2080 'chtml generic',
2081 + 'cityreview robot',
2082 + 'cloudflare-alwaysonline',
2083 + 'clumboot',
2084 + 'coccocbot', // Coc Coc https://darkvisitors.com/agents/coccocbot-web
2085 + 'cohere-ai', // Cohere AI https://darkvisitors.com/agents/cohere-ai
2086 + 'comodo http',
2087 + 'comodo-webinspector-crawler',
2088 + 'converacrawler',
2089 + 'cookieinformationscanner', // Internal ref p1699315886066389-slack-C0438NHCLSY
2090 + 'crawl-e',
2091 + 'crawlconvera',
2092 + 'crawldaddy',
1514 2093 'crawler',
2094 + 'crawlfire',
2095 + 'csimplespider',
2096 + 'dataforseobot', // https://www.dataforseo.com/dataforseo-bot
2097 + 'daumoa',
2098 + 'diffbot', // https://docs.diffbot.com/docs/how-to-use-custom-user-agents-with-extract-apis & https://darkvisitors.com/agents/diffbot
2099 + 'domaintunocrawler',
2100 + 'dotbot', // https://darkvisitors.com/agents/dotbot
2101 + 'duckassistbot', // DuckDuckGo AI Assistant https://darkvisitors.com/agents/duckassistbot
2102 + 'elisabot',
2103 + 'ezlynxbot', // https://www.ezoic.com/bot
1515 2104 'fastmobilecrawl',
1516 - 'feedfetcher-google',
2105 + 'feed seeker bot',
2106 + 'feedbin',
2107 + 'feedburner',
2108 + 'finderbots',
2109 + 'findlinks',
1517 2110 'firefly',
2111 + 'flamingo_searchengine',
2112 + 'followsite bot',
1518 2113 'froogle',
2114 + 'furlbot',
2115 + 'genieo',
2116 + 'germcrawler',
1519 2117 'gigabot',
1520 - 'googlebot',
1521 - 'googlebot-mobile',
2118 + 'gomezagent',
2119 + 'gonzo1',
2120 + 'grapeshotcrawler',
2121 + 'grokkit-crawler',
2122 + 'grub-client',
2123 + 'gsa-crawler',
1522 2124 'heritrix',
2125 + 'hiddenmarket',
2126 + 'holmes',
2127 + 'hoowwwer',
2128 + 'htdig',
1523 2129 'httrack',
1524 2130 'ia_archiver',
1525 - 'irlbot',
2131 + 'icarus6j',
2132 + 'icc-crawler',
2133 + 'ichiro',
2134 + 'iconsurf',
1526 2135 'iescholar',
2136 + 'iltrovatore',
2137 + 'index crawler',
1527 2138 'infoseek',
2139 + 'infuzapp',
2140 + 'innovazion crawler',
2141 + 'internetarchive',
2142 + 'irlbot',
2143 + 'jbot',
2144 + 'job roboter',
1528 2145 'jumpbot',
2146 + 'kaloogabot',
2147 + 'kiwistatus spider',
2148 + 'kraken',
2149 + 'kurzor',
2150 + 'larbin',
2151 + 'leia',
2152 + 'lesnikbot',
2153 + 'lijit crawler',
2154 + 'linguee bot',
2155 + 'linkaider',
1529 2156 'linkcheck',
2157 + 'linkdexbot',
2158 + 'linkedinbot',
2159 + 'linkfluence', // http://linkfluence.com/
2160 + 'linkwalker', // https://www.linkwalker.com/
2161 + 'lite bot',
2162 + 'livelapbot',
2163 + 'llaut',
1530 2164 'lycos',
2165 + 'mail.ru_bot',
2166 + 'masidani_bot',
2167 + 'masscan',
1531 2168 'mediapartners',
1532 2169 'mediobot',
2170 + 'mj12bot',
2171 + 'mogimogi',
2172 + 'mojeekbot', // https://www.mojeek.com/bot.html
1533 2173 'motionbot',
2174 + 'mozdex',
2175 + 'mshots',
1534 2176 'msnbot',
1535 - 'mshots',
2177 + 'msrbot',
2178 + 'mtps feed aggregation system',
2179 + 'netresearch',
2180 + 'netvibes',
2181 + 'newsgator',
2182 + 'ning',
2183 + 'nutch',
2184 + 'nymesis',
2185 + 'objectssearch',
2186 + 'ogscrper',
2187 + 'omgili', // Webz.io web crawler for a data seller https://darkvisitors.com/agents/omgili
2188 + 'oozbot',
1536 2189 'openbot',
2190 + 'openhosebot',
2191 + 'orbiter',
2192 + 'pagepeeker',
2193 + 'pagesinventory',
2194 + 'paxleframework',
2195 + 'peeplo screenshot bot',
2196 + 'phpcrawl',
2197 + 'pingdom.com_bot',
2198 + 'plantynet_webrobot',
2199 + 'pompos',
1537 2200 'pss-webkit-request',
1538 2201 'pythumbnail',
2202 + 'queryseekersp ider',
2203 + 'queryseekerspider',
2204 + 'qwantify',
2205 + 'read%20later',
2206 + 'reaper',
2207 + 'redcarpet',
2208 + 'retreiver',
2209 + 'riddler',
2210 + 'rival iq',
2211 + 'scollspider',
1539 2212 'scooter',
2213 + 'scrapy',
2214 + 'scrubby',
2215 + 'searchsight',
2216 + 'seekbot',
2217 + 'semanticdiscovery',
2218 + 'seostats',
2219 + 'simplepie',
2220 + 'simplerss',
2221 + 'simpy',
2222 + 'sitecat webbot',
2223 + 'sitecon',
2224 + 'slack-imgproxy',
2225 + 'slackbot-linkexpanding',
1540 2226 'slurp',
1541 2227 'snapbot',
2228 + 'snapchat', // https://developers.snap.com/robots
2229 + 'snappy',
2230 + 'speedy spider',
1542 2231 'spider',
2232 + 'squrl java',
2233 + 'stringer',
1543 2234 'taptubot',
1544 2235 'technoratisnoop',
1545 2236 'teoma',
2237 + 'theusefulbot',
2238 + 'thumbshots.ru',
2239 + 'thumbshotsbot',
2240 + 'timpibot', // LLM trainer https://darkvisitors.com/agents/timpibot
2241 + 'tiny tiny rss',
2242 + 'trendictionbot', // http://www.trendiction.de/bot;
2243 + 'trends crawler',
2244 + 'tweetmemebot',
1546 2245 'twiceler',
2246 + 'twitterbot', // https://developer.x.com/en/docs/x-for-websites/cards/guides/getting-started#crawling
2247 + 'url2png',
2248 + 'usyd-nlp-spider',
2249 + 'vagabondo',
2250 + 'voilabot',
2251 + 'vortex',
2252 + 'votay bot',
2253 + 'voyager',
2254 + 'wasalive.bot',
2255 + 'web-sniffer',
2256 + 'webthumb',
2257 + 'wesee',
2258 + 'whatsapp',
2259 + 'whatweb',
2260 + 'wire',
2261 + 'wordpress',
2262 + 'wotbox',
2263 + 'wp-e2e-tests', // WordPress e2e tests
2264 + 'www.almaden.ibm.com',
2265 + 'xenu',
2266 + 'yacybot', // http://yacy.net/bot.html
2267 + 'yahoo! slurp',
1547 2268 'yahooseeker',
1548 2269 'yahooysmcm',
1549 2270 'yammybot',
1550 - 'ahrefsbot',
1551 - 'pingdom.com_bot',
1552 - 'kraken',
1553 2271 'yandexbot',
1554 - 'twitterbot',
1555 - 'tweetmemebot',
1556 - 'openhosebot',
1557 - 'queryseekerspider',
1558 - 'linkdexbot',
1559 - 'grokkit-crawler',
1560 - 'livelapbot',
1561 - 'germcrawler',
1562 - 'domaintunocrawler',
1563 - 'grapeshotcrawler',
1564 - 'cloudflare-alwaysonline',
1565 - 'cookieinformationscanner', // p1699315886066389-slack-C0438NHCLSY
1566 - 'facebookexternalhit', // https://www.facebook.com/externalhit_uatext.php
1567 - 'feedburner',
1568 - 'yacybot', // http://yacy.net/bot.html
1569 - 'trendictionbot', // http://www.trendiction.de/bot;
1570 - 'elisabot',
1571 - 'linkfluence', // http://linkfluence.com/
1572 - 'semrushbot', // https://www.semrush.com/bot/
1573 - 'archive.org_bot', // http://archive.org/details/archive.org_bot
1574 - 'ezlynxbot', // https://www.ezoic.com/bot
1575 - 'siteauditbot', // https://www.semrush.com/bot/
1576 - 'snapchat', // https://developers.snap.com/robots
1577 - 'applebot', // https://support.apple.com/en-ca/HT204683
1578 - 'bne.es_bot', // https://www.bne.es/es/colecciones/archivo-web-espanola/aviso-webmasters
1579 - 'google-safety;', // https://www.google.com/bot.html
1580 - 'mojeekbot', // https://www.mojeek.com/bot.html
2272 + 'yottaamonitor',
2273 + 'youbot', // You.com AI assistant https://darkvisitors.com/agents/youbot
2274 + 'yowedo',
2275 + 'zao-crawler',
2276 + 'zao',
2277 + 'zebot_www.ze.bz',
2278 + 'zoombot', // SEOZOom https://darkvisitors.com/agents/zoombot
2279 + 'zooshot',
2280 + 'zyborg',
1581 2281 );
1582 2282
1583 2283 foreach ( $bot_agents as $bot_agent ) {
1584 2284 if ( false !== stripos( $ua, $bot_agent ) ) {
2285 + return true;
2286 + }
2287 + }
2288 +
2289 + return false;
2290 + }
2291 +
2292 + /**
2293 + * Is the current request from an agent?
2294 + *
2295 + * Detects bots, crawlers, AI assistants, and programmatic HTTP clients.
2296 + * Useful for showing machine-readable hints or alternative content to non-browser clients.
2297 + *
2298 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
2299 + *
2300 + * @return bool True if the request appears to be from an agent.
2301 + */
2302 + public static function is_agent( $user_agent = null ) {
2303 + static $cached_ua = null;
2304 + static $cached_result = null;
2305 +
2306 + if ( null === $user_agent ) {
2307 + if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
2308 + // Empty User-Agent is likely programmatic - real browsers always send one.
2309 + return true;
2310 + }
2311 + $user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
2312 +
2313 + // Use cache for default user agent, but invalidate if UA changed.
2314 + if ( $cached_ua === $user_agent && null !== $cached_result ) {
2315 + return $cached_result;
2316 + }
2317 +
2318 + $cached_ua = $user_agent;
2319 + $cached_result = self::is_agent_user_agent( $user_agent );
2320 +
2321 + /** This filter is documented below. */
2322 + if ( function_exists( 'apply_filters' ) ) {
2323 + $cached_result = apply_filters( 'jetpack_is_agent', $cached_result, $user_agent );
2324 + }
2325 + return $cached_result;
2326 + }
2327 +
2328 + if ( empty( $user_agent ) ) {
2329 + // Empty User-Agent is likely programmatic.
2330 + return true;
2331 + }
2332 +
2333 + $result = self::is_agent_user_agent( $user_agent );
2334 +
2335 + /**
2336 + * Filter to customize agent detection.
2337 + *
2338 + * @param bool $is_agent Whether the request is from an agent.
2339 + * @param string $user_agent The user agent string.
2340 + */
2341 + if ( function_exists( 'apply_filters' ) ) {
2342 + $result = apply_filters( 'jetpack_is_agent', $result, $user_agent );
2343 + }
2344 + return $result;
2345 + }
2346 +
2347 + /**
2348 + * Is the given user-agent from an agent?
2349 + *
2350 + * @param string $ua A user-agent string.
2351 + *
2352 + * @return bool True if the user-agent appears to be from an agent.
2353 + */
2354 + private static function is_agent_user_agent( $ua ) {
2355 + if ( self::is_bot_user_agent( $ua ) ) {
2356 + return true;
2357 + }
2358 +
2359 + // HTTP libraries and programmatic clients (alphabetized).
2360 + $http_clients = array(
2361 + 'aiohttp',
2362 + 'axios',
2363 + 'curl/',
2364 + 'go-http-client',
2365 + 'got/',
2366 + 'guzzlehttp',
2367 + 'httpie',
2368 + 'httpx',
2369 + 'insomnia',
2370 + 'java/',
2371 + 'libwww-perl',
2372 + 'node-fetch',
2373 + 'okhttp',
2374 + 'postman',
2375 + 'python-requests',
2376 + 'python-urllib',
2377 + 'ruby',
2378 + 'undici',
2379 + 'wget/',
2380 + );
2381 +
2382 + foreach ( $http_clients as $client ) {
2383 + if ( false !== stripos( $ua, $client ) ) {
1585 2384 return true;
1586 2385 }
1587 2386 }
1588 2387