Completed
Push — try/xmlrpc-server-package-clas... ( 6199c0...8e0ceb )
by
unknown
07:26
created

Jetpack_Debug_Data   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 374
Duplicated Lines 3.21 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
dl 12
loc 374
rs 9.92
c 0
b 0
f 0
wmc 31
lcom 1
cbo 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A what_jetpack_plan() 0 4 2
A seconds_to_time() 0 24 5
A core_debug_data() 0 19 1
F debug_data() 12 231 19
A human_readable_master_user() 0 15 3
A human_readable_user() 0 5 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * Jetpack Debug Data for the legacy Jetpack debugger page and the WP 5.2-era Site Health sections.
4
 *
5
 * @package jetpack
6
 */
7
8
use Automattic\Jetpack\Constants;
9
10
/**
11
 * Class Jetpack_Debug_Data
12
 *
13
 * Collect and return debug data for Jetpack.
14
 *
15
 * @since 7.3.0
16
 */
17
class Jetpack_Debug_Data {
18
	/**
19
	 * Determine the active plan and normalize it for the debugger results.
20
	 *
21
	 * @since 7.3.0
22
	 *
23
	 * @return string The plan slug.
24
	 */
25
	public static function what_jetpack_plan() {
26
		$plan = Jetpack_Plan::get();
27
		return ! empty( $plan['class'] ) ? $plan['class'] : 'undefined';
28
	}
29
30
	/**
31
	 * Convert seconds to human readable time.
32
	 *
33
	 * A dedication function instead of using Core functionality to allow for output in seconds.
34
	 *
35
	 * @since 7.3.0
36
	 *
37
	 * @param int $seconds Number of seconds to convert to human time.
38
	 *
39
	 * @return string Human readable time.
40
	 */
41
	public static function seconds_to_time( $seconds ) {
42
		$seconds = intval( $seconds );
43
		$units   = array(
44
			'week'   => WEEK_IN_SECONDS,
45
			'day'    => DAY_IN_SECONDS,
46
			'hour'   => HOUR_IN_SECONDS,
47
			'minute' => MINUTE_IN_SECONDS,
48
			'second' => 1,
49
		);
50
		// specifically handle zero.
51
		if ( 0 === $seconds ) {
52
			return '0 seconds';
53
		}
54
		$human_readable = '';
55
		foreach ( $units as $name => $divisor ) {
56
			$quot = intval( $seconds / $divisor );
57
			if ( $quot ) {
58
				$human_readable .= "$quot $name";
59
				$human_readable .= ( abs( $quot ) > 1 ? 's' : '' ) . ', ';
60
				$seconds        -= $quot * $divisor;
61
			}
62
		}
63
		return substr( $human_readable, 0, -2 );
64
	}
65
66
	/**
67
	 * Return debug data in the format expected by Core's Site Health Info tab.
68
	 *
69
	 * @since 7.3.0
70
	 *
71
	 * @param array $debug {
72
	 *     The debug information already compiled by Core.
73
	 *
74
	 *     @type string  $label        The title for this section of the debug output.
75
	 *     @type string  $description  Optional. A description for your information section which may contain basic HTML
76
	 *                                 markup: `em`, `strong` and `a` for linking to documentation or putting emphasis.
77
	 *     @type boolean $show_count   Optional. If set to `true` the amount of fields will be included in the title for
78
	 *                                 this section.
79
	 *     @type boolean $private      Optional. If set to `true` the section and all associated fields will be excluded
80
	 *                                 from the copy-paste text area.
81
	 *     @type array   $fields {
82
	 *         An associative array containing the data to be displayed.
83
	 *
84
	 *         @type string  $label    The label for this piece of information.
85
	 *         @type string  $value    The output that is of interest for this field.
86
	 *         @type boolean $private  Optional. If set to `true` the field will not be included in the copy-paste text area
87
	 *                                 on top of the page, allowing you to show, for example, API keys here.
88
	 *     }
89
	 * }
90
	 *
91
	 * @return array $args Debug information in the same format as the initial argument.
92
	 */
93
	public static function core_debug_data( $debug ) {
94
		$jetpack = array(
95
			'jetpack' => array(
96
				'label'       => __( 'Jetpack', 'jetpack' ),
97
				'description' => sprintf(
98
					/* translators: %1$s is URL to jetpack.com's contact support page. %2$s accessibility text */
99
					__(
100
						'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>',
101
						'jetpack'
102
					),
103
					esc_html( 'https://jetpack.com/contact-support/' ),
104
					__( '(opens in a new tab)', 'jetpack' )
105
				),
106
				'fields'      => self::debug_data(),
107
			),
108
		);
109
		$debug   = array_merge( $debug, $jetpack );
110
		return $debug;
111
	}
112
113
	/**
114
	 * Compile and return array of debug information.
115
	 *
116
	 * @since 7.3.0
117
	 *
118
	 * @return array $args {
119
	 *          Associated array of arrays with the following.
120
	 *         @type string  $label    The label for this piece of information.
121
	 *         @type string  $value    The output that is of interest for this field.
122
	 *         @type boolean $private  Optional. Set to true if data is sensitive (API keys, etc).
123
	 * }
124
	 */
125
	public static function debug_data() {
126
		$debug_info = array();
127
128
		/* Add various important Jetpack options */
129
		$debug_info['site_id']        = array(
130
			'label'   => 'Jetpack Site ID',
131
			'value'   => Jetpack_Options::get_option( 'id' ),
132
			'private' => false,
133
		);
134
		$debug_info['ssl_cert']       = array(
135
			'label'   => 'Jetpack SSL Verfication Bypass',
136
			'value'   => ( Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) ? 'Yes' : 'No',
137
			'private' => false,
138
		);
139
		$debug_info['time_diff']      = array(
140
			'label'   => "Offset between Jetpack server's time and this server's time.",
141
			'value'   => Jetpack_Options::get_option( 'time_diff' ),
142
			'private' => false,
143
		);
144
		$debug_info['version_option'] = array(
145
			'label'   => 'Current Jetpack Version Option',
146
			'value'   => Jetpack_Options::get_option( 'version' ),
147
			'private' => false,
148
		);
149
		$debug_info['old_version']    = array(
150
			'label'   => 'Previous Jetpack Version',
151
			'value'   => Jetpack_Options::get_option( 'old_version' ),
152
			'private' => false,
153
		);
154
		$debug_info['public']         = array(
155
			'label'   => 'Jetpack Site Public',
156
			'value'   => ( Jetpack_Options::get_option( 'public' ) ) ? 'Public' : 'Private',
157
			'private' => false,
158
		);
159
		$debug_info['master_user']    = array(
160
			'label'   => 'Jetpack Master User',
161
			'value'   => self::human_readable_master_user(),
162
			'private' => false,
163
		);
164
165
		/**
166
		 * Token information is private, but awareness if there one is set is helpful.
167
		 *
168
		 * To balance out information vs privacy, we only display and include the "key",
169
		 * which is a segment of the token prior to a period within the token and is
170
		 * technically not private.
171
		 *
172
		 * If a token does not contain a period, then it is malformed and we report it as such.
173
		 */
174
		$user_id    = get_current_user_id();
175
		$blog_token = Jetpack_Data::get_access_token();
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
176
		$user_token = Jetpack_Data::get_access_token( $user_id );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
177
178
		$tokenset = '';
179 View Code Duplication
		if ( $blog_token ) {
180
			$tokenset = 'Blog ';
181
			$blog_key = substr( $blog_token->secret, 0, strpos( $blog_token->secret, '.' ) );
182
			// Intentionally not translated since this is helpful when sent to Happiness.
183
			$blog_key = ( $blog_key ) ? $blog_key : 'Potentially Malformed Token.';
184
		}
185 View Code Duplication
		if ( $user_token ) {
186
			$tokenset .= 'User';
187
			$user_key  = substr( $user_token->secret, 0, strpos( $user_token->secret, '.' ) );
188
			// Intentionally not translated since this is helpful when sent to Happiness.
189
			$user_key = ( $user_key ) ? $user_key : 'Potentially Malformed Token.';
190
		}
191
		if ( ! $tokenset ) {
192
			$tokenset = 'None';
193
		}
194
195
		$debug_info['current_user'] = array(
196
			'label'   => 'Current User',
197
			'value'   => self::human_readable_user( $user_id ),
198
			'private' => false,
199
		);
200
		$debug_info['tokens_set']   = array(
201
			'label'   => 'Tokens defined',
202
			'value'   => $tokenset,
203
			'private' => false,
204
		);
205
		$debug_info['blog_token']   = array(
206
			'label'   => 'Blog Public Key',
207
			'value'   => ( $blog_token ) ? $blog_key : 'Not set.',
0 ignored issues
show
Bug introduced by
The variable $blog_key does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
208
			'private' => false,
209
		);
210
		$debug_info['user_token']   = array(
211
			'label'   => 'User Public Key',
212
			'value'   => ( $user_token ) ? $user_key : 'Not set.',
0 ignored issues
show
Bug introduced by
The variable $user_key does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
213
			'private' => false,
214
		);
215
216
		/** Jetpack Environmental Information */
217
		$debug_info['version']       = array(
218
			'label'   => 'Jetpack Version',
219
			'value'   => JETPACK__VERSION,
220
			'private' => false,
221
		);
222
		$debug_info['jp_plugin_dir'] = array(
223
			'label'   => 'Jetpack Directory',
224
			'value'   => JETPACK__PLUGIN_DIR,
225
			'private' => false,
226
		);
227
		$debug_info['plan']          = array(
228
			'label'   => 'Plan Type',
229
			'value'   => self::what_jetpack_plan(),
230
			'private' => false,
231
		);
232
233
		foreach ( array(
234
			'HTTP_HOST',
235
			'SERVER_PORT',
236
			'HTTPS',
237
			'GD_PHP_HANDLER',
238
			'HTTP_AKAMAI_ORIGIN_HOP',
239
			'HTTP_CF_CONNECTING_IP',
240
			'HTTP_CLIENT_IP',
241
			'HTTP_FASTLY_CLIENT_IP',
242
			'HTTP_FORWARDED',
243
			'HTTP_FORWARDED_FOR',
244
			'HTTP_INCAP_CLIENT_IP',
245
			'HTTP_TRUE_CLIENT_IP',
246
			'HTTP_X_CLIENTIP',
247
			'HTTP_X_CLUSTER_CLIENT_IP',
248
			'HTTP_X_FORWARDED',
249
			'HTTP_X_FORWARDED_FOR',
250
			'HTTP_X_IP_TRAIL',
251
			'HTTP_X_REAL_IP',
252
			'HTTP_X_VARNISH',
253
			'REMOTE_ADDR',
254
		) as $header ) {
255
			if ( isset( $_SERVER[ $header ] ) ) {
256
				$debug_info[ $header ] = array(
257
					'label'   => 'Server Variable ' . $header,
258
					'value'   => ( $_SERVER[ $header ] ) ? $_SERVER[ $header ] : 'false',
259
					'private' => false,
260
				);
261
			}
262
		}
263
264
		$debug_info['protect_header'] = array(
265
			'label'   => 'Trusted IP',
266
			'value'   => wp_json_encode( get_site_option( 'trusted_ip_header' ) ),
267
			'private' => false,
268
		);
269
270
		/** Sync Debug Information */
271
		$sync_module = Jetpack_Sync_Modules::get_module( 'full-sync' );
272
		if ( $sync_module ) {
273
			$sync_statuses              = $sync_module->get_status();
274
			$human_readable_sync_status = array();
275
			foreach ( $sync_statuses as $sync_status => $sync_status_value ) {
276
				$human_readable_sync_status[ $sync_status ] =
277
					in_array( $sync_status, array( 'started', 'queue_finished', 'send_started', 'finished' ), true )
278
						? date( 'r', $sync_status_value ) : $sync_status_value;
279
			}
280
			$debug_info['full_sync'] = array(
281
				'label'   => 'Full Sync Status',
282
				'value'   => wp_json_encode( $human_readable_sync_status ),
283
				'private' => false,
284
			);
285
		}
286
287
		$queue = Jetpack_Sync_Sender::get_instance()->get_sync_queue();
288
289
		$debug_info['sync_size'] = array(
290
			'label'   => 'Sync Queue Size',
291
			'value'   => $queue->size(),
292
			'private' => false,
293
		);
294
		$debug_info['sync_lag']  = array(
295
			'label'   => 'Sync Queue Lag',
296
			'value'   => self::seconds_to_time( $queue->lag() ),
297
			'private' => false,
298
		);
299
300
		$full_sync_queue = Jetpack_Sync_Sender::get_instance()->get_full_sync_queue();
301
302
		$debug_info['full_sync_size'] = array(
303
			'label'   => 'Full Sync Queue Size',
304
			'value'   => $full_sync_queue->size(),
305
			'private' => false,
306
		);
307
		$debug_info['full_sync_lag']  = array(
308
			'label'   => 'Full Sync Queue Lag',
309
			'value'   => self::seconds_to_time( $full_sync_queue->lag() ),
310
			'private' => false,
311
		);
312
313
		/**
314
		 * IDC Information
315
		 *
316
		 * Must follow sync debug since it depends on sync functionality.
317
		 */
318
		$idc_urls = array(
319
			'home'       => Jetpack_Sync_Functions::home_url(),
320
			'siteurl'    => Jetpack_Sync_Functions::site_url(),
321
			'WP_HOME'    => Constants::is_defined( 'WP_HOME' ) ? Constants::get_constant( 'WP_HOME' ) : '',
322
			'WP_SITEURL' => Constants::is_defined( 'WP_SITEURL' ) ? Constants::get_constant( 'WP_SITEURL' ) : '',
323
		);
324
325
		$debug_info['idc_urls']         = array(
326
			'label'   => 'IDC URLs',
327
			'value'   => wp_json_encode( $idc_urls ),
328
			'private' => false,
329
		);
330
		$debug_info['idc_error_option'] = array(
331
			'label'   => 'IDC Error Option',
332
			'value'   => wp_json_encode( Jetpack_Options::get_option( 'sync_error_idc' ) ),
333
			'private' => false,
334
		);
335
		$debug_info['idc_optin']        = array(
336
			'label'   => 'IDC Opt-in',
337
			'value'   => Jetpack::sync_idc_optin(),
338
			'private' => false,
339
		);
340
341
		// @todo -- Add testing results?
342
		$cxn_tests               = new Jetpack_Cxn_Tests();
343
		$debug_info['cxn_tests'] = array(
344
			'label'   => 'Connection Tests',
345
			'value'   => '',
346
			'private' => false,
347
		);
348
		if ( $cxn_tests->pass() ) {
349
			$debug_info['cxn_tests']['value'] = 'All Pass.';
350
		} else {
351
			$debug_info['cxn_tests']['value'] = wp_json_encode( $cxn_tests->list_fails() );
352
		}
353
354
		return $debug_info;
355
	}
356
357
	/**
358
	 * Returns a human readable string for which user is the master user.
359
	 *
360
	 * @return string
361
	 */
362
	private static function human_readable_master_user() {
363
		$master_user = Jetpack_Options::get_option( 'master_user' );
364
365
		if ( ! $master_user ) {
366
			return __( 'No master user set.', 'jetpack' );
367
		}
368
369
		$user = new WP_User( $master_user );
370
371
		if ( ! $user ) {
372
			return __( 'Master user no longer exists. Please disconnect and reconnect Jetpack.', 'jetpack' );
373
		}
374
375
		return self::human_readable_user( $user );
376
	}
377
378
	/**
379
	 * Return human readable string for a given user object.
380
	 *
381
	 * @param WP_User|int $user Object or ID.
382
	 *
383
	 * @return string
384
	 */
385
	private static function human_readable_user( $user ) {
386
		$user = new WP_User( $user );
387
388
		return sprintf( '#%1$d %2$s (%3$s)', $user->ID, $user->user_login, $user->user_email ); // Format: "#1 username ([email protected])".
389
	}
390
}
391