This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,358 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.4.5] - 2026-06-15
### Changed
- Internal updates.
## [3.4.4] - 2026-06-08
### Changed
- Internal updates.
## [3.4.3] - 2026-06-05
### Changed
- Internal updates.
## [3.4.2] - 2026-05-19
### Changed
- Internal updates.
## [3.4.1] - 2026-05-04
### Changed
- Internal: No longer require automattic/jetpack-changelogger as a per-project dev dependency. [#48225]
## [3.4.0] - 2026-02-02
### Added
- Add is_agent() method for detecting bots, AI assistants, and programmatic HTTP clients. [#46803]
## [3.3.0] - 2025-11-03
### Added
- Add a method that returns the browser display name. [#45674]
### Changed
- Update bot detection list to include new AI and LLM crawler oriented bots. [#45695]
## [3.2.0] - 2025-10-28
### Changed
- Improved user agent string parsing functionality and added browser detection for six more browsers (Samsung Internet, UC, Yandex, Vivaldi, MIUI, Amazon Silk) [#45632]
## [3.1.1] - 2025-10-27
### Changed
- Add missing bot agents to bot detection. [#45552]
## [3.1.0] - 2025-10-10
### Added
- Filter data for SEO bot. [#45431]
## [3.0.9] - 2025-06-27
### Changed
- Internal updates.
## [3.0.8] - 2025-06-09
### Fixed
- Tests: Ensure method param count matches PHPUnit data providers. [#43828]
## [3.0.7] - 2025-06-06
### Fixed
- Tests: Resolve warnings in PHPUnit 12.2. [#43815]
## [3.0.6] - 2025-04-28
### Changed
- Internal updates.
## [3.0.5] - 2025-03-21
### Changed
- Internal updates.
## [3.0.4] - 2025-03-17
### Changed
- Internal updates.
## [3.0.3] - 2025-03-12
### Changed
- Internal updates.
## [3.0.2] - 2025-03-05
### Changed
- Internal updates.
## [3.0.1] - 2025-02-24
### Changed
- Update dependencies.
## [3.0.0] - 2024-11-14
### Removed
- General: Update minimum PHP version to 7.2. [#40147]
## [2.1.6] - 2024-11-04
### Added
- Enable test coverage. [#39961]
## [2.1.5] - 2024-09-16
### Changed
- Device_Detection::get_info() will now memoize its result [#39338]
## [2.1.4] - 2024-08-23
### Changed
- Updated package dependencies. [#39004]
## [2.1.3] - 2024-04-08
### Added
- Added functionality for extracting the browser and desktop platform from a user agent. [#36568]
### Changed
- Add new bots. [#36477]
## [2.1.2] - 2024-03-18
### Changed
- Internal updates.
## [2.1.1] - 2024-03-14
### Changed
- Internal updates.
## [2.1.0] - 2024-02-22
### Changed
- Update the bot list with more bots [#35798]
## [2.0.1] - 2023-11-21
### Changed
- Added a note of non-usage of PHP8+ functions yet. [#34137]
## [2.0.0] - 2023-11-20
### Changed
- Updated required PHP version to >= 7.0. [#34192]
## [1.5.1] - 2023-11-14
## [1.5.0] - 2023-11-13
### Added
- Added 'cookieinformationscanner' and 'facebookexternalhit' to the bot user agent list. [#34026]
## [1.4.27] - 2023-08-23
### Changed
- Updated package dependencies. [#32605]
## [1.4.26] - 2023-06-12
### Fixed
- Catch PHP notice if User Agent is not available [#31279]
## [1.4.25] - 2023-04-10
### Added
- Add Jetpack Autoloader package suggestion. [#29988]
## [1.4.24] - 2023-02-20
### Changed
- Minor internal updates.
## [1.4.23] - 2023-01-11
### Changed
- Updated package dependencies.
## [1.4.22] - 2022-12-02
### Changed
- Updated package dependencies. [#27688]
## [1.4.21] - 2022-11-22
### Added
- Add a guard in `functions.php` against being loaded twice from different copies of the package. [#27475]
### Changed
- Updated package dependencies. [#27043]
## [1.4.20] - 2022-11-07
### Fixed
- Ensure that User_Agent is loaded in environments without autoload enabled. (e.g.: WordPress.com and Super Cache) [#27223]
## [1.4.19] - 2022-10-25
### Changed
- Update `wp_unslash` wrapper to possibly run on wpcom before WordPress is loaded. [#26971]
## [1.4.18] - 2022-07-26
### Changed
- Updated package dependencies. [#25158]
## [1.4.17] - 2022-06-21
### Changed
- Renaming master to trunk.
## [1.4.16] - 2022-04-26
### Changed
- Updated package dependencies.
## [1.4.15] - 2022-04-19
### Changed
- PHPCS: Fix `WordPress.Security.ValidatedSanitizedInput`
## [1.4.14] - 2022-03-29
### Changed
- Microperformance: Use === null instead of is_null
## [1.4.13] - 2022-02-09
### Fixed
- Fixed some new PHPCS warnings.
## [1.4.12] - 2022-01-25
### Changed
- Updated package dependencies.
## [1.4.11] - 2022-01-04
### Changed
- Switch to pcov for code coverage.
- Updated package dependencies
## [1.4.10] - 2021-12-14
### Changed
- Updated package dependencies.
## [1.4.9] - 2021-11-16
### Fixed
- Verify $_SERVER['HTTP_USER_AGENT'] exists before use.
## [1.4.8] - 2021-11-02
### Changed
- Set `convertDeprecationsToExceptions` true in PHPUnit config.
- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
## [1.4.7] - 2021-10-19
### Deprecated
- General: remove numerous long-deprecated functions.
## [1.4.6] - 2021-10-13
### Changed
- Updated package dependencies.
## [1.4.5] - 2021-10-12
### Changed
- Updated package dependencies
## [1.4.4] - 2021-09-28
### Changed
- Updated package dependencies.
## [1.4.3] - 2021-08-31
### Changed
- Run composer update on test-php command instead of phpunit.
- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
- Updated versions in annotations.
## [1.4.2] - 2021-05-25
### Changed
- Updated package dependencies.
## [1.4.1] - 2021-04-27
### Changed
- Updated package dependencies.
## [1.4.0] - 2021-03-30
### Added
- Added Opera Desktop detection
- Composer alias for dev-master, to improve dependencies
### Changed
- Update package dependencies.
### Fixed
- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
## [1.3.2] - 2021-02-05
- CI: Make tests more generic
## [1.3.1] - 2021-01-19
- Add mirror-repo information to all current composer packages
- Monorepo: Reorganize all projects
## [1.3.0] - 2020-12-09
- Codesniffer: Update mediawiki/mediawiki-codesniffer dependency
- Pin dependencies
- Packages: Update for PHP 8 testing
## [1.2.1] - 2020-11-10
- Improve PHP 8 compatibility
- Updated PHPCS: Packages and Debugger
## [1.2.0] - 2020-10-19
- Replaced intval() with (int) as part of issue #17432.
## [1.1.0] - 2020-08-13
- CI: Try collect js coverage
## 1.0.0 - 2020-06-25
- Moving jetpack_is_mobile into a package
[3.4.5]: https://github.com/Automattic/jetpack-device-detection/compare/v3.4.4...v3.4.5
[3.4.4]: https://github.com/Automattic/jetpack-device-detection/compare/v3.4.3...v3.4.4
[3.4.3]: https://github.com/Automattic/jetpack-device-detection/compare/v3.4.2...v3.4.3
[3.4.2]: https://github.com/Automattic/jetpack-device-detection/compare/v3.4.1...v3.4.2
[3.4.1]: https://github.com/Automattic/jetpack-device-detection/compare/v3.4.0...v3.4.1
[3.4.0]: https://github.com/Automattic/jetpack-device-detection/compare/v3.3.0...v3.4.0
[3.3.0]: https://github.com/Automattic/jetpack-device-detection/compare/v3.2.0...v3.3.0
[3.2.0]: https://github.com/Automattic/jetpack-device-detection/compare/v3.1.1...v3.2.0
[3.1.1]: https://github.com/Automattic/jetpack-device-detection/compare/v3.1.0...v3.1.1
[3.1.0]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.9...v3.1.0
[3.0.9]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.8...v3.0.9
[3.0.8]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.7...v3.0.8
[3.0.7]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.6...v3.0.7
[3.0.6]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.5...v3.0.6
[3.0.5]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.4...v3.0.5
[3.0.4]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.3...v3.0.4
[3.0.3]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.2...v3.0.3
[3.0.2]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.1...v3.0.2
[3.0.1]: https://github.com/Automattic/jetpack-device-detection/compare/v3.0.0...v3.0.1
[3.0.0]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.6...v3.0.0
[2.1.6]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.5...v2.1.6
[2.1.5]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.4...v2.1.5
[2.1.4]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.3...v2.1.4
[2.1.3]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.2...v2.1.3
[2.1.2]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.1...v2.1.2
[2.1.1]: https://github.com/Automattic/jetpack-device-detection/compare/v2.1.0...v2.1.1
[2.1.0]: https://github.com/Automattic/jetpack-device-detection/compare/v2.0.1...v2.1.0
[2.0.1]: https://github.com/Automattic/jetpack-device-detection/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.5.1...v2.0.0
[1.5.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.5.0...v1.5.1
[1.5.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.27...v1.5.0
[1.4.27]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.26...v1.4.27
[1.4.26]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.25...v1.4.26
[1.4.25]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.24...v1.4.25
[1.4.24]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.23...v1.4.24
[1.4.23]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.22...v1.4.23
[1.4.22]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.21...v1.4.22
[1.4.21]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.20...v1.4.21
[1.4.20]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.19...v1.4.20
[1.4.19]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.18...v1.4.19
[1.4.18]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.17...v1.4.18
[1.4.17]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.16...v1.4.17
[1.4.16]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.15...v1.4.16
[1.4.15]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.14...v1.4.15
[1.4.14]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.13...v1.4.14
[1.4.13]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.12...v1.4.13
[1.4.12]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.11...v1.4.12
[1.4.11]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.10...v1.4.11
[1.4.10]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.9...v1.4.10
[1.4.9]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.8...v1.4.9
[1.4.8]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.7...v1.4.8
[1.4.7]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.6...v1.4.7
[1.4.6]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.5...v1.4.6
[1.4.5]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.4...v1.4.5
[1.4.4]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.3...v1.4.4
[1.4.3]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.2...v1.4.3
[1.4.2]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.1...v1.4.2
[1.4.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.0...v1.4.1
[1.4.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.3.2...v1.4.0
[1.3.2]: https://github.com/Automattic/jetpack-device-detection/compare/v1.3.1...v1.3.2
[1.3.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.3.0...v1.3.1
[1.3.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.2.1...v1.3.0
[1.2.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.2.0...v1.2.1
[1.2.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.0.0...v1.1.0
@@ -0,0 +1,250 @@
<?php
/**
* Device detection for Jetpack.
*
* @package automattic/jetpack-device-detection
*/
namespace Automattic\Jetpack;
require_once __DIR__ . '/functions.php';
require_once __DIR__ . '/class-user-agent-info.php';
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
use function Automattic\Jetpack\Device_Detection\wp_unslash;
/**
* Class Device_Detection
*
* Determine if the current User Agent matches the passed $kind.
*
* Note: str_contains() and other PHP8+ functions that have a polyfill in core are not used here,
* as wp-includes/compat.php may not be loaded yet.
*/
class Device_Detection {
/**
* Memoization cache for get_info() results.
*
* @var array
*/
private static $get_info_memo = array();
/**
* Maximum size of the memoization cache.
*
* @var int
*/
private static $max_memo_size = 100;
/**
* Returns information about the current device accessing the page.
*
* @param string $ua (Optional) User-Agent string.
*
* @return array Device information.
*
* array(
* 'is_phone' => (bool) Whether the current device is a mobile phone.
* 'is_smartphone' => (bool) Whether the current device is a smartphone.
* 'is_tablet' => (bool) Whether the current device is a tablet device.
* 'is_handheld' => (bool) Whether the current device is a handheld device.
* 'is_desktop' => (bool) Whether the current device is a laptop / desktop device.
* 'platform' => (string) Detected platform.
* 'is_phone_matched_ua' => (string) Matched UA.
* );
*/
public static function get_info( $ua = '' ) {
// Return memoized result if available.
// phpcs:disable WordPress.Security.ValidatedSanitizedInput
$memo_key = ! empty( $ua ) ? $ua : ( $_SERVER['HTTP_USER_AGENT'] ?? '' );
// Note: UA string used raw for compatibility reasons.
// No sanitization is needed as the value is never output or persisted, and is only used for memoization.
// phpcs:enable WordPress.Security.ValidatedSanitizedInput
if ( isset( self::$get_info_memo[ $memo_key ] ) ) {
return self::$get_info_memo[ $memo_key ];
}
$ua_info = new User_Agent_Info( $ua );
$info = array(
'is_phone' => self::is_mobile( 'any', false, $ua_info ),
'is_phone_matched_ua' => self::is_mobile( 'any', true, $ua_info ),
'is_smartphone' => self::is_mobile( 'smart', false, $ua_info ),
'is_tablet' => $ua_info->is_tablet(),
'platform' => $ua_info->get_platform(),
'desktop_platform' => $ua_info->get_desktop_platform(),
'browser' => $ua_info->get_browser(),
);
$info['is_handheld'] = $info['is_phone'] || $info['is_tablet'];
$info['is_desktop'] = ! $info['is_handheld'];
if ( function_exists( 'apply_filters' ) ) {
/**
* Filter the value of Device_Detection::get_info.
*
* @since 1.0.0
*
* @param array $info Array of device information.
* @param string $ua User agent string passed to Device_Detection::get_info.
* @param User_Agent_Info $ua_info Instance of Automattic\Jetpack\Device_Detection\User_Agent_Info.
*/
$info = apply_filters( 'jetpack_device_detection_get_info', $info, $ua, $ua_info );
}
// Memoize the result.
self::$get_info_memo[ $memo_key ] = $info;
if ( count( self::$get_info_memo ) > self::$max_memo_size ) {
array_shift( self::$get_info_memo );
}
return $info;
}
/**
* Detects phone devices.
*
* @param string $ua User-Agent string.
*
* @return bool
*/
public static function is_phone( $ua = '' ) {
$device_info = self::get_info( $ua );
return true === $device_info['is_phone'];
}
/**
* Detects smartphone devices.
*
* @param string $ua User-Agent string.
*
* @return bool
*/
public static function is_smartphone( $ua = '' ) {
$device_info = self::get_info( $ua );
return true === $device_info['is_smartphone'];
}
/**
* Detects tablet devices.
*
* @param string $ua User-Agent string.
*
* @return bool
*/
public static function is_tablet( $ua = '' ) {
$device_info = self::get_info( $ua );
return true === $device_info['is_tablet'];
}
/**
* Detects desktop devices.
*
* @param string $ua User-Agent string.
*
* @return bool
*/
public static function is_desktop( $ua = '' ) {
$device_info = self::get_info( $ua );
return true === $device_info['is_desktop'];
}
/**
* Detects handheld (i.e. phone + tablet) devices.
*
* @param string $ua User-Agent string.
*
* @return bool
*/
public static function is_handheld( $ua = '' ) {
$device_info = self::get_info( $ua );
return true === $device_info['is_handheld'];
}
/**
* Determine if the current User Agent matches the passed $kind.
*
* @param string $kind Category of mobile device to check for. Either: any, dumb, smart.
* @param bool $return_matched_agent Boolean indicating if the UA should be returned.
* @param User_Agent_Info $ua_info Boolean indicating if the UA should be returned.
*
* @return bool|string Boolean indicating if current UA matches $kind. If `$return_matched_agent` is true, returns the UA string.
*/
private static function is_mobile( $kind, $return_matched_agent, $ua_info ) {
$kinds = array(
'smart' => false,
'dumb' => false,
'any' => false,
);
$matched_agent = '';
// If an invalid kind is passed in, reset it to default.
if ( ! isset( $kinds[ $kind ] ) ) {
$kind = 'any';
}
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
$agent = strtolower( filter_var( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) );
if ( strpos( $agent, 'ipad' ) ) {
return false;
}
// Remove Samsung Galaxy tablets (SCH-I800) from being mobile devices.
if ( strpos( $agent, 'sch-i800' ) ) {
return false;
}
if ( $ua_info->is_android_tablet() && false === $ua_info->is_kindle_touch() ) {
return false;
}
if ( $ua_info->is_blackberry_tablet() ) {
return false;
}
// checks for iPhoneTier devices & RichCSS devices.
if ( $ua_info->isTierIphone() || $ua_info->isTierRichCSS() ) {
$kinds['smart'] = true;
$matched_agent = $ua_info->matched_agent;
}
if ( ! $kinds['smart'] ) {
// if smart, we are not dumb so no need to check.
$dumb_agents = $ua_info->dumb_agents;
foreach ( $dumb_agents as $dumb_agent ) {
if ( false !== strpos( $agent, $dumb_agent ) ) {
$kinds['dumb'] = true;
$matched_agent = $dumb_agent;
break;
}
}
if ( ! $kinds['dumb'] ) {
if ( isset( $_SERVER['HTTP_X_WAP_PROFILE'] ) ) {
$kinds['dumb'] = true;
$matched_agent = 'http_x_wap_profile';
} elseif ( isset( $_SERVER['HTTP_ACCEPT'] ) && ( preg_match( '/wap\.|\.wap/i', $_SERVER['HTTP_ACCEPT'] ) || false !== strpos( strtolower( $_SERVER['HTTP_ACCEPT'] ), 'application/vnd.wap.xhtml+xml' ) ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is doing the validating.
$kinds['dumb'] = true;
$matched_agent = 'vnd.wap.xhtml+xml';
}
}
}
if ( $kinds['dumb'] || $kinds['smart'] ) {
$kinds['any'] = true;
}
$value = $kinds[ $kind ];
if ( $return_matched_agent ) {
$value = $matched_agent;
}
return $value;
}
}
@@ -0,0 +1,36 @@
<?php
/**
* Utility functions for device detection.
*
* @package automattic/jetpack-device-detection
*/
namespace Automattic\Jetpack\Device_Detection;
// Check if the function is already defined, in case someone bypassed the autoloader or something
// to get the two classes from different copies of the package.
if ( ! function_exists( __NAMESPACE__ . '\\wp_unslash' ) ) {
/**
* A wrapper for WordPress's `wp_unslash()`.
*
* Even though PHP itself dropped the option to add slashes to superglobals a decade ago,
* WordPress still does it through some misguided extreme backwards compatibility. 🙄
*
* If WordPress's function exists, assume it needs to be called.
* Else if on WordPress.com, do a simplified version because we're running really early.
* Else, assume it's not needed.
*
* @param string $value String of data to unslash.
* @return string Possibly unslashed $value.
*/
function wp_unslash( $value ) {
if ( function_exists( '\\wp_unslash' ) ) {
return \wp_unslash( $value );
} elseif ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
return stripslashes( $value );
} else {
return $value;
}
}
}