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 +1088 -266 12.2.3 → 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
@@ -16,8 +17,11 @@
16 17 require_once __DIR__ . '/functions.php';
17 18
18 19 /**
19 20 * A class providing device properties detection.
21 + *
22 + * Note: str_contains() and other PHP8+ functions that have a polyfill in core are not used here,
23 + * as wp-includes/compat.php may not be loaded yet.
20 24 */
21 25 class User_Agent_Info {
22 26
23 27 /**
@@ -59,22 +63,55 @@
59 63 * Stores the device platform name
60 64 *
61 65 * @var null|string
62 66 */
63 - private $platform = null;
64 - const PLATFORM_WINDOWS = 'windows';
65 - const PLATFORM_IPHONE = 'iphone';
66 - const PLATFORM_IPOD = 'ipod';
67 - const PLATFORM_IPAD = 'ipad';
68 - const PLATFORM_BLACKBERRY = 'blackberry';
69 - const PLATFORM_BLACKBERRY_10 = 'blackberry_10';
70 - const PLATFORM_SYMBIAN = 'symbian_series60';
71 - const PLATFORM_SYMBIAN_S40 = 'symbian_series40';
72 - const PLATFORM_J2ME_MIDP = 'j2me_midp';
73 - const PLATFORM_ANDROID = 'android';
74 - const PLATFORM_ANDROID_TABLET = 'android_tablet';
75 - 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';
76 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 +
77 114 /**
78 115 * A list of dumb-phone user agent parts.
79 116 *
80 117 * @var array
@@ -136,9 +173,9 @@
136 173 public function __construct( $ua = '' ) {
137 174 if ( $ua ) {
138 175 $this->useragent = $ua;
139 176 } elseif ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
140 - $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.
141 178 }
142 179 }
143 180
144 181 /**
@@ -146,65 +183,65 @@
146 183 *
147 184 * @return string The matched User Agent name, false otherwise.
148 185 */
149 186 public function get_mobile_user_agent_name() {
150 - if ( $this->is_chrome_for_iOS() ) { // Keep this check before the safari rule.
187 + if ( static::is_chrome_for_iOS() ) { // Keep this check before the safari rule.
151 188 return 'chrome-for-ios';
152 - } elseif ( $this->is_iphone_or_ipod( 'iphone-safari' ) ) {
189 + } elseif ( static::is_iphone_or_ipod( 'iphone-safari' ) ) {
153 190 return 'iphone';
154 - } elseif ( $this->is_ipad( 'ipad-safari' ) ) {
191 + } elseif ( static::is_ipad( 'ipad-safari' ) ) {
155 192 return 'ipad';
156 - } elseif ( $this->is_android_tablet() ) { // Keep this check before the android rule.
193 + } elseif ( static::is_android_tablet() ) { // Keep this check before the android rule.
157 194 return 'android_tablet';
158 - } elseif ( $this->is_android() ) {
195 + } elseif ( static::is_android() ) {
159 196 return 'android';
160 - } elseif ( $this->is_blackberry_10() ) {
197 + } elseif ( static::is_blackberry_10() ) {
161 198 return 'blackberry_10';
162 - } elseif ( $this->is_blackbeberry() ) {
199 + } elseif ( static::is_blackbeberry() ) {
163 200 return 'blackberry';
164 - } elseif ( $this->is_WindowsPhone7() ) {
201 + } elseif ( static::is_WindowsPhone7() ) {
165 202 return 'win7';
166 - } elseif ( $this->is_windows_phone_8() ) {
203 + } elseif ( static::is_windows_phone_8() ) {
167 204 return 'winphone8';
168 - } elseif ( $this->is_opera_mini() ) {
205 + } elseif ( static::is_opera_mini() ) {
169 206 return 'opera-mini';
170 - } elseif ( $this->is_opera_mini_dumb() ) {
207 + } elseif ( static::is_opera_mini_dumb() ) {
171 208 return 'opera-mini-dumb';
172 - } elseif ( $this->is_opera_mobile() ) {
209 + } elseif ( static::is_opera_mobile() ) {
173 210 return 'opera-mobi';
174 - } elseif ( $this->is_blackberry_tablet() ) {
211 + } elseif ( static::is_blackberry_tablet() ) {
175 212 return 'blackberry_tablet';
176 - } elseif ( $this->is_kindle_fire() ) {
213 + } elseif ( static::is_kindle_fire() ) {
177 214 return 'kindle-fire';
178 - } elseif ( $this->is_PalmWebOS() ) {
215 + } elseif ( static::is_PalmWebOS() ) {
179 216 return 'webos';
180 - } elseif ( $this->is_S60_OSSBrowser() ) {
217 + } elseif ( static::is_S60_OSSBrowser() ) {
181 218 return 'series60';
182 - } elseif ( $this->is_firefox_os() ) {
219 + } elseif ( static::is_firefox_os() ) {
183 220 return 'firefoxOS';
184 - } elseif ( $this->is_firefox_mobile() ) {
221 + } elseif ( static::is_firefox_mobile() ) {
185 222 return 'firefox_mobile';
186 - } elseif ( $this->is_MaemoTablet() ) {
223 + } elseif ( static::is_MaemoTablet() ) {
187 224 return 'maemo';
188 - } elseif ( $this->is_MeeGo() ) {
225 + } elseif ( static::is_MeeGo() ) {
189 226 return 'meego';
190 - } elseif ( $this->is_TouchPad() ) {
227 + } elseif ( static::is_TouchPad() ) {
191 228 return 'hp_tablet';
192 - } elseif ( $this->is_facebook_for_iphone() ) {
229 + } elseif ( static::is_facebook_for_iphone() ) {
193 230 return 'facebook-for-iphone';
194 - } elseif ( $this->is_facebook_for_ipad() ) {
231 + } elseif ( static::is_facebook_for_ipad() ) {
195 232 return 'facebook-for-ipad';
196 - } elseif ( $this->is_twitter_for_iphone() ) {
233 + } elseif ( static::is_twitter_for_iphone() ) {
197 234 return 'twitter-for-iphone';
198 - } elseif ( $this->is_twitter_for_ipad() ) {
235 + } elseif ( static::is_twitter_for_ipad() ) {
199 236 return 'twitter-for-ipad';
200 - } elseif ( $this->is_wordpress_for_ios() ) {
237 + } elseif ( static::is_wordpress_for_ios() ) {
201 238 return 'ios-app';
202 - } elseif ( $this->is_iphone_or_ipod( 'iphone-not-safari' ) ) {
239 + } elseif ( static::is_iphone_or_ipod( 'iphone-not-safari' ) ) {
203 240 return 'iphone-unknown';
204 - } elseif ( $this->is_ipad( 'ipad-not-safari' ) ) {
241 + } elseif ( static::is_ipad( 'ipad-not-safari' ) ) {
205 242 return 'ipad-unknown';
206 - } elseif ( $this->is_Nintendo_3DS() ) {
243 + } elseif ( static::is_Nintendo_3DS() ) {
207 244 return 'nintendo-3ds';
208 245 } else {
209 246 $agent = $this->useragent;
210 247 $dumb_agents = $this->dumb_agents;
@@ -222,46 +259,51 @@
222 259 * This method detects the mobile device's platform. All return strings are from the class constants.
223 260 * Note that this function returns the platform name, not the UA name/type. You should use a different function
224 261 * if you need to test the UA capabilites.
225 262 *
226 - * @return string Name of the platform, false otherwise.
263 + * @return string|bool Name of the platform, false otherwise.
227 264 */
228 265 public function get_platform() {
229 266 if ( isset( $this->platform ) ) {
230 - return $this->platform;
267 + return $this->platform;
231 268 }
232 269
233 - if ( strpos( $this->useragent, 'windows phone' ) !== false ) {
234 - $this->platform = self::PLATFORM_WINDOWS;
235 - } elseif ( strpos( $this->useragent, 'windows ce' ) !== false ) {
270 + if ( empty( $this->useragent ) ) {
271 + return false;
272 + }
273 +
274 + $ua = strtolower( $this->useragent );
275 + if ( strpos( $ua, 'windows phone' ) !== false ) {
236 276 $this->platform = self::PLATFORM_WINDOWS;
237 - } 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 ) {
238 280 $this->platform = self::PLATFORM_IPAD;
239 - } elseif ( strpos( $this->useragent, 'ipod' ) !== false ) {
281 + } elseif ( strpos( $ua, 'ipod' ) !== false ) {
240 282 $this->platform = self::PLATFORM_IPOD;
241 - } elseif ( strpos( $this->useragent, 'iphone' ) !== false ) {
283 + } elseif ( strpos( $ua, 'iphone' ) !== false ) {
242 284 $this->platform = self::PLATFORM_IPHONE;
243 - } elseif ( strpos( $this->useragent, 'android' ) !== false ) {
244 - if ( $this->is_android_tablet() ) {
285 + } elseif ( strpos( $ua, 'android' ) !== false ) {
286 + if ( static::is_android_tablet() ) {
245 287 $this->platform = self::PLATFORM_ANDROID_TABLET;
246 288 } else {
247 289 $this->platform = self::PLATFORM_ANDROID;
248 290 }
249 - } elseif ( $this->is_kindle_fire() ) {
291 + } elseif ( static::is_kindle_fire() ) {
250 292 $this->platform = self::PLATFORM_ANDROID_TABLET;
251 - } elseif ( $this->is_blackberry_10() ) {
293 + } elseif ( static::is_blackberry_10() ) {
252 294 $this->platform = self::PLATFORM_BLACKBERRY_10;
253 - } elseif ( strpos( $this->useragent, 'blackberry' ) !== false ) {
295 + } elseif ( strpos( $ua, 'blackberry' ) !== false ) {
254 296 $this->platform = self::PLATFORM_BLACKBERRY;
255 - } elseif ( $this->is_blackberry_tablet() ) {
297 + } elseif ( static::is_blackberry_tablet() ) {
256 298 $this->platform = self::PLATFORM_BLACKBERRY;
257 - } elseif ( $this->is_symbian_platform() ) {
299 + } elseif ( static::is_symbian_platform() ) {
258 300 $this->platform = self::PLATFORM_SYMBIAN;
259 - } elseif ( $this->is_symbian_s40_platform() ) {
301 + } elseif ( static::is_symbian_s40_platform() ) {
260 302 $this->platform = self::PLATFORM_SYMBIAN_S40;
261 - } elseif ( $this->is_J2ME_platform() ) {
303 + } elseif ( static::is_J2ME_platform() ) {
262 304 $this->platform = self::PLATFORM_J2ME_MIDP;
263 - } elseif ( $this->is_firefox_os() ) {
305 + } elseif ( static::is_firefox_os() ) {
264 306 $this->platform = self::PLATFORM_FIREFOX_OS;
265 307 } else {
266 308 $this->platform = false;
267 309 }
@@ -269,8 +311,85 @@
269 311 return $this->platform;
270 312 }
271 313
272 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 + /**
273 392 * This method detects for UA which can display iPhone-optimized web content.
274 393 * Includes iPhone, iPod Touch, Android, WebOS, Fennec (Firefox mobile), etc.
275 394 */
276 395 public function isTierIphone() {
@@ -281,79 +400,79 @@
281 400 $this->matched_agent = 'iphone';
282 401 $this->isTierIphone = true;
283 402 $this->isTierRichCss = false;
284 403 $this->isTierGenericMobile = false;
285 - } elseif ( $this->is_android() ) {
404 + } elseif ( static::is_android() ) {
286 405 $this->matched_agent = 'android';
287 406 $this->isTierIphone = true;
288 407 $this->isTierRichCss = false;
289 408 $this->isTierGenericMobile = false;
290 - } elseif ( $this->is_windows_phone_8() ) {
409 + } elseif ( static::is_windows_phone_8() ) {
291 410 $this->matched_agent = 'winphone8';
292 411 $this->isTierIphone = true;
293 412 $this->isTierRichCss = false;
294 413 $this->isTierGenericMobile = false;
295 - } elseif ( $this->is_WindowsPhone7() ) {
414 + } elseif ( static::is_WindowsPhone7() ) {
296 415 $this->matched_agent = 'win7';
297 416 $this->isTierIphone = true;
298 417 $this->isTierRichCss = false;
299 418 $this->isTierGenericMobile = false;
300 - } elseif ( $this->is_blackberry_10() ) {
419 + } elseif ( static::is_blackberry_10() ) {
301 420 $this->matched_agent = 'blackberry-10';
302 421 $this->isTierIphone = true;
303 422 $this->isTierRichCss = false;
304 423 $this->isTierGenericMobile = false;
305 - } elseif ( $this->is_blackbeberry() && 'blackberry-webkit' === $this->detect_blackberry_browser_version() ) {
424 + } elseif ( static::is_blackbeberry() && 'blackberry-webkit' === static::detect_blackberry_browser_version() ) {
306 425 $this->matched_agent = 'blackberry-webkit';
307 426 $this->isTierIphone = true;
308 427 $this->isTierRichCss = false;
309 428 $this->isTierGenericMobile = false;
310 - } elseif ( $this->is_blackberry_tablet() ) {
429 + } elseif ( static::is_blackberry_tablet() ) {
311 430 $this->matched_agent = 'blackberry_tablet';
312 431 $this->isTierIphone = true;
313 432 $this->isTierRichCss = false;
314 433 $this->isTierGenericMobile = false;
315 - } elseif ( $this->is_PalmWebOS() ) {
434 + } elseif ( static::is_PalmWebOS() ) {
316 435 $this->matched_agent = 'webos';
317 436 $this->isTierIphone = true;
318 437 $this->isTierRichCss = false;
319 438 $this->isTierGenericMobile = false;
320 - } elseif ( $this->is_TouchPad() ) {
439 + } elseif ( static::is_TouchPad() ) {
321 440 $this->matched_agent = 'hp_tablet';
322 441 $this->isTierIphone = true;
323 442 $this->isTierRichCss = false;
324 443 $this->isTierGenericMobile = false;
325 - } elseif ( $this->is_firefox_os() ) {
444 + } elseif ( static::is_firefox_os() ) {
326 445 $this->matched_agent = 'firefoxOS';
327 446 $this->isTierIphone = true;
328 447 $this->isTierRichCss = false;
329 448 $this->isTierGenericMobile = false;
330 - } elseif ( $this->is_firefox_mobile() ) {
449 + } elseif ( static::is_firefox_mobile() ) {
331 450 $this->matched_agent = 'fennec';
332 451 $this->isTierIphone = true;
333 452 $this->isTierRichCss = false;
334 453 $this->isTierGenericMobile = false;
335 - } elseif ( $this->is_opera_mobile() ) {
454 + } elseif ( static::is_opera_mobile() ) {
336 455 $this->matched_agent = 'opera-mobi';
337 456 $this->isTierIphone = true;
338 457 $this->isTierRichCss = false;
339 458 $this->isTierGenericMobile = false;
340 - } elseif ( $this->is_MaemoTablet() ) {
459 + } elseif ( static::is_MaemoTablet() ) {
341 460 $this->matched_agent = 'maemo';
342 461 $this->isTierIphone = true;
343 462 $this->isTierRichCss = false;
344 463 $this->isTierGenericMobile = false;
345 - } elseif ( $this->is_MeeGo() ) {
464 + } elseif ( static::is_MeeGo() ) {
346 465 $this->matched_agent = 'meego';
347 466 $this->isTierIphone = true;
348 467 $this->isTierRichCss = false;
349 468 $this->isTierGenericMobile = false;
350 - } elseif ( $this->is_kindle_touch() ) {
469 + } elseif ( static::is_kindle_touch() ) {
351 470 $this->matched_agent = 'kindle-touch';
352 471 $this->isTierIphone = true;
353 472 $this->isTierRichCss = false;
354 473 $this->isTierGenericMobile = false;
355 - } elseif ( $this->is_Nintendo_3DS() ) {
474 + } elseif ( static::is_Nintendo_3DS() ) {
356 475 $this->matched_agent = 'nintendo-3ds';
357 476 $this->isTierIphone = true;
358 477 $this->isTierRichCss = false;
359 478 $this->isTierGenericMobile = false;
@@ -376,20 +495,20 @@
376 495 return false;
377 496 }
378 497
379 498 // The following devices are explicitly ok.
380 - if ( $this->is_S60_OSSBrowser() ) {
499 + if ( static::is_S60_OSSBrowser() ) {
381 500 $this->matched_agent = 'series60';
382 501 $this->isTierIphone = false;
383 502 $this->isTierRichCss = true;
384 503 $this->isTierGenericMobile = false;
385 - } elseif ( $this->is_opera_mini() ) {
504 + } elseif ( static::is_opera_mini() ) {
386 505 $this->matched_agent = 'opera-mini';
387 506 $this->isTierIphone = false;
388 507 $this->isTierRichCss = true;
389 508 $this->isTierGenericMobile = false;
390 - } elseif ( $this->is_blackbeberry() ) {
391 - $detectedDevice = $this->detect_blackberry_browser_version();
509 + } elseif ( static::is_blackbeberry() ) {
510 + $detectedDevice = static::detect_blackberry_browser_version();
392 511 if (
393 512 'blackberry-5' === $detectedDevice
394 513 || 'blackberry-4.7' === $detectedDevice
395 514 || 'blackberry-4.6' === $detectedDevice
@@ -412,15 +531,16 @@
412 531 *
413 532 * @return bool
414 533 */
415 534 public function is_tablet() {
535 + $ua = $this->useragent;
416 536 return ( 0 // Never true, but makes it easier to manage our list of tablet conditions.
417 - || self::is_ipad()
418 - || self::is_android_tablet()
419 - || self::is_blackberry_tablet()
420 - || self::is_kindle_fire()
421 - || self::is_MaemoTablet()
422 - || 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 )
423 543 );
424 544 }
425 545
426 546 /**
@@ -445,8 +565,25 @@
445 565 }
446 566 }
447 567
448 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 + /**
449 586 * Detects if the current UA is iPhone Mobile Safari or another iPhone or iPod Touch Browser.
450 587 *
451 588 * They type can check for any iPhone, an iPhone using Safari, or an iPhone using something other than Safari.
452 589 *
@@ -453,16 +590,19 @@
453 590 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPhone browser),
454 591 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
455 592 * Otherwise those browsers will be 'catched' by the iphone string.
456 593 *
457 - * @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'].
458 596 */
459 - public static function is_iphone_or_ipod( $type = 'iphone-any' ) {
460 - 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 ) ) {
461 601 return false;
462 602 }
463 603
464 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
604 + $ua = strtolower( wp_unslash( $user_agent ) );
465 605 $is_iphone = ( strpos( $ua, 'iphone' ) !== false ) || ( strpos( $ua, 'ipod' ) !== false );
466 606 $is_safari = ( false !== strpos( $ua, 'safari' ) );
467 607
468 608 if ( 'iphone-safari' === $type ) {
@@ -478,19 +618,22 @@
478 618 * Detects if the current UA is Chrome for iOS
479 619 *
480 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>.
481 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'].
482 624 */
483 - public static function is_chrome_for_iOS() {
484 - 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 ) ) {
485 628 return false;
486 629 }
487 630
488 - if ( self::is_iphone_or_ipod( 'iphone-safari' ) === false ) {
631 + if ( self::is_iphone_or_ipod( 'iphone-safari', $user_agent ) === false ) {
489 632 return false;
490 633 }
491 634
492 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
635 + $ua = strtolower( wp_unslash( $user_agent ) );
493 636
494 637 if ( strpos( $ua, 'crios/' ) !== false ) {
495 638 return true;
496 639 } else {
@@ -502,15 +645,18 @@
502 645 * Detects if the current UA is Twitter for iPhone
503 646 *
504 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
505 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'].
506 651 */
507 - public static function is_twitter_for_iphone() {
508 - 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 ) ) {
509 655 return false;
510 656 }
511 657
512 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
658 + $ua = strtolower( wp_unslash( $user_agent ) );
513 659
514 660 if ( strpos( $ua, 'ipad' ) !== false ) {
515 661 return false;
516 662 }
@@ -526,15 +672,18 @@
526 672 * Detects if the current UA is Twitter for iPad
527 673 *
528 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
529 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'].
530 678 */
531 - public static function is_twitter_for_ipad() {
532 - 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 ) ) {
533 682 return false;
534 683 }
535 684
536 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
685 + $ua = strtolower( wp_unslash( $user_agent ) );
537 686
538 687 if ( strpos( $ua, 'twitter for ipad' ) !== false ) {
539 688 return true;
540 689 } elseif ( strpos( $ua, 'ipad' ) !== false && strpos( $ua, 'twitter for iphone' ) !== false ) {
@@ -548,15 +697,18 @@
548 697 * Detects if the current UA is Facebook for iPhone
549 698 * - Facebook 4020.0 (iPhone; iPhone OS 5.0.1; fr_FR)
550 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]
551 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'].
552 703 */
553 - public static function is_facebook_for_iphone() {
554 - 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 ) ) {
555 707 return false;
556 708 }
557 709
558 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
710 + $ua = strtolower( wp_unslash( $user_agent ) );
559 711
560 712 if ( false === strpos( $ua, 'iphone' ) ) {
561 713 return false;
562 714 }
@@ -576,15 +728,18 @@
576 728 * Detects if the current UA is Facebook for iPad
577 729 * - Facebook 4020.0 (iPad; iPhone OS 5.0.1; en_US)
578 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]
579 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'].
580 734 */
581 - public static function is_facebook_for_ipad() {
582 - 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 ) ) {
583 738 return false;
584 739 }
585 740
586 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
741 + $ua = strtolower( wp_unslash( $user_agent ) );
587 742
588 743 if ( false === strpos( $ua, 'ipad' ) ) {
589 744 return false;
590 745 }
@@ -597,15 +752,18 @@
597 752 }
598 753
599 754 /**
600 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'].
601 758 */
602 - public static function is_wordpress_for_ios() {
603 - 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 ) ) {
604 762 return false;
605 763 }
606 764
607 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
765 + $ua = strtolower( wp_unslash( $user_agent ) );
608 766 if ( false !== strpos( $ua, 'wp-iphone' ) ) {
609 767 return true;
610 768 } else {
611 769 return false;
@@ -619,17 +777,18 @@
619 777 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPad browser),
620 778 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
621 779 * Otherwise those browsers will be 'catched' by the ipad string.
622 780 *
623 - * @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'].
624 783 */
625 - public static function is_ipad( $type = 'ipad-any' ) {
626 -
627 - 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 ) ) {
628 787 return false;
629 788 }
630 789
631 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
790 + $ua = strtolower( wp_unslash( $user_agent ) );
632 791
633 792 $is_ipad = ( false !== strpos( $ua, 'ipad' ) );
634 793 $is_safari = ( false !== strpos( $ua, 'safari' ) );
635 794
@@ -647,16 +806,18 @@
647 806 *
648 807 * See http://www.useragentstring.com/pages/Fennec/
649 808 * Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.1.1) Gecko/20110415 Firefox/4.0.2pre Fennec/4.0.1
650 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'].
651 812 */
652 - public static function is_firefox_mobile() {
653 -
654 - 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 ) ) {
655 816 return false;
656 817 }
657 818
658 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
819 + $ua = strtolower( wp_unslash( $user_agent ) );
659 820
660 821 if ( strpos( $ua, 'fennec' ) !== false ) {
661 822 return true;
662 823 } else {
@@ -669,16 +830,18 @@
669 830 *
670 831 * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
671 832 * Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion
672 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'].
673 836 */
674 - public static function is_firefox_desktop() {
675 -
676 - 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 ) ) {
677 840 return false;
678 841 }
679 842
680 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
843 + $ua = strtolower( wp_unslash( $user_agent ) );
681 844
682 845 if ( false !== strpos( $ua, 'firefox' ) && false === strpos( $ua, 'mobile' ) && false === strpos( $ua, 'tablet' ) ) {
683 846 return true;
684 847 } else {
@@ -689,16 +852,18 @@
689 852 /**
690 853 * Detects if the current browser is FirefoxOS Native browser
691 854 *
692 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'].
693 858 */
694 - public static function is_firefox_os() {
695 -
696 - 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 ) ) {
697 862 return false;
698 863 }
699 864
700 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
865 + $ua = strtolower( wp_unslash( $user_agent ) );
701 866
702 867 if ( strpos( $ua, 'mozilla' ) !== false && strpos( $ua, 'mobile' ) !== false && strpos( $ua, 'gecko' ) !== false && strpos( $ua, 'firefox' ) !== false ) {
703 868 return true;
704 869 } else {
@@ -706,20 +871,75 @@
706 871 }
707 872 }
708 873
709 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 + /**
710 927 * Detect modern Opera desktop
711 928 *
712 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
713 930 *
714 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'].
715 934 */
716 - public static function is_opera_desktop() {
717 - 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 ) ) {
718 938 return false;
719 939 }
720 940
721 - 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/' ) ) {
722 942 return false;
723 943 }
724 944
725 945 return true;
@@ -735,15 +955,18 @@
735 955 * The rendering engine is on Opera's server.)
736 956 *
737 957 * Opera/9.80 (Windows NT 6.1; Opera Mobi/14316; U; en) Presto/2.7.81 Version/11.00"
738 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'].
739 961 */
740 - public static function is_opera_mobile() {
741 - 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 ) ) {
742 965 return false;
743 966 }
744 967
745 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
968 + $ua = strtolower( wp_unslash( $user_agent ) );
746 969
747 970 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mobi' ) !== false ) {
748 971 return true;
749 972 } elseif ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'nintendo dsi' ) !== false ) {
@@ -762,15 +985,18 @@
762 985 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
763 986 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
764 987 * Opera/9.80 (Series 60; Opera Mini/5.1.22783/23.334; U; en) Presto/2.5.25 Version/10.54
765 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'].
766 991 */
767 - public static function is_opera_mini() {
768 - 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 ) ) {
769 995 return false;
770 996 }
771 997
772 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
998 + $ua = strtolower( wp_unslash( $user_agent ) );
773 999
774 1000 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mini' ) !== false ) {
775 1001 return true;
776 1002 } else {
@@ -780,16 +1006,20 @@
780 1006
781 1007 /**
782 1008 * Detects if the current browser is Opera Mini, but not on a smart device OS(Android, iOS, etc)
783 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'].
784 1012 */
785 - public static function is_opera_mini_dumb() {
786 - 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 ) ) {
787 1016 return false;
788 1017 }
789 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
790 1018
791 - if ( self::is_opera_mini() ) {
1019 + $ua = strtolower( wp_unslash( $user_agent ) );
1020 +
1021 + if ( self::is_opera_mini( $user_agent ) ) {
792 1022 if ( strpos( $ua, 'android' ) !== false || strpos( $ua, 'iphone' ) !== false || strpos( $ua, 'ipod' ) !== false
793 1023 || strpos( $ua, 'ipad' ) !== false || strpos( $ua, 'blackberry' ) !== false ) {
794 1024 return false;
795 1025 } else {
@@ -800,21 +1030,132 @@
800 1030 }
801 1031 }
802 1032
803 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 + /**
804 1142 * Detects if the current browser is a Windows Phone 7 device.
805 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'].
806 1146 */
807 - public static function is_WindowsPhone7() {
808 - 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 ) ) {
809 1150 return false;
810 1151 }
811 1152
812 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1153 + $ua = strtolower( wp_unslash( $user_agent ) );
813 1154
814 1155 if ( false === strpos( $ua, 'windows phone os 7' ) ) {
815 1156 return false;
816 - } 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 ) ) {
817 1158 return false;
818 1159 } else {
819 1160 return true;
820 1161 }
@@ -822,15 +1163,18 @@
822 1163
823 1164 /**
824 1165 * Detects if the current browser is a Windows Phone 8 device.
825 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'].
826 1169 */
827 - public static function is_windows_phone_8() {
828 - 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 ) ) {
829 1173 return false;
830 1174 }
831 1175
832 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1176 + $ua = strtolower( wp_unslash( $user_agent ) );
833 1177 if ( strpos( $ua, 'windows phone 8' ) === false ) {
834 1178 return false;
835 1179 } else {
836 1180 return true;
@@ -841,19 +1185,22 @@
841 1185 * Detects if the current browser is on a Palm device running the new WebOS. This EXCLUDES TouchPad.
842 1186 *
843 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
844 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'].
845 1191 */
846 - public static function is_PalmWebOS() {
847 - 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 ) ) {
848 1195 return false;
849 1196 }
850 1197
851 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1198 + $ua = strtolower( wp_unslash( $user_agent ) );
852 1199
853 1200 if ( false === strpos( $ua, 'webos' ) ) {
854 1201 return false;
855 - } 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 ) ) {
856 1203 return false;
857 1204 } else {
858 1205 return true;
859 1206 }
@@ -863,17 +1210,20 @@
863 1210 * Detects if the current browser is the HP TouchPad default browser. This excludes phones wt WebOS.
864 1211 *
865 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
866 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'].
867 1216 */
868 - public static function is_TouchPad() {
869 - 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 ) ) {
870 1220 return false;
871 1221 }
872 1222
873 - $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 ) );
874 1224 if ( false !== strpos( $http_user_agent, 'hp-tablet' ) || false !== strpos( $http_user_agent, 'hpwos' ) || false !== strpos( $http_user_agent, 'touchpad' ) ) {
875 - 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 ) ) {
876 1226 return false;
877 1227 } else {
878 1228 return true;
879 1229 }
@@ -889,17 +1239,19 @@
889 1239 *
890 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
891 1241 *
892 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'].
893 1245 */
894 - public static function is_S60_OSSBrowser() {
895 -
896 - 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 ) ) {
897 1249 return false;
898 1250 }
899 1251
900 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
901 - 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 ) ) {
902 1254 return false;
903 1255 }
904 1256
905 1257 $pos_webkit = strpos( $agent, 'webkit' );
@@ -920,16 +1272,18 @@
920 1272 }
921 1273
922 1274 /**
923 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'].
924 1278 */
925 - public static function is_symbian_platform() {
926 -
927 - 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 ) ) {
928 1282 return false;
929 1283 }
930 1284
931 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1285 + $agent = strtolower( wp_unslash( $user_agent ) );
932 1286
933 1287 $pos_webkit = strpos( $agent, 'webkit' );
934 1288 if ( false !== $pos_webkit ) {
935 1289 // First, test for WebKit, then make sure it's either Symbian or S60.
@@ -954,16 +1308,18 @@
954 1308 /**
955 1309 * Detects if the device platform is the Symbian Series 40.
956 1310 * Nokia Browser for Series 40 is a proxy based browser, previously known as Ovi Browser.
957 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'].
958 1314 */
959 - public static function is_symbian_s40_platform() {
960 -
961 - 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 ) ) {
962 1318 return false;
963 1319 }
964 1320
965 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1321 + $agent = strtolower( wp_unslash( $user_agent ) );
966 1322
967 1323 if ( strpos( $agent, 'series40' ) !== false ) {
968 1324 if ( strpos( $agent, 'nokia' ) !== false || strpos( $agent, 'ovibrowser' ) !== false || strpos( $agent, 'nokiabrowser' ) !== false ) {
969 1325 return true;
@@ -975,17 +1331,19 @@
975 1331
976 1332 /**
977 1333 * Returns if the device belongs to J2ME capable family.
978 1334 *
1335 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1336 + *
979 1337 * @return bool
980 1338 */
981 - public static function is_J2ME_platform() {
982 -
983 - 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 ) ) {
984 1342 return false;
985 1343 }
986 1344
987 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1345 + $agent = strtolower( wp_unslash( $user_agent ) );
988 1346
989 1347 if ( strpos( $agent, 'j2me/midp' ) !== false ) {
990 1348 return true;
991 1349 } elseif ( strpos( $agent, 'midp' ) !== false && strpos( $agent, 'cldc' ) ) {
@@ -995,16 +1353,18 @@
995 1353 }
996 1354
997 1355 /**
998 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'].
999 1359 */
1000 - public static function is_MaemoTablet() {
1001 -
1002 - 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 ) ) {
1003 1363 return false;
1004 1364 }
1005 1365
1006 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1366 + $agent = strtolower( wp_unslash( $user_agent ) );
1007 1367
1008 1368 $pos_maemo = strpos( $agent, 'maemo' );
1009 1369 if ( false === $pos_maemo ) {
1010 1370 return false;
@@ -1011,9 +1371,9 @@
1011 1371 }
1012 1372
1013 1373 // Must be Linux + Tablet, or else it could be something else.
1014 1374 if ( strpos( $agent, 'tablet' ) !== false && strpos( $agent, 'linux' ) !== false ) {
1015 - 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 ) ) {
1016 1376 return false;
1017 1377 } else {
1018 1378 return true;
1019 1379 }
@@ -1023,20 +1383,22 @@
1023 1383 }
1024 1384
1025 1385 /**
1026 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'].
1027 1389 */
1028 - public static function is_MeeGo() {
1029 -
1030 - 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 ) ) {
1031 1393 return false;
1032 1394 }
1033 1395
1034 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1396 + $ua = strtolower( wp_unslash( $user_agent ) );
1035 1397
1036 1398 if ( false === strpos( $ua, 'meego' ) ) {
1037 1399 return false;
1038 - } 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 ) ) {
1039 1401 return false;
1040 1402 } else {
1041 1403 return true;
1042 1404 }
@@ -1043,16 +1405,18 @@
1043 1405 }
1044 1406
1045 1407 /**
1046 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'].
1047 1411 */
1048 - public static function is_webkit() {
1049 -
1050 - 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 ) ) {
1051 1415 return false;
1052 1416 }
1053 1417
1054 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1418 + $agent = strtolower( wp_unslash( $user_agent ) );
1055 1419
1056 1420 $pos_webkit = strpos( $agent, 'webkit' );
1057 1421
1058 1422 if ( false !== $pos_webkit ) {
@@ -1064,19 +1428,22 @@
1064 1428
1065 1429 /**
1066 1430 * Detects if the current browser is the Native Android browser.
1067 1431 *
1432 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1433 + *
1068 1434 * @return boolean true if the browser is Android otherwise false
1069 1435 */
1070 - public static function is_android() {
1071 - 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 ) ) {
1072 1439 return false;
1073 1440 }
1074 1441
1075 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1442 + $agent = strtolower( wp_unslash( $user_agent ) );
1076 1443 $pos_android = strpos( $agent, 'android' );
1077 1444 if ( false !== $pos_android ) {
1078 - 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 ) ) {
1079 1446 return false;
1080 1447 } else {
1081 1448 return true;
1082 1449 }
@@ -1088,16 +1455,19 @@
1088 1455 /**
1089 1456 * Detects if the current browser is the Native Android Tablet browser.
1090 1457 * Assumes 'Android' should be in the user agent, but not 'mobile'
1091 1458 *
1459 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1460 + *
1092 1461 * @return boolean true if the browser is Android and not 'mobile' otherwise false
1093 1462 */
1094 - public static function is_android_tablet() {
1095 - 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 ) ) {
1096 1466 return false;
1097 1467 }
1098 1468
1099 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1469 + $agent = strtolower( wp_unslash( $user_agent ) );
1100 1470
1101 1471 $pos_android = strpos( $agent, 'android' );
1102 1472 $pos_mobile = strpos( $agent, 'mobile' );
1103 1473 $post_android_app = strpos( $agent, 'wp-android' );
@@ -1102,9 +1472,9 @@
1102 1472 $pos_mobile = strpos( $agent, 'mobile' );
1103 1473 $post_android_app = strpos( $agent, 'wp-android' );
1104 1474
1105 1475 if ( false !== $pos_android && false === $pos_mobile && false === $post_android_app ) {
1106 - 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 ) ) {
1107 1477 return false;
1108 1478 } else {
1109 1479 return true;
1110 1480 }
@@ -1118,16 +1488,19 @@
1118 1488 *
1119 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
1120 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
1121 1491 *
1492 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1493 + *
1122 1494 * @return boolean true if the browser is Kindle Fire Native browser otherwise false
1123 1495 */
1124 - public static function is_kindle_fire() {
1125 - 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 ) ) {
1126 1499 return false;
1127 1500 }
1128 1501
1129 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1502 + $agent = strtolower( wp_unslash( $user_agent ) );
1130 1503 $pos_silk = strpos( $agent, 'silk/' );
1131 1504 $pos_silk_acc = strpos( $agent, 'silk-accelerated=' );
1132 1505 if ( false !== $pos_silk && false !== $pos_silk_acc ) {
1133 1506 return true;
@@ -1140,17 +1513,21 @@
1140 1513 * Detects if the current browser is the Kindle Touch Native browser
1141 1514 *
1142 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+
1143 1516 *
1517 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1518 + *
1144 1519 * @return boolean true if the browser is Kindle monochrome Native browser otherwise false
1145 1520 */
1146 - public static function is_kindle_touch() {
1147 - 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 ) ) {
1148 1524 return false;
1149 1525 }
1150 - $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 ) );
1151 1528 $pos_kindle_touch = strpos( $agent, 'kindle/3.0+' );
1152 - if ( false !== $pos_kindle_touch && false === self::is_kindle_fire() ) {
1529 + if ( false !== $pos_kindle_touch && false === self::is_kindle_fire( $user_agent ) ) {
1153 1530 return true;
1154 1531 } else {
1155 1532 return false;
1156 1533 }
@@ -1157,15 +1534,18 @@
1157 1534 }
1158 1535
1159 1536 /**
1160 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'].
1161 1540 */
1162 - public static function is_windows8_auth() {
1163 - 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 ) ) {
1164 1544 return false;
1165 1545 }
1166 1546
1167 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1547 + $agent = strtolower( wp_unslash( $user_agent ) );
1168 1548 $pos = strpos( $agent, 'msauthhost' );
1169 1549 if ( false !== $pos ) {
1170 1550 return true;
1171 1551 } else {
@@ -1174,15 +1554,18 @@
1174 1554 }
1175 1555
1176 1556 /**
1177 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'].
1178 1560 */
1179 - public static function is_wordpress_for_win8() {
1180 - 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 ) ) {
1181 1564 return false;
1182 1565 }
1183 1566
1184 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1567 + $agent = strtolower( wp_unslash( $user_agent ) );
1185 1568 $pos = strpos( $agent, 'wp-windows8' );
1186 1569 if ( false !== $pos ) {
1187 1570 return true;
1188 1571 } else {
@@ -1191,15 +1574,18 @@
1191 1574 }
1192 1575
1193 1576 /**
1194 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'].
1195 1580 */
1196 - public static function is_wordpress_desktop_app() {
1197 - 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 ) ) {
1198 1584 return false;
1199 1585 }
1200 1586
1201 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1587 + $agent = strtolower( wp_unslash( $user_agent ) );
1202 1588 $pos = strpos( $agent, 'WordPressDesktop' );
1203 1589 if ( false !== $pos ) {
1204 1590 return true;
1205 1591 } else {
@@ -1210,16 +1596,18 @@
1210 1596 /**
1211 1597 * The is_blackberry_tablet() method can be used to check the User Agent for a RIM blackberry tablet.
1212 1598 * The user agent of the BlackBerry® Tablet OS follows a format similar to the following:
1213 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'].
1214 1602 */
1215 - public static function is_blackberry_tablet() {
1216 -
1217 - 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 ) ) {
1218 1606 return false;
1219 1607 }
1220 1608
1221 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1609 + $agent = strtolower( wp_unslash( $user_agent ) );
1222 1610 $pos_playbook = stripos( $agent, 'PlayBook' );
1223 1611 $pos_rim_tablet = stripos( $agent, 'RIM Tablet' );
1224 1612
1225 1613 if ( ( false === $pos_playbook ) || ( false === $pos_rim_tablet ) ) {
@@ -1231,19 +1619,22 @@
1231 1619
1232 1620 /**
1233 1621 * The is_blackbeberry() method can be used to check the User Agent for a blackberry device.
1234 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'].
1235 1625 */
1236 - public static function is_blackbeberry() {
1237 - 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 ) ) {
1238 1629 return false;
1239 1630 }
1240 1631
1241 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1632 + $agent = strtolower( wp_unslash( $user_agent ) );
1242 1633
1243 1634 $pos_blackberry = strpos( $agent, 'blackberry' );
1244 1635 if ( false !== $pos_blackberry ) {
1245 - 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 ) ) {
1246 1637 return false;
1247 1638 } else {
1248 1639 return true;
1249 1640 }
@@ -1253,18 +1644,98 @@
1253 1644 }
1254 1645
1255 1646 /**
1256 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'].
1257 1650 */
1258 - public static function is_blackberry_10() {
1259 - 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 ) ) {
1260 1654 return false;
1261 1655 }
1262 - $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 ) );
1263 1658 return ( strpos( $agent, 'bb10' ) !== false ) && ( strpos( $agent, 'mobile' ) !== false );
1264 1659 }
1265 1660
1266 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 + /**
1267 1738 * Retrieve the blackberry OS version.
1268 1739 *
1269 1740 * Return strings are from the following list:
1270 1741 * - blackberry-10
@@ -1275,22 +1746,24 @@
1275 1746 * - blackberry-4.7
1276 1747 * - blackberry-4.6
1277 1748 * - blackberry-4.5
1278 1749 *
1750 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1751 + *
1279 1752 * @return string Version of the BB OS.
1280 1753 * If version is not found, get_blackbeberry_OS_version will return boolean false.
1281 1754 */
1282 - public static function get_blackbeberry_OS_version() {
1283 -
1284 - 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 ) ) {
1285 1758 return false;
1286 1759 }
1287 1760
1288 - if ( self::is_blackberry_10() ) {
1761 + if ( self::is_blackberry_10( $user_agent ) ) {
1289 1762 return 'blackberry-10';
1290 1763 }
1291 1764
1292 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1765 + $agent = strtolower( wp_unslash( $user_agent ) );
1293 1766
1294 1767 $pos_blackberry = stripos( $agent, 'blackberry' );
1295 1768 if ( false === $pos_blackberry ) {
1296 1769 // Not a blackberry device.
@@ -1309,9 +1782,9 @@
1309 1782 return 'blackberry-torch'; // Match the torch first edition. the 2nd edition should use the OS7 and doesn't need any special rule.
1310 1783 } elseif ( preg_match( '#Version\/([\d\.]+)#i', $agent, $matches ) ) { // Detecting the BB OS version for devices running OS 6.0 or higher.
1311 1784 $version = $matches[1];
1312 1785 $version_num = explode( '.', $version );
1313 - if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
1786 + if ( count( $version_num ) <= 1 ) {
1314 1787 return 'blackberry-6'; // not a BB device that match our rule.
1315 1788 } else {
1316 1789 return 'blackberry-' . $version_num[0];
1317 1790 }
@@ -1330,9 +1803,9 @@
1330 1803 }
1331 1804
1332 1805 $version_num = explode( '.', $version );
1333 1806
1334 - if ( is_array( $version_num ) === false || count( $version_num ) <= 1 ) {
1807 + if ( count( $version_num ) <= 1 ) {
1335 1808 return false;
1336 1809 }
1337 1810
1338 1811 $version_num_major = (int) $version_num[0];
@@ -1360,20 +1833,22 @@
1360 1833 * - blackberry-5
1361 1834 * - blackberry-4.7
1362 1835 * - blackberry-4.6
1363 1836 *
1837 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1838 + *
1364 1839 * @return string Type of the BB browser.
1365 1840 * If browser's version is not found, detect_blackbeberry_browser_version will return boolean false.
1366 1841 */
1367 - public static function detect_blackberry_browser_version() {
1368 -
1369 - 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 ) ) {
1370 1845 return false;
1371 1846 }
1372 1847
1373 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1848 + $agent = strtolower( wp_unslash( $user_agent ) );
1374 1849
1375 - if ( self::is_blackberry_10() ) {
1850 + if ( self::is_blackberry_10( $user_agent ) ) {
1376 1851 return 'blackberry-10';
1377 1852 }
1378 1853
1379 1854 $pos_blackberry = strpos( $agent, 'blackberry' );
@@ -1392,9 +1867,9 @@
1392 1867 }
1393 1868
1394 1869 $version_num = explode( '.', $matches[1] );
1395 1870
1396 - if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
1871 + if ( count( $version_num ) <= 1 ) {
1397 1872 return false;
1398 1873 }
1399 1874
1400 1875 $version_num_major = (int) $version_num[0];
@@ -1415,17 +1890,19 @@
1415 1890
1416 1891 /**
1417 1892 * Checks if a visitor is coming from one of the WordPress mobile apps.
1418 1893 *
1894 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1895 + *
1419 1896 * @return bool
1420 1897 */
1421 - public static function is_mobile_app() {
1422 -
1423 - 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 ) ) {
1424 1901 return false;
1425 1902 }
1426 1903
1427 - $agent = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1904 + $agent = strtolower( wp_unslash( $user_agent ) );
1428 1905
1429 1906 if ( isset( $_SERVER['X_USER_AGENT'] ) && preg_match( '|wp-webos|', $_SERVER['X_USER_AGENT'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
1430 1907 return true; // Wp4webos 1.1 or higher.
1431 1908 }
@@ -1449,15 +1926,18 @@
1449 1926 * Detects if the current browser is Nintendo 3DS handheld.
1450 1927 *
1451 1928 * Example: Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7498.US
1452 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'].
1453 1932 */
1454 - public static function is_Nintendo_3DS() {
1455 - 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 ) ) {
1456 1936 return false;
1457 1937 }
1458 1938
1459 - $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
1939 + $ua = strtolower( wp_unslash( $user_agent ) );
1460 1940 if ( strpos( $ua, 'nintendo 3ds' ) !== false ) {
1461 1941 return true;
1462 1942 }
1463 1943 return false;
@@ -1465,22 +1945,34 @@
1465 1945
1466 1946 /**
1467 1947 * Was the current request made by a known bot?
1468 1948 *
1949 + * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1950 + *
1469 1951 * @return boolean
1470 1952 */
1471 - public static function is_bot() {
1953 + public static function is_bot( $user_agent = null ) {
1472 1954 static $is_bot = null;
1473 1955
1474 - if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1475 - 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;
1476 1967 }
1477 1968
1478 - if ( $is_bot === null ) {
1479 - $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;
1480 1971 }
1481 1972
1482 - return $is_bot;
1973 + // Don't use cache when a custom user agent is provided.
1974 + return self::is_bot_user_agent( $user_agent );
1483 1975 }
1484 1976
1485 1977 /**
1486 1978 * Is the given user-agent a known bot?
@@ -1495,71 +1987,401 @@
1495 1987 if ( empty( $ua ) ) {
1496 1988 return false;
1497 1989 }
1498 1990
1991 + // Some sourced via
1992 + // https://github.com/ua-parser/uap-core/blob/432e95f6767cc8bab4c20c255784cd6f7e93bc15/regexes.yaml#L151
1499 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
1500 2055 'alexa',
1501 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',
1502 2062 'ask jeeves',
1503 2063 'attentio',
1504 2064 'baiduspider',
1505 - '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',
1506 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',
1507 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
1508 2104 'fastmobilecrawl',
1509 - 'feedfetcher-google',
2105 + 'feed seeker bot',
2106 + 'feedbin',
2107 + 'feedburner',
2108 + 'finderbots',
2109 + 'findlinks',
1510 2110 'firefly',
2111 + 'flamingo_searchengine',
2112 + 'followsite bot',
1511 2113 'froogle',
2114 + 'furlbot',
2115 + 'genieo',
2116 + 'germcrawler',
1512 2117 'gigabot',
1513 - 'googlebot',
1514 - 'googlebot-mobile',
2118 + 'gomezagent',
2119 + 'gonzo1',
2120 + 'grapeshotcrawler',
2121 + 'grokkit-crawler',
2122 + 'grub-client',
2123 + 'gsa-crawler',
1515 2124 'heritrix',
2125 + 'hiddenmarket',
2126 + 'holmes',
2127 + 'hoowwwer',
2128 + 'htdig',
1516 2129 'httrack',
1517 2130 'ia_archiver',
1518 - 'irlbot',
2131 + 'icarus6j',
2132 + 'icc-crawler',
2133 + 'ichiro',
2134 + 'iconsurf',
1519 2135 'iescholar',
2136 + 'iltrovatore',
2137 + 'index crawler',
1520 2138 'infoseek',
2139 + 'infuzapp',
2140 + 'innovazion crawler',
2141 + 'internetarchive',
2142 + 'irlbot',
2143 + 'jbot',
2144 + 'job roboter',
1521 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',
1522 2156 'linkcheck',
2157 + 'linkdexbot',
2158 + 'linkedinbot',
2159 + 'linkfluence', // http://linkfluence.com/
2160 + 'linkwalker', // https://www.linkwalker.com/
2161 + 'lite bot',
2162 + 'livelapbot',
2163 + 'llaut',
1523 2164 'lycos',
2165 + 'mail.ru_bot',
2166 + 'masidani_bot',
2167 + 'masscan',
1524 2168 'mediapartners',
1525 2169 'mediobot',
2170 + 'mj12bot',
2171 + 'mogimogi',
2172 + 'mojeekbot', // https://www.mojeek.com/bot.html
1526 2173 'motionbot',
2174 + 'mozdex',
2175 + 'mshots',
1527 2176 'msnbot',
1528 - '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',
1529 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',
1530 2200 'pss-webkit-request',
1531 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',
1532 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',
1533 2226 'slurp',
1534 2227 'snapbot',
2228 + 'snapchat', // https://developers.snap.com/robots
2229 + 'snappy',
2230 + 'speedy spider',
1535 2231 'spider',
2232 + 'squrl java',
2233 + 'stringer',
1536 2234 'taptubot',
1537 2235 'technoratisnoop',
1538 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',
1539 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',
1540 2268 'yahooseeker',
1541 2269 'yahooysmcm',
1542 2270 'yammybot',
1543 - 'ahrefsbot',
1544 - 'pingdom.com_bot',
1545 - 'kraken',
1546 2271 'yandexbot',
1547 - 'twitterbot',
1548 - 'tweetmemebot',
1549 - 'openhosebot',
1550 - 'queryseekerspider',
1551 - 'linkdexbot',
1552 - 'grokkit-crawler',
1553 - 'livelapbot',
1554 - 'germcrawler',
1555 - 'domaintunocrawler',
1556 - 'grapeshotcrawler',
1557 - 'cloudflare-alwaysonline',
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',
1558 2281 );
1559 2282
1560 2283 foreach ( $bot_agents as $bot_agent ) {
1561 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 ) ) {
1562 2384 return true;
1563 2385 }
1564 2386 }
1565 2387