Completed
Push — renovate/mocha-8.x ( 07030e...e8e64c )
by
unknown
28:17 queued 19:09
created

Jetpack_Cxn_Tests   F

Complexity

Total Complexity 78

Size/Duplication

Total Lines 843
Duplicated Lines 14 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
dl 118
loc 843
rs 1.917
c 0
b 0
f 0
wmc 78
lcom 1
cbo 12

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 39 4
A helper_retrieve_local_master_user() 0 4 1
A helper_is_jetpack_connected() 0 3 2
A helper_get_blog_token() 0 3 1
A helper_get_support_url() 0 5 2
A helper_get_support_text() 0 3 1
A helper_enable_outbound_requests() 0 11 1
A increase_timeout() 0 3 1
A test__blog_token_if_exists() 12 18 2
B test__check_if_connected() 0 56 4
A test__master_user_exists_on_site() 8 27 3
A test__master_user_can_manage_options() 8 28 3
A test__xml_parser_available() 11 17 2
A test__outbound_http() 18 18 2
A test__outbound_https() 18 18 2
A test__identity_crisis() 8 31 3
B test__token_health() 0 36 6
B test__wpcom_connection_test() 32 65 9
C test__server_port_value() 0 53 11
B test__full_sync_health() 0 64 5
B test__sync_health() 0 141 5
B last__wpcom_self_test() 3 43 8

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Cxn_Tests often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Cxn_Tests, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Collection of tests to run on the Jetpack connection locally.
4
 *
5
 * @package Jetpack
6
 */
7
8
use Automattic\Jetpack\Connection\Client;
9
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
10
use Automattic\Jetpack\Status;
11
use Automattic\Jetpack\Connection\Utils as Connection_Utils;
12
use Automattic\Jetpack\Sync\Modules;
13
use Automattic\Jetpack\Sync\Settings as Sync_Settings;
14
use Automattic\Jetpack\Sync\Health as Sync_Health;
15
use Automattic\Jetpack\Sync\Sender as Sync_Sender;
16
use Automattic\Jetpack\Redirect;
17
18
/**
19
 * Class Jetpack_Cxn_Tests contains all of the actual tests.
20
 */
21
class Jetpack_Cxn_Tests extends Jetpack_Cxn_Test_Base {
22
23
	/**
24
	 * Jetpack_Cxn_Tests constructor.
25
	 */
26
	public function __construct() {
27
		parent::__construct();
28
29
		$methods = get_class_methods( 'Jetpack_Cxn_Tests' );
30
31
		foreach ( $methods as $method ) {
32
			if ( false === strpos( $method, 'test__' ) ) {
33
				continue;
34
			}
35
			$this->add_test( array( $this, $method ), $method, 'direct' );
36
		}
37
38
		/**
39
		 * Fires after loading default Jetpack Connection tests.
40
		 *
41
		 * @since 7.1.0
42
		 * @since 8.3.0 Passes the Jetpack_Cxn_Tests instance.
43
		 */
44
		do_action( 'jetpack_connection_tests_loaded', $this );
45
46
		/**
47
		 * Determines if the WP.com testing suite should be included.
48
		 *
49
		 * @since 7.1.0
50
		 * @since 8.1.0 Default false.
51
		 *
52
		 * @param bool $run_test To run the WP.com testing suite. Default false.
53
		 */
54
		if ( apply_filters( 'jetpack_debugger_run_self_test', false ) ) {
55
			/**
56
			 * Intentionally added last as it checks for an existing failure state before attempting.
57
			 * Generally, any failed location condition would result in the WP.com check to fail too, so
58
			 * we will skip it to avoid confusing error messages.
59
			 *
60
			 * Note: This really should be an 'async' test.
61
			 */
62
			$this->add_test( array( $this, 'last__wpcom_self_test' ), 'test__wpcom_self_test', 'direct' );
63
		}
64
	}
65
66
	/**
67
	 * Helper function to look up the expected master user and return the local WP_User.
68
	 *
69
	 * @return WP_User Jetpack's expected master user.
70
	 */
71
	protected function helper_retrieve_local_master_user() {
72
		$master_user = Jetpack_Options::get_option( 'master_user' );
73
		return new WP_User( $master_user );
74
	}
75
76
	/**
77
	 * Is Jetpack even connected and supposed to be talking to WP.com?
78
	 */
79
	protected function helper_is_jetpack_connected() {
80
		return Jetpack::is_active() && ! ( new Status() )->is_offline_mode();
81
	}
82
83
	/**
84
	 * Retrieve the `blog_token` if it exists.
85
	 *
86
	 * @return object|false
87
	 */
88
	protected function helper_get_blog_token() {
89
		return Jetpack::connection()->get_access_token();
90
	}
91
92
	/**
93
	 * Returns a support url based on using a development version.
94
	 */
95
	protected function helper_get_support_url() {
96
		return Jetpack::is_development_version()
97
			? Redirect::get_url( 'jetpack-contact-support-beta-group' )
98
			: Redirect::get_url( 'jetpack-contact-support' );
99
	}
100
101
	/**
102
	 * Gets translated support text.
103
	 */
104
	protected function helper_get_support_text() {
105
		return __( 'Please contact Jetpack support.', 'jetpack' );
106
	}
107
108
	/**
109
	 * Gets translated text to enable outbound requests.
110
	 *
111
	 * @param string $protocol Either 'HTTP' or 'HTTPS'.
112
	 *
113
	 * @return string The translated text.
114
	 */
115
	protected function helper_enable_outbound_requests( $protocol ) {
116
		return sprintf(
117
			/* translators: %1$s - request protocol, either http or https */
118
			__(
119
				'Your server did not successfully connect to the Jetpack server using %1$s
120
				Please ask your hosting provider to confirm your server can make outbound requests to jetpack.com.',
121
				'jetpack'
122
			),
123
			$protocol
124
		);
125
	}
126
127
	/**
128
	 * Returns 30 for use with a filter.
129
	 *
130
	 * To allow time for WP.com to run upstream testing, this function exists to increase the http_request_timeout value
131
	 * to 30.
132
	 *
133
	 * @return int 30
134
	 */
135
	public static function increase_timeout() {
136
		return 30; // seconds.
137
	}
138
139
	/**
140
	 * The test verifies the blog token exists.
141
	 *
142
	 * @return array
143
	 */
144
	protected function test__blog_token_if_exists() {
145
		$blog_token = $this->helper_get_blog_token();
146
147 View Code Duplication
		if ( $blog_token ) {
148
			$result = self::passing_test( array( 'name' => __FUNCTION__ ) );
149
		} else {
150
			$result = self::failing_test(
151
				array(
152
					'name'              => __FUNCTION__,
153
					'short_description' => __( 'Blog token is missing.', 'jetpack' ),
154
					'action'            => admin_url( 'admin.php?page=jetpack#/dashboard' ),
155
					'action_label'      => __( 'Disconnect your site from WordPress.com, and connect it again.', 'jetpack' ),
156
				)
157
			);
158
		}
159
160
		return $result;
161
	}
162
163
	/**
164
	 * Test if Jetpack is connected.
165
	 */
166
	protected function test__check_if_connected() {
167
		$name = __FUNCTION__;
168
169
		if ( ! $this->helper_get_blog_token() ) {
170
			return self::skipped_test(
171
				array(
172
					'name'              => $name,
173
					'short_description' => __( 'Blog token is missing.', 'jetpack' ),
174
				)
175
			);
176
		}
177
178
		if ( $this->helper_is_jetpack_connected() ) {
179
			$result = self::passing_test(
180
				array(
181
					'name'             => $name,
182
					'label'            => __( 'Your site is connected to Jetpack', 'jetpack' ),
183
					'long_description' => sprintf(
184
						'<p>%1$s</p>' .
185
						'<p><span class="dashicons pass"><span class="screen-reader-text">%2$s</span></span> %3$s</p>',
186
						__( 'A healthy connection ensures Jetpack essential services are provided to your WordPress site, such as Stats and Site Security.', 'jetpack' ),
187
						/* translators: Screen reader text indicating a test has passed */
188
						__( 'Passed', 'jetpack' ),
189
						__( 'Your site is connected to Jetpack.', 'jetpack' )
190
					),
191
				)
192
			);
193
		} elseif ( ( new Status() )->is_offline_mode() ) {
194
			$result = self::skipped_test(
195
				array(
196
					'name'              => $name,
197
					'short_description' => __( 'Jetpack is in Offline Mode:', 'jetpack' ) . ' ' . Jetpack::development_mode_trigger_text(),
198
				)
199
			);
200
		} else {
201
			$result = self::failing_test(
202
				array(
203
					'name'              => $name,
204
					'short_description' => __( 'Your site is not connected to Jetpack', 'jetpack' ),
205
					'action'            => admin_url( 'admin.php?page=jetpack#/dashboard' ),
206
					'action_label'      => __( 'Reconnect your site now', 'jetpack' ),
207
					'long_description'  => sprintf(
208
						'<p>%1$s</p>' .
209
						'<p><span class="dashicons fail"><span class="screen-reader-text">%2$s</span></span> %3$s<strong> %4$s</strong></p>',
210
						__( 'A healthy connection ensures Jetpack essential services are provided to your WordPress site, such as Stats and Site Security.', 'jetpack' ),
211
						/* translators: screen reader text indicating a test failed */
212
						__( 'Error', 'jetpack' ),
213
						__( 'Your site is not connected to Jetpack.', 'jetpack' ),
214
						__( 'We recommend reconnecting Jetpack.', 'jetpack' )
215
					),
216
				)
217
			);
218
		}
219
220
		return $result;
221
	}
222
223
	/**
224
	 * Test that the master user still exists on this site.
225
	 *
226
	 * @return array Test results.
227
	 */
228
	protected function test__master_user_exists_on_site() {
229
		$name = __FUNCTION__;
230 View Code Duplication
		if ( ! $this->helper_is_jetpack_connected() ) {
231
			return self::skipped_test(
232
				array(
233
					'name'              => $name,
234
					'short_description' => __( 'Jetpack is not connected. No master user to check.', 'jetpack' ),
235
				)
236
			);
237
		}
238
		$local_user = $this->helper_retrieve_local_master_user();
239
240
		if ( $local_user->exists() ) {
241
			$result = self::passing_test( array( 'name' => $name ) );
242
		} else {
243
			$result = self::failing_test(
244
				array(
245
					'name'              => $name,
246
					'short_description' => __( 'The user who setup the Jetpack connection no longer exists on this site.', 'jetpack' ),
247
					'action_label'      => __( 'Please disconnect and reconnect Jetpack.', 'jetpack' ),
248
					'action'            => Redirect::get_url( 'jetpack-support-reconnecting-reinstalling-jetpack' ),
249
				)
250
			);
251
		}
252
253
		return $result;
254
	}
255
256
	/**
257
	 * Test that the master user has the manage options capability (e.g. is an admin).
258
	 *
259
	 * Generic calls from WP.com execute on Jetpack as the master user. If it isn't an admin, random things will fail.
260
	 *
261
	 * @return array Test results.
262
	 */
263
	protected function test__master_user_can_manage_options() {
264
		$name = __FUNCTION__;
265 View Code Duplication
		if ( ! $this->helper_is_jetpack_connected() ) {
266
			return self::skipped_test(
267
				array(
268
					'name'              => $name,
269
					'short_description' => __( 'Jetpack is not connected.', 'jetpack' ),
270
				)
271
			);
272
		}
273
		$master_user = $this->helper_retrieve_local_master_user();
274
275
		if ( user_can( $master_user, 'manage_options' ) ) {
276
			$result = self::passing_test( array( 'name' => $name ) );
277
		} else {
278
			$result = self::failing_test(
279
				array(
280
					'name'              => $name,
281
					/* translators: a WordPress username */
282
					'short_description' => sprintf( __( 'The user (%s) who setup the Jetpack connection is not an administrator.', 'jetpack' ), $master_user->user_login ),
283
					'action_label'      => __( 'Either upgrade the user or disconnect and reconnect Jetpack.', 'jetpack' ),
284
					'action'            => Redirect::get_url( 'jetpack-support-reconnecting-reinstalling-jetpack' ),
285
				)
286
			);
287
		}
288
289
		return $result;
290
	}
291
292
	/**
293
	 * Test that the PHP's XML library is installed.
294
	 *
295
	 * While it should be installed by default, increasingly in PHP 7, some OSes require an additional php-xml package.
296
	 *
297
	 * @return array Test results.
298
	 */
299
	protected function test__xml_parser_available() {
300
		$name = __FUNCTION__;
301
		if ( function_exists( 'xml_parser_create' ) ) {
302
			$result = self::passing_test( array( 'name' => $name ) );
303 View Code Duplication
		} else {
304
			$result = self::failing_test(
305
				array(
306
					'name'              => $name,
307
					'label'             => __( 'PHP XML manipulation libraries are not available.', 'jetpack' ),
308
					'short_description' => __( 'Please ask your hosting provider to refer to our server requirements and enable PHP\'s XML module.', 'jetpack' ),
309
					'action_label'      => __( 'View our server requirements', 'jetpack' ),
310
					'action'            => Redirect::get_url( 'jetpack-support-server-requirements' ),
311
				)
312
			);
313
		}
314
		return $result;
315
	}
316
317
	/**
318
	 * Test that the server is able to send an outbound http communication.
319
	 *
320
	 * @return array Test results.
321
	 */
322 View Code Duplication
	protected function test__outbound_http() {
323
		$name    = __FUNCTION__;
324
		$request = wp_remote_get( preg_replace( '/^https:/', 'http:', JETPACK__API_BASE ) . 'test/1/' );
325
		$code    = wp_remote_retrieve_response_code( $request );
326
327
		if ( 200 === intval( $code ) ) {
328
			$result = self::passing_test( array( 'name' => $name ) );
329
		} else {
330
			$result = self::failing_test(
331
				array(
332
					'name'              => $name,
333
					'short_description' => $this->helper_enable_outbound_requests( 'HTTP' ),
334
				)
335
			);
336
		}
337
338
		return $result;
339
	}
340
341
	/**
342
	 * Test that the server is able to send an outbound https communication.
343
	 *
344
	 * @return array Test results.
345
	 */
346 View Code Duplication
	protected function test__outbound_https() {
347
		$name    = __FUNCTION__;
348
		$request = wp_remote_get( preg_replace( '/^http:/', 'https:', JETPACK__API_BASE ) . 'test/1/' );
349
		$code    = wp_remote_retrieve_response_code( $request );
350
351
		if ( 200 === intval( $code ) ) {
352
			$result = self::passing_test( array( 'name' => $name ) );
353
		} else {
354
			$result = self::failing_test(
355
				array(
356
					'name'              => $name,
357
					'short_description' => $this->helper_enable_outbound_requests( 'HTTPS' ),
358
				)
359
			);
360
		}
361
362
		return $result;
363
	}
364
365
	/**
366
	 * Check for an IDC.
367
	 *
368
	 * @return array Test results.
369
	 */
370
	protected function test__identity_crisis() {
371
		$name = __FUNCTION__;
372 View Code Duplication
		if ( ! $this->helper_is_jetpack_connected() ) {
373
			return self::skipped_test(
374
				array(
375
					'name'              => $name,
376
					'short_description' => __( 'Jetpack is not connected.', 'jetpack' ),
377
				)
378
			);
379
		}
380
		$identity_crisis = Jetpack::check_identity_crisis();
381
382
		if ( ! $identity_crisis ) {
383
			$result = self::passing_test( array( 'name' => $name ) );
384
		} else {
385
			$result = self::failing_test(
386
				array(
387
					'name'              => $name,
388
					'short_description' => sprintf(
389
						/* translators: Two URLs. The first is the locally-recorded value, the second is the value as recorded on WP.com. */
390
						__( 'Your url is set as `%1$s`, but your WordPress.com connection lists it as `%2$s`!', 'jetpack' ),
391
						$identity_crisis['home'],
392
						$identity_crisis['wpcom_home']
393
					),
394
					'action_label'      => $this->helper_get_support_text(),
395
					'action'            => $this->helper_get_support_url(),
396
				)
397
			);
398
		}
399
		return $result;
400
	}
401
402
	/**
403
	 * Tests blog and current user's token against wp.com's check-token-health endpoint.
404
	 *
405
	 * @since 9.0.0
406
	 *
407
	 * @return array Test results.
408
	 */
409
	protected function test__token_health() {
410
		$name = __FUNCTION__;
411
412
		$m                = new Connection_Manager();
413
		$validated_tokens = $m->validate_tokens();
414
415
		if ( ! is_array( $validated_tokens ) || count( array_diff_key( array_flip( array( 'blog_token', 'user_token' ) ), $validated_tokens ) ) ) {
416
			return self::skipped_test(
417
				array(
418
					'name'              => $name,
419
					'short_description' => __( 'Token health check failed to validate tokens.', 'jetpack' ),
420
				)
421
			);
422
		}
423
424
		$invalid_tokens_exist = false;
425
		foreach ( $validated_tokens as $validated_token ) {
426
			if ( ! $validated_token['is_healthy'] ) {
427
				$invalid_tokens_exist = true;
428
				break;
429
			}
430
		}
431
432
		if ( false === $invalid_tokens_exist ) {
433
			return self::passing_test( array( 'name' => $name ) );
434
		}
435
436
		return self::failing_test(
437
			array(
438
				'name'              => $name,
439
				'short_description' => __( 'Connection test failed: Invalid Jetpack tokens', 'jetpack' ),
440
				'action_label'      => __( 'Please disconnect and reconnect Jetpack.', 'jetpack' ),
441
				'action'            => Redirect::get_url( 'jetpack-support-reconnecting-reinstalling-jetpack' ),
442
			)
443
		);
444
	}
445
446
	/**
447
	 * Tests connection status against wp.com's test-connection endpoint.
448
	 *
449
	 * @todo: Compare with the wpcom_self_test. We only need one of these.
450
	 *
451
	 * @return array Test results.
452
	 */
453
	protected function test__wpcom_connection_test() {
454
		$name = __FUNCTION__;
455
456
		$status = new Status();
457 View Code Duplication
		if ( ! Jetpack::is_active() || $status->is_offline_mode() || $status->is_staging_site() || ! $this->pass ) {
458
			return self::skipped_test( array( 'name' => $name ) );
459
		}
460
461
		add_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ) );
462
		$response = Client::wpcom_json_api_request_as_blog(
463
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
464
			Client::WPCOM_JSON_API_VERSION
465
		);
466
		remove_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ) );
467
468 View Code Duplication
		if ( is_wp_error( $response ) ) {
469
			return self::failing_test(
470
				array(
471
					'name'              => $name,
472
					/* translators: %1$s is the error code, %2$s is the error message */
473
					'short_description' => sprintf( __( 'Connection test failed (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() ),
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
474
					'action_label'      => $this->helper_get_support_text(),
475
					'action'            => $this->helper_get_support_url(),
476
				)
477
			);
478
		}
479
480
		$body = wp_remote_retrieve_body( $response );
481 View Code Duplication
		if ( ! $body ) {
482
			return self::failing_test(
483
				array(
484
					'name'              => $name,
485
					'short_description' => __( 'Connection test failed (empty response body)', 'jetpack' ) . wp_remote_retrieve_response_code( $response ),
486
					'action_label'      => $this->helper_get_support_text(),
487
					'action'            => $this->helper_get_support_url(),
488
				)
489
			);
490
		}
491
492 View Code Duplication
		if ( 404 === wp_remote_retrieve_response_code( $response ) ) {
493
			return self::skipped_test(
494
				array(
495
					'name'              => $name,
496
					'short_description' => __( 'The WordPress.com API returned a 404 error.', 'jetpack' ),
497
				)
498
			);
499
		}
500
501
		$result       = json_decode( $body );
502
		$is_connected = ! empty( $result->connected );
503
		$message      = $result->message . ': ' . wp_remote_retrieve_response_code( $response );
504
505
		if ( $is_connected ) {
506
			return self::passing_test( array( 'name' => $name ) );
507
		} else {
508
			return self::failing_test(
509
				array(
510
					'name'              => $name,
511
					'short_description' => $message,
512
					'action_label'      => $this->helper_get_support_text(),
513
					'action'            => $this->helper_get_support_url(),
514
				)
515
			);
516
		}
517
	}
518
519
	/**
520
	 * Tests the port number to ensure it is an expected value.
521
	 *
522
	 * We expect that sites on be on one of:
523
	 * port 80,
524
	 * port 443 (https sites only),
525
	 * the value of JETPACK_SIGNATURE__HTTP_PORT,
526
	 * unless the site is intentionally on a different port (e.g. example.com:8080 is the site's URL).
527
	 *
528
	 * If the value isn't one of those and the site's URL doesn't include a port, then the signature verification will fail.
529
	 *
530
	 * This happens most commonly on sites with reverse proxies, so the edge (e.g. Varnish) is running on 80/443, but nginx
531
	 * or Apache is responding internally on a different port (e.g. 81).
532
	 *
533
	 * @return array Test results
534
	 */
535
	protected function test__server_port_value() {
536
		$name = __FUNCTION__;
537
		if ( ! isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) && ! isset( $_SERVER['SERVER_PORT'] ) ) {
538
			return self::skipped_test(
539
				array(
540
					'name'              => $name,
541
					'short_description' => __( 'The server port values are not defined. This is most common when running PHP via a CLI.', 'jetpack' ),
542
				)
543
			);
544
		}
545
		$site_port   = wp_parse_url( home_url(), PHP_URL_PORT );
0 ignored issues
show
Unused Code introduced by
The call to wp_parse_url() has too many arguments starting with PHP_URL_PORT.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
546
		$server_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? (int) $_SERVER['HTTP_X_FORWARDED_PORT'] : (int) $_SERVER['SERVER_PORT'];
547
		$http_ports  = array( 80 );
548
		$https_ports = array( 80, 443 );
549
550
		if ( defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ) {
551
			$http_ports[] = JETPACK_SIGNATURE__HTTP_PORT;
552
		}
553
554
		if ( defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ) {
555
			$https_ports[] = JETPACK_SIGNATURE__HTTPS_PORT;
556
		}
557
558
		if ( $site_port ) {
559
			return self::skipped_test( array( 'name' => $name ) ); // Not currently testing for this situation.
560
		}
561
562
		if ( is_ssl() && in_array( $server_port, $https_ports, true ) ) {
563
			return self::passing_test( array( 'name' => $name ) );
564
		} elseif ( in_array( $server_port, $http_ports, true ) ) {
565
			return self::passing_test( array( 'name' => $name ) );
566
		} else {
567
			if ( is_ssl() ) {
568
				$needed_constant = 'JETPACK_SIGNATURE__HTTPS_PORT';
569
			} else {
570
				$needed_constant = 'JETPACK_SIGNATURE__HTTP_PORT';
571
			}
572
			return self::failing_test(
573
				array(
574
					'name'              => $name,
575
					'short_description' => sprintf(
576
						/* translators: %1$s - a PHP code snippet */
577
						__(
578
							'The server port value is unexpected.
579
						Try adding the following to your wp-config.php file: %1$s',
580
							'jetpack'
581
						),
582
						"define( '$needed_constant', $server_port )"
583
					),
584
				)
585
			);
586
		}
587
	}
588
589
	/**
590
	 * Full Sync Health Test.
591
	 *
592
	 * Sync Disabled: Results in a skipped test
593
	 * Not In Progress : Results in a skipped test
594
	 * In Progress: Results in skipped test w/ status in CLI
595
	 */
596
	protected function test__full_sync_health() {
597
598
		$name = __FUNCTION__;
599
600
		if ( ! $this->helper_is_jetpack_connected() ) {
601
			// If the site is not connected, there is no point in testing Sync health.
602
			return self::skipped_test(
603
				array(
604
					'name'                => $name,
605
					'show_in_site_health' => false,
606
				)
607
			);
608
		}
609
610
		// Sync is enabled.
611
		if ( Sync_Settings::is_sync_enabled() ) {
612
613
			// Get Full Sync Progress.
614
			$full_sync_module = Modules::get_module( 'full-sync' );
615
			$progress_percent = $full_sync_module ? $full_sync_module->get_sync_progress_percentage() : null;
616
617
			// Full Sync in Progress.
618
			if ( $progress_percent ) {
619
620
				return self::informational_test(
621
					array(
622
						'name'              => $name,
623
						'label'             => __( 'Jetpack is performing a full sync of your site', 'jetpack' ),
624
						'severity'          => 'recommended',
625
						/* translators: placeholder is a percentage number. */
626
						'short_description' => sprintf( __( 'Jetpack is performing a full sync of your site. Current Progress: %1$d %%', 'jetpack' ), $progress_percent ),
627
						'long_description'  => sprintf(
628
							'<p>%1$s</p><p><span class="dashicons dashicons-update"><span class="screen-reader-text">%2$s</span></span> %3$s</p><div class="jetpack-sync-progress-ui"><div class="jetpack-sync-progress-label"></div><div class="jetpack-sync-progress-bar"></div></div>',
629
							__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' ), /* translators: screen reader text indicating data is updating. */
630
							__( 'Updating', 'jetpack' ),
631
							__( 'Jetpack is currently performing a full sync of your site data.', 'jetpack' )
632
						),
633
					)
634
				);
635
636
			} else {
637
638
				// no Full Sync in Progress.
639
				return self::skipped_test(
640
					array(
641
						'name'                => $name,
642
						'show_in_site_health' => false,
643
					)
644
				);
645
646
			}
647
		} else {
648
649
			// If sync is not enabled no Full Sync can occur.
650
			return self::skipped_test(
651
				array(
652
					'name'                => $name,
653
					'show_in_site_health' => false,
654
				)
655
			);
656
657
		}
658
659
	}
660
661
	/**
662
	 * Sync Health Tests.
663
	 *
664
	 * Disabled: Results in a failing test (recommended)
665
	 * Delayed: Results in failing test (recommended)
666
	 * Error: Results in failing test (critical)
667
	 */
668
	protected function test__sync_health() {
669
670
		$name = __FUNCTION__;
671
672
		if ( ! $this->helper_is_jetpack_connected() ) {
673
			// If the site is not connected, there is no point in testing Sync health.
674
			return self::skipped_test(
675
				array(
676
					'name'                => $name,
677
					'show_in_site_health' => false,
678
				)
679
			);
680
		}
681
682
		// Sync is enabled.
683
		if ( Sync_Settings::is_sync_enabled() ) {
684
685
			if ( Sync_Health::get_status() === Sync_Health::STATUS_OUT_OF_SYNC ) {
686
				/*
687
				 * Sync has experienced Data Loss.
688
				 */
689
				$description  = '<p>';
690
				$description .= esc_html__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' );
691
				$description .= '</p>';
692
				$description .= '<p>';
693
				$description .= sprintf(
694
					'<span class="dashicons fail"><span class="screen-reader-text">%1$s</span></span> ',
695
					esc_html__( 'Error', 'jetpack' )
696
				);
697
				$description .= wp_kses(
698
					__( 'Jetpack has detected an error while syncing your site. <strong>We recommend <a id="full_sync_request_link" href="#">a full sync</a> to align Jetpack with your site data.</strong>', 'jetpack' ),
699
					array(
700
						'a'      => array(
701
							'id'   => array(),
702
							'href' => array(),
703
						),
704
						'strong' => array(),
705
					)
706
				);
707
				$description .= '</p>';
708
709
				return self::failing_test(
710
					array(
711
						'name'              => $name,
712
						'label'             => __( 'Jetpack has detected an error syncing your site.', 'jetpack' ),
713
						'severity'          => 'critical',
714
						'action'            => Redirect::get_url( 'jetpack-contact-support' ),
715
						'action_label'      => __( 'Contact Jetpack Support', 'jetpack' ),
716
						'short_description' => __( 'Jetpack has detected an error while syncing your site. We recommend a full sync to align Jetpack with your site data.', 'jetpack' ),
717
						'long_description'  => $description,
718
					)
719
				);
720
721
			} else {
722
				// Get the Sync Queue.
723
				$sender     = Sync_Sender::get_instance();
724
				$sync_queue = $sender->get_sync_queue();
725
726
				// lag exceeds 5 minutes.
727
				if ( $sync_queue->lag() > 5 * MINUTE_IN_SECONDS ) {
728
729
					$description  = '<p>';
730
					$description .= esc_html__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' );
731
					$description .= '</p>';
732
					$description .= '<p>';
733
					$description .= sprintf(
734
						'<span class="dashicons dashicons-clock" style="color: orange;"><span class="screen-reader-text">%1$s</span></span> ',
735
						/* translators: name, used to describe a clock icon. */
736
						esc_html__( 'Clock', 'jetpack' )
737
					);
738
					$description .= wp_kses(
739
						sprintf(
740
							/* translators: placeholder is a number of minutes. */
741
							_n(
742
								'Jetpack has identified a delay while syncing individual content updates. Certain features might be slower than usual, but this is only temporary while sync catches up with recent changes to your site. <strong>We’re seeing a current delay of %1$d minute.</strong>',
743
								'Jetpack has identified a delay while syncing individual content updates. Certain features might be slower than usual, but this is only temporary while sync catches up with recent changes to your site. <strong>We’re seeing a current delay of %1$d minutes.</strong>',
744
								intval( $sync_queue->lag() / MINUTE_IN_SECONDS ),
745
								'jetpack'
746
							),
747
							number_format_i18n( $sync_queue->lag() / MINUTE_IN_SECONDS )
748
						),
749
						array( 'strong' => array() )
750
					);
751
					$description .= '</p>';
752
753
					return self::informational_test(
754
						array(
755
							'name'              => $name,
756
							'label'             => __( 'Jetpack is experiencing a delay syncing your site.', 'jetpack' ),
757
							'severity'          => 'recommended',
758
							'action'            => null,
759
							'action_label'      => null,
760
							'short_description' => __( 'Jetpack is experiencing a delay syncing your site.', 'jetpack' ),
761
							'long_description'  => $description,
762
						)
763
					);
764
765
				} else {
766
767
					// Sync is Healthy.
768
					return self::passing_test( array( 'name' => $name ) );
769
770
				}
771
			}
772
		} else {
773
			/*
774
			 * Sync is disabled.
775
			 */
776
777
			$description  = '<p>';
778
			$description .= esc_html__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' );
779
			$description .= '</p>';
780
			$description .= '<p>';
781
			$description .= __( 'Developers may enable / disable syncing using the Sync Settings API.', 'jetpack' );
782
			$description .= '</p>';
783
			$description .= '<p>';
784
			$description .= sprintf(
785
				'<span class="dashicons fail"><span class="screen-reader-text">%1$s</span></span> ',
786
				esc_html__( 'Error', 'jetpack' )
787
			);
788
			$description .= wp_kses(
789
				__( 'Jetpack Sync has been disabled on your site. Without it, certain Jetpack features will not work. <strong>We recommend enabling Sync.</strong>', 'jetpack' ),
790
				array( 'strong' => array() )
791
			);
792
			$description .= '</p>';
793
794
			return self::failing_test(
795
				array(
796
					'name'              => $name,
797
					'label'             => __( 'Jetpack Sync has been disabled on your site.', 'jetpack' ),
798
					'severity'          => 'recommended',
799
					'action'            => 'https://github.com/Automattic/jetpack/blob/master/packages/sync/src/class-settings.php',
800
					'action_label'      => __( 'See Github for more on Sync Settings', 'jetpack' ),
801
					'short_description' => __( 'Jetpack Sync has been disabled on your site.', 'jetpack' ),
802
					'long_description'  => $description,
803
				)
804
			);
805
806
		}
807
808
	}
809
810
	/**
811
	 * Calls to WP.com to run the connection diagnostic testing suite.
812
	 *
813
	 * Intentionally added last as it will be skipped if any local failed conditions exist.
814
	 *
815
	 * @since 7.1.0
816
	 * @since 7.9.0 Timeout waiting for a WP.com response no longer fails the test. Test is marked skipped instead.
817
	 *
818
	 * @return array Test results.
819
	 */
820
	protected function last__wpcom_self_test() {
821
		$name = 'test__wpcom_self_test';
822
823
		$status = new Status();
824 View Code Duplication
		if ( ! Jetpack::is_active() || $status->is_offline_mode() || $status->is_staging_site() || ! $this->pass ) {
825
			return self::skipped_test( array( 'name' => $name ) );
826
		}
827
828
		$self_xml_rpc_url = site_url( 'xmlrpc.php' );
829
830
		$testsite_url = Connection_Utils::fix_url_for_bad_hosts( JETPACK__API_BASE . 'testsite/1/?url=' );
831
832
		// Using PHP_INT_MAX - 1 so that there is still a way to override this if needed and since it only impacts this one call.
833
		add_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ), PHP_INT_MAX - 1 );
834
835
		$response = wp_remote_get( $testsite_url . $self_xml_rpc_url );
836
837
		remove_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ), PHP_INT_MAX - 1 );
838
839
		if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
840
			return self::passing_test( array( 'name' => $name ) );
841
		} elseif ( is_wp_error( $response ) && false !== strpos( $response->get_error_message(), 'cURL error 28' ) ) { // Timeout.
842
			return self::skipped_test(
843
				array(
844
					'name'              => $name,
845
					'short_description' => __( 'The test timed out which may sometimes indicate a failure or may be a false failure.', 'jetpack' ),
846
				)
847
			);
848
		} else {
849
			return self::failing_test(
850
				array(
851
					'name'              => $name,
852
					'short_description' => sprintf(
853
						/* translators: %1$s - A debugging url */
854
						__( 'Jetpack.com detected an error on the WP.com Self Test. Visit the Jetpack Debug page for more info: %1$s, or contact support.', 'jetpack' ),
855
						Redirect::get_url( 'jetpack-support-debug', array( 'query' => 'url=' . rawurlencode( site_url() ) ) )
856
					),
857
					'action_label'      => $this->helper_get_support_text(),
858
					'action'            => $this->helper_get_support_url(),
859
				)
860
			);
861
		}
862
	}
863
}
864