PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-sync / src / modules / class-full-sync-immediately.php
jetpack / jetpack_vendor / automattic / jetpack-sync / src / modules Last commit date
class-attachments.php 8 months ago class-callables.php 1 month ago class-comments.php 2 months ago class-constants.php 8 months ago class-full-sync-immediately.php 3 months ago class-full-sync.php 2 months ago class-import.php 8 months ago class-menus.php 2 months ago class-meta.php 2 months ago class-module.php 5 months ago class-network-options.php 8 months ago class-options.php 2 months ago class-plugins.php 2 months ago class-posts.php 2 months ago class-protect.php 8 months ago class-search.php 2 months ago class-stats.php 6 months ago class-term-relationships.php 8 months ago class-terms.php 2 months ago class-themes.php 2 months ago class-updates.php 8 months ago class-users.php 1 month ago class-woocommerce-hpos-orders.php 1 month ago class-woocommerce-products.php 8 months ago class-woocommerce.php 1 month ago class-wp-super-cache.php 8 months ago
class-full-sync-immediately.php
580 lines
1 <?php
2 /**
3 * Full sync module.
4 *
5 * @package automattic/jetpack-sync
6 */
7
8 namespace Automattic\Jetpack\Sync\Modules;
9
10 use Automattic\Jetpack\Sync\Actions;
11 use Automattic\Jetpack\Sync\Defaults;
12 use Automattic\Jetpack\Sync\Lock;
13 use Automattic\Jetpack\Sync\Modules;
14 use Automattic\Jetpack\Sync\Settings;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit( 0 );
18 }
19
20 /**
21 * This class does a full resync of the database by
22 * sending an outbound action for every single object
23 * that we care about.
24 */
25 class Full_Sync_Immediately extends Module {
26 /**
27 * Prefix of the full sync status option name.
28 *
29 * @var string
30 */
31 const STATUS_OPTION = 'jetpack_sync_full_status';
32
33 /**
34 * Sync Lock name.
35 *
36 * @var string
37 */
38 const LOCK_NAME = 'full_sync';
39
40 /**
41 * Sync module name.
42 *
43 * @access public
44 *
45 * @return string
46 */
47 public function name() {
48 return 'full-sync';
49 }
50
51 /**
52 * Initialize action listeners for full sync.
53 *
54 * @access public
55 *
56 * @param callable $callable Action handler callable.
57 */
58 public function init_full_sync_listeners( $callable ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
59 }
60
61 /**
62 * Start a full sync.
63 *
64 * @access public
65 *
66 * @param array $full_sync_config Full sync configuration.
67 * @param mixed $context The context where the full sync was initiated from.
68 *
69 * @return bool Always returns true at success.
70 */
71 public function start( $full_sync_config = null, $context = null ) {
72 // Check if there was a full sync in progress already before resetting the data.
73 $should_process_cancelled_action = $this->get_status()['start_action_processed'] && ! $this->is_finished();
74 // Remove all evidence of previous full sync items and status.
75 $this->reset_data();
76
77 // Update status to indicate that a new full sync is starting and need to cancel previous one.
78 if ( $should_process_cancelled_action ) {
79 $this->update_status(
80 array(
81 'cancelled_action_processed' => false,
82 )
83 );
84 }
85
86 if ( ! is_array( $full_sync_config ) ) {
87 /*
88 * Filter default sync config to allow injecting custom configuration.
89 *
90 * @param array $full_sync_config Sync configuration for all sync modules.
91 *
92 * @since 3.10.0
93 */
94 $full_sync_config = apply_filters( 'jetpack_full_sync_config', Defaults::$default_full_sync_config );
95 if ( is_multisite() ) {
96 $full_sync_config['network_options'] = 1;
97 }
98 }
99
100 if ( isset( $full_sync_config['users'] ) && 'initial' === $full_sync_config['users'] ) {
101 $users_module = Modules::get_module( 'users' );
102 '@phan-var Users $users_module';
103 $full_sync_config['users'] = $users_module->get_initial_sync_user_config();
104 }
105
106 $this->update_status(
107 array(
108 'started' => time(),
109 'config' => $full_sync_config,
110 'context' => $context,
111 )
112 );
113
114 return true;
115 }
116
117 /**
118 * Whether full sync has started.
119 *
120 * @access public
121 *
122 * @return boolean
123 */
124 public function is_started() {
125 return (bool) $this->get_status()['started'];
126 }
127
128 /**
129 * Retrieve the status of the current full sync.
130 *
131 * @access public
132 *
133 * @return array Full sync status.
134 */
135 public function get_status() {
136 $default = array(
137 'start_action_processed' => false,
138 'cancelled_action_processed' => true, // true by default to avoid sending the action when there is no need,
139 'started' => false,
140 'finished' => false,
141 'progress' => array(),
142 'config' => array(),
143 'context' => null,
144 );
145
146 return wp_parse_args( \Jetpack_Options::get_raw_option( self::STATUS_OPTION ), $default );
147 }
148
149 /**
150 * Returns the progress percentage of a full sync.
151 *
152 * @access public
153 *
154 * @return int|null
155 */
156 public function get_sync_progress_percentage() {
157 if ( ! $this->is_started() || $this->is_finished() ) {
158 return null;
159 }
160 $status = $this->get_status();
161 if ( empty( $status['progress'] ) ) {
162 return null;
163 }
164 $total_items = array_reduce(
165 array_values( $status['progress'] ),
166 function ( $sum, $sync_item ) {
167 return isset( $sync_item['total'] ) ? ( $sum + (int) $sync_item['total'] ) : $sum;
168 },
169 0
170 );
171 $total_sent = array_reduce(
172 array_values( $status['progress'] ),
173 function ( $sum, $sync_item ) {
174 return isset( $sync_item['sent'] ) ? ( $sum + (int) $sync_item['sent'] ) : $sum;
175 },
176 0
177 );
178 return floor( ( $total_sent / $total_items ) * 100 );
179 }
180
181 /**
182 * Whether full sync has finished.
183 *
184 * @access public
185 *
186 * @return boolean
187 */
188 public function is_finished() {
189 return (bool) $this->get_status()['finished'];
190 }
191
192 /**
193 * Clear all the full sync data.
194 *
195 * @access public
196 */
197 public function reset_data() {
198 $this->clear_status();
199 ( new Lock() )->remove( self::LOCK_NAME, true );
200 }
201
202 /**
203 * Clear all the full sync status options.
204 *
205 * @access public
206 */
207 public function clear_status() {
208 \Jetpack_Options::delete_raw_option( self::STATUS_OPTION );
209 }
210
211 /**
212 * Updates the status of the current full sync.
213 *
214 * @access public
215 *
216 * @param array $values New values to set.
217 *
218 * @return bool True if success.
219 */
220 public function update_status( $values ) {
221 return $this->set_status( wp_parse_args( $values, $this->get_status() ) );
222 }
223
224 /**
225 * Retrieve the status of the current full sync.
226 *
227 * @param array $values New values to set.
228 *
229 * @access public
230 *
231 * @return boolean Full sync status.
232 */
233 public function set_status( $values ) {
234 return \Jetpack_Options::update_raw_option( self::STATUS_OPTION, $values );
235 }
236
237 /**
238 * Given an initial Full Sync configuration get the initial status.
239 *
240 * @param array $full_sync_config Full sync configuration.
241 * @param array $range Range of the sync items, containing min, max and count IDs for some item types.
242 *
243 * @return array Initial Sent status.
244 */
245 public function get_initial_progress( $full_sync_config, $range = null ) {
246 // Set default configuration, calculate totals, and save configuration if totals > 0.
247 $status = array();
248 foreach ( $full_sync_config as $name => $config ) {
249 $module = Modules::get_module( $name );
250 if ( ! $module ) {
251 continue;
252 }
253 $status[ $name ] = array(
254 // If we have a range for the module, use the count from the range to avoid querying the database again.
255 'total' => $range[ $name ]->count ?? $module->total( $config ),
256 'sent' => 0,
257 'finished' => false,
258 );
259 }
260
261 return $status;
262 }
263
264 /**
265 * Get the range for content (posts and comments) to sync.
266 *
267 * @access private
268 *
269 * @param array $full_sync_config Full sync configuration.
270 *
271 * @return array Array of range (min ID, max ID, total items) for all content types.
272 */
273 private function get_content_range( $full_sync_config ) {
274 $range = array();
275 foreach ( $full_sync_config as $module_name => $config ) {
276 // Calculate ranges only for modules that get chunked.
277 if ( in_array( $module_name, array( 'constants', 'functions', 'network_options', 'options', 'themes', 'updates' ), true ) ) {
278 continue;
279 }
280 $module = Modules::get_module( $module_name );
281 if ( ! $module ) {
282 continue;
283 }
284 if ( $config ) {
285 $range[ $module_name ] = $this->get_range( $module_name );
286 }
287 }
288
289 return $range;
290 }
291
292 /**
293 * Get the range (min ID, max ID and total items) of items to sync.
294 *
295 * @access public
296 *
297 * @param string $type Type of sync item to get the range for.
298 *
299 * @return array Array of min ID, max ID and total items in the range.
300 */
301 public function get_range( $type ) {
302 global $wpdb;
303 $module = Modules::get_module( $type );
304 if ( ! $module ) {
305 return array();
306 }
307
308 $table = $module->table();
309 $id = $module->id_field();
310 if ( 'terms' === $module ) { // Terms module relies on the term_taxonomy and term_taxonomy_id for the where sql, let's use term_id instead.
311 $id = 'term_id';
312 }
313 $where_sql = $module->get_where_sql( array() );
314
315 // TODO: Call $wpdb->prepare on the following query.
316 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
317 $results = $wpdb->get_results( "SELECT MAX({$id}) as max, MIN({$id}) as min, COUNT({$id}) as count FROM {$table} WHERE {$where_sql}" );
318 if ( isset( $results[0] ) ) {
319 return $results[0];
320 }
321
322 return array();
323 }
324
325 /**
326 * Continue sending instead of enqueueing.
327 *
328 * @access public
329 */
330 public function continue_enqueuing() {
331 $this->continue_sending();
332 }
333
334 /**
335 * Continue sending.
336 *
337 * @access public
338 */
339 public function continue_sending() {
340 // Return early if Full Sync is not running.
341 if ( ! $this->is_started() || $this->get_status()['finished'] ) {
342 return;
343 }
344
345 // Return early if we've gotten a retry-after header response.
346 $retry_time = get_option( Actions::RETRY_AFTER_PREFIX . 'immediate-send' );
347 if ( $retry_time ) {
348 // If expired delete but don't send. Send will occurr in new request to avoid race conditions.
349 if ( microtime( true ) > $retry_time ) {
350 update_option( Actions::RETRY_AFTER_PREFIX . 'immediate-send', false, false );
351 }
352 return false;
353 }
354
355 // Obtain send Lock.
356 $lock = new Lock();
357 $lock_expiration = $lock->attempt( self::LOCK_NAME );
358
359 // Return if unable to obtain lock.
360 if ( false === $lock_expiration ) {
361 return;
362 }
363
364 // Send Full Sync actions.
365 $success = $this->send();
366
367 // Remove lock.
368 if ( $success ) {
369 $lock->remove( self::LOCK_NAME, $lock_expiration );
370 }
371 }
372
373 /**
374 * Immediately send the next items to full sync.
375 *
376 * @access public
377 */
378 public function send() {
379
380 if ( ! $this->maybe_send_cancelled_action() ) {
381 return false;
382 }
383
384 if ( ! $this->maybe_send_full_sync_start() ) {
385 return false;
386 }
387 $config = $this->get_status()['config'];
388
389 $max_duration = Settings::get_setting( 'full_sync_send_duration' );
390 $send_until = microtime( true ) + $max_duration;
391
392 $progress = $this->get_status()['progress'];
393
394 $started = $this->get_status()['started'];
395
396 $remaining_modules = $this->get_remaining_modules_to_send();
397
398 foreach ( $remaining_modules as $module ) {
399 $module_name = $module->name();
400 if ( array_key_exists( $module_name, $progress ) && array_key_exists( $module_name, $config ) ) {
401 $progress[ $module_name ] = $module->send_full_sync_actions( $config[ $module_name ], $progress[ $module_name ], $send_until, $started );
402 if ( isset( $progress[ $module_name ]['error'] ) ) {
403 unset( $progress[ $module_name ]['error'] );
404 $this->update_status( array( 'progress' => $progress ) );
405 return false;
406 } elseif ( ! $progress[ $module_name ]['finished'] ) {
407 $this->update_status( array( 'progress' => $progress ) );
408 return true;
409 }
410 }
411
412 if ( $this->get_status()['started'] !== $started ) {
413 // Full sync was restarted, stop sending.
414 return false;
415 }
416 }
417
418 // Check that all remaining modules in progress are actually finished.
419 // If a module was skipped in the main loop (due to being unfinished), but still exists in progress, we shouldn't mark the sync as complete.
420 foreach ( $remaining_modules as $module ) {
421 $name = $module->name();
422 if ( array_key_exists( $name, $progress ) && empty( $progress[ $name ]['finished'] ) ) {
423 $this->update_status( array( 'progress' => $progress ) );
424 return true;
425 }
426 }
427
428 $this->send_full_sync_end();
429 $this->update_status( array( 'progress' => $progress ) );
430 return true;
431 }
432
433 /**
434 * Get Modules that are configured to Full Sync and haven't finished sending
435 *
436 * @return array
437 */
438 public function get_remaining_modules_to_send() {
439 $status = $this->get_status();
440 $remaining_modules = array();
441 foreach ( array_keys( $status['config'] ) as $module_name ) {
442 $module = Modules::get_module( $module_name );
443 if ( ! $module ) {
444 continue;
445 }
446 if ( isset( $status['progress'][ $module_name ]['finished'] ) &&
447 true === $status['progress'][ $module_name ]['finished'] ) {
448 continue;
449 }
450 // Ensure that 'constants', 'options', and 'callables' are sent first.
451 if ( in_array( $module_name, array( 'network_options', 'options', 'functions', 'constants' ), true ) ) {
452 array_unshift( $remaining_modules, $module );
453 } else {
454 $remaining_modules[] = $module;
455 }
456 }
457 return $remaining_modules;
458 }
459
460 /**
461 * Sends the `jetpack_full_sync_start` action if it hasn't been processed yet.
462 *
463 * Prepares the full sync start action, sends it to WordPress.com, fires the local action,
464 * and updates the sync status to reflect that the start action has been processed.
465 *
466 * @return bool True if the action was successfully sent or already processed, false on failure.
467 */
468 private function maybe_send_full_sync_start() {
469 $status = $this->get_status();
470
471 // If already processed, nothing to do.
472 if ( true === $status['start_action_processed'] ) {
473 return true;
474 }
475
476 $config = $status['config'];
477 $context = $status['context'];
478 $range = $this->get_content_range( $config );
479
480 $result = $this->send_action( 'jetpack_full_sync_start', array( $config, $range, $context ) );
481
482 // If the action failed on WordPress.com, return false.
483 if ( is_wp_error( $result ) ) {
484 return false;
485 }
486
487 /**
488 * Fires when a full sync begins. This action is serialized
489 * and sent to the server so that it knows a full sync is coming.
490 *
491 * @param array $config Sync configuration for all sync modules.
492 * @param array $range Range of the sync items, containing min and max IDs for some item types.
493 * @param mixed $context The context where the full sync was initiated from.
494 *
495 * @since 1.6.3
496 * @since-jetpack 4.2.0
497 * @since-jetpack 7.3.0 Added $range arg.
498 * @since 4.4.0 Added $context arg.
499 */
500 do_action( 'jetpack_full_sync_start', $config, $range );
501
502 $this->update_status(
503 array(
504 'start_action_processed' => true,
505 'progress' => $this->get_initial_progress( $config, $range ),
506 )
507 );
508
509 return true;
510 }
511
512 /**
513 * Sends the `jetpack_full_sync_cancelled` action if it hasn't been processed yet.
514 *
515 * @return bool True if the action was successfully sent or already processed, false on failure.
516 */
517 private function maybe_send_cancelled_action() {
518 $status = $this->get_status();
519
520 if ( true === $status['cancelled_action_processed'] ) {
521 return true;
522 }
523
524 $result = $this->send_action( 'jetpack_full_sync_cancelled' );
525
526 if ( is_wp_error( $result ) ) {
527 return false;
528 }
529
530 /**
531 * Fires when a full sync is cancelled.
532 *
533 * @since 1.6.3
534 * @since-jetpack 4.2.0
535 */
536 do_action( 'jetpack_full_sync_cancelled' );
537 $this->update_status( array( 'cancelled_action_processed' => true ) );
538 return true;
539 }
540
541 /**
542 * Sends the `jetpack_full_sync_end` action and updates the status when the full sync end action is processed.
543 *
544 * @access public
545 */
546 public function send_full_sync_end() {
547 $status = $this->get_status();
548 $range = $this->get_content_range( $status['config'] );
549 $context = $status['context'];
550
551 $result = $this->send_action( 'jetpack_full_sync_end', array( '', $range, $context ) );
552
553 if ( is_wp_error( $result ) ) { // Do not set finished status if we get an error.
554 return;
555 }
556 /**
557 * Fires when a full sync ends. This action is serialized
558 * and sent to the server.
559 *
560 * @param string $checksum Deprecated since 7.3.0 - @see https://github.com/Automattic/jetpack/pull/11945/
561 * @param array $range Range of the sync items, containing min and max IDs for some item types.
562 *
563 * @since 1.6.3
564 * @since-jetpack 4.2.0
565 * @since-jetpack 7.3.0 Added $range arg.
566 */
567 do_action( 'jetpack_full_sync_end', '', $range );
568
569 // Setting autoload to true means that it's faster to check whether we should continue enqueuing.
570 $this->update_status( array( 'finished' => time() ) );
571 }
572
573 /**
574 * Empty Function as we don't close buffers on Immediate Full Sync.
575 *
576 * @param array $actions an array of actions, ignored for queueless sync.
577 */
578 public function update_sent_progress_action( $actions ) { } // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
579 }
580