Completed
Push — sync/ixr-disable-rewind ( a80063 )
by
unknown
08:04
created

Actions::get_sync_status()   F

Complexity

Conditions 12
Paths 528

Size

Total Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 528
nop 1
dl 0
loc 60
rs 3.6371
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * A class that defines syncable actions for Jetpack.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync;
9
10
use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
11
use Automattic\Jetpack\Constants;
12
use Automattic\Jetpack\Status;
13
14
/**
15
 * The role of this class is to hook the Sync subsystem into WordPress - when to listen for actions,
16
 * when to send, when to perform a full sync, etc.
17
 *
18
 * It also binds the action to send data to WPCOM to Jetpack's XMLRPC client object.
19
 */
20
class Actions {
21
	/**
22
	 * A variable to hold a sync sender object.
23
	 *
24
	 * @access public
25
	 * @static
26
	 *
27
	 * @var Automattic\Jetpack\Sync\Sender
28
	 */
29
	public static $sender = null;
30
31
	/**
32
	 * A variable to hold a sync listener object.
33
	 *
34
	 * @access public
35
	 * @static
36
	 *
37
	 * @var Automattic\Jetpack\Sync\Listener
38
	 */
39
	public static $listener = null;
40
41
	/**
42
	 * Name of the sync cron schedule.
43
	 *
44
	 * @access public
45
	 *
46
	 * @var string
47
	 */
48
	const DEFAULT_SYNC_CRON_INTERVAL_NAME = 'jetpack_sync_interval';
49
50
	/**
51
	 * Interval between the last and the next sync cron action.
52
	 *
53
	 * @access public
54
	 *
55
	 * @var int
56
	 */
57
	const DEFAULT_SYNC_CRON_INTERVAL_VALUE = 300; // 5 * MINUTE_IN_SECONDS;
58
59
	/**
60
	 * Initialize Sync for cron jobs, set up listeners for WordPress Actions,
61
	 * and set up a shut-down action for sending actions to WordPress.com
62
	 *
63
	 * @access public
64
	 * @static
65
	 */
66
	public static function init() {
67
		// Everything below this point should only happen if we're a valid sync site.
68
		if ( ! self::sync_allowed() ) {
69
			return;
70
		}
71
72
		if ( self::sync_via_cron_allowed() ) {
73
			self::init_sync_cron_jobs();
74
		} elseif ( wp_next_scheduled( 'jetpack_sync_cron' ) ) {
75
			self::clear_sync_cron_jobs();
76
		}
77
		// When importing via cron, do not sync.
78
		add_action( 'wp_cron_importer_hook', array( __CLASS__, 'set_is_importing_true' ), 1 );
79
80
		// Sync connected user role changes to WordPress.com.
81
		Users::init();
82
83
		// Publicize filter to prevent publicizing blacklisted post types.
84
		add_filter( 'publicize_should_publicize_published_post', array( __CLASS__, 'prevent_publicize_blacklisted_posts' ), 10, 2 );
85
86
		/**
87
		 * Fires on every request before default loading sync listener code.
88
		 * Return false to not load sync listener code that monitors common
89
		 * WP actions to be serialized.
90
		 *
91
		 * By default this returns true for cron jobs, non-GET-requests, or requests where the
92
		 * user is logged-in.
93
		 *
94
		 * @since 4.2.0
95
		 *
96
		 * @param bool should we load sync listener code for this request
97
		 */
98
		if ( apply_filters( 'jetpack_sync_listener_should_load', true ) ) {
99
			self::initialize_listener();
100
		}
101
102
		add_action( 'init', array( __CLASS__, '' ), 90 );
103
	}
104
105
	/**
106
	 * Prepares sync to send actions on shutdown for the current request.
107
	 *
108
	 * @access public
109
	 * @static
110
	 */
111
	public static function add_sender_shutdown() {
112
		/**
113
		 * Fires on every request before default loading sync sender code.
114
		 * Return false to not load sync sender code that serializes pending
115
		 * data and sends it to WPCOM for processing.
116
		 *
117
		 * By default this returns true for cron jobs, POST requests, admin requests, or requests
118
		 * by users who can manage_options.
119
		 *
120
		 * @since 4.2.0
121
		 *
122
		 * @param bool should we load sync sender code for this request
123
		 */
124
		if ( apply_filters(
125
			'jetpack_sync_sender_should_load',
126
			self::should_initialize_sender()
127
		) ) {
128
			self::initialize_sender();
129
			add_action( 'shutdown', array( self::$sender, 'do_sync' ) );
130
			add_action( 'shutdown', array( self::$sender, 'do_full_sync' ), 9999 );
131
		}
132
	}
133
134
	/**
135
	 * Define JETPACK_SYNC_READ_ONLY constant if not defined.
136
	 * This notifies sync to not run in shutdown if it was initialized during init.
137
	 *
138
	 * @access public
139
	 * @static
140
	 */
141
	public static function mark_sync_read_only() {
142
		Constants::set_constant( 'JETPACK_SYNC_READ_ONLY', true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
143
	}
144
145
	/**
146
	 * Decides if the sender should run on shutdown for this request.
147
	 *
148
	 * @access public
149
	 * @static
150
	 *
151
	 * @return bool
152
	 */
153
	public static function should_initialize_sender() {
154
		if ( Constants::is_true( 'DOING_CRON' ) ) {
155
			return self::sync_via_cron_allowed();
156
		}
157
158
		if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
159
			return true;
160
		}
161
162
		if ( current_user_can( 'manage_options' ) ) {
163
			return true;
164
		}
165
166
		if ( is_admin() ) {
167
			return true;
168
		}
169
170
		if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
171
			return true;
172
		}
173
174
		if ( Constants::get_constant( 'WP_CLI' ) ) {
175
			return true;
176
		}
177
178
		return false;
179
	}
180
181
	/**
182
	 * Decides if the sender should run on shutdown when actions are queued.
183
	 *
184
	 * @access public
185
	 * @static
186
	 *
187
	 * @return bool
188
	 */
189
	public static function should_initialize_sender_enqueue() {
190
		if ( Constants::is_true( 'DOING_CRON' ) ) {
191
			return self::sync_via_cron_allowed();
192
		}
193
194
		return true;
195
	}
196
197
	/**
198
	 * Decides if sync should run at all during this request.
199
	 *
200
	 * @access public
201
	 * @static
202
	 *
203
	 * @return bool
204
	 */
205
	public static function sync_allowed() {
206
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
207
			return false;
208
		}
209
210
		if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
211
			return true;
212
		}
213
214
		if ( ! Settings::is_sync_enabled() ) {
215
			return false;
216
		}
217
218
		if ( ( new Status() )->is_offline_mode() ) {
219
			return false;
220
		}
221
222
		if ( ( new Status() )->is_staging_site() ) {
223
			return false;
224
		}
225
226
		$connection = new Jetpack_Connection();
227
		if ( ! $connection->is_active() ) {
228
			if ( ! doing_action( 'jetpack_user_authorized' ) ) {
229
				return false;
230
			}
231
		}
232
233
		return true;
234
	}
235
236
	/**
237
	 * Helper function to get details as to why sync is not allowed, if it is not allowed.
238
	 *
239
	 * @return array
240
	 */
241
	public static function get_debug_details() {
242
		$debug                                  = array();
243
		$debug['debug_details']['sync_allowed'] = self::sync_allowed();
244
		$debug['debug_details']['sync_health']  = Health::get_status();
245
		if ( false === $debug['debug_details']['sync_allowed'] ) {
246
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
247
				$debug['debug_details']['is_wpcom'] = true;
248
			}
249
			if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
250
				$debug['debug_details']['PHPUNIT_JETPACK_TESTSUITE'] = true;
251
			}
252
			if ( ! Settings::is_sync_enabled() ) {
253
				$debug['debug_details']['is_sync_enabled']              = false;
254
				$debug['debug_details']['jetpack_sync_disable']         = Settings::get_setting( 'disable' );
255
				$debug['debug_details']['jetpack_sync_network_disable'] = Settings::get_setting( 'network_disable' );
256
			}
257
			if ( ( new Status() )->is_offline_mode() ) {
258
				$debug['debug_details']['is_offline_mode'] = true;
259
			}
260
			if ( ( new Status() )->is_staging_site() ) {
261
				$debug['debug_details']['is_staging_site'] = true;
262
			}
263
			$connection = new Jetpack_Connection();
264
			if ( ! $connection->is_active() ) {
265
				$debug['debug_details']['active_connection'] = false;
266
			}
267
		}
268
		return $debug;
269
270
	}
271
272
	/**
273
	 * Determines if syncing during a cron job is allowed.
274
	 *
275
	 * @access public
276
	 * @static
277
	 *
278
	 * @return bool|int
279
	 */
280
	public static function sync_via_cron_allowed() {
281
		return ( Settings::get_setting( 'sync_via_cron' ) );
282
	}
283
284
	/**
285
	 * Decides if the given post should be Publicized based on its type.
286
	 *
287
	 * @access public
288
	 * @static
289
	 *
290
	 * @param bool     $should_publicize  Publicize status prior to this filter running.
291
	 * @param \WP_Post $post              The post to test for Publicizability.
292
	 * @return bool
293
	 */
294
	public static function prevent_publicize_blacklisted_posts( $should_publicize, $post ) {
295
		if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
296
			return false;
297
		}
298
299
		return $should_publicize;
300
	}
301
302
	/**
303
	 * Set an importing flag to `true` in sync settings.
304
	 *
305
	 * @access public
306
	 * @static
307
	 */
308
	public static function set_is_importing_true() {
309
		Settings::set_importing( true );
310
	}
311
312
	/**
313
	 * Sends data to WordPress.com via an XMLRPC request.
314
	 *
315
	 * @access public
316
	 * @static
317
	 *
318
	 * @param object $data                   Data relating to a sync action.
319
	 * @param string $codec_name             The name of the codec that encodes the data.
320
	 * @param float  $sent_timestamp         Current server time so we can compensate for clock differences.
321
	 * @param string $queue_id               The queue the action belongs to, sync or full_sync.
322
	 * @param float  $checkout_duration      Time spent retrieving queue items from the DB.
323
	 * @param float  $preprocess_duration    Time spent converting queue items into data to send.
324
	 * @param int    $queue_size             The size of the sync queue at the time of processing.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $queue_size not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
325
	 * @param string $buffer_id              The ID of the Queue buffer checked out for processing.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $buffer_id not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
326
	 * @return Jetpack_Error|mixed|WP_Error  The result of the sending request.
327
	 */
328
	public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size = null, $buffer_id = null ) {
329
330
		$query_args = array(
331
			'sync'       => '1',             // Add an extra parameter to the URL so we can tell it's a sync action.
332
			'codec'      => $codec_name,
333
			'timestamp'  => $sent_timestamp,
334
			'queue'      => $queue_id,
335
			'home'       => Functions::home_url(),  // Send home url option to check for Identity Crisis server-side.
336
			'siteurl'    => Functions::site_url(),  // Send siteurl option to check for Identity Crisis server-side.
337
			'cd'         => sprintf( '%.4f', $checkout_duration ),
338
			'pd'         => sprintf( '%.4f', $preprocess_duration ),
339
			'queue_size' => $queue_size,
340
			'buffer_id'  => $buffer_id,
341
		);
342
343
		// Has the site opted in to IDC mitigation?
344
		if ( \Jetpack::sync_idc_optin() ) {
345
			$query_args['idc'] = true;
346
		}
347
348
		if ( \Jetpack_Options::get_option( 'migrate_for_idc', false ) ) {
349
			$query_args['migrate_for_idc'] = true;
350
		}
351
352
		$query_args['timeout'] = Settings::is_doing_cron() ? 30 : 15;
353
354
		/**
355
		 * Filters query parameters appended to the Sync request URL sent to WordPress.com.
356
		 *
357
		 * @since 4.7.0
358
		 *
359
		 * @param array $query_args associative array of query parameters.
360
		 */
361
		$query_args = apply_filters( 'jetpack_sync_send_data_query_args', $query_args );
362
363
		$connection = new Jetpack_Connection();
364
		$url        = add_query_arg( $query_args, $connection->xmlrpc_api_url() );
365
366
		// If we're currently updating to Jetpack 7.7, the IXR client may be missing briefly
367
		// because since 7.7 it's being autoloaded with Composer.
368
		if ( ! class_exists( '\\Jetpack_IXR_Client' ) ) {
369
			return new \WP_Error(
370
				'ixr_client_missing',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'ixr_client_missing'.

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...
371
				esc_html__( 'Sync has been aborted because the IXR client is missing.', 'jetpack' )
372
			);
373
		}
374
375
		$rpc = new \Jetpack_IXR_Client(
376
			array(
377
				'url'     => $url,
378
				'timeout' => $query_args['timeout'],
379
			)
380
		);
381
382
		$result = $rpc->query( 'jetpack.syncActions', $data );
383
384
		if ( ! $result ) {
385
			return $rpc->get_jetpack_error();
386
		}
387
388
		$response = $rpc->getResponse();
389
390
		// Check if WordPress.com IDC mitigation blocked the sync request.
391
		if ( is_array( $response ) && isset( $response['error_code'] ) ) {
392
			$error_code              = $response['error_code'];
393
			$allowed_idc_error_codes = array(
394
				'jetpack_url_mismatch',
395
				'jetpack_home_url_mismatch',
396
				'jetpack_site_url_mismatch',
397
			);
398
399
			if ( in_array( $error_code, $allowed_idc_error_codes, true ) ) {
400
				\Jetpack_Options::update_option(
401
					'sync_error_idc',
402
					\Jetpack::get_sync_error_idc_option( $response )
403
				);
404
			}
405
406
			return new \WP_Error(
407
				'sync_error_idc',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'sync_error_idc'.

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...
408
				esc_html__( 'Sync has been blocked from WordPress.com because it would cause an identity crisis', 'jetpack' )
409
			);
410
		}
411
412
		return $response;
413
	}
414
415
	/**
416
	 * Kicks off the initial sync.
417
	 *
418
	 * @access public
419
	 * @static
420
	 *
421
	 * @return bool|null False if sync is not allowed.
422
	 */
423
	public static function do_initial_sync() {
424
		// Lets not sync if we are not suppose to.
425
		if ( ! self::sync_allowed() ) {
426
			return false;
427
		}
428
429
		// Don't start new sync if a full sync is in process.
430
		$full_sync_module = Modules::get_module( 'full-sync' );
431
		if ( $full_sync_module && $full_sync_module->is_started() && ! $full_sync_module->is_finished() ) {
432
			return false;
433
		}
434
435
		$initial_sync_config = array(
436
			'options'         => true,
437
			'functions'       => true,
438
			'constants'       => true,
439
			'users'           => array( get_current_user_id() ),
440
			'network_options' => true,
441
		);
442
443
		self::do_full_sync( $initial_sync_config );
444
	}
445
446
	/**
447
	 * Kicks off a full sync.
448
	 *
449
	 * @access public
450
	 * @static
451
	 *
452
	 * @param array $modules  The sync modules should be included in this full sync. All will be included if null.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $modules not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
453
	 * @return bool           True if full sync was successfully started.
454
	 */
455
	public static function do_full_sync( $modules = null ) {
456
		if ( ! self::sync_allowed() ) {
457
			return false;
458
		}
459
460
		$full_sync_module = Modules::get_module( 'full-sync' );
461
462
		if ( ! $full_sync_module ) {
463
			return false;
464
		}
465
466
		self::initialize_listener();
467
468
		$full_sync_module->start( $modules );
469
470
		return true;
471
	}
472
473
	/**
474
	 * Adds a cron schedule for regular syncing via cron, unless the schedule already exists.
475
	 *
476
	 * @access public
477
	 * @static
478
	 *
479
	 * @param array $schedules  The list of WordPress cron schedules prior to this filter.
480
	 * @return array            A list of WordPress cron schedules with the Jetpack sync interval added.
481
	 */
482
	public static function jetpack_cron_schedule( $schedules ) {
483
		if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
484
			$minutes = intval( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
485
			$display = ( 1 === $minutes ) ?
486
				__( 'Every minute', 'jetpack' ) :
487
				/* translators: %d is an integer indicating the number of minutes. */
488
				sprintf( __( 'Every %d minutes', 'jetpack' ), $minutes );
489
			$schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
490
				'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
491
				'display'  => $display,
492
			);
493
		}
494
		return $schedules;
495
	}
496
497
	/**
498
	 * Starts an incremental sync via cron.
499
	 *
500
	 * @access public
501
	 * @static
502
	 */
503
	public static function do_cron_sync() {
504
		self::do_cron_sync_by_type( 'sync' );
505
	}
506
507
	/**
508
	 * Starts a full sync via cron.
509
	 *
510
	 * @access public
511
	 * @static
512
	 */
513
	public static function do_cron_full_sync() {
514
		self::do_cron_sync_by_type( 'full_sync' );
515
	}
516
517
	/**
518
	 * Try to send actions until we run out of things to send,
519
	 * or have to wait more than 15s before sending again,
520
	 * or we hit a lock or some other sending issue
521
	 *
522
	 * @access public
523
	 * @static
524
	 *
525
	 * @param string $type Sync type. Can be `sync` or `full_sync`.
526
	 */
527
	public static function do_cron_sync_by_type( $type ) {
528
		if ( ! self::sync_allowed() || ( 'sync' !== $type && 'full_sync' !== $type ) ) {
529
			return;
530
		}
531
532
		self::initialize_sender();
533
534
		$time_limit = Settings::get_setting( 'cron_sync_time_limit' );
535
		$start_time = time();
536
		$executions = 0;
537
538
		do {
539
			$next_sync_time = self::$sender->get_next_sync_time( $type );
540
541
			if ( $next_sync_time ) {
542
				$delay = $next_sync_time - time() + 1;
543
				if ( $delay > 15 ) {
544
					break;
545
				} elseif ( $delay > 0 ) {
546
					sleep( $delay );
547
				}
548
			}
549
550
			// Explicitly only allow 1 do_full_sync call until issue with Immediate Full Sync is resolved.
551
			// For more context see p1HpG7-9pe-p2.
552
			if ( 'full_sync' === $type && $executions >= 1 ) {
553
				break;
554
			}
555
556
			$result = 'full_sync' === $type ? self::$sender->do_full_sync() : self::$sender->do_sync();
557
558
			// # of send actions performed.
559
			$executions ++;
560
561
		} while ( $result && ! is_wp_error( $result ) && ( $start_time + $time_limit ) > time() );
562
563
		return $executions;
564
	}
565
566
	/**
567
	 * Initialize the sync listener.
568
	 *
569
	 * @access public
570
	 * @static
571
	 */
572
	public static function initialize_listener() {
573
		self::$listener = Listener::get_instance();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Automattic\Jetpack\Sync\Listener::get_instance() of type object<Automattic\Jetpack\Sync\Listener> is incompatible with the declared type object<Automattic\Jetpac...\Jetpack\Sync\Listener> of property $listener.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
574
	}
575
576
	/**
577
	 * Initializes the sync sender.
578
	 *
579
	 * @access public
580
	 * @static
581
	 */
582
	public static function initialize_sender() {
583
		self::$sender = Sender::get_instance();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Automattic\Jetpack\Sync\Sender::get_instance() of type object<Automattic\Jetpack\Sync\Sender> is incompatible with the declared type object<Automattic\Jetpac...ic\Jetpack\Sync\Sender> of property $sender.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
584
		add_filter( 'jetpack_sync_send_data', array( __CLASS__, 'send_data' ), 10, 8 );
585
	}
586
587
	/**
588
	 * Initializes sync for WooCommerce.
589
	 *
590
	 * @access public
591
	 * @static
592
	 */
593
	public static function initialize_woocommerce() {
594
		if ( false === class_exists( 'WooCommerce' ) ) {
595
			return;
596
		}
597
		add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
598
	}
599
600
	/**
601
	 * Adds Woo's sync modules to existing modules for sending.
602
	 *
603
	 * @access public
604
	 * @static
605
	 *
606
	 * @param array $sync_modules The list of sync modules declared prior to this filter.
607
	 * @return array A list of sync modules that now includes Woo's modules.
608
	 */
609
	public static function add_woocommerce_sync_module( $sync_modules ) {
610
		$sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce';
611
		return $sync_modules;
612
	}
613
614
	/**
615
	 * Initializes sync for WP Super Cache.
616
	 *
617
	 * @access public
618
	 * @static
619
	 */
620
	public static function initialize_wp_super_cache() {
621
		if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
622
			return;
623
		}
624
		add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
625
	}
626
627
	/**
628
	 * Adds WP Super Cache's sync modules to existing modules for sending.
629
	 *
630
	 * @access public
631
	 * @static
632
	 *
633
	 * @param array $sync_modules The list of sync modules declared prior to this filer.
634
	 * @return array A list of sync modules that now includes WP Super Cache's modules.
635
	 */
636
	public static function add_wp_super_cache_sync_module( $sync_modules ) {
637
		$sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache';
638
		return $sync_modules;
639
	}
640
641
	/**
642
	 * Sanitizes the name of sync's cron schedule.
643
	 *
644
	 * @access public
645
	 * @static
646
	 *
647
	 * @param string $schedule The name of a WordPress cron schedule.
648
	 * @return string The sanitized name of sync's cron schedule.
649
	 */
650
	public static function sanitize_filtered_sync_cron_schedule( $schedule ) {
651
		$schedule  = sanitize_key( $schedule );
652
		$schedules = wp_get_schedules();
653
654
		// Make sure that the schedule has actually been registered using the `cron_intervals` filter.
655
		if ( isset( $schedules[ $schedule ] ) ) {
656
			return $schedule;
657
		}
658
659
		return self::DEFAULT_SYNC_CRON_INTERVAL_NAME;
660
	}
661
662
	/**
663
	 * Allows offsetting of start times for sync cron jobs.
664
	 *
665
	 * @access public
666
	 * @static
667
	 *
668
	 * @param string $schedule The name of a cron schedule.
669
	 * @param string $hook     The hook that this method is responding to.
670
	 * @return int The offset for the sync cron schedule.
671
	 */
672
	public static function get_start_time_offset( $schedule = '', $hook = '' ) {
673
		$start_time_offset = is_multisite()
674
			? wp_rand( 0, ( 2 * self::DEFAULT_SYNC_CRON_INTERVAL_VALUE ) )
675
			: 0;
676
677
		/**
678
		 * Allows overriding the offset that the sync cron jobs will first run. This can be useful when scheduling
679
		 * cron jobs across multiple sites in a network.
680
		 *
681
		 * @since 4.5.0
682
		 *
683
		 * @param int    $start_time_offset
684
		 * @param string $hook
685
		 * @param string $schedule
686
		 */
687
		return intval(
688
			apply_filters(
689
				'jetpack_sync_cron_start_time_offset',
690
				$start_time_offset,
691
				$hook,
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $hook.

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...
692
				$schedule
693
			)
694
		);
695
	}
696
697
	/**
698
	 * Decides if a sync cron should be scheduled.
699
	 *
700
	 * @access public
701
	 * @static
702
	 *
703
	 * @param string $schedule The name of a cron schedule.
704
	 * @param string $hook     The hook that this method is responding to.
705
	 */
706
	public static function maybe_schedule_sync_cron( $schedule, $hook ) {
707
		if ( ! $hook ) {
708
			return;
709
		}
710
		$schedule = self::sanitize_filtered_sync_cron_schedule( $schedule );
711
712
		$start_time = time() + self::get_start_time_offset( $schedule, $hook );
713
		if ( ! wp_next_scheduled( $hook ) ) {
714
			// Schedule a job to send pending queue items once a minute.
715
			wp_schedule_event( $start_time, $schedule, $hook );
716
		} elseif ( wp_get_schedule( $hook ) !== $schedule ) {
717
			// If the schedule has changed, update the schedule.
718
			wp_clear_scheduled_hook( $hook );
719
			wp_schedule_event( $start_time, $schedule, $hook );
720
		}
721
	}
722
723
	/**
724
	 * Clears Jetpack sync cron jobs.
725
	 *
726
	 * @access public
727
	 * @static
728
	 */
729
	public static function clear_sync_cron_jobs() {
730
		wp_clear_scheduled_hook( 'jetpack_sync_cron' );
731
		wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
732
	}
733
734
	/**
735
	 * Initializes Jetpack sync cron jobs.
736
	 *
737
	 * @access public
738
	 * @static
739
	 */
740
	public static function init_sync_cron_jobs() {
741
		add_filter( 'cron_schedules', array( __CLASS__, 'jetpack_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
742
743
		add_action( 'jetpack_sync_cron', array( __CLASS__, 'do_cron_sync' ) );
744
		add_action( 'jetpack_sync_full_cron', array( __CLASS__, 'do_cron_full_sync' ) );
745
746
		/**
747
		 * Allows overriding of the default incremental sync cron schedule which defaults to once every 5 minutes.
748
		 *
749
		 * @since 4.3.2
750
		 *
751
		 * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
752
		 */
753
		$incremental_sync_cron_schedule = apply_filters( 'jetpack_sync_incremental_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
754
		self::maybe_schedule_sync_cron( $incremental_sync_cron_schedule, 'jetpack_sync_cron' );
755
756
		/**
757
		 * Allows overriding of the full sync cron schedule which defaults to once every 5 minutes.
758
		 *
759
		 * @since 4.3.2
760
		 *
761
		 * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
762
		 */
763
		$full_sync_cron_schedule = apply_filters( 'jetpack_sync_full_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
764
		self::maybe_schedule_sync_cron( $full_sync_cron_schedule, 'jetpack_sync_full_cron' );
765
	}
766
767
	/**
768
	 * Perform maintenance when a plugin upgrade occurs.
769
	 *
770
	 * @access public
771
	 * @static
772
	 *
773
	 * @param string $new_version New version of the plugin.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $new_version not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
774
	 * @param string $old_version Old version of the plugin.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $old_version not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
775
	 */
776
	public static function cleanup_on_upgrade( $new_version = null, $old_version = null ) {
777
		if ( wp_next_scheduled( 'jetpack_sync_send_db_checksum' ) ) {
778
			wp_clear_scheduled_hook( 'jetpack_sync_send_db_checksum' );
779
		}
780
781
		$is_new_sync_upgrade = version_compare( $old_version, '4.2', '>=' );
782
		if ( ! empty( $old_version ) && $is_new_sync_upgrade && version_compare( $old_version, '4.5', '<' ) ) {
783
			self::clear_sync_cron_jobs();
784
			Settings::update_settings(
785
				array(
786
					'render_filtered_content' => Defaults::$default_render_filtered_content,
787
				)
788
			);
789
		}
790
791
		Health::on_jetpack_upgraded();
792
	}
793
794
	/**
795
	 * Get syncing status for the given fields.
796
	 *
797
	 * @access public
798
	 * @static
799
	 *
800
	 * @param string|null $fields A comma-separated string of the fields to include in the array from the JSON response.
801
	 * @return array An associative array with the status report.
802
	 */
803
	public static function get_sync_status( $fields = null ) {
804
		self::initialize_sender();
805
806
		$sync_module = Modules::get_module( 'full-sync' );
807
		$queue       = self::$sender->get_sync_queue();
808
809
		// _get_cron_array can be false
810
		$cron_timestamps = ( _get_cron_array() ) ? array_keys( _get_cron_array() ) : array();
811
		$next_cron       = ( ! empty( $cron_timestamps ) ) ? $cron_timestamps[0] - time() : '';
812
813
		$checksums = array();
814
		$debug     = array();
815
816
		if ( ! empty( $fields ) ) {
817
			$store         = new Replicastore();
818
			$fields_params = array_map( 'trim', explode( ',', $fields ) );
819
820
			if ( in_array( 'posts_checksum', $fields_params, true ) ) {
821
				$checksums['posts_checksum'] = $store->posts_checksum();
822
			}
823
			if ( in_array( 'comments_checksum', $fields_params, true ) ) {
824
				$checksums['comments_checksum'] = $store->comments_checksum();
825
			}
826
			if ( in_array( 'post_meta_checksum', $fields_params, true ) ) {
827
				$checksums['post_meta_checksum'] = $store->post_meta_checksum();
828
			}
829
			if ( in_array( 'comment_meta_checksum', $fields_params, true ) ) {
830
				$checksums['comment_meta_checksum'] = $store->comment_meta_checksum();
831
			}
832
833
			if ( in_array( 'debug_details', $fields_params, true ) ) {
834
				$debug = self::get_debug_details();
835
			}
836
		}
837
838
		$full_sync_status = ( $sync_module ) ? $sync_module->get_status() : array();
839
840
		$full_queue = self::$sender->get_full_sync_queue();
841
842
		$result = array_merge(
843
			$full_sync_status,
844
			$checksums,
845
			$debug,
846
			array(
847
				'cron_size'            => count( $cron_timestamps ),
848
				'next_cron'            => $next_cron,
849
				'queue_size'           => $queue->size(),
850
				'queue_lag'            => $queue->lag(),
851
				'queue_next_sync'      => ( self::$sender->get_next_sync_time( 'sync' ) - microtime( true ) ),
852
				'full_queue_next_sync' => ( self::$sender->get_next_sync_time( 'full_sync' ) - microtime( true ) ),
853
			)
854
		);
855
856
		// Verify $sync_module is not false.
857
		if ( ( $sync_module ) && false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
858
			$result['full_queue_size'] = $full_queue->size();
859
			$result['full_queue_lag']  = $full_queue->lag();
860
		}
861
		return $result;
862
	}
863
}
864