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 +884 -249 13.6.2 → 16.3 View file →
@@ -86,10 +86,32 @@
86 86 const BROWSER_SAFARI = 'safari';
87 87 const BROWSER_EDGE = 'edge';
88 88 const BROWSER_OPERA = 'opera';
89 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';
90 96 const OTHER = 'other';
91 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 +
92 114 /**
93 115 * A list of dumb-phone user agent parts.
94 116 *
95 117 * @var array
@@ -151,9 +173,9 @@
151 173 public function __construct( $ua = '' ) {
152 174 if ( $ua ) {
153 175 $this->useragent = $ua;
154 176 } elseif ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
155 - $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.
156 178 }
157 179 }
158 180
159 181 /**
@@ -241,9 +263,9 @@
241 263 * @return string|bool Name of the platform, false otherwise.
242 264 */
243 265 public function get_platform() {
244 266 if ( isset( $this->platform ) ) {
245 - return $this->platform;
267 + return $this->platform;
246 268 }
247 269
248 270 if ( empty( $this->useragent ) ) {
249 271 return false;
@@ -248,19 +270,20 @@
248 270 if ( empty( $this->useragent ) ) {
249 271 return false;
250 272 }
251 273
252 - if ( strpos( $this->useragent, 'windows phone' ) !== false ) {
253 - $this->platform = self::PLATFORM_WINDOWS;
254 - } elseif ( strpos( $this->useragent, 'windows ce' ) !== false ) {
274 + $ua = strtolower( $this->useragent );
275 + if ( strpos( $ua, 'windows phone' ) !== false ) {
255 276 $this->platform = self::PLATFORM_WINDOWS;
256 - } 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 ) {
257 280 $this->platform = self::PLATFORM_IPAD;
258 - } elseif ( strpos( $this->useragent, 'ipod' ) !== false ) {
281 + } elseif ( strpos( $ua, 'ipod' ) !== false ) {
259 282 $this->platform = self::PLATFORM_IPOD;
260 - } elseif ( strpos( $this->useragent, 'iphone' ) !== false ) {
283 + } elseif ( strpos( $ua, 'iphone' ) !== false ) {
261 284 $this->platform = self::PLATFORM_IPHONE;
262 - } elseif ( strpos( $this->useragent, 'android' ) !== false ) {
285 + } elseif ( strpos( $ua, 'android' ) !== false ) {
263 286 if ( static::is_android_tablet() ) {
264 287 $this->platform = self::PLATFORM_ANDROID_TABLET;
265 288 } else {
266 289 $this->platform = self::PLATFORM_ANDROID;
@@ -268,9 +291,9 @@
268 291 } elseif ( static::is_kindle_fire() ) {
269 292 $this->platform = self::PLATFORM_ANDROID_TABLET;
270 293 } elseif ( static::is_blackberry_10() ) {
271 294 $this->platform = self::PLATFORM_BLACKBERRY_10;
272 - } elseif ( strpos( $this->useragent, 'blackberry' ) !== false ) {
295 + } elseif ( strpos( $ua, 'blackberry' ) !== false ) {
273 296 $this->platform = self::PLATFORM_BLACKBERRY;
274 297 } elseif ( static::is_blackberry_tablet() ) {
275 298 $this->platform = self::PLATFORM_BLACKBERRY;
276 299 } elseif ( static::is_symbian_platform() ) {
@@ -299,15 +322,15 @@
299 322 return false;
300 323 }
301 324 $platform = self::OTHER;
302 325
303 - if ( static::is_linux_desktop() ) {
326 + if ( static::is_linux_desktop( $ua ) ) {
304 327 $platform = self::PLATFORM_DESKTOP_LINUX;
305 - } elseif ( static::is_mac_desktop() ) {
328 + } elseif ( static::is_mac_desktop( $ua ) ) {
306 329 $platform = self::PLATFORM_DESKTOP_MAC;
307 - } elseif ( static::is_windows_desktop() ) {
330 + } elseif ( static::is_windows_desktop( $ua ) ) {
308 331 $platform = self::PLATFORM_DESKTOP_WINDOWS;
309 - } elseif ( static::is_chrome_desktop() ) {
332 + } elseif ( static::is_chrome_desktop( $ua ) ) {
310 333 $platform = self::PLATFORM_DESKTOP_CHROME;
311 334 }
312 335 return $platform;
313 336 }
@@ -322,19 +345,35 @@
322 345 if ( empty( $ua ) ) {
323 346 return self::OTHER;
324 347 }
325 348
326 - if ( static::is_opera_mini() || static::is_opera_mobile() || static::is_opera_desktop() || static::is_opera_mini_dumb() ) {
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 ) ) {
327 366 return self::BROWSER_OPERA;
328 - } elseif ( static::is_edge_browser() ) {
367 + } elseif ( static::is_edge_browser( $ua ) ) {
329 368 return self::BROWSER_EDGE;
330 - } elseif ( static::is_chrome_desktop() || self::is_chrome_for_iOS() ) {
369 + } elseif ( static::is_chrome_desktop( $ua ) || self::is_chrome_for_iOS( $ua ) ) {
331 370 return self::BROWSER_CHROME;
332 - } elseif ( static::is_safari_browser() ) {
371 + } elseif ( static::is_safari_browser( $ua ) ) {
333 372 return self::BROWSER_SAFARI;
334 - } elseif ( static::is_firefox_mobile() || static::is_firefox_desktop() ) {
373 + } elseif ( static::is_firefox_mobile( $ua ) || static::is_firefox_desktop( $ua ) ) {
335 374 return self::BROWSER_FIREFOX;
336 - } elseif ( static::is_ie_browser() ) {
375 + } elseif ( static::is_ie_browser( $ua ) ) {
337 376 return self::BROWSER_IE;
338 377 }
339 378 return self::OTHER;
340 379 }
@@ -339,8 +378,18 @@
339 378 return self::OTHER;
340 379 }
341 380
342 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 + /**
343 392 * This method detects for UA which can display iPhone-optimized web content.
344 393 * Includes iPhone, iPod Touch, Android, WebOS, Fennec (Firefox mobile), etc.
345 394 */
346 395 public function isTierIphone() {
@@ -482,15 +531,16 @@
482 531 *
483 532 * @return bool
484 533 */
485 534 public function is_tablet() {
535 + $ua = $this->useragent;
486 536 return ( 0 // Never true, but makes it easier to manage our list of tablet conditions.
487 - || self::is_ipad()
488 - || self::is_android_tablet()
489 - || self::is_blackberry_tablet()
490 - || self::is_kindle_fire()
491 - || self::is_MaemoTablet()
492 - || 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 )
493 543 );
494 544 }
495 545
496 546 /**
@@ -515,8 +565,25 @@
515 565 }
516 566 }
517 567
518 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 + /**
519 586 * Detects if the current UA is iPhone Mobile Safari or another iPhone or iPod Touch Browser.
520 587 *
521 588 * They type can check for any iPhone, an iPhone using Safari, or an iPhone using something other than Safari.
522 589 *
@@ -523,16 +590,19 @@
523 590 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPhone browser),
524 591 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
525 592 * Otherwise those browsers will be 'catched' by the iphone string.
526 593 *
527 - * @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'].
528 596 */
529 - public static function is_iphone_or_ipod( $type = 'iphone-any' ) {
530 - 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 ) ) {
531 601 return false;
532 602 }
533 603
534 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
604 + $ua = strtolower( wp_unslash( $user_agent ) );
535 605 $is_iphone = ( strpos( $ua, 'iphone' ) !== false ) || ( strpos( $ua, 'ipod' ) !== false );
536 606 $is_safari = ( false !== strpos( $ua, 'safari' ) );
537 607
538 608 if ( 'iphone-safari' === $type ) {
@@ -548,19 +618,22 @@
548 618 * Detects if the current UA is Chrome for iOS
549 619 *
550 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>.
551 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'].
552 624 */
553 - public static function is_chrome_for_iOS() {
554 - 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 ) ) {
555 628 return false;
556 629 }
557 630
558 - if ( self::is_iphone_or_ipod( 'iphone-safari' ) === false ) {
631 + if ( self::is_iphone_or_ipod( 'iphone-safari', $user_agent ) === false ) {
559 632 return false;
560 633 }
561 634
562 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
635 + $ua = strtolower( wp_unslash( $user_agent ) );
563 636
564 637 if ( strpos( $ua, 'crios/' ) !== false ) {
565 638 return true;
566 639 } else {
@@ -572,15 +645,18 @@
572 645 * Detects if the current UA is Twitter for iPhone
573 646 *
574 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
575 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'].
576 651 */
577 - public static function is_twitter_for_iphone() {
578 - 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 ) ) {
579 655 return false;
580 656 }
581 657
582 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
658 + $ua = strtolower( wp_unslash( $user_agent ) );
583 659
584 660 if ( strpos( $ua, 'ipad' ) !== false ) {
585 661 return false;
586 662 }
@@ -596,15 +672,18 @@
596 672 * Detects if the current UA is Twitter for iPad
597 673 *
598 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
599 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'].
600 678 */
601 - public static function is_twitter_for_ipad() {
602 - 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 ) ) {
603 682 return false;
604 683 }
605 684
606 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
685 + $ua = strtolower( wp_unslash( $user_agent ) );
607 686
608 687 if ( strpos( $ua, 'twitter for ipad' ) !== false ) {
609 688 return true;
610 689 } elseif ( strpos( $ua, 'ipad' ) !== false && strpos( $ua, 'twitter for iphone' ) !== false ) {
@@ -618,15 +697,18 @@
618 697 * Detects if the current UA is Facebook for iPhone
619 698 * - Facebook 4020.0 (iPhone; iPhone OS 5.0.1; fr_FR)
620 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]
621 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'].
622 703 */
623 - public static function is_facebook_for_iphone() {
624 - 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 ) ) {
625 707 return false;
626 708 }
627 709
628 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
710 + $ua = strtolower( wp_unslash( $user_agent ) );
629 711
630 712 if ( false === strpos( $ua, 'iphone' ) ) {
631 713 return false;
632 714 }
@@ -646,15 +728,18 @@
646 728 * Detects if the current UA is Facebook for iPad
647 729 * - Facebook 4020.0 (iPad; iPhone OS 5.0.1; en_US)
648 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]
649 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'].
650 734 */
651 - public static function is_facebook_for_ipad() {
652 - 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 ) ) {
653 738 return false;
654 739 }
655 740
656 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
741 + $ua = strtolower( wp_unslash( $user_agent ) );
657 742
658 743 if ( false === strpos( $ua, 'ipad' ) ) {
659 744 return false;
660 745 }
@@ -667,15 +752,18 @@
667 752 }
668 753
669 754 /**
670 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'].
671 758 */
672 - public static function is_wordpress_for_ios() {
673 - 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 ) ) {
674 762 return false;
675 763 }
676 764
677 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
765 + $ua = strtolower( wp_unslash( $user_agent ) );
678 766 if ( false !== strpos( $ua, 'wp-iphone' ) ) {
679 767 return true;
680 768 } else {
681 769 return false;
@@ -689,17 +777,18 @@
689 777 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPad browser),
690 778 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
691 779 * Otherwise those browsers will be 'catched' by the ipad string.
692 780 *
693 - * @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'].
694 783 */
695 - public static function is_ipad( $type = 'ipad-any' ) {
696 -
697 - 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 ) ) {
698 787 return false;
699 788 }
700 789
701 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
790 + $ua = strtolower( wp_unslash( $user_agent ) );
702 791
703 792 $is_ipad = ( false !== strpos( $ua, 'ipad' ) );
704 793 $is_safari = ( false !== strpos( $ua, 'safari' ) );
705 794
@@ -717,16 +806,18 @@
717 806 *
718 807 * See http://www.useragentstring.com/pages/Fennec/
719 808 * Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.1.1) Gecko/20110415 Firefox/4.0.2pre Fennec/4.0.1
720 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'].
721 812 */
722 - public static function is_firefox_mobile() {
723 -
724 - 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 ) ) {
725 816 return false;
726 817 }
727 818
728 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
819 + $ua = strtolower( wp_unslash( $user_agent ) );
729 820
730 821 if ( strpos( $ua, 'fennec' ) !== false ) {
731 822 return true;
732 823 } else {
@@ -739,16 +830,18 @@
739 830 *
740 831 * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
741 832 * Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion
742 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'].
743 836 */
744 - public static function is_firefox_desktop() {
745 -
746 - 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 ) ) {
747 840 return false;
748 841 }
749 842
750 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
843 + $ua = strtolower( wp_unslash( $user_agent ) );
751 844
752 845 if ( false !== strpos( $ua, 'firefox' ) && false === strpos( $ua, 'mobile' ) && false === strpos( $ua, 'tablet' ) ) {
753 846 return true;
754 847 } else {
@@ -759,16 +852,18 @@
759 852 /**
760 853 * Detects if the current browser is FirefoxOS Native browser
761 854 *
762 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'].
763 858 */
764 - public static function is_firefox_os() {
765 -
766 - 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 ) ) {
767 862 return false;
768 863 }
769 864
770 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
865 + $ua = strtolower( wp_unslash( $user_agent ) );
771 866
772 867 if ( strpos( $ua, 'mozilla' ) !== false && strpos( $ua, 'mobile' ) !== false && strpos( $ua, 'gecko' ) !== false && strpos( $ua, 'firefox' ) !== false ) {
773 868 return true;
774 869 } else {
@@ -777,14 +872,18 @@
777 872 }
778 873
779 874 /**
780 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'].
781 878 */
782 - public static function is_safari_browser() {
783 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
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 ) ) {
784 882 return false;
785 883 }
786 - if ( false === strpos( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), 'Safari' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
884 +
885 + if ( false === strpos( wp_unslash( $user_agent ), 'Safari' ) ) {
787 886 return false;
788 887 }
789 888 return true;
790 889 }
@@ -790,14 +889,21 @@
790 889 }
791 890
792 891 /**
793 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'].
794 895 */
795 - public static function is_edge_browser() {
796 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
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 ) ) {
797 899 return false;
798 900 }
799 - if ( false === strpos( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), 'Edge' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
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/' ) ) {
800 906 return false;
801 907 }
802 908 return true;
803 909 }
@@ -802,19 +908,20 @@
802 908 return true;
803 909 }
804 910
805 911 /**
806 - * Detect Edge browser
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'].
807 915 */
808 - public static function is_ie_browser() {
809 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
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 ) ) {
810 919 return false;
811 920 }
812 - $ua = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
813 - if ( false === ( strpos( $ua, 'MSIE' ) || strpos( $ua, 'Trident/7' ) ) ) {
814 - return false;
815 - }
816 - return true;
921 +
922 + $ua = wp_unslash( $user_agent );
923 + return strpos( $ua, 'MSIE' ) !== false || strpos( $ua, 'Trident/7' ) !== false;
817 924 }
818 925
819 926 /**
820 927 * Detect modern Opera desktop
@@ -821,15 +928,18 @@
821 928 *
822 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
823 930 *
824 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'].
825 934 */
826 - public static function is_opera_desktop() {
827 - 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 ) ) {
828 938 return false;
829 939 }
830 940
831 - 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/' ) ) {
832 942 return false;
833 943 }
834 944
835 945 return true;
@@ -845,15 +955,18 @@
845 955 * The rendering engine is on Opera's server.)
846 956 *
847 957 * Opera/9.80 (Windows NT 6.1; Opera Mobi/14316; U; en) Presto/2.7.81 Version/11.00"
848 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'].
849 961 */
850 - public static function is_opera_mobile() {
851 - 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 ) ) {
852 965 return false;
853 966 }
854 967
855 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
968 + $ua = strtolower( wp_unslash( $user_agent ) );
856 969
857 970 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mobi' ) !== false ) {
858 971 return true;
859 972 } elseif ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'nintendo dsi' ) !== false ) {
@@ -872,15 +985,18 @@
872 985 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
873 986 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
874 987 * Opera/9.80 (Series 60; Opera Mini/5.1.22783/23.334; U; en) Presto/2.5.25 Version/10.54
875 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'].
876 991 */
877 - public static function is_opera_mini() {
878 - 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 ) ) {
879 995 return false;
880 996 }
881 997
882 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
998 + $ua = strtolower( wp_unslash( $user_agent ) );
883 999
884 1000 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mini' ) !== false ) {
885 1001 return true;
886 1002 } else {
@@ -890,16 +1006,20 @@
890 1006
891 1007 /**
892 1008 * Detects if the current browser is Opera Mini, but not on a smart device OS(Android, iOS, etc)
893 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'].
894 1012 */
895 - public static function is_opera_mini_dumb() {
896 - 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 ) ) {
897 1016 return false;
898 1017 }
899 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
900 1018
901 - if ( self::is_opera_mini() ) {
1019 + $ua = strtolower( wp_unslash( $user_agent ) );
1020 +
1021 + if ( self::is_opera_mini( $user_agent ) ) {
902 1022 if ( strpos( $ua, 'android' ) !== false || strpos( $ua, 'iphone' ) !== false || strpos( $ua, 'ipod' ) !== false
903 1023 || strpos( $ua, 'ipad' ) !== false || strpos( $ua, 'blackberry' ) !== false ) {
904 1024 return false;
905 1025 } else {
@@ -910,21 +1030,132 @@
910 1030 }
911 1031 }
912 1032
913 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 + /**
914 1142 * Detects if the current browser is a Windows Phone 7 device.
915 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'].
916 1146 */
917 - public static function is_WindowsPhone7() {
918 - 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 ) ) {
919 1150 return false;
920 1151 }
921 1152
922 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1153 + $ua = strtolower( wp_unslash( $user_agent ) );
923 1154
924 1155 if ( false === strpos( $ua, 'windows phone os 7' ) ) {
925 1156 return false;
926 - } 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 ) ) {
927 1158 return false;
928 1159 } else {
929 1160 return true;
930 1161 }
@@ -932,15 +1163,18 @@
932 1163
933 1164 /**
934 1165 * Detects if the current browser is a Windows Phone 8 device.
935 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'].
936 1169 */
937 - public static function is_windows_phone_8() {
938 - 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 ) ) {
939 1173 return false;
940 1174 }
941 1175
942 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1176 + $ua = strtolower( wp_unslash( $user_agent ) );
943 1177 if ( strpos( $ua, 'windows phone 8' ) === false ) {
944 1178 return false;
945 1179 } else {
946 1180 return true;
@@ -951,19 +1185,22 @@
951 1185 * Detects if the current browser is on a Palm device running the new WebOS. This EXCLUDES TouchPad.
952 1186 *
953 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
954 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'].
955 1191 */
956 - public static function is_PalmWebOS() {
957 - 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 ) ) {
958 1195 return false;
959 1196 }
960 1197
961 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1198 + $ua = strtolower( wp_unslash( $user_agent ) );
962 1199
963 1200 if ( false === strpos( $ua, 'webos' ) ) {
964 1201 return false;
965 - } 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 ) ) {
966 1203 return false;
967 1204 } else {
968 1205 return true;
969 1206 }
@@ -973,17 +1210,20 @@
973 1210 * Detects if the current browser is the HP TouchPad default browser. This excludes phones wt WebOS.
974 1211 *
975 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
976 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'].
977 1216 */
978 - public static function is_TouchPad() {
979 - 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 ) ) {
980 1220 return false;
981 1221 }
982 1222
983 - $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 ) );
984 1224 if ( false !== strpos( $http_user_agent, 'hp-tablet' ) || false !== strpos( $http_user_agent, 'hpwos' ) || false !== strpos( $http_user_agent, 'touchpad' ) ) {
985 - 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 ) ) {
986 1226 return false;
987 1227 } else {
988 1228 return true;
989 1229 }
@@ -999,17 +1239,19 @@
999 1239 *
1000 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
1001 1241 *
1002 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'].
1003 1245 */
1004 - public static function is_S60_OSSBrowser() {
1005 -
1006 - 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 ) ) {
1007 1249 return false;
1008 1250 }
1009 1251
1010 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1011 - 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 ) ) {
1012 1254 return false;
1013 1255 }
1014 1256
1015 1257 $pos_webkit = strpos( $agent, 'webkit' );
@@ -1030,16 +1272,18 @@
1030 1272 }
1031 1273
1032 1274 /**
1033 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'].
1034 1278 */
1035 - public static function is_symbian_platform() {
1036 -
1037 - 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 ) ) {
1038 1282 return false;
1039 1283 }
1040 1284
1041 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1285 + $agent = strtolower( wp_unslash( $user_agent ) );
1042 1286
1043 1287 $pos_webkit = strpos( $agent, 'webkit' );
1044 1288 if ( false !== $pos_webkit ) {
1045 1289 // First, test for WebKit, then make sure it's either Symbian or S60.
@@ -1064,16 +1308,18 @@
1064 1308 /**
1065 1309 * Detects if the device platform is the Symbian Series 40.
1066 1310 * Nokia Browser for Series 40 is a proxy based browser, previously known as Ovi Browser.
1067 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'].
1068 1314 */
1069 - public static function is_symbian_s40_platform() {
1070 -
1071 - 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 ) ) {
1072 1318 return false;
1073 1319 }
1074 1320
1075 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1321 + $agent = strtolower( wp_unslash( $user_agent ) );
1076 1322
1077 1323 if ( strpos( $agent, 'series40' ) !== false ) {
1078 1324 if ( strpos( $agent, 'nokia' ) !== false || strpos( $agent, 'ovibrowser' ) !== false || strpos( $agent, 'nokiabrowser' ) !== false ) {
1079 1325 return true;
@@ -1085,17 +1331,19 @@
1085 1331
1086 1332 /**
1087 1333 * Returns if the device belongs to J2ME capable family.
1088 1334 *
1335 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1336 + *
1089 1337 * @return bool
1090 1338 */
1091 - public static function is_J2ME_platform() {
1092 -
1093 - 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 ) ) {
1094 1342 return false;
1095 1343 }
1096 1344
1097 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1345 + $agent = strtolower( wp_unslash( $user_agent ) );
1098 1346
1099 1347 if ( strpos( $agent, 'j2me/midp' ) !== false ) {
1100 1348 return true;
1101 1349 } elseif ( strpos( $agent, 'midp' ) !== false && strpos( $agent, 'cldc' ) ) {
@@ -1105,16 +1353,18 @@
1105 1353 }
1106 1354
1107 1355 /**
1108 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'].
1109 1359 */
1110 - public static function is_MaemoTablet() {
1111 -
1112 - 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 ) ) {
1113 1363 return false;
1114 1364 }
1115 1365
1116 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1366 + $agent = strtolower( wp_unslash( $user_agent ) );
1117 1367
1118 1368 $pos_maemo = strpos( $agent, 'maemo' );
1119 1369 if ( false === $pos_maemo ) {
1120 1370 return false;
@@ -1121,9 +1371,9 @@
1121 1371 }
1122 1372
1123 1373 // Must be Linux + Tablet, or else it could be something else.
1124 1374 if ( strpos( $agent, 'tablet' ) !== false && strpos( $agent, 'linux' ) !== false ) {
1125 - 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 ) ) {
1126 1376 return false;
1127 1377 } else {
1128 1378 return true;
1129 1379 }
@@ -1133,20 +1383,22 @@
1133 1383 }
1134 1384
1135 1385 /**
1136 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'].
1137 1389 */
1138 - public static function is_MeeGo() {
1139 -
1140 - 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 ) ) {
1141 1393 return false;
1142 1394 }
1143 1395
1144 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1396 + $ua = strtolower( wp_unslash( $user_agent ) );
1145 1397
1146 1398 if ( false === strpos( $ua, 'meego' ) ) {
1147 1399 return false;
1148 - } 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 ) ) {
1149 1401 return false;
1150 1402 } else {
1151 1403 return true;
1152 1404 }
@@ -1153,16 +1405,18 @@
1153 1405 }
1154 1406
1155 1407 /**
1156 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'].
1157 1411 */
1158 - public static function is_webkit() {
1159 -
1160 - 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 ) ) {
1161 1415 return false;
1162 1416 }
1163 1417
1164 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1418 + $agent = strtolower( wp_unslash( $user_agent ) );
1165 1419
1166 1420 $pos_webkit = strpos( $agent, 'webkit' );
1167 1421
1168 1422 if ( false !== $pos_webkit ) {
@@ -1174,19 +1428,22 @@
1174 1428
1175 1429 /**
1176 1430 * Detects if the current browser is the Native Android browser.
1177 1431 *
1432 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1433 + *
1178 1434 * @return boolean true if the browser is Android otherwise false
1179 1435 */
1180 - public static function is_android() {
1181 - 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 ) ) {
1182 1439 return false;
1183 1440 }
1184 1441
1185 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1442 + $agent = strtolower( wp_unslash( $user_agent ) );
1186 1443 $pos_android = strpos( $agent, 'android' );
1187 1444 if ( false !== $pos_android ) {
1188 - 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 ) ) {
1189 1446 return false;
1190 1447 } else {
1191 1448 return true;
1192 1449 }
@@ -1198,16 +1455,19 @@
1198 1455 /**
1199 1456 * Detects if the current browser is the Native Android Tablet browser.
1200 1457 * Assumes 'Android' should be in the user agent, but not 'mobile'
1201 1458 *
1459 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1460 + *
1202 1461 * @return boolean true if the browser is Android and not 'mobile' otherwise false
1203 1462 */
1204 - public static function is_android_tablet() {
1205 - 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 ) ) {
1206 1466 return false;
1207 1467 }
1208 1468
1209 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1469 + $agent = strtolower( wp_unslash( $user_agent ) );
1210 1470
1211 1471 $pos_android = strpos( $agent, 'android' );
1212 1472 $pos_mobile = strpos( $agent, 'mobile' );
1213 1473 $post_android_app = strpos( $agent, 'wp-android' );
@@ -1212,9 +1472,9 @@
1212 1472 $pos_mobile = strpos( $agent, 'mobile' );
1213 1473 $post_android_app = strpos( $agent, 'wp-android' );
1214 1474
1215 1475 if ( false !== $pos_android && false === $pos_mobile && false === $post_android_app ) {
1216 - 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 ) ) {
1217 1477 return false;
1218 1478 } else {
1219 1479 return true;
1220 1480 }
@@ -1228,16 +1488,19 @@
1228 1488 *
1229 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
1230 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
1231 1491 *
1492 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1493 + *
1232 1494 * @return boolean true if the browser is Kindle Fire Native browser otherwise false
1233 1495 */
1234 - public static function is_kindle_fire() {
1235 - 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 ) ) {
1236 1499 return false;
1237 1500 }
1238 1501
1239 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1502 + $agent = strtolower( wp_unslash( $user_agent ) );
1240 1503 $pos_silk = strpos( $agent, 'silk/' );
1241 1504 $pos_silk_acc = strpos( $agent, 'silk-accelerated=' );
1242 1505 if ( false !== $pos_silk && false !== $pos_silk_acc ) {
1243 1506 return true;
@@ -1250,17 +1513,21 @@
1250 1513 * Detects if the current browser is the Kindle Touch Native browser
1251 1514 *
1252 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+
1253 1516 *
1517 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1518 + *
1254 1519 * @return boolean true if the browser is Kindle monochrome Native browser otherwise false
1255 1520 */
1256 - public static function is_kindle_touch() {
1257 - 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 ) ) {
1258 1524 return false;
1259 1525 }
1260 - $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 ) );
1261 1528 $pos_kindle_touch = strpos( $agent, 'kindle/3.0+' );
1262 - if ( false !== $pos_kindle_touch && false === self::is_kindle_fire() ) {
1529 + if ( false !== $pos_kindle_touch && false === self::is_kindle_fire( $user_agent ) ) {
1263 1530 return true;
1264 1531 } else {
1265 1532 return false;
1266 1533 }
@@ -1267,15 +1534,18 @@
1267 1534 }
1268 1535
1269 1536 /**
1270 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'].
1271 1540 */
1272 - public static function is_windows8_auth() {
1273 - 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 ) ) {
1274 1544 return false;
1275 1545 }
1276 1546
1277 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1547 + $agent = strtolower( wp_unslash( $user_agent ) );
1278 1548 $pos = strpos( $agent, 'msauthhost' );
1279 1549 if ( false !== $pos ) {
1280 1550 return true;
1281 1551 } else {
@@ -1284,15 +1554,18 @@
1284 1554 }
1285 1555
1286 1556 /**
1287 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'].
1288 1560 */
1289 - public static function is_wordpress_for_win8() {
1290 - 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 ) ) {
1291 1564 return false;
1292 1565 }
1293 1566
1294 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1567 + $agent = strtolower( wp_unslash( $user_agent ) );
1295 1568 $pos = strpos( $agent, 'wp-windows8' );
1296 1569 if ( false !== $pos ) {
1297 1570 return true;
1298 1571 } else {
@@ -1301,15 +1574,18 @@
1301 1574 }
1302 1575
1303 1576 /**
1304 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'].
1305 1580 */
1306 - public static function is_wordpress_desktop_app() {
1307 - 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 ) ) {
1308 1584 return false;
1309 1585 }
1310 1586
1311 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1587 + $agent = strtolower( wp_unslash( $user_agent ) );
1312 1588 $pos = strpos( $agent, 'WordPressDesktop' );
1313 1589 if ( false !== $pos ) {
1314 1590 return true;
1315 1591 } else {
@@ -1320,16 +1596,18 @@
1320 1596 /**
1321 1597 * The is_blackberry_tablet() method can be used to check the User Agent for a RIM blackberry tablet.
1322 1598 * The user agent of the BlackBerry® Tablet OS follows a format similar to the following:
1323 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'].
1324 1602 */
1325 - public static function is_blackberry_tablet() {
1326 -
1327 - 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 ) ) {
1328 1606 return false;
1329 1607 }
1330 1608
1331 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1609 + $agent = strtolower( wp_unslash( $user_agent ) );
1332 1610 $pos_playbook = stripos( $agent, 'PlayBook' );
1333 1611 $pos_rim_tablet = stripos( $agent, 'RIM Tablet' );
1334 1612
1335 1613 if ( ( false === $pos_playbook ) || ( false === $pos_rim_tablet ) ) {
@@ -1341,19 +1619,22 @@
1341 1619
1342 1620 /**
1343 1621 * The is_blackbeberry() method can be used to check the User Agent for a blackberry device.
1344 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'].
1345 1625 */
1346 - public static function is_blackbeberry() {
1347 - 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 ) ) {
1348 1629 return false;
1349 1630 }
1350 1631
1351 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1632 + $agent = strtolower( wp_unslash( $user_agent ) );
1352 1633
1353 1634 $pos_blackberry = strpos( $agent, 'blackberry' );
1354 1635 if ( false !== $pos_blackberry ) {
1355 - 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 ) ) {
1356 1637 return false;
1357 1638 } else {
1358 1639 return true;
1359 1640 }
@@ -1363,14 +1644,18 @@
1363 1644 }
1364 1645
1365 1646 /**
1366 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'].
1367 1650 */
1368 - public static function is_blackberry_10() {
1369 - 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 ) ) {
1370 1654 return false;
1371 1655 }
1372 - $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 ) );
1373 1658 return ( strpos( $agent, 'bb10' ) !== false ) && ( strpos( $agent, 'mobile' ) !== false );
1374 1659 }
1375 1660
1376 1661 /**
@@ -1375,15 +1660,19 @@
1375 1660
1376 1661 /**
1377 1662 * Determines whether a desktop platform is Linux OS
1378 1663 *
1664 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1665 + *
1379 1666 * @return bool
1380 1667 */
1381 - public static function is_linux_desktop() {
1382 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
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 ) ) {
1383 1671 return false;
1384 1672 }
1385 - if ( ! preg_match( '/linux/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1673 +
1674 + if ( ! preg_match( '/linux/i', wp_unslash( $user_agent ) ) ) {
1386 1675 return false;
1387 1676 }
1388 1677 return true;
1389 1678 }
@@ -1390,15 +1679,19 @@
1390 1679
1391 1680 /**
1392 1681 * Determines whether a desktop platform is Mac OS
1393 1682 *
1683 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1684 + *
1394 1685 * @return bool
1395 1686 */
1396 - public static function is_mac_desktop() {
1397 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
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 ) ) {
1398 1690 return false;
1399 1691 }
1400 - if ( ! preg_match( '/macintosh|mac os x/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1692 +
1693 + if ( ! preg_match( '/macintosh|mac os x/i', wp_unslash( $user_agent ) ) ) {
1401 1694 return false;
1402 1695 }
1403 1696 return true;
1404 1697 }
@@ -1405,15 +1698,19 @@
1405 1698
1406 1699 /**
1407 1700 * Determines whether a desktop platform is Windows OS
1408 1701 *
1702 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1703 + *
1409 1704 * @return bool
1410 1705 */
1411 - public static function is_windows_desktop() {
1412 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
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 ) ) {
1413 1709 return false;
1414 1710 }
1415 - if ( ! preg_match( '/windows|win32/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1711 +
1712 + if ( ! preg_match( '/windows|win32/i', wp_unslash( $user_agent ) ) ) {
1416 1713 return false;
1417 1714 }
1418 1715 return true;
1419 1716 }
@@ -1420,15 +1717,19 @@
1420 1717
1421 1718 /**
1422 1719 * Determines whether a desktop platform is Chrome OS
1423 1720 *
1721 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1722 + *
1424 1723 * @return bool
1425 1724 */
1426 - public static function is_chrome_desktop() {
1427 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
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 ) ) {
1428 1728 return false;
1429 1729 }
1430 - if ( ! preg_match( '/chrome/i', wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1730 +
1731 + if ( ! preg_match( '/chrome/i', wp_unslash( $user_agent ) ) ) {
1431 1732 return false;
1432 1733 }
1433 1734 return true;
1434 1735 }
@@ -1445,22 +1746,24 @@
1445 1746 * - blackberry-4.7
1446 1747 * - blackberry-4.6
1447 1748 * - blackberry-4.5
1448 1749 *
1750 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1751 + *
1449 1752 * @return string Version of the BB OS.
1450 1753 * If version is not found, get_blackbeberry_OS_version will return boolean false.
1451 1754 */
1452 - public static function get_blackbeberry_OS_version() {
1453 -
1454 - 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 ) ) {
1455 1758 return false;
1456 1759 }
1457 1760
1458 - if ( self::is_blackberry_10() ) {
1761 + if ( self::is_blackberry_10( $user_agent ) ) {
1459 1762 return 'blackberry-10';
1460 1763 }
1461 1764
1462 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1765 + $agent = strtolower( wp_unslash( $user_agent ) );
1463 1766
1464 1767 $pos_blackberry = stripos( $agent, 'blackberry' );
1465 1768 if ( false === $pos_blackberry ) {
1466 1769 // Not a blackberry device.
@@ -1479,9 +1782,9 @@
1479 1782 return 'blackberry-torch'; // Match the torch first edition. the 2nd edition should use the OS7 and doesn't need any special rule.
1480 1783 } elseif ( preg_match( '#Version\/([\d\.]+)#i', $agent, $matches ) ) { // Detecting the BB OS version for devices running OS 6.0 or higher.
1481 1784 $version = $matches[1];
1482 1785 $version_num = explode( '.', $version );
1483 - if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
1786 + if ( count( $version_num ) <= 1 ) {
1484 1787 return 'blackberry-6'; // not a BB device that match our rule.
1485 1788 } else {
1486 1789 return 'blackberry-' . $version_num[0];
1487 1790 }
@@ -1500,9 +1803,9 @@
1500 1803 }
1501 1804
1502 1805 $version_num = explode( '.', $version );
1503 1806
1504 - if ( is_array( $version_num ) === false || count( $version_num ) <= 1 ) {
1807 + if ( count( $version_num ) <= 1 ) {
1505 1808 return false;
1506 1809 }
1507 1810
1508 1811 $version_num_major = (int) $version_num[0];
@@ -1530,20 +1833,22 @@
1530 1833 * - blackberry-5
1531 1834 * - blackberry-4.7
1532 1835 * - blackberry-4.6
1533 1836 *
1837 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1838 + *
1534 1839 * @return string Type of the BB browser.
1535 1840 * If browser's version is not found, detect_blackbeberry_browser_version will return boolean false.
1536 1841 */
1537 - public static function detect_blackberry_browser_version() {
1538 -
1539 - 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 ) ) {
1540 1845 return false;
1541 1846 }
1542 1847
1543 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1848 + $agent = strtolower( wp_unslash( $user_agent ) );
1544 1849
1545 - if ( self::is_blackberry_10() ) {
1850 + if ( self::is_blackberry_10( $user_agent ) ) {
1546 1851 return 'blackberry-10';
1547 1852 }
1548 1853
1549 1854 $pos_blackberry = strpos( $agent, 'blackberry' );
@@ -1562,9 +1867,9 @@
1562 1867 }
1563 1868
1564 1869 $version_num = explode( '.', $matches[1] );
1565 1870
1566 - if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
1871 + if ( count( $version_num ) <= 1 ) {
1567 1872 return false;
1568 1873 }
1569 1874
1570 1875 $version_num_major = (int) $version_num[0];
@@ -1585,17 +1890,19 @@
1585 1890
1586 1891 /**
1587 1892 * Checks if a visitor is coming from one of the WordPress mobile apps.
1588 1893 *
1894 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1895 + *
1589 1896 * @return bool
1590 1897 */
1591 - public static function is_mobile_app() {
1592 -
1593 - 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 ) ) {
1594 1901 return false;
1595 1902 }
1596 1903
1597 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1904 + $agent = strtolower( wp_unslash( $user_agent ) );
1598 1905
1599 1906 if ( isset( $_SERVER['X_USER_AGENT'] ) && preg_match( '|wp-webos|', $_SERVER['X_USER_AGENT'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
1600 1907 return true; // Wp4webos 1.1 or higher.
1601 1908 }
@@ -1619,15 +1926,18 @@
1619 1926 * Detects if the current browser is Nintendo 3DS handheld.
1620 1927 *
1621 1928 * Example: Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7498.US
1622 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'].
1623 1932 */
1624 - public static function is_Nintendo_3DS() {
1625 - 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 ) ) {
1626 1936 return false;
1627 1937 }
1628 1938
1629 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1939 + $ua = strtolower( wp_unslash( $user_agent ) );
1630 1940 if ( strpos( $ua, 'nintendo 3ds' ) !== false ) {
1631 1941 return true;
1632 1942 }
1633 1943 return false;
@@ -1635,22 +1945,34 @@
1635 1945
1636 1946 /**
1637 1947 * Was the current request made by a known bot?
1638 1948 *
1949 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1950 + *
1639 1951 * @return boolean
1640 1952 */
1641 - public static function is_bot() {
1953 + public static function is_bot( $user_agent = null ) {
1642 1954 static $is_bot = null;
1643 1955
1644 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1645 - 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;
1646 1967 }
1647 1968
1648 - if ( $is_bot === null ) {
1649 - $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;
1650 1971 }
1651 1972
1652 - return $is_bot;
1973 + // Don't use cache when a custom user agent is provided.
1974 + return self::is_bot_user_agent( $user_agent );
1653 1975 }
1654 1976
1655 1977 /**
1656 1978 * Is the given user-agent a known bot?
@@ -1665,88 +1987,401 @@
1665 1987 if ( empty( $ua ) ) {
1666 1988 return false;
1667 1989 }
1668 1990
1991 + // Some sourced via
1992 + // https://github.com/ua-parser/uap-core/blob/432e95f6767cc8bab4c20c255784cd6f7e93bc15/regexes.yaml#L151
1669 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
1670 2055 'alexa',
1671 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',
1672 2062 'ask jeeves',
1673 2063 'attentio',
1674 2064 'baiduspider',
1675 - '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',
1676 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',
1677 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
1678 2104 'fastmobilecrawl',
1679 - 'feedfetcher-google',
2105 + 'feed seeker bot',
2106 + 'feedbin',
2107 + 'feedburner',
2108 + 'finderbots',
2109 + 'findlinks',
1680 2110 'firefly',
2111 + 'flamingo_searchengine',
2112 + 'followsite bot',
1681 2113 'froogle',
2114 + 'furlbot',
2115 + 'genieo',
2116 + 'germcrawler',
1682 2117 'gigabot',
1683 - 'googlebot',
1684 - 'googlebot-mobile',
2118 + 'gomezagent',
2119 + 'gonzo1',
2120 + 'grapeshotcrawler',
2121 + 'grokkit-crawler',
2122 + 'grub-client',
2123 + 'gsa-crawler',
1685 2124 'heritrix',
2125 + 'hiddenmarket',
2126 + 'holmes',
2127 + 'hoowwwer',
2128 + 'htdig',
1686 2129 'httrack',
1687 2130 'ia_archiver',
1688 - 'irlbot',
2131 + 'icarus6j',
2132 + 'icc-crawler',
2133 + 'ichiro',
2134 + 'iconsurf',
1689 2135 'iescholar',
2136 + 'iltrovatore',
2137 + 'index crawler',
1690 2138 'infoseek',
2139 + 'infuzapp',
2140 + 'innovazion crawler',
2141 + 'internetarchive',
2142 + 'irlbot',
2143 + 'jbot',
2144 + 'job roboter',
1691 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',
1692 2156 'linkcheck',
2157 + 'linkdexbot',
2158 + 'linkedinbot',
2159 + 'linkfluence', // http://linkfluence.com/
2160 + 'linkwalker', // https://www.linkwalker.com/
2161 + 'lite bot',
2162 + 'livelapbot',
2163 + 'llaut',
1693 2164 'lycos',
2165 + 'mail.ru_bot',
2166 + 'masidani_bot',
2167 + 'masscan',
1694 2168 'mediapartners',
1695 2169 'mediobot',
2170 + 'mj12bot',
2171 + 'mogimogi',
2172 + 'mojeekbot', // https://www.mojeek.com/bot.html
1696 2173 'motionbot',
2174 + 'mozdex',
2175 + 'mshots',
1697 2176 'msnbot',
1698 - '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',
1699 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',
1700 2200 'pss-webkit-request',
1701 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',
1702 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',
1703 2226 'slurp',
1704 2227 'snapbot',
2228 + 'snapchat', // https://developers.snap.com/robots
2229 + 'snappy',
2230 + 'speedy spider',
1705 2231 'spider',
2232 + 'squrl java',
2233 + 'stringer',
1706 2234 'taptubot',
1707 2235 'technoratisnoop',
1708 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',
1709 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',
1710 2268 'yahooseeker',
1711 2269 'yahooysmcm',
1712 2270 'yammybot',
1713 - 'ahrefsbot',
1714 - 'pingdom.com_bot',
1715 - 'kraken',
1716 2271 'yandexbot',
1717 - 'twitterbot',
1718 - 'tweetmemebot',
1719 - 'openhosebot',
1720 - 'queryseekerspider',
1721 - 'linkdexbot',
1722 - 'grokkit-crawler',
1723 - 'livelapbot',
1724 - 'germcrawler',
1725 - 'domaintunocrawler',
1726 - 'grapeshotcrawler',
1727 - 'cloudflare-alwaysonline',
1728 - 'cookieinformationscanner', // p1699315886066389-slack-C0438NHCLSY
1729 - 'facebookexternalhit', // https://www.facebook.com/externalhit_uatext.php
1730 - 'feedburner',
1731 - 'yacybot', // http://yacy.net/bot.html
1732 - 'trendictionbot', // http://www.trendiction.de/bot;
1733 - 'elisabot',
1734 - 'linkfluence', // http://linkfluence.com/
1735 - 'semrushbot', // https://www.semrush.com/bot/
1736 - 'archive.org_bot', // http://archive.org/details/archive.org_bot
1737 - 'ezlynxbot', // https://www.ezoic.com/bot
1738 - 'siteauditbot', // https://www.semrush.com/bot/
1739 - 'snapchat', // https://developers.snap.com/robots
1740 - 'applebot', // https://support.apple.com/en-ca/HT204683
1741 - 'bne.es_bot', // https://www.bne.es/es/colecciones/archivo-web-espanola/aviso-webmasters
1742 - 'google-safety;', // https://www.google.com/bot.html
1743 - 'mojeekbot', // https://www.mojeek.com/bot.html
1744 - 'linkwalker', // https://www.linkwalker.com/
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',
1745 2281 );
1746 2282
1747 2283 foreach ( $bot_agents as $bot_agent ) {
1748 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 ) ) {
1749 2384 return true;
1750 2385 }
1751 2386 }
1752 2387