Completed
Push — add/sync-health ( 184033...bcdded )
by
unknown
30:52 queued 21:40
created

Actions::init()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 7
nop 0
dl 0
loc 41
rs 8.9528
c 0
b 0
f 0
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;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Automattic\Jetpack\Sync\Status.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
13
use Automattic\Jetpack\Sync\Modules;
14
15
/**
16
 * The role of this class is to hook the Sync subsystem into WordPress - when to listen for actions,
17
 * when to send, when to perform a full sync, etc.
18
 *
19
 * It also binds the action to send data to WPCOM to Jetpack's XMLRPC client object.
20
 */
21
class Actions {
22
	/**
23
	 * A variable to hold a sync sender object.
24
	 *
25
	 * @access public
26
	 * @static
27
	 *
28
	 * @var Automattic\Jetpack\Sync\Sender
29
	 */
30
	public static $sender = null;
31
32
	/**
33
	 * A variable to hold a sync listener object.
34
	 *
35
	 * @access public
36
	 * @static
37
	 *
38
	 * @var Automattic\Jetpack\Sync\Listener
39
	 */
40
	public static $listener = null;
41
42
	/**
43
	 * Name of the sync cron schedule.
44
	 *
45
	 * @access public
46
	 *
47
	 * @var string
48
	 */
49
	const DEFAULT_SYNC_CRON_INTERVAL_NAME = 'jetpack_sync_interval';
50
51
	/**
52
	 * Interval between the last and the next sync cron action.
53
	 *
54
	 * @access public
55
	 *
56
	 * @var int
57
	 */
58
	const DEFAULT_SYNC_CRON_INTERVAL_VALUE = 300; // 5 * MINUTE_IN_SECONDS;
59
60
	/**
61
	 * Initialize Sync for cron jobs, set up listeners for WordPress Actions,
62
	 * and set up a shut-down action for sending actions to WordPress.com
63
	 *
64
	 * @access public
65
	 * @static
66
	 */
67
	public static function init() {
68
		// Everything below this point should only happen if we're a valid sync site.
69
		if ( ! self::sync_allowed() ) {
70
			return;
71
		}
72
73
		if ( self::sync_via_cron_allowed() ) {
74
			self::init_sync_cron_jobs();
75
		} elseif ( wp_next_scheduled( 'jetpack_sync_cron' ) ) {
76
			self::clear_sync_cron_jobs();
77
		}
78
		// When importing via cron, do not sync.
79
		add_action( 'wp_cron_importer_hook', array( __CLASS__, 'set_is_importing_true' ), 1 );
80
81
		// Sync connected user role changes to WordPress.com.
82
		Users::init();
83
84
		// Publicize filter to prevent publicizing blacklisted post types.
85
		add_filter( 'publicize_should_publicize_published_post', array( __CLASS__, 'prevent_publicize_blacklisted_posts' ), 10, 2 );
86
87
		// Full Sync Completed, Update Status
88
		add_action( 'jetpack_full_sync_end', arra( __ClASS__, 'full_sync_end_update_status'), 10, 2 );
89
90
		/**
91
		 * Fires on every request before default loading sync listener code.
92
		 * Return false to not load sync listener code that monitors common
93
		 * WP actions to be serialized.
94
		 *
95
		 * By default this returns true for cron jobs, non-GET-requests, or requests where the
96
		 * user is logged-in.
97
		 *
98
		 * @since 4.2.0
99
		 *
100
		 * @param bool should we load sync listener code for this request
101
		 */
102
		if ( apply_filters( 'jetpack_sync_listener_should_load', true ) ) {
103
			self::initialize_listener();
104
		}
105
106
		add_action( 'init', array( __CLASS__, 'add_sender_shutdown' ), 90 );
107
	}
108
109
	/**
110
	 * Prepares sync to send actions on shutdown for the current request.
111
	 *
112
	 * @access public
113
	 * @static
114
	 */
115
	public static function add_sender_shutdown() {
116
		/**
117
		 * Fires on every request before default loading sync sender code.
118
		 * Return false to not load sync sender code that serializes pending
119
		 * data and sends it to WPCOM for processing.
120
		 *
121
		 * By default this returns true for cron jobs, POST requests, admin requests, or requests
122
		 * by users who can manage_options.
123
		 *
124
		 * @since 4.2.0
125
		 *
126
		 * @param bool should we load sync sender code for this request
127
		 */
128
		if ( apply_filters(
129
			'jetpack_sync_sender_should_load',
130
			self::should_initialize_sender()
131
		) ) {
132
			self::initialize_sender();
133
			add_action( 'shutdown', array( self::$sender, 'do_sync' ) );
134
			add_action( 'shutdown', array( self::$sender, 'do_full_sync' ), 9999 );
135
		}
136
	}
137
138
	/**
139
	 * Decides if the sender should run on shutdown for this request.
140
	 *
141
	 * @access public
142
	 * @static
143
	 *
144
	 * @return bool
145
	 */
146
	public static function should_initialize_sender() {
147
		if ( Constants::is_true( 'DOING_CRON' ) ) {
148
			return self::sync_via_cron_allowed();
149
		}
150
151
		if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
152
			return true;
153
		}
154
155
		if ( current_user_can( 'manage_options' ) ) {
156
			return true;
157
		}
158
159
		if ( is_admin() ) {
160
			return true;
161
		}
162
163
		if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
164
			return true;
165
		}
166
167
		if ( Constants::get_constant( 'WP_CLI' ) ) {
168
			return true;
169
		}
170
171
		return false;
172
	}
173
174
	/**
175
	 * Decides if sync should run at all during this request.
176
	 *
177
	 * @access public
178
	 * @static
179
	 *
180
	 * @return bool
181
	 */
182
	public static function sync_allowed() {
183
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
184
			return false;
185
		}
186
187
		if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
188
			return true;
189
		}
190
191
		if ( ! Settings::is_sync_enabled() ) {
192
			return false;
193
		}
194
195
		if ( ( new Status() )->is_development_mode() ) {
196
			return false;
197
		}
198
199
		if ( ( new Status() )->is_staging_site() ) {
200
			return false;
201
		}
202
203
		$connection = new Jetpack_Connection();
204
		if ( ! $connection->is_active() ) {
205
			if ( ! doing_action( 'jetpack_user_authorized' ) ) {
206
				return false;
207
			}
208
		}
209
210
		return true;
211
	}
212
213
	/**
214
	 * Determines if syncing during a cron job is allowed.
215
	 *
216
	 * @access public
217
	 * @static
218
	 *
219
	 * @return bool|int
220
	 */
221
	public static function sync_via_cron_allowed() {
222
		return ( Settings::get_setting( 'sync_via_cron' ) );
223
	}
224
225
	/**
226
	 * Decides if the given post should be Publicized based on its type.
227
	 *
228
	 * @access public
229
	 * @static
230
	 *
231
	 * @param bool     $should_publicize  Publicize status prior to this filter running.
232
	 * @param \WP_Post $post              The post to test for Publicizability.
233
	 * @return bool
234
	 */
235
	public static function prevent_publicize_blacklisted_posts( $should_publicize, $post ) {
236
		if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
237
			return false;
238
		}
239
240
		return $should_publicize;
241
	}
242
243
	/**
244
	 * Set an importing flag to `true` in sync settings.
245
	 *
246
	 * @access public
247
	 * @static
248
	 */
249
	public static function set_is_importing_true() {
250
		Settings::set_importing( true );
251
	}
252
253
	/**
254
	 * Sends data to WordPress.com via an XMLRPC request.
255
	 *
256
	 * @access public
257
	 * @static
258
	 *
259
	 * @param object $data                   Data relating to a sync action.
260
	 * @param string $codec_name             The name of the codec that encodes the data.
261
	 * @param float  $sent_timestamp         Current server time so we can compensate for clock differences.
262
	 * @param string $queue_id               The queue the action belongs to, sync or full_sync.
263
	 * @param float  $checkout_duration      Time spent retrieving queue items from the DB.
264
	 * @param float  $preprocess_duration    Time spent converting queue items into data to send.
265
	 * @return Jetpack_Error|mixed|WP_Error  The result of the sending request.
266
	 */
267
	public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration ) {
268
		$query_args = array(
269
			'sync'      => '1',             // Add an extra parameter to the URL so we can tell it's a sync action.
270
			'codec'     => $codec_name,
271
			'timestamp' => $sent_timestamp,
272
			'queue'     => $queue_id,
273
			'home'      => Functions::home_url(),  // Send home url option to check for Identity Crisis server-side.
274
			'siteurl'   => Functions::site_url(),  // Send siteurl option to check for Identity Crisis server-side.
275
			'cd'        => sprintf( '%.4f', $checkout_duration ),
276
			'pd'        => sprintf( '%.4f', $preprocess_duration ),
277
		);
278
279
		// Has the site opted in to IDC mitigation?
280
		if ( \Jetpack::sync_idc_optin() ) {
281
			$query_args['idc'] = true;
282
		}
283
284
		if ( \Jetpack_Options::get_option( 'migrate_for_idc', false ) ) {
285
			$query_args['migrate_for_idc'] = true;
286
		}
287
288
		$query_args['timeout'] = Settings::is_doing_cron() ? 30 : 15;
289
290
		/**
291
		 * Filters query parameters appended to the Sync request URL sent to WordPress.com.
292
		 *
293
		 * @since 4.7.0
294
		 *
295
		 * @param array $query_args associative array of query parameters.
296
		 */
297
		$query_args = apply_filters( 'jetpack_sync_send_data_query_args', $query_args );
298
299
		$connection = new Jetpack_Connection();
300
		$url        = add_query_arg( $query_args, $connection->xmlrpc_api_url() );
301
302
		// If we're currently updating to Jetpack 7.7, the IXR client may be missing briefly
303
		// because since 7.7 it's being autoloaded with Composer.
304
		if ( ! class_exists( '\\Jetpack_IXR_Client' ) ) {
305
			return new \WP_Error(
306
				'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...
307
				esc_html__( 'Sync has been aborted because the IXR client is missing.', 'jetpack' )
308
			);
309
		}
310
311
		$rpc = new \Jetpack_IXR_Client(
312
			array(
313
				'url'     => $url,
314
				'user_id' => JETPACK_MASTER_USER,
315
				'timeout' => $query_args['timeout'],
316
			)
317
		);
318
319
		$result = $rpc->query( 'jetpack.syncActions', $data );
320
321
		if ( ! $result ) {
322
			return $rpc->get_jetpack_error();
323
		}
324
325
		$response = $rpc->getResponse();
326
327
		// Check if WordPress.com IDC mitigation blocked the sync request.
328
		if ( is_array( $response ) && isset( $response['error_code'] ) ) {
329
			$error_code              = $response['error_code'];
330
			$allowed_idc_error_codes = array(
331
				'jetpack_url_mismatch',
332
				'jetpack_home_url_mismatch',
333
				'jetpack_site_url_mismatch',
334
			);
335
336
			if ( in_array( $error_code, $allowed_idc_error_codes, true ) ) {
337
				\Jetpack_Options::update_option(
338
					'sync_error_idc',
339
					\Jetpack::get_sync_error_idc_option( $response )
340
				);
341
			}
342
343
			return new \WP_Error(
344
				'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...
345
				esc_html__( 'Sync has been blocked from WordPress.com because it would cause an identity crisis', 'jetpack' )
346
			);
347
		}
348
349
		return $response;
350
	}
351
352
	/**
353
	 * Kicks off the initial sync.
354
	 *
355
	 * @access public
356
	 * @static
357
	 *
358
	 * @return bool|null False if sync is not allowed.
359
	 */
360
	public static function do_initial_sync() {
361
		// Lets not sync if we are not suppose to.
362
		if ( ! self::sync_allowed() ) {
363
			return false;
364
		}
365
366
		// Don't start new sync if a full sync is in process.
367
		$full_sync_module = Modules::get_module( 'full-sync' );
368
		if ( $full_sync_module && $full_sync_module->is_started() && ! $full_sync_module->is_finished() ) {
369
			return false;
370
		}
371
372
		$initial_sync_config = array(
373
			'options'   => true,
374
			'functions' => true,
375
			'constants' => true,
376
			'users'     => array( get_current_user_id() ),
377
		);
378
379
		if ( is_multisite() ) {
380
			$initial_sync_config['network_options'] = true;
381
		}
382
383
		self::do_full_sync( $initial_sync_config );
384
	}
385
386
	/**
387
	 * Kicks off a full sync.
388
	 *
389
	 * @access public
390
	 * @static
391
	 *
392
	 * @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...
393
	 * @return bool           True if full sync was successfully started.
394
	 */
395
	public static function do_full_sync( $modules = null ) {
396
		if ( ! self::sync_allowed() ) {
397
			return false;
398
		}
399
400
		$full_sync_module = Modules::get_module( 'full-sync' );
401
402
		if ( ! $full_sync_module ) {
403
			return false;
404
		}
405
406
		self::initialize_listener();
407
408
		$full_sync_module->start( $modules );
409
410
		return true;
411
	}
412
413
	/**
414
	 * Adds a cron schedule for regular syncing via cron, unless the schedule already exists.
415
	 *
416
	 * @access public
417
	 * @static
418
	 *
419
	 * @param array $schedules  The list of WordPress cron schedules prior to this filter.
420
	 * @return array            A list of WordPress cron schedules with the Jetpack sync interval added.
421
	 */
422
	public static function jetpack_cron_schedule( $schedules ) {
423
		if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
424
			$minutes = intval( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
425
			$display = ( 1 === $minutes ) ?
426
				__( 'Every minute', 'jetpack' ) :
427
				/* translators: %d is an integer indicating the number of minutes. */
428
				sprintf( __( 'Every %d minutes', 'jetpack' ), $minutes );
429
			$schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
430
				'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
431
				'display'  => $display,
432
			);
433
		}
434
		return $schedules;
435
	}
436
437
	/**
438
	 * Starts an incremental sync via cron.
439
	 *
440
	 * @access public
441
	 * @static
442
	 */
443
	public static function do_cron_sync() {
444
		self::do_cron_sync_by_type( 'sync' );
445
	}
446
447
	/**
448
	 * Starts a full sync via cron.
449
	 *
450
	 * @access public
451
	 * @static
452
	 */
453
	public static function do_cron_full_sync() {
454
		self::do_cron_sync_by_type( 'full_sync' );
455
	}
456
457
	/**
458
	 * Try to send actions until we run out of things to send,
459
	 * or have to wait more than 15s before sending again,
460
	 * or we hit a lock or some other sending issue
461
	 *
462
	 * @access public
463
	 * @static
464
	 *
465
	 * @param string $type Sync type. Can be `sync` or `full_sync`.
466
	 */
467
	public static function do_cron_sync_by_type( $type ) {
468
		if ( ! self::sync_allowed() || ( 'sync' !== $type && 'full_sync' !== $type ) ) {
469
			return;
470
		}
471
472
		self::initialize_sender();
473
474
		$time_limit = Settings::get_setting( 'cron_sync_time_limit' );
475
		$start_time = time();
476
477
		do {
478
			$next_sync_time = self::$sender->get_next_sync_time( $type );
479
480
			if ( $next_sync_time ) {
481
				$delay = $next_sync_time - time() + 1;
482
				if ( $delay > 15 ) {
483
					break;
484
				} elseif ( $delay > 0 ) {
485
					sleep( $delay );
486
				}
487
			}
488
489
			$result = 'full_sync' === $type ? self::$sender->do_full_sync() : self::$sender->do_sync();
490
		} while ( $result && ! is_wp_error( $result ) && ( $start_time + $time_limit ) > time() );
491
	}
492
493
	/**
494
	 * Initialize the sync listener.
495
	 *
496
	 * @access public
497
	 * @static
498
	 */
499
	public static function initialize_listener() {
500
		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...
501
	}
502
503
	/**
504
	 * Initializes the sync sender.
505
	 *
506
	 * @access public
507
	 * @static
508
	 */
509
	public static function initialize_sender() {
510
		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...
511
		add_filter( 'jetpack_sync_send_data', array( __CLASS__, 'send_data' ), 10, 6 );
512
	}
513
514
	/**
515
	 * Initializes sync for WooCommerce.
516
	 *
517
	 * @access public
518
	 * @static
519
	 */
520
	public static function initialize_woocommerce() {
521
		if ( false === class_exists( 'WooCommerce' ) ) {
522
			return;
523
		}
524
		add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
525
	}
526
527
	/**
528
	 * Adds Woo's sync modules to existing modules for sending.
529
	 *
530
	 * @access public
531
	 * @static
532
	 *
533
	 * @param array $sync_modules The list of sync modules declared prior to this filter.
534
	 * @return array A list of sync modules that now includes Woo's modules.
535
	 */
536
	public static function add_woocommerce_sync_module( $sync_modules ) {
537
		$sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce';
538
		return $sync_modules;
539
	}
540
541
	/**
542
	 * Initializes sync for WP Super Cache.
543
	 *
544
	 * @access public
545
	 * @static
546
	 */
547
	public static function initialize_wp_super_cache() {
548
		if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
549
			return;
550
		}
551
		add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
552
	}
553
554
	/**
555
	 * Adds WP Super Cache's sync modules to existing modules for sending.
556
	 *
557
	 * @access public
558
	 * @static
559
	 *
560
	 * @param array $sync_modules The list of sync modules declared prior to this filer.
561
	 * @return array A list of sync modules that now includes WP Super Cache's modules.
562
	 */
563
	public static function add_wp_super_cache_sync_module( $sync_modules ) {
564
		$sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache';
565
		return $sync_modules;
566
	}
567
568
	/**
569
	 * Update Sync Status if Full Sync ended of Posts
570
	 *
571
	 * @param $data empty string
572
	 * @param $range Post/Comment Range
573
	 */
574
	public static function full_sync_end_update_status( $data, $range ) {
575
		if( isset( $range['posts'] ) ) {
576
			\Automattic\Jetpack\Sync\Status::update_status( \Automattic\Jetpack\Sync\Status::STATUS_IN_SYNC );
577
		}
578
	}
579
580
	/**
581
	 * Sanitizes the name of sync's cron schedule.
582
	 *
583
	 * @access public
584
	 * @static
585
	 *
586
	 * @param string $schedule The name of a WordPress cron schedule.
587
	 * @return string The sanitized name of sync's cron schedule.
588
	 */
589
	public static function sanitize_filtered_sync_cron_schedule( $schedule ) {
590
		$schedule  = sanitize_key( $schedule );
591
		$schedules = wp_get_schedules();
592
593
		// Make sure that the schedule has actually been registered using the `cron_intervals` filter.
594
		if ( isset( $schedules[ $schedule ] ) ) {
595
			return $schedule;
596
		}
597
598
		return self::DEFAULT_SYNC_CRON_INTERVAL_NAME;
599
	}
600
601
	/**
602
	 * Allows offsetting of start times for sync cron jobs.
603
	 *
604
	 * @access public
605
	 * @static
606
	 *
607
	 * @param string $schedule The name of a cron schedule.
608
	 * @param string $hook     The hook that this method is responding to.
609
	 * @return int The offset for the sync cron schedule.
610
	 */
611
	public static function get_start_time_offset( $schedule = '', $hook = '' ) {
612
		$start_time_offset = is_multisite()
613
			? wp_rand( 0, ( 2 * self::DEFAULT_SYNC_CRON_INTERVAL_VALUE ) )
614
			: 0;
615
616
		/**
617
		 * Allows overriding the offset that the sync cron jobs will first run. This can be useful when scheduling
618
		 * cron jobs across multiple sites in a network.
619
		 *
620
		 * @since 4.5.0
621
		 *
622
		 * @param int    $start_time_offset
623
		 * @param string $hook
624
		 * @param string $schedule
625
		 */
626
		return intval(
627
			apply_filters(
628
				'jetpack_sync_cron_start_time_offset',
629
				$start_time_offset,
630
				$hook,
631
				$schedule
632
			)
633
		);
634
	}
635
636
	/**
637
	 * Decides if a sync cron should be scheduled.
638
	 *
639
	 * @access public
640
	 * @static
641
	 *
642
	 * @param string $schedule The name of a cron schedule.
643
	 * @param string $hook     The hook that this method is responding to.
644
	 */
645
	public static function maybe_schedule_sync_cron( $schedule, $hook ) {
646
		if ( ! $hook ) {
647
			return;
648
		}
649
		$schedule = self::sanitize_filtered_sync_cron_schedule( $schedule );
650
651
		$start_time = time() + self::get_start_time_offset( $schedule, $hook );
652
		if ( ! wp_next_scheduled( $hook ) ) {
653
			// Schedule a job to send pending queue items once a minute.
654
			wp_schedule_event( $start_time, $schedule, $hook );
655
		} elseif ( wp_get_schedule( $hook ) !== $schedule ) {
656
			// If the schedule has changed, update the schedule.
657
			wp_clear_scheduled_hook( $hook );
658
			wp_schedule_event( $start_time, $schedule, $hook );
659
		}
660
	}
661
662
	/**
663
	 * Clears Jetpack sync cron jobs.
664
	 *
665
	 * @access public
666
	 * @static
667
	 */
668
	public static function clear_sync_cron_jobs() {
669
		wp_clear_scheduled_hook( 'jetpack_sync_cron' );
670
		wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
671
	}
672
673
	/**
674
	 * Initializes Jetpack sync cron jobs.
675
	 *
676
	 * @access public
677
	 * @static
678
	 */
679
	public static function init_sync_cron_jobs() {
680
		add_filter( 'cron_schedules', array( __CLASS__, 'jetpack_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
681
682
		add_action( 'jetpack_sync_cron', array( __CLASS__, 'do_cron_sync' ) );
683
		add_action( 'jetpack_sync_full_cron', array( __CLASS__, 'do_cron_full_sync' ) );
684
685
		/**
686
		 * Allows overriding of the default incremental sync cron schedule which defaults to once every 5 minutes.
687
		 *
688
		 * @since 4.3.2
689
		 *
690
		 * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
691
		 */
692
		$incremental_sync_cron_schedule = apply_filters( 'jetpack_sync_incremental_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
693
		self::maybe_schedule_sync_cron( $incremental_sync_cron_schedule, 'jetpack_sync_cron' );
694
695
		/**
696
		 * Allows overriding of the full sync cron schedule which defaults to once every 5 minutes.
697
		 *
698
		 * @since 4.3.2
699
		 *
700
		 * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
701
		 */
702
		$full_sync_cron_schedule = apply_filters( 'jetpack_sync_full_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
703
		self::maybe_schedule_sync_cron( $full_sync_cron_schedule, 'jetpack_sync_full_cron' );
704
	}
705
706
	/**
707
	 * Perform maintenance when a plugin upgrade occurs.
708
	 *
709
	 * @access public
710
	 * @static
711
	 *
712
	 * @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...
713
	 * @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...
714
	 */
715
	public static function cleanup_on_upgrade( $new_version = null, $old_version = null ) {
716
		if ( wp_next_scheduled( 'jetpack_sync_send_db_checksum' ) ) {
717
			wp_clear_scheduled_hook( 'jetpack_sync_send_db_checksum' );
718
		}
719
720
		$is_new_sync_upgrade = version_compare( $old_version, '4.2', '>=' );
721
		if ( ! empty( $old_version ) && $is_new_sync_upgrade && version_compare( $old_version, '4.5', '<' ) ) {
722
			self::clear_sync_cron_jobs();
723
			Settings::update_settings(
724
				array(
725
					'render_filtered_content' => Defaults::$default_render_filtered_content,
726
				)
727
			);
728
		}
729
	}
730
731
	/**
732
	 * Get syncing status for the given fields.
733
	 *
734
	 * @access public
735
	 * @static
736
	 *
737
	 * @param string|null $fields A comma-separated string of the fields to include in the array from the JSON response.
738
	 * @return array An associative array with the status report.
739
	 */
740
	public static function get_sync_status( $fields = null ) {
741
		self::initialize_sender();
742
743
		$sync_module     = Modules::get_module( 'full-sync' );
744
		$queue           = self::$sender->get_sync_queue();
745
		$cron_timestamps = array_keys( _get_cron_array() );
746
		$next_cron       = $cron_timestamps[0] - time();
747
748
		$checksums = array();
749
750
		if ( ! empty( $fields ) ) {
751
			$store         = new Replicastore();
752
			$fields_params = array_map( 'trim', explode( ',', $fields ) );
753
754
			if ( in_array( 'posts_checksum', $fields_params, true ) ) {
755
				$checksums['posts_checksum'] = $store->posts_checksum();
756
			}
757
			if ( in_array( 'comments_checksum', $fields_params, true ) ) {
758
				$checksums['comments_checksum'] = $store->comments_checksum();
759
			}
760
			if ( in_array( 'post_meta_checksum', $fields_params, true ) ) {
761
				$checksums['post_meta_checksum'] = $store->post_meta_checksum();
762
			}
763
			if ( in_array( 'comment_meta_checksum', $fields_params, true ) ) {
764
				$checksums['comment_meta_checksum'] = $store->comment_meta_checksum();
765
			}
766
		}
767
768
		$full_sync_status = ( $sync_module ) ? $sync_module->get_status() : array();
769
770
		$full_queue = self::$sender->get_full_sync_queue();
771
772
		$result = array_merge(
773
			$full_sync_status,
774
			$checksums,
775
			array(
776
				'cron_size'            => count( $cron_timestamps ),
777
				'next_cron'            => $next_cron,
778
				'queue_size'           => $queue->size(),
779
				'queue_lag'            => $queue->lag(),
780
				'queue_next_sync'      => ( self::$sender->get_next_sync_time( 'sync' ) - microtime( true ) ),
781
				'full_queue_next_sync' => ( self::$sender->get_next_sync_time( 'full_sync' ) - microtime( true ) ),
782
			)
783
		);
784
785
		if ( false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
786
			$result['full_queue_size'] = $full_queue->size();
787
			$result['full_queue_lag']  = $full_queue->lag();
788
		}
789
		return $result;
790
	}
791
}
792