initial
This commit is contained in:
+250
@@ -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;
|
||||
}
|
||||
}
|
||||
+2386
File diff suppressed because it is too large
Load Diff
+36
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user