PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.2
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.2
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-device-detection / src / class-user-agent-info.php
jetpack / jetpack_vendor / automattic / jetpack-device-detection / src Last commit date
class-device-detection.php 9 months ago class-user-agent-info.php 2 months ago functions.php 3 years ago
class-user-agent-info.php
2387 lines
1 <?php
2 /**
3 * User agent detection for Jetpack.
4 *
5 * @package automattic/jetpack-device-detection
6 *
7 * We don't want to rename public members.
8 *
9 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
10 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
11 * @phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
12 * @phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
13 */
14
15 namespace Automattic\Jetpack\Device_Detection;
16
17 require_once __DIR__ . '/functions.php';
18
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.
24 */
25 class User_Agent_Info {
26
27 /**
28 * Provided or fetched User-Agent string.
29 *
30 * @var string
31 */
32 public $useragent;
33
34 /**
35 * A device group matched user agent, e.g. 'iphone'.
36 *
37 * @var string
38 */
39 public $matched_agent;
40
41 /**
42 * Stores whether is the iPhone tier of devices.
43 *
44 * @var bool
45 */
46 public $isTierIphone;
47
48 /**
49 * Stores whether the device can probably support Rich CSS, but JavaScript (jQuery) support is not assumed.
50 *
51 * @var bool
52 */
53 public $isTierRichCss;
54
55 /**
56 * Stores whether it is another mobile device, which cannot be assumed to support CSS or JS (eg, older BlackBerry, RAZR)
57 *
58 * @var bool
59 */
60 public $isTierGenericMobile;
61
62 /**
63 * Stores the device platform name
64 *
65 * @var null|string
66 */
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';
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
114 /**
115 * A list of dumb-phone user agent parts.
116 *
117 * @var array
118 */
119 public $dumb_agents = array(
120 'nokia',
121 'blackberry',
122 'philips',
123 'samsung',
124 'sanyo',
125 'sony',
126 'panasonic',
127 'webos',
128 'ericsson',
129 'alcatel',
130 'palm',
131 'windows ce',
132 'opera mini',
133 'series60',
134 'series40',
135 'au-mic,',
136 'audiovox',
137 'avantgo',
138 'blazer',
139 'danger',
140 'docomo',
141 'epoc',
142 'ericy',
143 'i-mode',
144 'ipaq',
145 'midp-',
146 'mot-',
147 'netfront',
148 'nitro',
149 'palmsource',
150 'pocketpc',
151 'portalmmm',
152 'rover',
153 'sie-',
154 'symbian',
155 'cldc-',
156 'j2me',
157 'smartphone',
158 'up.browser',
159 'up.link',
160 'up.link',
161 'vodafone/',
162 'wap1.',
163 'wap2.',
164 'mobile',
165 'googlebot-mobile',
166 );
167
168 /**
169 * The constructor.
170 *
171 * @param string $ua (Optional) User agent.
172 */
173 public function __construct( $ua = '' ) {
174 if ( $ua ) {
175 $this->useragent = $ua;
176 } elseif ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
177 $this->useragent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This class is all about validating.
178 }
179 }
180
181 /**
182 * This method detects the mobile User Agent name.
183 *
184 * @return string The matched User Agent name, false otherwise.
185 */
186 public function get_mobile_user_agent_name() {
187 if ( static::is_chrome_for_iOS() ) { // Keep this check before the safari rule.
188 return 'chrome-for-ios';
189 } elseif ( static::is_iphone_or_ipod( 'iphone-safari' ) ) {
190 return 'iphone';
191 } elseif ( static::is_ipad( 'ipad-safari' ) ) {
192 return 'ipad';
193 } elseif ( static::is_android_tablet() ) { // Keep this check before the android rule.
194 return 'android_tablet';
195 } elseif ( static::is_android() ) {
196 return 'android';
197 } elseif ( static::is_blackberry_10() ) {
198 return 'blackberry_10';
199 } elseif ( static::is_blackbeberry() ) {
200 return 'blackberry';
201 } elseif ( static::is_WindowsPhone7() ) {
202 return 'win7';
203 } elseif ( static::is_windows_phone_8() ) {
204 return 'winphone8';
205 } elseif ( static::is_opera_mini() ) {
206 return 'opera-mini';
207 } elseif ( static::is_opera_mini_dumb() ) {
208 return 'opera-mini-dumb';
209 } elseif ( static::is_opera_mobile() ) {
210 return 'opera-mobi';
211 } elseif ( static::is_blackberry_tablet() ) {
212 return 'blackberry_tablet';
213 } elseif ( static::is_kindle_fire() ) {
214 return 'kindle-fire';
215 } elseif ( static::is_PalmWebOS() ) {
216 return 'webos';
217 } elseif ( static::is_S60_OSSBrowser() ) {
218 return 'series60';
219 } elseif ( static::is_firefox_os() ) {
220 return 'firefoxOS';
221 } elseif ( static::is_firefox_mobile() ) {
222 return 'firefox_mobile';
223 } elseif ( static::is_MaemoTablet() ) {
224 return 'maemo';
225 } elseif ( static::is_MeeGo() ) {
226 return 'meego';
227 } elseif ( static::is_TouchPad() ) {
228 return 'hp_tablet';
229 } elseif ( static::is_facebook_for_iphone() ) {
230 return 'facebook-for-iphone';
231 } elseif ( static::is_facebook_for_ipad() ) {
232 return 'facebook-for-ipad';
233 } elseif ( static::is_twitter_for_iphone() ) {
234 return 'twitter-for-iphone';
235 } elseif ( static::is_twitter_for_ipad() ) {
236 return 'twitter-for-ipad';
237 } elseif ( static::is_wordpress_for_ios() ) {
238 return 'ios-app';
239 } elseif ( static::is_iphone_or_ipod( 'iphone-not-safari' ) ) {
240 return 'iphone-unknown';
241 } elseif ( static::is_ipad( 'ipad-not-safari' ) ) {
242 return 'ipad-unknown';
243 } elseif ( static::is_Nintendo_3DS() ) {
244 return 'nintendo-3ds';
245 } else {
246 $agent = $this->useragent;
247 $dumb_agents = $this->dumb_agents;
248 foreach ( $dumb_agents as $dumb_agent ) {
249 if ( false !== strpos( $agent, $dumb_agent ) ) {
250 return $dumb_agent;
251 }
252 }
253 }
254
255 return false;
256 }
257
258 /**
259 * This method detects the mobile device's platform. All return strings are from the class constants.
260 * Note that this function returns the platform name, not the UA name/type. You should use a different function
261 * if you need to test the UA capabilites.
262 *
263 * @return string|bool Name of the platform, false otherwise.
264 */
265 public function get_platform() {
266 if ( isset( $this->platform ) ) {
267 return $this->platform;
268 }
269
270 if ( empty( $this->useragent ) ) {
271 return false;
272 }
273
274 $ua = strtolower( $this->useragent );
275 if ( strpos( $ua, 'windows phone' ) !== false ) {
276 $this->platform = self::PLATFORM_WINDOWS;
277 } elseif ( strpos( $ua, 'windows ce' ) !== false ) {
278 $this->platform = self::PLATFORM_WINDOWS;
279 } elseif ( strpos( $ua, 'ipad' ) !== false ) {
280 $this->platform = self::PLATFORM_IPAD;
281 } elseif ( strpos( $ua, 'ipod' ) !== false ) {
282 $this->platform = self::PLATFORM_IPOD;
283 } elseif ( strpos( $ua, 'iphone' ) !== false ) {
284 $this->platform = self::PLATFORM_IPHONE;
285 } elseif ( strpos( $ua, 'android' ) !== false ) {
286 if ( static::is_android_tablet() ) {
287 $this->platform = self::PLATFORM_ANDROID_TABLET;
288 } else {
289 $this->platform = self::PLATFORM_ANDROID;
290 }
291 } elseif ( static::is_kindle_fire() ) {
292 $this->platform = self::PLATFORM_ANDROID_TABLET;
293 } elseif ( static::is_blackberry_10() ) {
294 $this->platform = self::PLATFORM_BLACKBERRY_10;
295 } elseif ( strpos( $ua, 'blackberry' ) !== false ) {
296 $this->platform = self::PLATFORM_BLACKBERRY;
297 } elseif ( static::is_blackberry_tablet() ) {
298 $this->platform = self::PLATFORM_BLACKBERRY;
299 } elseif ( static::is_symbian_platform() ) {
300 $this->platform = self::PLATFORM_SYMBIAN;
301 } elseif ( static::is_symbian_s40_platform() ) {
302 $this->platform = self::PLATFORM_SYMBIAN_S40;
303 } elseif ( static::is_J2ME_platform() ) {
304 $this->platform = self::PLATFORM_J2ME_MIDP;
305 } elseif ( static::is_firefox_os() ) {
306 $this->platform = self::PLATFORM_FIREFOX_OS;
307 } else {
308 $this->platform = false;
309 }
310
311 return $this->platform;
312 }
313
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 /**
392 * This method detects for UA which can display iPhone-optimized web content.
393 * Includes iPhone, iPod Touch, Android, WebOS, Fennec (Firefox mobile), etc.
394 */
395 public function isTierIphone() {
396 if ( isset( $this->isTierIphone ) ) {
397 return $this->isTierIphone;
398 }
399 if ( $this->is_iphoneOrIpod() ) {
400 $this->matched_agent = 'iphone';
401 $this->isTierIphone = true;
402 $this->isTierRichCss = false;
403 $this->isTierGenericMobile = false;
404 } elseif ( static::is_android() ) {
405 $this->matched_agent = 'android';
406 $this->isTierIphone = true;
407 $this->isTierRichCss = false;
408 $this->isTierGenericMobile = false;
409 } elseif ( static::is_windows_phone_8() ) {
410 $this->matched_agent = 'winphone8';
411 $this->isTierIphone = true;
412 $this->isTierRichCss = false;
413 $this->isTierGenericMobile = false;
414 } elseif ( static::is_WindowsPhone7() ) {
415 $this->matched_agent = 'win7';
416 $this->isTierIphone = true;
417 $this->isTierRichCss = false;
418 $this->isTierGenericMobile = false;
419 } elseif ( static::is_blackberry_10() ) {
420 $this->matched_agent = 'blackberry-10';
421 $this->isTierIphone = true;
422 $this->isTierRichCss = false;
423 $this->isTierGenericMobile = false;
424 } elseif ( static::is_blackbeberry() && 'blackberry-webkit' === static::detect_blackberry_browser_version() ) {
425 $this->matched_agent = 'blackberry-webkit';
426 $this->isTierIphone = true;
427 $this->isTierRichCss = false;
428 $this->isTierGenericMobile = false;
429 } elseif ( static::is_blackberry_tablet() ) {
430 $this->matched_agent = 'blackberry_tablet';
431 $this->isTierIphone = true;
432 $this->isTierRichCss = false;
433 $this->isTierGenericMobile = false;
434 } elseif ( static::is_PalmWebOS() ) {
435 $this->matched_agent = 'webos';
436 $this->isTierIphone = true;
437 $this->isTierRichCss = false;
438 $this->isTierGenericMobile = false;
439 } elseif ( static::is_TouchPad() ) {
440 $this->matched_agent = 'hp_tablet';
441 $this->isTierIphone = true;
442 $this->isTierRichCss = false;
443 $this->isTierGenericMobile = false;
444 } elseif ( static::is_firefox_os() ) {
445 $this->matched_agent = 'firefoxOS';
446 $this->isTierIphone = true;
447 $this->isTierRichCss = false;
448 $this->isTierGenericMobile = false;
449 } elseif ( static::is_firefox_mobile() ) {
450 $this->matched_agent = 'fennec';
451 $this->isTierIphone = true;
452 $this->isTierRichCss = false;
453 $this->isTierGenericMobile = false;
454 } elseif ( static::is_opera_mobile() ) {
455 $this->matched_agent = 'opera-mobi';
456 $this->isTierIphone = true;
457 $this->isTierRichCss = false;
458 $this->isTierGenericMobile = false;
459 } elseif ( static::is_MaemoTablet() ) {
460 $this->matched_agent = 'maemo';
461 $this->isTierIphone = true;
462 $this->isTierRichCss = false;
463 $this->isTierGenericMobile = false;
464 } elseif ( static::is_MeeGo() ) {
465 $this->matched_agent = 'meego';
466 $this->isTierIphone = true;
467 $this->isTierRichCss = false;
468 $this->isTierGenericMobile = false;
469 } elseif ( static::is_kindle_touch() ) {
470 $this->matched_agent = 'kindle-touch';
471 $this->isTierIphone = true;
472 $this->isTierRichCss = false;
473 $this->isTierGenericMobile = false;
474 } elseif ( static::is_Nintendo_3DS() ) {
475 $this->matched_agent = 'nintendo-3ds';
476 $this->isTierIphone = true;
477 $this->isTierRichCss = false;
478 $this->isTierGenericMobile = false;
479 } else {
480 $this->isTierIphone = false;
481 }
482 return $this->isTierIphone;
483 }
484
485 /**
486 * This method detects for UA which are likely to be capable
487 * but may not necessarily support JavaScript.
488 * Excludes all iPhone Tier UA.
489 */
490 public function isTierRichCss() {
491 if ( isset( $this->isTierRichCss ) ) {
492 return $this->isTierRichCss;
493 }
494 if ( $this->isTierIphone() ) {
495 return false;
496 }
497
498 // The following devices are explicitly ok.
499 if ( static::is_S60_OSSBrowser() ) {
500 $this->matched_agent = 'series60';
501 $this->isTierIphone = false;
502 $this->isTierRichCss = true;
503 $this->isTierGenericMobile = false;
504 } elseif ( static::is_opera_mini() ) {
505 $this->matched_agent = 'opera-mini';
506 $this->isTierIphone = false;
507 $this->isTierRichCss = true;
508 $this->isTierGenericMobile = false;
509 } elseif ( static::is_blackbeberry() ) {
510 $detectedDevice = static::detect_blackberry_browser_version();
511 if (
512 'blackberry-5' === $detectedDevice
513 || 'blackberry-4.7' === $detectedDevice
514 || 'blackberry-4.6' === $detectedDevice
515 ) {
516 $this->matched_agent = $detectedDevice;
517 $this->isTierIphone = false;
518 $this->isTierRichCss = true;
519 $this->isTierGenericMobile = false;
520 }
521 } else {
522 $this->isTierRichCss = false;
523 }
524
525 return $this->isTierRichCss;
526 }
527
528 /**
529 * Detects if the user is using a tablet.
530 * props Corey Gilmore, BGR.com
531 *
532 * @return bool
533 */
534 public function is_tablet() {
535 $ua = $this->useragent;
536 return ( 0 // Never true, but makes it easier to manage our list of tablet conditions.
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 )
543 );
544 }
545
546 /**
547 * Detects if the current UA is the default iPhone or iPod Touch Browser.
548 *
549 * DEPRECATED: use is_iphone_or_ipod
550 */
551 public function is_iphoneOrIpod() {
552 if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
553 return false;
554 }
555
556 $ua = strtolower( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
557 if ( ( strpos( $ua, 'iphone' ) !== false ) || ( strpos( $ua, 'ipod' ) !== false ) ) {
558 if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
559 return false;
560 } else {
561 return true;
562 }
563 } else {
564 return false;
565 }
566 }
567
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 /**
586 * Detects if the current UA is iPhone Mobile Safari or another iPhone or iPod Touch Browser.
587 *
588 * They type can check for any iPhone, an iPhone using Safari, or an iPhone using something other than Safari.
589 *
590 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPhone browser),
591 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
592 * Otherwise those browsers will be 'catched' by the iphone string.
593 *
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'].
596 */
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 ) ) {
601 return false;
602 }
603
604 $ua = strtolower( wp_unslash( $user_agent ) );
605 $is_iphone = ( strpos( $ua, 'iphone' ) !== false ) || ( strpos( $ua, 'ipod' ) !== false );
606 $is_safari = ( false !== strpos( $ua, 'safari' ) );
607
608 if ( 'iphone-safari' === $type ) {
609 return $is_iphone && $is_safari;
610 } elseif ( 'iphone-not-safari' === $type ) {
611 return $is_iphone && ! $is_safari;
612 } else {
613 return $is_iphone;
614 }
615 }
616
617 /**
618 * Detects if the current UA is Chrome for iOS
619 *
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>.
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'].
624 */
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 ) ) {
628 return false;
629 }
630
631 if ( self::is_iphone_or_ipod( 'iphone-safari', $user_agent ) === false ) {
632 return false;
633 }
634
635 $ua = strtolower( wp_unslash( $user_agent ) );
636
637 if ( strpos( $ua, 'crios/' ) !== false ) {
638 return true;
639 } else {
640 return false;
641 }
642 }
643
644 /**
645 * Detects if the current UA is Twitter for iPhone
646 *
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
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'].
651 */
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 ) ) {
655 return false;
656 }
657
658 $ua = strtolower( wp_unslash( $user_agent ) );
659
660 if ( strpos( $ua, 'ipad' ) !== false ) {
661 return false;
662 }
663
664 if ( strpos( $ua, 'twitter for iphone' ) !== false ) {
665 return true;
666 } else {
667 return false;
668 }
669 }
670
671 /**
672 * Detects if the current UA is Twitter for iPad
673 *
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
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'].
678 */
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 ) ) {
682 return false;
683 }
684
685 $ua = strtolower( wp_unslash( $user_agent ) );
686
687 if ( strpos( $ua, 'twitter for ipad' ) !== false ) {
688 return true;
689 } elseif ( strpos( $ua, 'ipad' ) !== false && strpos( $ua, 'twitter for iphone' ) !== false ) {
690 return true;
691 } else {
692 return false;
693 }
694 }
695
696 /**
697 * Detects if the current UA is Facebook for iPhone
698 * - Facebook 4020.0 (iPhone; iPhone OS 5.0.1; fr_FR)
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]
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'].
703 */
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 ) ) {
707 return false;
708 }
709
710 $ua = strtolower( wp_unslash( $user_agent ) );
711
712 if ( false === strpos( $ua, 'iphone' ) ) {
713 return false;
714 }
715
716 if ( false !== strpos( $ua, 'facebook' ) && false === strpos( $ua, 'ipad' ) ) {
717 return true;
718 } elseif ( false !== strpos( $ua, 'fbforiphone' ) && false === strpos( $ua, 'tablet' ) ) {
719 return true;
720 } elseif ( false !== strpos( $ua, 'fban/fbios;' ) && false === strpos( $ua, 'tablet' ) ) { // FB app v5.0 or higher.
721 return true;
722 } else {
723 return false;
724 }
725 }
726
727 /**
728 * Detects if the current UA is Facebook for iPad
729 * - Facebook 4020.0 (iPad; iPhone OS 5.0.1; en_US)
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]
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'].
734 */
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 ) ) {
738 return false;
739 }
740
741 $ua = strtolower( wp_unslash( $user_agent ) );
742
743 if ( false === strpos( $ua, 'ipad' ) ) {
744 return false;
745 }
746
747 if ( false !== strpos( $ua, 'facebook' ) || false !== strpos( $ua, 'fbforiphone' ) || false !== strpos( $ua, 'fban/fbios;' ) ) {
748 return true;
749 } else {
750 return false;
751 }
752 }
753
754 /**
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'].
758 */
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 ) ) {
762 return false;
763 }
764
765 $ua = strtolower( wp_unslash( $user_agent ) );
766 if ( false !== strpos( $ua, 'wp-iphone' ) ) {
767 return true;
768 } else {
769 return false;
770 }
771 }
772
773 /**
774 * Detects if the current device is an iPad.
775 * They type can check for any iPad, an iPad using Safari, or an iPad using something other than Safari.
776 *
777 * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPad browser),
778 * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
779 * Otherwise those browsers will be 'catched' by the ipad string.
780 *
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'].
783 */
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 ) ) {
787 return false;
788 }
789
790 $ua = strtolower( wp_unslash( $user_agent ) );
791
792 $is_ipad = ( false !== strpos( $ua, 'ipad' ) );
793 $is_safari = ( false !== strpos( $ua, 'safari' ) );
794
795 if ( 'ipad-safari' === $type ) {
796 return $is_ipad && $is_safari;
797 } elseif ( 'ipad-not-safari' === $type ) {
798 return $is_ipad && ! $is_safari;
799 } else {
800 return $is_ipad;
801 }
802 }
803
804 /**
805 * Detects if the current browser is Firefox Mobile (Fennec)
806 *
807 * See http://www.useragentstring.com/pages/Fennec/
808 * Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.1.1) Gecko/20110415 Firefox/4.0.2pre Fennec/4.0.1
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'].
812 */
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 ) ) {
816 return false;
817 }
818
819 $ua = strtolower( wp_unslash( $user_agent ) );
820
821 if ( strpos( $ua, 'fennec' ) !== false ) {
822 return true;
823 } else {
824 return false;
825 }
826 }
827
828 /**
829 * Detects if the current browser is Firefox for desktop
830 *
831 * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
832 * Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion
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'].
836 */
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 ) ) {
840 return false;
841 }
842
843 $ua = strtolower( wp_unslash( $user_agent ) );
844
845 if ( false !== strpos( $ua, 'firefox' ) && false === strpos( $ua, 'mobile' ) && false === strpos( $ua, 'tablet' ) ) {
846 return true;
847 } else {
848 return false;
849 }
850 }
851
852 /**
853 * Detects if the current browser is FirefoxOS Native browser
854 *
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'].
858 */
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 ) ) {
862 return false;
863 }
864
865 $ua = strtolower( wp_unslash( $user_agent ) );
866
867 if ( strpos( $ua, 'mozilla' ) !== false && strpos( $ua, 'mobile' ) !== false && strpos( $ua, 'gecko' ) !== false && strpos( $ua, 'firefox' ) !== false ) {
868 return true;
869 } else {
870 return false;
871 }
872 }
873
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 /**
927 * Detect modern Opera desktop
928 *
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
930 *
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'].
934 */
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 ) ) {
938 return false;
939 }
940
941 if ( false === strpos( wp_unslash( $user_agent ), 'OPR/' ) ) {
942 return false;
943 }
944
945 return true;
946 }
947
948 /**
949 * Detects if the current browser is Opera Mobile
950 *
951 * What is the difference between Opera Mobile and Opera Mini?
952 * - Opera Mobile is a full Internet browser for mobile devices.
953 * - Opera Mini always uses a transcoder to convert the page for a small display.
954 * (it uses Opera advanced server compression technology to compress web content before it gets to a device.
955 * The rendering engine is on Opera's server.)
956 *
957 * Opera/9.80 (Windows NT 6.1; Opera Mobi/14316; U; en) Presto/2.7.81 Version/11.00"
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'].
961 */
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 ) ) {
965 return false;
966 }
967
968 $ua = strtolower( wp_unslash( $user_agent ) );
969
970 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mobi' ) !== false ) {
971 return true;
972 } elseif ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'nintendo dsi' ) !== false ) {
973 return true;
974 } else {
975 return false;
976 }
977 }
978
979 /**
980 * Detects if the current browser is Opera Mini
981 *
982 * Opera/8.01 (J2ME/MIDP; Opera Mini/3.0.6306/1528; en; U; ssr)
983 * Opera/9.80 (Android;Opera Mini/6.0.24212/24.746 U;en) Presto/2.5.25 Version/10.5454
984 * Opera/9.80 (iPhone; Opera Mini/5.0.019802/18.738; U; en) Presto/2.4.15
985 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
986 * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
987 * Opera/9.80 (Series 60; Opera Mini/5.1.22783/23.334; U; en) Presto/2.5.25 Version/10.54
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'].
991 */
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 ) ) {
995 return false;
996 }
997
998 $ua = strtolower( wp_unslash( $user_agent ) );
999
1000 if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mini' ) !== false ) {
1001 return true;
1002 } else {
1003 return false;
1004 }
1005 }
1006
1007 /**
1008 * Detects if the current browser is Opera Mini, but not on a smart device OS(Android, iOS, etc)
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'].
1012 */
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 ) ) {
1016 return false;
1017 }
1018
1019 $ua = strtolower( wp_unslash( $user_agent ) );
1020
1021 if ( self::is_opera_mini( $user_agent ) ) {
1022 if ( strpos( $ua, 'android' ) !== false || strpos( $ua, 'iphone' ) !== false || strpos( $ua, 'ipod' ) !== false
1023 || strpos( $ua, 'ipad' ) !== false || strpos( $ua, 'blackberry' ) !== false ) {
1024 return false;
1025 } else {
1026 return true;
1027 }
1028 } else {
1029 return false;
1030 }
1031 }
1032
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 /**
1142 * Detects if the current browser is a Windows Phone 7 device.
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'].
1146 */
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 ) ) {
1150 return false;
1151 }
1152
1153 $ua = strtolower( wp_unslash( $user_agent ) );
1154
1155 if ( false === strpos( $ua, 'windows phone os 7' ) ) {
1156 return false;
1157 } elseif ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1158 return false;
1159 } else {
1160 return true;
1161 }
1162 }
1163
1164 /**
1165 * Detects if the current browser is a Windows Phone 8 device.
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'].
1169 */
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 ) ) {
1173 return false;
1174 }
1175
1176 $ua = strtolower( wp_unslash( $user_agent ) );
1177 if ( strpos( $ua, 'windows phone 8' ) === false ) {
1178 return false;
1179 } else {
1180 return true;
1181 }
1182 }
1183
1184 /**
1185 * Detects if the current browser is on a Palm device running the new WebOS. This EXCLUDES TouchPad.
1186 *
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
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'].
1191 */
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 ) ) {
1195 return false;
1196 }
1197
1198 $ua = strtolower( wp_unslash( $user_agent ) );
1199
1200 if ( false === strpos( $ua, 'webos' ) ) {
1201 return false;
1202 } elseif ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1203 return false;
1204 } else {
1205 return true;
1206 }
1207 }
1208
1209 /**
1210 * Detects if the current browser is the HP TouchPad default browser. This excludes phones wt WebOS.
1211 *
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
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'].
1216 */
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 ) ) {
1220 return false;
1221 }
1222
1223 $http_user_agent = strtolower( wp_unslash( $user_agent ) );
1224 if ( false !== strpos( $http_user_agent, 'hp-tablet' ) || false !== strpos( $http_user_agent, 'hpwos' ) || false !== strpos( $http_user_agent, 'touchpad' ) ) {
1225 if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1226 return false;
1227 } else {
1228 return true;
1229 }
1230 } else {
1231 return false;
1232 }
1233 }
1234
1235 /**
1236 * Detects if the current browser is the Series 60 Open Source Browser.
1237 *
1238 * OSS Browser 3.2 on E75: Mozilla/5.0 (SymbianOS/9.3; U; Series60/3.2 NokiaE75-1/110.48.125 Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413
1239 *
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
1241 *
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'].
1245 */
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 ) ) {
1249 return false;
1250 }
1251
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 ) ) {
1254 return false;
1255 }
1256
1257 $pos_webkit = strpos( $agent, 'webkit' );
1258 if ( false !== $pos_webkit ) {
1259 // First, test for WebKit, then make sure it's either Symbian or S60.
1260 if ( strpos( $agent, 'symbian' ) !== false || strpos( $agent, 'series60' ) !== false ) {
1261 return true;
1262 } else {
1263 return false;
1264 }
1265 } elseif ( strpos( $agent, 'symbianos' ) !== false && strpos( $agent, 'series60' ) !== false ) {
1266 return true;
1267 } elseif ( strpos( $agent, 'nokia' ) !== false && strpos( $agent, 'series60' ) !== false ) {
1268 return true;
1269 }
1270
1271 return false;
1272 }
1273
1274 /**
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'].
1278 */
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 ) ) {
1282 return false;
1283 }
1284
1285 $agent = strtolower( wp_unslash( $user_agent ) );
1286
1287 $pos_webkit = strpos( $agent, 'webkit' );
1288 if ( false !== $pos_webkit ) {
1289 // First, test for WebKit, then make sure it's either Symbian or S60.
1290 if ( strpos( $agent, 'symbian' ) !== false || strpos( $agent, 'series60' ) !== false ) {
1291 return true;
1292 } else {
1293 return false;
1294 }
1295 } elseif ( strpos( $agent, 'symbianos' ) !== false && strpos( $agent, 'series60' ) !== false ) {
1296 return true;
1297 } elseif ( strpos( $agent, 'nokia' ) !== false && strpos( $agent, 'series60' ) !== false ) {
1298 return true;
1299 } elseif ( strpos( $agent, 'opera mini' ) !== false ) {
1300 if ( strpos( $agent, 'symbianos' ) !== false || strpos( $agent, 'symbos' ) !== false || strpos( $agent, 'series 60' ) !== false ) {
1301 return true;
1302 }
1303 }
1304
1305 return false;
1306 }
1307
1308 /**
1309 * Detects if the device platform is the Symbian Series 40.
1310 * Nokia Browser for Series 40 is a proxy based browser, previously known as Ovi Browser.
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'].
1314 */
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 ) ) {
1318 return false;
1319 }
1320
1321 $agent = strtolower( wp_unslash( $user_agent ) );
1322
1323 if ( strpos( $agent, 'series40' ) !== false ) {
1324 if ( strpos( $agent, 'nokia' ) !== false || strpos( $agent, 'ovibrowser' ) !== false || strpos( $agent, 'nokiabrowser' ) !== false ) {
1325 return true;
1326 }
1327 }
1328
1329 return false;
1330 }
1331
1332 /**
1333 * Returns if the device belongs to J2ME capable family.
1334 *
1335 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1336 *
1337 * @return bool
1338 */
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 ) ) {
1342 return false;
1343 }
1344
1345 $agent = strtolower( wp_unslash( $user_agent ) );
1346
1347 if ( strpos( $agent, 'j2me/midp' ) !== false ) {
1348 return true;
1349 } elseif ( strpos( $agent, 'midp' ) !== false && strpos( $agent, 'cldc' ) ) {
1350 return true;
1351 }
1352 return false;
1353 }
1354
1355 /**
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'].
1359 */
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 ) ) {
1363 return false;
1364 }
1365
1366 $agent = strtolower( wp_unslash( $user_agent ) );
1367
1368 $pos_maemo = strpos( $agent, 'maemo' );
1369 if ( false === $pos_maemo ) {
1370 return false;
1371 }
1372
1373 // Must be Linux + Tablet, or else it could be something else.
1374 if ( strpos( $agent, 'tablet' ) !== false && strpos( $agent, 'linux' ) !== false ) {
1375 if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1376 return false;
1377 } else {
1378 return true;
1379 }
1380 } else {
1381 return false;
1382 }
1383 }
1384
1385 /**
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'].
1389 */
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 ) ) {
1393 return false;
1394 }
1395
1396 $ua = strtolower( wp_unslash( $user_agent ) );
1397
1398 if ( false === strpos( $ua, 'meego' ) ) {
1399 return false;
1400 } elseif ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1401 return false;
1402 } else {
1403 return true;
1404 }
1405 }
1406
1407 /**
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'].
1411 */
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 ) ) {
1415 return false;
1416 }
1417
1418 $agent = strtolower( wp_unslash( $user_agent ) );
1419
1420 $pos_webkit = strpos( $agent, 'webkit' );
1421
1422 if ( false !== $pos_webkit ) {
1423 return true;
1424 } else {
1425 return false;
1426 }
1427 }
1428
1429 /**
1430 * Detects if the current browser is the Native Android browser.
1431 *
1432 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1433 *
1434 * @return boolean true if the browser is Android otherwise false
1435 */
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 ) ) {
1439 return false;
1440 }
1441
1442 $agent = strtolower( wp_unslash( $user_agent ) );
1443 $pos_android = strpos( $agent, 'android' );
1444 if ( false !== $pos_android ) {
1445 if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1446 return false;
1447 } else {
1448 return true;
1449 }
1450 } else {
1451 return false;
1452 }
1453 }
1454
1455 /**
1456 * Detects if the current browser is the Native Android Tablet browser.
1457 * Assumes 'Android' should be in the user agent, but not 'mobile'
1458 *
1459 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1460 *
1461 * @return boolean true if the browser is Android and not 'mobile' otherwise false
1462 */
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 ) ) {
1466 return false;
1467 }
1468
1469 $agent = strtolower( wp_unslash( $user_agent ) );
1470
1471 $pos_android = strpos( $agent, 'android' );
1472 $pos_mobile = strpos( $agent, 'mobile' );
1473 $post_android_app = strpos( $agent, 'wp-android' );
1474
1475 if ( false !== $pos_android && false === $pos_mobile && false === $post_android_app ) {
1476 if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1477 return false;
1478 } else {
1479 return true;
1480 }
1481 } else {
1482 return false;
1483 }
1484 }
1485
1486 /**
1487 * Detects if the current browser is the Kindle Fire Native browser.
1488 *
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
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
1491 *
1492 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1493 *
1494 * @return boolean true if the browser is Kindle Fire Native browser otherwise false
1495 */
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 ) ) {
1499 return false;
1500 }
1501
1502 $agent = strtolower( wp_unslash( $user_agent ) );
1503 $pos_silk = strpos( $agent, 'silk/' );
1504 $pos_silk_acc = strpos( $agent, 'silk-accelerated=' );
1505 if ( false !== $pos_silk && false !== $pos_silk_acc ) {
1506 return true;
1507 } else {
1508 return false;
1509 }
1510 }
1511
1512 /**
1513 * Detects if the current browser is the Kindle Touch Native browser
1514 *
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+
1516 *
1517 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1518 *
1519 * @return boolean true if the browser is Kindle monochrome Native browser otherwise false
1520 */
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 ) ) {
1524 return false;
1525 }
1526
1527 $agent = strtolower( wp_unslash( $user_agent ) );
1528 $pos_kindle_touch = strpos( $agent, 'kindle/3.0+' );
1529 if ( false !== $pos_kindle_touch && false === self::is_kindle_fire( $user_agent ) ) {
1530 return true;
1531 } else {
1532 return false;
1533 }
1534 }
1535
1536 /**
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'].
1540 */
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 ) ) {
1544 return false;
1545 }
1546
1547 $agent = strtolower( wp_unslash( $user_agent ) );
1548 $pos = strpos( $agent, 'msauthhost' );
1549 if ( false !== $pos ) {
1550 return true;
1551 } else {
1552 return false;
1553 }
1554 }
1555
1556 /**
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'].
1560 */
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 ) ) {
1564 return false;
1565 }
1566
1567 $agent = strtolower( wp_unslash( $user_agent ) );
1568 $pos = strpos( $agent, 'wp-windows8' );
1569 if ( false !== $pos ) {
1570 return true;
1571 } else {
1572 return false;
1573 }
1574 }
1575
1576 /**
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'].
1580 */
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 ) ) {
1584 return false;
1585 }
1586
1587 $agent = strtolower( wp_unslash( $user_agent ) );
1588 $pos = strpos( $agent, 'WordPressDesktop' );
1589 if ( false !== $pos ) {
1590 return true;
1591 } else {
1592 return false;
1593 }
1594 }
1595
1596 /**
1597 * The is_blackberry_tablet() method can be used to check the User Agent for a RIM blackberry tablet.
1598 * The user agent of the BlackBerry® Tablet OS follows a format similar to the following:
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'].
1602 */
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 ) ) {
1606 return false;
1607 }
1608
1609 $agent = strtolower( wp_unslash( $user_agent ) );
1610 $pos_playbook = stripos( $agent, 'PlayBook' );
1611 $pos_rim_tablet = stripos( $agent, 'RIM Tablet' );
1612
1613 if ( ( false === $pos_playbook ) || ( false === $pos_rim_tablet ) ) {
1614 return false;
1615 } else {
1616 return true;
1617 }
1618 }
1619
1620 /**
1621 * The is_blackbeberry() method can be used to check the User Agent for a blackberry device.
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'].
1625 */
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 ) ) {
1629 return false;
1630 }
1631
1632 $agent = strtolower( wp_unslash( $user_agent ) );
1633
1634 $pos_blackberry = strpos( $agent, 'blackberry' );
1635 if ( false !== $pos_blackberry ) {
1636 if ( self::is_opera_mini( $user_agent ) || self::is_opera_mobile( $user_agent ) || self::is_firefox_mobile( $user_agent ) ) {
1637 return false;
1638 } else {
1639 return true;
1640 }
1641 } else {
1642 return false;
1643 }
1644 }
1645
1646 /**
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'].
1650 */
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 ) ) {
1654 return false;
1655 }
1656
1657 $agent = strtolower( wp_unslash( $user_agent ) );
1658 return ( strpos( $agent, 'bb10' ) !== false ) && ( strpos( $agent, 'mobile' ) !== false );
1659 }
1660
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 /**
1738 * Retrieve the blackberry OS version.
1739 *
1740 * Return strings are from the following list:
1741 * - blackberry-10
1742 * - blackberry-7
1743 * - blackberry-6
1744 * - blackberry-torch //only the first edition. The 2nd edition has the OS7 onboard and doesn't need any special rule.
1745 * - blackberry-5
1746 * - blackberry-4.7
1747 * - blackberry-4.6
1748 * - blackberry-4.5
1749 *
1750 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1751 *
1752 * @return string Version of the BB OS.
1753 * If version is not found, get_blackbeberry_OS_version will return boolean false.
1754 */
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 ) ) {
1758 return false;
1759 }
1760
1761 if ( self::is_blackberry_10( $user_agent ) ) {
1762 return 'blackberry-10';
1763 }
1764
1765 $agent = strtolower( wp_unslash( $user_agent ) );
1766
1767 $pos_blackberry = stripos( $agent, 'blackberry' );
1768 if ( false === $pos_blackberry ) {
1769 // Not a blackberry device.
1770 return false;
1771 }
1772
1773 // Blackberry devices OS 6.0 or higher.
1774 // Mozilla/5.0 (BlackBerry; U; BlackBerry 9670; en) AppleWebKit/534.3+ (KHTML, like Gecko) Version/6.0.0.286 Mobile Safari/534.3+.
1775 // Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, Like Gecko) Version/6.0.0.141 Mobile Safari/534.1+.
1776 // Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0 Mobile Safari/534.11+.
1777 $pos_webkit = stripos( $agent, 'webkit' );
1778 if ( false !== $pos_webkit ) {
1779 // Detected blackberry webkit browser.
1780 $pos_torch = stripos( $agent, 'BlackBerry 9800' );
1781 if ( false !== $pos_torch ) {
1782 return 'blackberry-torch'; // Match the torch first edition. the 2nd edition should use the OS7 and doesn't need any special rule.
1783 } elseif ( preg_match( '#Version\/([\d\.]+)#i', $agent, $matches ) ) { // Detecting the BB OS version for devices running OS 6.0 or higher.
1784 $version = $matches[1];
1785 $version_num = explode( '.', $version );
1786 if ( count( $version_num ) <= 1 ) {
1787 return 'blackberry-6'; // not a BB device that match our rule.
1788 } else {
1789 return 'blackberry-' . $version_num[0];
1790 }
1791 } else {
1792 // if doesn't match returns the minimun version with a webkit browser. we should never fall here.
1793 return 'blackberry-6'; // not a BB device that match our rule.
1794 }
1795 }
1796
1797 // Blackberry devices <= 5.XX.
1798 // BlackBerry9000/5.0.0.93 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/179.
1799 if ( preg_match( '#BlackBerry\w+\/([\d\.]+)#i', $agent, $matches ) ) {
1800 $version = $matches[1];
1801 } else {
1802 return false; // not a BB device that match our rule.
1803 }
1804
1805 $version_num = explode( '.', $version );
1806
1807 if ( count( $version_num ) <= 1 ) {
1808 return false;
1809 }
1810
1811 $version_num_major = (int) $version_num[0];
1812 $version_num_minor = (int) $version_num[1];
1813
1814 if ( 5 === $version_num_major ) {
1815 return 'blackberry-5';
1816 } elseif ( 4 === $version_num_major && 7 === $version_num_minor ) {
1817 return 'blackberry-4.7';
1818 } elseif ( 4 === $version_num_major && 6 === $version_num_minor ) {
1819 return 'blackberry-4.6';
1820 } elseif ( 4 === $version_num_major && 5 === $version_num_minor ) {
1821 return 'blackberry-4.5';
1822 } else {
1823 return false;
1824 }
1825 }
1826
1827 /**
1828 * Retrieve the blackberry browser version.
1829 *
1830 * Return string are from the following list:
1831 * - blackberry-10
1832 * - blackberry-webkit
1833 * - blackberry-5
1834 * - blackberry-4.7
1835 * - blackberry-4.6
1836 *
1837 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1838 *
1839 * @return string Type of the BB browser.
1840 * If browser's version is not found, detect_blackbeberry_browser_version will return boolean false.
1841 */
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 ) ) {
1845 return false;
1846 }
1847
1848 $agent = strtolower( wp_unslash( $user_agent ) );
1849
1850 if ( self::is_blackberry_10( $user_agent ) ) {
1851 return 'blackberry-10';
1852 }
1853
1854 $pos_blackberry = strpos( $agent, 'blackberry' );
1855 if ( false === $pos_blackberry ) {
1856 // Not a blackberry device.
1857 return false;
1858 }
1859
1860 $pos_webkit = strpos( $agent, 'webkit' );
1861
1862 if ( ! ( false === $pos_webkit ) ) {
1863 return 'blackberry-webkit';
1864 } else {
1865 if ( ! preg_match( '#BlackBerry\w+\/([\d\.]+)#i', $agent, $matches ) ) {
1866 return false; // not a BB device that match our rule.
1867 }
1868
1869 $version_num = explode( '.', $matches[1] );
1870
1871 if ( count( $version_num ) <= 1 ) {
1872 return false;
1873 }
1874
1875 $version_num_major = (int) $version_num[0];
1876 $version_num_minor = (int) $version_num[1];
1877
1878 if ( 5 === $version_num_major ) {
1879 return 'blackberry-5';
1880 } elseif ( 4 === $version_num_major && 7 === $version_num_minor ) {
1881 return 'blackberry-4.7';
1882 } elseif ( 4 === $version_num_major && 6 === $version_num_minor ) {
1883 return 'blackberry-4.6';
1884 } else {
1885 // A very old BB device is found or this is a BB device that doesn't match our rules.
1886 return false;
1887 }
1888 }
1889 }
1890
1891 /**
1892 * Checks if a visitor is coming from one of the WordPress mobile apps.
1893 *
1894 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1895 *
1896 * @return bool
1897 */
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 ) ) {
1901 return false;
1902 }
1903
1904 $agent = strtolower( wp_unslash( $user_agent ) );
1905
1906 if ( isset( $_SERVER['X_USER_AGENT'] ) && preg_match( '|wp-webos|', $_SERVER['X_USER_AGENT'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
1907 return true; // Wp4webos 1.1 or higher.
1908 }
1909
1910 $app_agents = array( 'wp-android', 'wp-blackberry', 'wp-iphone', 'wp-nokia', 'wp-webos', 'wp-windowsphone' );
1911 // the mobile reader on iOS has an incorrect UA when loading the reader
1912 // currently it is the default one provided by the iOS framework which
1913 // causes problems with 2-step-auth
1914 // User-Agent WordPress/3.1.4 CFNetwork/609 Darwin/13.0.0.
1915 $app_agents[] = 'wordpress/3.1';
1916
1917 foreach ( $app_agents as $app_agent ) {
1918 if ( false !== strpos( $agent, $app_agent ) ) {
1919 return true;
1920 }
1921 }
1922 return false;
1923 }
1924
1925 /**
1926 * Detects if the current browser is Nintendo 3DS handheld.
1927 *
1928 * Example: Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7498.US
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'].
1932 */
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 ) ) {
1936 return false;
1937 }
1938
1939 $ua = strtolower( wp_unslash( $user_agent ) );
1940 if ( strpos( $ua, 'nintendo 3ds' ) !== false ) {
1941 return true;
1942 }
1943 return false;
1944 }
1945
1946 /**
1947 * Was the current request made by a known bot?
1948 *
1949 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
1950 *
1951 * @return boolean
1952 */
1953 public static function is_bot( $user_agent = null ) {
1954 static $is_bot = null;
1955
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;
1967 }
1968
1969 if ( empty( $user_agent ) ) {
1970 return false;
1971 }
1972
1973 // Don't use cache when a custom user agent is provided.
1974 return self::is_bot_user_agent( $user_agent );
1975 }
1976
1977 /**
1978 * Is the given user-agent a known bot?
1979 * If you want an is_bot check for the current request's UA, use is_bot() instead of passing a user-agent to this method.
1980 *
1981 * @param string $ua A user-agent string.
1982 *
1983 * @return boolean
1984 */
1985 public static function is_bot_user_agent( $ua = null ) {
1986
1987 if ( empty( $ua ) ) {
1988 return false;
1989 }
1990
1991 // Some sourced via
1992 // https://github.com/ua-parser/uap-core/blob/432e95f6767cc8bab4c20c255784cd6f7e93bc15/regexes.yaml#L151
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 // Meta https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/
2031 'facebookbot', // AI data scraper https://darkvisitors.com/agents/facebookbot
2032 'facebookexternalhit', // shares https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify
2033 'facebookcatalog', // shares https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify
2034 'meta-webindexer', // Meta AI search indexer https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#meta-webindexer
2035 'meta-externalads', // web crawler improving ads https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#meta-externalads
2036 'meta-externalagent', // training AI models https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify-2
2037 'meta-externalfetcher', // user-initiated fetches, may skip robots.txt https://developers.facebook.com/docs/sharing/webmasters/web-crawlers/#identify-3
2038
2039 // Semrush https://www.semrush.com/bot/
2040 'semrushbot',
2041 'siteauditbot',
2042
2043 // Other bots (alphabetized list)
2044 '123metaspider-bot',
2045 '1470.net crawler',
2046 '50.nu',
2047 '8bo crawler bot',
2048 'aboundex',
2049 'ahrefsbot',
2050 'ai2bot', // AI2 crawler for LLMm training https://allenai.org/crawler
2051 'alexa',
2052 'altavista',
2053 'amazonbot', // https://developer.amazon.com/amazonbot
2054 'applebot', // https://support.apple.com/en-ca/HT204683
2055 'arcgis hub indexer',
2056 'archive.org_bot', // http://archive.org/details/archive.org_bot
2057 'archiver',
2058 'ask jeeves',
2059 'attentio',
2060 'baiduspider',
2061 'blexbot',
2062 'blitzbot',
2063 'blogbridge',
2064 'bloglovin',
2065 'bne.es_bot', // https://www.bne.es/es/colecciones/archivo-web-espanola/aviso-webmasters
2066 'boardreader blog indexer',
2067 'boardreader favicon fetcher',
2068 'boitho.com-dc',
2069 'botseer',
2070 'bubing',
2071 'bytespider', // ByteDance (owner of TikTok) to train LLMs for Doubao https://darkvisitors.com/agents/bytespider
2072 'catchpoint',
2073 'ccbot', // CommonCrawl non-profit https://commoncrawl.org/ccbot
2074 'charlotte',
2075 'checklinks',
2076 'chtml generic',
2077 'cityreview robot',
2078 'cloudflare-alwaysonline',
2079 'clumboot',
2080 'coccocbot', // Coc Coc https://darkvisitors.com/agents/coccocbot-web
2081 'cohere-ai', // Cohere AI https://darkvisitors.com/agents/cohere-ai
2082 'comodo http',
2083 'comodo-webinspector-crawler',
2084 'converacrawler',
2085 'cookieinformationscanner', // Internal ref p1699315886066389-slack-C0438NHCLSY
2086 'crawl-e',
2087 'crawlconvera',
2088 'crawldaddy',
2089 'crawler',
2090 'crawlfire',
2091 'csimplespider',
2092 'dataforseobot', // https://www.dataforseo.com/dataforseo-bot
2093 'daumoa',
2094 'diffbot', // https://docs.diffbot.com/docs/how-to-use-custom-user-agents-with-extract-apis & https://darkvisitors.com/agents/diffbot
2095 'domaintunocrawler',
2096 'dotbot', // https://darkvisitors.com/agents/dotbot
2097 'duckassistbot', // DuckDuckGo AI Assistant https://darkvisitors.com/agents/duckassistbot
2098 'elisabot',
2099 'ezlynxbot', // https://www.ezoic.com/bot
2100 'fastmobilecrawl',
2101 'feed seeker bot',
2102 'feedbin',
2103 'feedburner',
2104 'finderbots',
2105 'findlinks',
2106 'firefly',
2107 'flamingo_searchengine',
2108 'followsite bot',
2109 'froogle',
2110 'furlbot',
2111 'genieo',
2112 'germcrawler',
2113 'gigabot',
2114 'gomezagent',
2115 'gonzo1',
2116 'grapeshotcrawler',
2117 'grokkit-crawler',
2118 'grub-client',
2119 'gsa-crawler',
2120 'heritrix',
2121 'hiddenmarket',
2122 'holmes',
2123 'hoowwwer',
2124 'htdig',
2125 'httrack',
2126 'ia_archiver',
2127 'icarus6j',
2128 'icc-crawler',
2129 'ichiro',
2130 'iconsurf',
2131 'iescholar',
2132 'iltrovatore',
2133 'index crawler',
2134 'infoseek',
2135 'infuzapp',
2136 'innovazion crawler',
2137 'internetarchive',
2138 'irlbot',
2139 'jbot',
2140 'job roboter',
2141 'jumpbot',
2142 'kaloogabot',
2143 'kiwistatus spider',
2144 'kraken',
2145 'kurzor',
2146 'larbin',
2147 'leia',
2148 'lesnikbot',
2149 'lijit crawler',
2150 'linguee bot',
2151 'linkaider',
2152 'linkcheck',
2153 'linkdexbot',
2154 'linkedinbot',
2155 'linkfluence', // http://linkfluence.com/
2156 'linkwalker', // https://www.linkwalker.com/
2157 'lite bot',
2158 'livelapbot',
2159 'llaut',
2160 'lycos',
2161 'mail.ru_bot',
2162 'masidani_bot',
2163 'masscan',
2164 'mediapartners',
2165 'mediobot',
2166 'mj12bot',
2167 'mogimogi',
2168 'mojeekbot', // https://www.mojeek.com/bot.html
2169 'motionbot',
2170 'mozdex',
2171 'mshots',
2172 'msnbot',
2173 'msrbot',
2174 'mtps feed aggregation system',
2175 'netresearch',
2176 'netvibes',
2177 'newsgator',
2178 'ning',
2179 'nutch',
2180 'nymesis',
2181 'objectssearch',
2182 'ogscrper',
2183 'omgili', // Webz.io web crawler for a data seller https://darkvisitors.com/agents/omgili
2184 'oozbot',
2185 'openbot',
2186 'openhosebot',
2187 'orbiter',
2188 'pagepeeker',
2189 'pagesinventory',
2190 'paxleframework',
2191 'peeplo screenshot bot',
2192 'phpcrawl',
2193 'pingdom.com_bot',
2194 'plantynet_webrobot',
2195 'pompos',
2196 'pss-webkit-request',
2197 'pythumbnail',
2198 'queryseekersp ider',
2199 'queryseekerspider',
2200 'qwantify',
2201 'read%20later',
2202 'reaper',
2203 'redcarpet',
2204 'retreiver',
2205 'riddler',
2206 'rival iq',
2207 'scollspider',
2208 'scooter',
2209 'scrapy',
2210 'scrubby',
2211 'searchsight',
2212 'seekbot',
2213 'semanticdiscovery',
2214 'seostats',
2215 'simplepie',
2216 'simplerss',
2217 'simpy',
2218 'sitecat webbot',
2219 'sitecon',
2220 'slack-imgproxy',
2221 'slackbot-linkexpanding',
2222 'slurp',
2223 'snapbot',
2224 'snapchat', // https://developers.snap.com/robots
2225 'snappy',
2226 'speedy spider',
2227 'spider',
2228 'squrl java',
2229 'stringer',
2230 'taptubot',
2231 'technoratisnoop',
2232 'teoma',
2233 'theusefulbot',
2234 'thumbshots.ru',
2235 'thumbshotsbot',
2236 'timpibot', // LLM trainer https://darkvisitors.com/agents/timpibot
2237 'tiny tiny rss',
2238 'trendictionbot', // http://www.trendiction.de/bot;
2239 'trends crawler',
2240 'tweetmemebot',
2241 'twiceler',
2242 'twitterbot', // https://developer.x.com/en/docs/x-for-websites/cards/guides/getting-started#crawling
2243 'url2png',
2244 'usyd-nlp-spider',
2245 'vagabondo',
2246 'voilabot',
2247 'vortex',
2248 'votay bot',
2249 'voyager',
2250 'wasalive.bot',
2251 'web-sniffer',
2252 'webthumb',
2253 'wesee',
2254 'whatsapp',
2255 'whatweb',
2256 'wire',
2257 'wordpress',
2258 'wotbox',
2259 'wp-e2e-tests', // WordPress e2e tests
2260 'www.almaden.ibm.com',
2261 'xenu',
2262 'yacybot', // http://yacy.net/bot.html
2263 'yahoo! slurp',
2264 'yahooseeker',
2265 'yahooysmcm',
2266 'yammybot',
2267 'yandexbot',
2268 'yottaamonitor',
2269 'youbot', // You.com AI assistant https://darkvisitors.com/agents/youbot
2270 'yowedo',
2271 'zao-crawler',
2272 'zao',
2273 'zebot_www.ze.bz',
2274 'zoombot', // SEOZOom https://darkvisitors.com/agents/zoombot
2275 'zooshot',
2276 'zyborg',
2277 );
2278
2279 foreach ( $bot_agents as $bot_agent ) {
2280 if ( false !== stripos( $ua, $bot_agent ) ) {
2281 return true;
2282 }
2283 }
2284
2285 return false;
2286 }
2287
2288 /**
2289 * Is the current request from an agent?
2290 *
2291 * Detects bots, crawlers, AI assistants, and programmatic HTTP clients.
2292 * Useful for showing machine-readable hints or alternative content to non-browser clients.
2293 *
2294 * @param string|null $user_agent Optional. User agent string to check. If not provided, uses $_SERVER['HTTP_USER_AGENT'].
2295 *
2296 * @return bool True if the request appears to be from an agent.
2297 */
2298 public static function is_agent( $user_agent = null ) {
2299 static $cached_ua = null;
2300 static $cached_result = null;
2301
2302 if ( null === $user_agent ) {
2303 if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
2304 // Empty User-Agent is likely programmatic - real browsers always send one.
2305 return true;
2306 }
2307 $user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is validating.
2308
2309 // Use cache for default user agent, but invalidate if UA changed.
2310 if ( $cached_ua === $user_agent && null !== $cached_result ) {
2311 return $cached_result;
2312 }
2313
2314 $cached_ua = $user_agent;
2315 $cached_result = self::is_agent_user_agent( $user_agent );
2316
2317 /** This filter is documented below. */
2318 if ( function_exists( 'apply_filters' ) ) {
2319 $cached_result = apply_filters( 'jetpack_is_agent', $cached_result, $user_agent );
2320 }
2321 return $cached_result;
2322 }
2323
2324 if ( empty( $user_agent ) ) {
2325 // Empty User-Agent is likely programmatic.
2326 return true;
2327 }
2328
2329 $result = self::is_agent_user_agent( $user_agent );
2330
2331 /**
2332 * Filter to customize agent detection.
2333 *
2334 * @param bool $is_agent Whether the request is from an agent.
2335 * @param string $user_agent The user agent string.
2336 */
2337 if ( function_exists( 'apply_filters' ) ) {
2338 $result = apply_filters( 'jetpack_is_agent', $result, $user_agent );
2339 }
2340 return $result;
2341 }
2342
2343 /**
2344 * Is the given user-agent from an agent?
2345 *
2346 * @param string $ua A user-agent string.
2347 *
2348 * @return bool True if the user-agent appears to be from an agent.
2349 */
2350 private static function is_agent_user_agent( $ua ) {
2351 if ( self::is_bot_user_agent( $ua ) ) {
2352 return true;
2353 }
2354
2355 // HTTP libraries and programmatic clients (alphabetized).
2356 $http_clients = array(
2357 'aiohttp',
2358 'axios',
2359 'curl/',
2360 'go-http-client',
2361 'got/',
2362 'guzzlehttp',
2363 'httpie',
2364 'httpx',
2365 'insomnia',
2366 'java/',
2367 'libwww-perl',
2368 'node-fetch',
2369 'okhttp',
2370 'postman',
2371 'python-requests',
2372 'python-urllib',
2373 'ruby',
2374 'undici',
2375 'wget/',
2376 );
2377
2378 foreach ( $http_clients as $client ) {
2379 if ( false !== stripos( $ua, $client ) ) {
2380 return true;
2381 }
2382 }
2383
2384 return false;
2385 }
2386 }
2387