PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.2-beta
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 in Jetpack – WP Security, Backup, Speed, & Growth 16.2-beta, at _inc/lib/debugger/class-jetpack-debug-data.php

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