summaryrefslogtreecommitdiff
blob: 21b6c3ab1c5bcefeeae6fef099e83aace3b878f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
<?php
/**
 * Device detection for Jetpack.
 *
 * @package automattic/jetpack-device-detection
 */

namespace Automattic\Jetpack;

require_once __DIR__ . '/functions.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.
 */
class Device_Detection {

	/**
	 * 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 = '' ) {
		$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(),
		);

		$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 );
		}
		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,
		);
		$first_run     = true;
		$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;
		}

		if ( $first_run ) {
			$first_run = 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;
	}
}