Completed
Push — branch-7.3-built ( fbb50d...81e744 )
by
unknown
43:38 queued 36:05
created

Jetpack_Debug_Data   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 386
Duplicated Lines 3.11 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 12
loc 386
rs 9.76
c 0
b 0
f 0
wmc 33
lcom 1
cbo 8

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