PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 12.2.2
Jetpack – WP Security, Backup, Speed, & Growth v12.2.2
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / _inc / lib / debugger / class-jetpack-debug-data.php
class-jetpack-debug-data.php
412 lines 13.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Jetpack Debug Data for the Site Health sections.
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Connection\Tokens;
9 use Automattic\Jetpack\Connection\Urls;
10 use Automattic\Jetpack\Constants;
11 use Automattic\Jetpack\Identity_Crisis;
12 use Automattic\Jetpack\Redirect;
13 use Automattic\Jetpack\Status;
14 use Automattic\Jetpack\Sync\Modules;
15 use Automattic\Jetpack\Sync\Sender;
16
17 /**
18 * Class Jetpack_Debug_Data
19 *
20 * Collect and return debug data for Jetpack.
21 *
22 * @since 7.3.0
23 */
24 class Jetpack_Debug_Data {
25 /**
26 * Determine the active plan and normalize it for the debugger results.
27 *
28 * @since 7.3.0
29 *
30 * @return string The plan slug.
31 */
32 public static function what_jetpack_plan() {
33 $plan = Jetpack_Plan::get();
34 return ! empty( $plan['class'] ) ? $plan['class'] : 'undefined';
35 }
36
37 /**
38 * Convert seconds to human readable time.
39 *
40 * A dedication function instead of using Core functionality to allow for output in seconds.
41 *
42 * @since 7.3.0
43 *
44 * @param int $seconds Number of seconds to convert to human time.
45 *
46 * @return string Human readable time.
47 */
48 public static function seconds_to_time( $seconds ) {
49 $seconds = (int) $seconds;
50 $units = array(
51 'week' => WEEK_IN_SECONDS,
52 'day' => DAY_IN_SECONDS,
53 'hour' => HOUR_IN_SECONDS,
54 'minute' => MINUTE_IN_SECONDS,
55 'second' => 1,
56 );
57 // specifically handle zero.
58 if ( 0 === $seconds ) {
59 return '0 seconds';
60 }
61 $human_readable = '';
62 foreach ( $units as $name => $divisor ) {
63 $quot = (int) ( $seconds / $divisor );
64 if ( $quot ) {
65 $human_readable .= "$quot $name";
66 $human_readable .= ( abs( $quot ) > 1 ? 's' : '' ) . ', ';
67 $seconds -= $quot * $divisor;
68 }
69 }
70 return substr( $human_readable, 0, -2 );
71 }
72
73 /**
74 * Return debug data in the format expected by Core's Site Health Info tab.
75 *
76 * @since 7.3.0
77 *
78 * @param array $debug {
79 * The debug information already compiled by Core.
80 *
81 * @type string $label The title for this section of the debug output.
82 * @type string $description Optional. A description for your information section which may contain basic HTML
83 * markup: `em`, `strong` and `a` for linking to documentation or putting emphasis.
84 * @type boolean $show_count Optional. If set to `true` the amount of fields will be included in the title for
85 * this section.
86 * @type boolean $private Optional. If set to `true` the section and all associated fields will be excluded
87 * from the copy-paste text area.
88 * @type array $fields {
89 * An associative array containing the data to be displayed.
90 *
91 * @type string $label The label for this piece of information.
92 * @type string $value The output that is of interest for this field.
93 * @type boolean $private Optional. If set to `true` the field will not be included in the copy-paste text area
94 * on top of the page, allowing you to show, for example, API keys here.
95 * }
96 * }
97 *
98 * @return array $args Debug information in the same format as the initial argument.
99 */
100 public static function core_debug_data( $debug ) {
101 $support_url = Jetpack::is_development_version()
102 ? Redirect::get_url( 'jetpack-contact-support-beta-group' )
103 : Redirect::get_url( 'jetpack-contact-support' );
104
105 $jetpack = array(
106 'jetpack' => array(
107 'label' => __( 'Jetpack', 'jetpack' ),
108 'description' => sprintf(
109 /* translators: %1$s is URL to jetpack.com's contact support page. %2$s accessibility text */
110 __(
111 'Diagnostic information helpful to <a href="%1$s" target="_blank" rel="noopener noreferrer">your Jetpack Happiness team<span class="screen-reader-text">%2$s</span></a>',
112 'jetpack'
113 ),
114 esc_url( $support_url ),
115 __( '(opens in a new tab)', 'jetpack' )
116 ),
117 'fields' => self::debug_data(),
118 ),
119 );
120 $debug = array_merge( $debug, $jetpack );
121 return $debug;
122 }
123
124 /**
125 * Compile and return array of debug information.
126 *
127 * @since 7.3.0
128 *
129 * @return array $args {
130 * Associated array of arrays with the following.
131 * @type string $label The label for this piece of information.
132 * @type string $value The output that is of interest for this field.
133 * @type boolean $private Optional. Set to true if data is sensitive (API keys, etc).
134 * }
135 */
136 public static function debug_data() {
137 $debug_info = array();
138
139 /* Add various important Jetpack options */
140 $debug_info['site_id'] = array(
141 'label' => 'Jetpack Site ID',
142 'value' => Jetpack_Options::get_option( 'id' ),
143 'private' => false,
144 );
145 $debug_info['ssl_cert'] = array(
146 'label' => 'Jetpack SSL Verfication Bypass',
147 'value' => ( Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) ? 'Yes' : 'No',
148 'private' => false,
149 );
150 $debug_info['time_diff'] = array(
151 'label' => "Offset between Jetpack server's time and this server's time.",
152 'value' => Jetpack_Options::get_option( 'time_diff' ),
153 'private' => false,
154 );
155 $debug_info['version_option'] = array(
156 'label' => 'Current Jetpack Version Option',
157 'value' => Jetpack_Options::get_option( 'version' ),
158 'private' => false,
159 );
160 $debug_info['old_version'] = array(
161 'label' => 'Previous Jetpack Version',
162 'value' => Jetpack_Options::get_option( 'old_version' ),
163 'private' => false,
164 );
165 $debug_info['public'] = array(
166 'label' => 'Jetpack Site Public',
167 'value' => ( Jetpack_Options::get_option( 'public' ) ) ? 'Public' : 'Private',
168 'private' => false,
169 );
170 $debug_info['master_user'] = array(
171 'label' => 'Jetpack Master User',
172 'value' => self::human_readable_master_user(), // Only ID number and user name.
173 'private' => false,
174 );
175 $debug_info['is_offline_mode'] = array(
176 'label' => 'Jetpack Offline Mode',
177 'value' => ( new Status() )->is_offline_mode() ? 'on' : 'off',
178 'private' => false,
179 );
180 $debug_info['is_offline_mode_constant'] = array(
181 'label' => 'JETPACK_DEV_DEBUG Constant',
182 'value' => ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) ? 'on' : 'off',
183 'private' => false,
184 );
185
186 /**
187 * Token information is private, but awareness if there one is set is helpful.
188 *
189 * To balance out information vs privacy, we only display and include the "key",
190 * which is a segment of the token prior to a period within the token and is
191 * technically not private.
192 *
193 * If a token does not contain a period, then it is malformed and we report it as such.
194 */
195 $user_id = get_current_user_id();
196 $blog_token = ( new Tokens() )->get_access_token();
197 $user_token = ( new Tokens() )->get_access_token( $user_id );
198
199 $tokenset = '';
200 if ( $blog_token ) {
201 $tokenset = 'Blog ';
202 $blog_key = substr( $blog_token->secret, 0, strpos( $blog_token->secret, '.' ) );
203 // Intentionally not translated since this is helpful when sent to Happiness.
204 $blog_key = ( $blog_key ) ? $blog_key : 'Potentially Malformed Token.';
205 }
206 if ( $user_token ) {
207 $tokenset .= 'User';
208 $user_key = substr( $user_token->secret, 0, strpos( $user_token->secret, '.' ) );
209 // Intentionally not translated since this is helpful when sent to Happiness.
210 $user_key = ( $user_key ) ? $user_key : 'Potentially Malformed Token.';
211 }
212 if ( ! $tokenset ) {
213 $tokenset = 'None';
214 }
215
216 $debug_info['current_user'] = array(
217 'label' => 'Current User',
218 'value' => self::human_readable_user( $user_id ),
219 'private' => false,
220 );
221 $debug_info['tokens_set'] = array(
222 'label' => 'Tokens defined',
223 'value' => $tokenset,
224 'private' => false,
225 );
226 $debug_info['blog_token'] = array(
227 'label' => 'Blog Public Key',
228 'value' => ( $blog_token ) ? $blog_key : 'Not set.',
229 'private' => false,
230 );
231 $debug_info['user_token'] = array(
232 'label' => 'User Public Key',
233 'value' => ( $user_token ) ? $user_key : 'Not set.',
234 'private' => false,
235 );
236
237 /** Jetpack Environmental Information */
238 $debug_info['version'] = array(
239 'label' => 'Jetpack Version',
240 'value' => JETPACK__VERSION,
241 'private' => false,
242 );
243 $debug_info['jp_plugin_dir'] = array(
244 'label' => 'Jetpack Directory',
245 'value' => JETPACK__PLUGIN_DIR,
246 'private' => false,
247 );
248 $debug_info['plan'] = array(
249 'label' => 'Plan Type',
250 'value' => self::what_jetpack_plan(),
251 'private' => false,
252 );
253
254 foreach ( array(
255 'HTTP_HOST',
256 'SERVER_PORT',
257 'HTTPS',
258 'GD_PHP_HANDLER',
259 'HTTP_AKAMAI_ORIGIN_HOP',
260 'HTTP_CF_CONNECTING_IP',
261 'HTTP_CLIENT_IP',
262 'HTTP_FASTLY_CLIENT_IP',
263 'HTTP_FORWARDED',
264 'HTTP_FORWARDED_FOR',
265 'HTTP_INCAP_CLIENT_IP',
266 'HTTP_TRUE_CLIENT_IP',
267 'HTTP_X_CLIENTIP',
268 'HTTP_X_CLUSTER_CLIENT_IP',
269 'HTTP_X_FORWARDED',
270 'HTTP_X_FORWARDED_FOR',
271 'HTTP_X_IP_TRAIL',
272 'HTTP_X_REAL_IP',
273 'HTTP_X_VARNISH',
274 'REMOTE_ADDR',
275 ) as $header ) {
276 if ( isset( $_SERVER[ $header ] ) ) {
277 $debug_info[ $header ] = array(
278 'label' => 'Server Variable ' . $header,
279 'value' => empty( $_SERVER[ $header ] ) ? 'false' : filter_var( wp_unslash( $_SERVER[ $header ] ) ),
280 'private' => true, // This isn't really 'private' information, but we don't want folks to easily paste these into public forums.
281 );
282 }
283 }
284
285 $debug_info['protect_header'] = array(
286 'label' => 'Trusted IP',
287 'value' => wp_json_encode( get_site_option( 'trusted_ip_header' ) ),
288 'private' => false,
289 );
290
291 /** Sync Debug Information */
292 $sync_module = Modules::get_module( 'full-sync' );
293 if ( $sync_module ) {
294 $sync_statuses = $sync_module->get_status();
295 $human_readable_sync_status = array();
296 foreach ( $sync_statuses as $sync_status => $sync_status_value ) {
297 $human_readable_sync_status[ $sync_status ] =
298 in_array( $sync_status, array( 'started', 'queue_finished', 'send_started', 'finished' ), true )
299 ? gmdate( 'r', $sync_status_value ) : $sync_status_value;
300 }
301 $debug_info['full_sync'] = array(
302 'label' => 'Full Sync Status',
303 'value' => wp_json_encode( $human_readable_sync_status ),
304 'private' => false,
305 );
306 }
307
308 $queue = Sender::get_instance()->get_sync_queue();
309
310 $debug_info['sync_size'] = array(
311 'label' => 'Sync Queue Size',
312 'value' => $queue->size(),
313 'private' => false,
314 );
315 $debug_info['sync_lag'] = array(
316 'label' => 'Sync Queue Lag',
317 'value' => self::seconds_to_time( $queue->lag() ),
318 'private' => false,
319 );
320
321 $full_sync_queue = Sender::get_instance()->get_full_sync_queue();
322
323 $debug_info['full_sync_size'] = array(
324 'label' => 'Full Sync Queue Size',
325 'value' => $full_sync_queue->size(),
326 'private' => false,
327 );
328 $debug_info['full_sync_lag'] = array(
329 'label' => 'Full Sync Queue Lag',
330 'value' => self::seconds_to_time( $full_sync_queue->lag() ),
331 'private' => false,
332 );
333
334 /**
335 * IDC Information
336 *
337 * Must follow sync debug since it depends on sync functionality.
338 */
339 $idc_urls = array(
340 'home' => Urls::home_url(),
341 'siteurl' => Urls::site_url(),
342 'WP_HOME' => Constants::is_defined( 'WP_HOME' ) ? Constants::get_constant( 'WP_HOME' ) : '',
343 'WP_SITEURL' => Constants::is_defined( 'WP_SITEURL' ) ? Constants::get_constant( 'WP_SITEURL' ) : '',
344 );
345
346 $debug_info['idc_urls'] = array(
347 'label' => 'IDC URLs',
348 'value' => wp_json_encode( $idc_urls ),
349 'private' => false,
350 );
351 $debug_info['idc_error_option'] = array(
352 'label' => 'IDC Error Option',
353 'value' => wp_json_encode( Jetpack_Options::get_option( 'sync_error_idc' ) ),
354 'private' => false,
355 );
356 $debug_info['idc_optin'] = array(
357 'label' => 'IDC Opt-in',
358 'value' => Identity_Crisis::should_handle_idc(),
359 'private' => false,
360 );
361
362 // @todo -- Add testing results?
363 $cxn_tests = new Jetpack_Cxn_Tests();
364 $debug_info['cxn_tests'] = array(
365 'label' => 'Connection Tests',
366 'value' => '',
367 'private' => false,
368 );
369 if ( $cxn_tests->pass() ) {
370 $debug_info['cxn_tests']['value'] = 'All Pass.';
371 } else {
372 $debug_info['cxn_tests']['value'] = wp_json_encode( $cxn_tests->list_fails() );
373 }
374
375 return $debug_info;
376 }
377
378 /**
379 * Returns a human readable string for which user is the master user.
380 *
381 * @return string
382 */
383 private static function human_readable_master_user() {
384 $master_user = Jetpack_Options::get_option( 'master_user' );
385
386 if ( ! $master_user ) {
387 return __( 'No master user set.', 'jetpack' );
388 }
389
390 $user = new WP_User( $master_user );
391
392 if ( ! $user ) {
393 return __( 'Master user no longer exists. Please disconnect and reconnect Jetpack.', 'jetpack' );
394 }
395
396 return self::human_readable_user( $user );
397 }
398
399 /**
400 * Return human readable string for a given user object.
401 *
402 * @param WP_User|int $user Object or ID.
403 *
404 * @return string
405 */
406 private static function human_readable_user( $user ) {
407 $user = new WP_User( $user );
408
409 return sprintf( '#%1$d %2$s', $user->ID, $user->user_login ); // Format: "#1 username".
410 }
411 }
412