PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-sync/src/class-actions.php +264 -69 13.0.2 → 16.3-beta View file →
@@ -6,8 +6,9 @@
6 6 */
7 7
8 8 namespace Automattic\Jetpack\Sync;
9 9
10 +use Automattic\Jetpack\Connection\Client;
10 11 use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
11 12 use Automattic\Jetpack\Constants;
12 13 use Automattic\Jetpack\Identity_Crisis;
13 14 use Automattic\Jetpack\Status;
@@ -55,9 +56,9 @@
55 56 *
56 57 * @access public
57 58 * @static
58 59 *
59 - * @var Automattic\Jetpack\Sync\Sender
60 + * @var \Automattic\Jetpack\Sync\Sender
60 61 */
61 62 public static $sender = null;
62 63
63 64 /**
@@ -65,9 +66,9 @@
65 66 *
66 67 * @access public
67 68 * @static
68 69 *
69 - * @var Automattic\Jetpack\Sync\Listener
70 + * @var \Automattic\Jetpack\Sync\Listener
70 71 */
71 72 public static $listener = null;
72 73
73 74 /**
@@ -114,9 +115,9 @@
114 115 return;
115 116 }
116 117
117 118 if ( self::sync_via_cron_allowed() ) {
118 - self::init_sync_cron_jobs();
119 + add_action( 'init', array( __CLASS__, 'init_sync_cron_jobs' ), 1 );
119 120 } elseif ( wp_next_scheduled( 'jetpack_sync_cron' ) ) {
120 121 self::clear_sync_cron_jobs();
121 122 }
122 123 // When importing via cron, do not sync.
@@ -173,9 +174,12 @@
173 174 self::should_initialize_sender()
174 175 ) ) {
175 176 self::initialize_sender();
176 177 add_action( 'shutdown', array( self::$sender, 'do_sync' ), 9998 );
177 - add_action( 'shutdown', array( self::$sender, 'do_full_sync' ), 9999 );
178 +
179 + if ( self::should_initialize_sender( true ) ) {
180 + add_action( 'shutdown', array( self::$sender, 'do_full_sync' ), 9999 );
181 + }
178 182 }
179 183 }
180 184
181 185 /**
@@ -210,11 +214,13 @@
210 214 *
211 215 * @access public
212 216 * @static
213 217 *
218 + * @param bool $full_sync Whether the Full Sync sender should run on shutdown for this request.
219 + *
214 220 * @return bool
215 221 */
216 - public static function should_initialize_sender() {
222 + public static function should_initialize_sender( $full_sync = false ) {
217 223
218 224 // Allow for explicit disable of Sync from request param jetpack_sync_read_only.
219 225 if ( isset( $_REQUEST['jetpack_sync_read_only'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
220 226 self::mark_sync_read_only();
@@ -225,11 +231,12 @@
225 231 return self::sync_via_cron_allowed();
226 232 }
227 233
228 234 /**
229 - * For now, if dedicated Sync is enabled we will always initialize send, even for GET and unauthenticated requests.
235 + * For now, if dedicated Sync is enabled we will always initialize send, even for GET and unauthenticated requests
236 + * but not for Full Sync, since it will still happen on shutdown.
230 237 */
231 - if ( Settings::is_dedicated_sync_enabled() ) {
238 + if ( false === $full_sync && Settings::is_dedicated_sync_enabled() ) {
232 239 return true;
233 240 }
234 241
235 242 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
@@ -302,12 +309,8 @@
302 309 if ( ( new Status() )->is_offline_mode() ) {
303 310 return false;
304 311 }
305 312
306 - if ( ( new Status() )->is_staging_site() ) {
307 - return false;
308 - }
309 -
310 313 $connection = new Jetpack_Connection();
311 314 if ( ! $connection->is_connected() ) {
312 315 if ( ! doing_action( 'jetpack_site_registered' ) ) {
313 316 return false;
@@ -313,8 +316,13 @@
313 316 return false;
314 317 }
315 318 }
316 319
320 + // By now, we know the site is connected, so we can return false if in safe mode.
321 + if ( ( new Status() )->in_safe_mode() ) {
322 + return false;
323 + }
324 +
317 325 return true;
318 326 }
319 327
320 328 /**
@@ -340,10 +348,10 @@
340 348 }
341 349 if ( ( new Status() )->is_offline_mode() ) {
342 350 $debug['debug_details']['is_offline_mode'] = true;
343 351 }
344 - if ( ( new Status() )->is_staging_site() ) {
345 - $debug['debug_details']['is_staging_site'] = true;
352 + if ( ( new Status() )->in_safe_mode() ) {
353 + $debug['debug_details']['in_safe_mode'] = true;
346 354 }
347 355 $connection = new Jetpack_Connection();
348 356 if ( ! $connection->is_connected() ) {
349 357 $debug['debug_details']['active_connection'] = false;
@@ -354,8 +362,12 @@
354 362 $debug['debug_details']['dedicated_sync_enabled'] = Settings::is_dedicated_sync_enabled();
355 363
356 364 $queue = self::$sender->get_sync_queue();
357 365 $full_queue = self::$sender->get_full_sync_queue();
366 + // We are sending the expiry vs the actual dedicated lock value to ensure backwards compatibility
367 + // with previous versions where the lock value was a timestamp.
368 + $dedicated_sync_lock_option_name = Dedicated_Sender::DEDICATED_SYNC_REQUEST_LOCK_OPTION_NAME;
369 + $dedicated_sync_lock_expires_name = $dedicated_sync_lock_option_name . '_expires';
358 370
359 371 $debug['debug_details']['sync_locks'] = array(
360 372 'retry_time_sync' => get_option( self::RETRY_AFTER_PREFIX . 'sync' ),
361 373 'retry_time_full_sync' => get_option( self::RETRY_AFTER_PREFIX . 'full_sync' ),
@@ -362,9 +374,9 @@
362 374 'next_sync_time_sync' => self::$sender->get_next_sync_time( 'sync' ),
363 375 'next_sync_time_full_sync' => self::$sender->get_next_sync_time( 'full_sync' ),
364 376 'queue_locked_sync' => $queue->is_locked(),
365 377 'queue_locked_full_sync' => $full_queue->is_locked(),
366 - 'dedicated_sync_request_lock' => \Jetpack_Options::get_raw_option( Dedicated_Sender::DEDICATED_SYNC_REQUEST_LOCK_OPTION_NAME, null ),
378 + 'dedicated_sync_request_lock' => \Jetpack_Options::get_raw_option( $dedicated_sync_lock_expires_name, null ),
367 379 'dedicated_sync_temporary_disable_flag' => get_transient( Dedicated_Sender::DEDICATED_SYNC_TEMPORARY_DISABLE_FLAG ),
368 380 );
369 381
370 382 // Sync Logs.
@@ -414,9 +426,9 @@
414 426 Settings::set_importing( true );
415 427 }
416 428
417 429 /**
418 - * Sends data to WordPress.com via an XMLRPC request.
430 + * Sends data to WordPress.com via an XMLRPC or a REST API request based on the settings.
419 431 *
420 432 * @access public
421 433 * @static
422 434 *
@@ -432,8 +444,9 @@
432 444 */
433 445 public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size = null, $buffer_id = null ) {
434 446
435 447 $query_args = array(
448 +
436 449 'sync' => '1', // Add an extra parameter to the URL so we can tell it's a sync action.
437 450 'codec' => $codec_name,
438 451 'timestamp' => $sent_timestamp,
439 452 'queue' => $queue_id,
@@ -461,35 +474,60 @@
461 474 * @param array $query_args associative array of query parameters.
462 475 */
463 476 $query_args = apply_filters( 'jetpack_sync_send_data_query_args', $query_args );
464 477
465 - $connection = new Jetpack_Connection();
466 - $url = add_query_arg( $query_args, $connection->xmlrpc_api_url() );
478 + $retry_after_header = false;
479 + $dedicated_sync_header = false;
467 480
468 - // If we're currently updating to Jetpack 7.7, the IXR client may be missing briefly
469 - // because since 7.7 it's being autoloaded with Composer.
470 - if ( ! class_exists( '\\Jetpack_IXR_Client' ) ) {
471 - return new WP_Error(
472 - 'ixr_client_missing',
473 - esc_html__( 'Sync has been aborted because the IXR client is missing.', 'jetpack-sync' )
481 + // If REST API is enabled, use it.
482 + if ( Settings::is_wpcom_rest_api_enabled() ) {
483 + $jsonl_data = self::prepare_jsonl_data( $data );
484 + $url = '/sites/' . \Jetpack_Options::get_option( 'id' ) . '/jetpack-sync-actions';
485 + $url = add_query_arg( $query_args, $url );
486 + $args = array(
487 + 'method' => 'POST',
488 + 'format' => 'jsonl',
489 + 'timeout' => $query_args['timeout'],
474 490 );
475 - }
476 491
477 - $rpc = new \Jetpack_IXR_Client(
478 - array(
479 - 'url' => $url,
480 - 'timeout' => $query_args['timeout'],
481 - )
482 - );
492 + $response = Client::wpcom_json_api_request_as_blog( $url, '2', $args, $jsonl_data, 'wpcom' );
493 + $retry_after_header = wp_remote_retrieve_header( $response, 'Retry-After' ) ? wp_remote_retrieve_header( $response, 'Retry-After' ) : false;
494 + $dedicated_sync_header = wp_remote_retrieve_header( $response, 'Jetpack-Dedicated-Sync' ) ? wp_remote_retrieve_header( $response, 'Jetpack-Dedicated-Sync' ) : false;
495 + $response = self::process_rest_api_response( $response );
496 + } else { // Use XML-RPC.
497 + $connection = new Jetpack_Connection();
498 + $url = add_query_arg( $query_args, $connection->xmlrpc_api_url() );
483 499
484 - $result = $rpc->query( 'jetpack.syncActions', $data );
500 + // If we're currently updating to Jetpack 7.7, the IXR client may be missing briefly
501 + // because since 7.7 it's being autoloaded with Composer.
502 + if ( ! class_exists( '\\Jetpack_IXR_Client' ) ) {
503 + return new WP_Error(
504 + 'ixr_client_missing',
505 + esc_html__( 'Sync has been aborted because the IXR client is missing.', 'jetpack-sync' )
506 + );
507 + }
485 508
486 - // Adhere to Retry-After headers.
487 - $retry_after = $rpc->get_response_header( 'Retry-After' );
488 - if ( false !== $retry_after ) {
489 - if ( (int) $retry_after > 0 ) {
490 - update_option( self::RETRY_AFTER_PREFIX . $queue_id, microtime( true ) + (int) $retry_after, false );
509 + $rpc = new \Jetpack_IXR_Client(
510 + array(
511 + 'url' => $url,
512 + 'timeout' => $query_args['timeout'],
513 + )
514 + );
515 + $result = $rpc->query( 'jetpack.syncActions', $data );
516 + $retry_after_header = $rpc->get_response_header( 'Retry-After' );
517 + $dedicated_sync_header = $rpc->get_response_header( 'Jetpack-Dedicated-Sync' );
518 + if ( $result ) {
519 + $response = $rpc->getResponse();
491 520 } else {
521 + $response = $rpc->get_jetpack_error();
522 + }
523 + }
524 +
525 + // Adhere to Retry-After headers.
526 + if ( false !== $retry_after_header ) {
527 + if ( (int) $retry_after_header > 0 ) {
528 + update_option( self::RETRY_AFTER_PREFIX . $queue_id, microtime( true ) + (int) $retry_after_header, false );
529 + } else {
492 530 // if unexpected value default to 3 minutes.
493 531 update_option( self::RETRY_AFTER_PREFIX . $queue_id, microtime( true ) + 180, false );
494 532 }
495 533 }
@@ -494,15 +532,15 @@
494 532 }
495 533 }
496 534
497 535 // Enable/Disable Dedicated Sync flow via response headers.
498 - $dedicated_sync_header = $rpc->get_response_header( 'Jetpack-Dedicated-Sync' );
499 536 if ( false !== $dedicated_sync_header ) {
500 537 Dedicated_Sender::maybe_change_dedicated_sync_status_from_wpcom_header( $dedicated_sync_header );
501 538 }
502 539
503 - if ( ! $result ) {
504 - if ( false === $retry_after ) {
540 + if ( is_wp_error( $response ) ) {
541 + $error = $response;
542 + if ( false === $retry_after_header ) {
505 543 // We received a non standard response from WP.com, lets backoff from sending requests for 1 minute.
506 544 update_option( self::RETRY_AFTER_PREFIX . $queue_id, microtime( true ) + 60, false );
507 545 }
508 546 // Record Sync Errors.
@@ -514,20 +552,21 @@
514 552 if ( 5 <= count( $error_log ) ) {
515 553 $error_log = array_slice( $error_log, -4, null, true );
516 554 }
517 555 // Add new error indexed to time.
518 - $error = $rpc->get_jetpack_error();
519 - $error->add_data( $rpc->get_last_response() );
520 - $error_log[ (string) microtime( true ) ] = $error;
556 + if ( isset( $rpc ) && ! empty( $rpc->get_last_response() ) ) {
557 + $error_with_last_response = clone $error;
558 + $error_with_last_response->add_data( $rpc->get_last_response() );
559 + $error_log[ (string) microtime( true ) ] = $error_with_last_response;
560 + } else {
561 + $error_log[ (string) microtime( true ) ] = $error;
562 + }
563 +
521 564 // Update the error log.
522 565 update_option( self::ERROR_LOG_PREFIX . $queue_id, $error_log );
523 -
524 - // return request error.
525 - return $rpc->get_jetpack_error();
566 + return $error;
526 567 }
527 568
528 - $response = $rpc->getResponse();
529 -
530 569 // Check if WordPress.com IDC mitigation blocked the sync request.
531 570 if ( Identity_Crisis::init()->check_response_for_idc( $response ) ) {
532 571 return new WP_Error(
533 572 'sync_error_idc',
@@ -534,8 +573,12 @@
534 573 esc_html__( 'Sync has been blocked from WordPress.com because it would cause an identity crisis', 'jetpack-sync' )
535 574 );
536 575 }
537 576
577 + if ( isset( $response['processed_items'] ) ) { // Return only processed items.
578 + $response = $response['processed_items'];
579 + }
580 +
538 581 // Record last successful sync.
539 582 update_option( self::LAST_SUCCESS_PREFIX . $queue_id, microtime( true ), false );
540 583
541 584 return $response;
@@ -556,8 +599,9 @@
556 599 }
557 600
558 601 // Don't start new sync if a full sync is in process.
559 602 $full_sync_module = Modules::get_module( 'full-sync' );
603 + '@phan-var Modules\Full_Sync_Immediately|Modules\Full_Sync $full_sync_module';
560 604 if ( $full_sync_module && $full_sync_module->is_started() && ! $full_sync_module->is_finished() ) {
561 605 return false;
562 606 }
563 607
@@ -568,9 +612,9 @@
568 612 'users' => array( get_current_user_id() ),
569 613 'network_options' => true,
570 614 );
571 615
572 - self::do_full_sync( $initial_sync_config );
616 + self::do_full_sync( $initial_sync_config, 'initial_sync' );
573 617 }
574 618
575 619 /**
576 620 * Do an initial full sync only if one has not already been started.
@@ -578,8 +622,9 @@
578 622 * @return bool|null False if the initial full sync was already started, otherwise null.
579 623 */
580 624 public static function do_only_first_initial_sync() {
581 625 $full_sync_module = Modules::get_module( 'full-sync' );
626 + '@phan-var Modules\Full_Sync_Immediately|Modules\Full_Sync $full_sync_module';
582 627 if ( $full_sync_module && $full_sync_module->is_started() ) {
583 628 return false;
584 629 }
585 630
@@ -592,16 +637,18 @@
592 637 * @access public
593 638 * @static
594 639 *
595 640 * @param array $modules The sync modules should be included in this full sync. All will be included if null.
641 + * @param mixed $context The context where the full sync was initiated from.
596 642 * @return bool True if full sync was successfully started.
597 643 */
598 - public static function do_full_sync( $modules = null ) {
644 + public static function do_full_sync( $modules = null, $context = null ) {
599 645 if ( ! self::sync_allowed() ) {
600 646 return false;
601 647 }
602 648
603 649 $full_sync_module = Modules::get_module( 'full-sync' );
650 + '@phan-var Modules\Full_Sync_Immediately|Modules\Full_Sync $full_sync_module';
604 651
605 652 if ( ! $full_sync_module ) {
606 653 return false;
607 654 }
@@ -607,9 +654,9 @@
607 654 }
608 655
609 656 self::initialize_listener();
610 657
611 - $full_sync_module->start( $modules );
658 + $full_sync_module->start( $modules, $context );
612 659
613 660 return true;
614 661 }
615 662
@@ -623,13 +670,11 @@
623 670 * @return array A list of WordPress cron schedules with the Jetpack sync interval added.
624 671 */
625 672 public static function jetpack_cron_schedule( $schedules ) {
626 673 if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
627 - $minutes = (int) ( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
628 - $display = ( 1 === $minutes ) ?
629 - __( 'Every minute', 'jetpack-sync' ) :
630 - /* translators: %d is an integer indicating the number of minutes. */
631 - sprintf( __( 'Every %d minutes', 'jetpack-sync' ), $minutes );
674 + $minutes = ( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
675 + /* translators: %d is an integer indicating the number of minutes. */
676 + $display = sprintf( __( 'Every %d minutes', 'jetpack-sync' ), $minutes );
632 677 $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
633 678 'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
634 679 'display' => $display,
635 680 );
@@ -643,9 +688,47 @@
643 688 * @access public
644 689 * @static
645 690 */
646 691 public static function do_cron_sync() {
647 - self::do_cron_sync_by_type( 'sync' );
692 + if ( ! self::sync_allowed() ) {
693 + return;
694 + }
695 +
696 + self::initialize_sender();
697 +
698 + $time_limit = Settings::get_setting( 'cron_sync_time_limit' );
699 + $start_time = time();
700 + $executions = 0;
701 +
702 + $lock_id = Dedicated_Sender::try_lock_spawn_request();
703 +
704 + do {
705 + $next_sync_time = self::$sender->get_next_sync_time( 'sync' );
706 +
707 + if ( $next_sync_time ) {
708 + $delay = $next_sync_time - time() + 1;
709 + if ( $delay > 15 ) {
710 + break;
711 + } elseif ( $delay > 0 ) {
712 + sleep( (int) $delay );
713 + }
714 + }
715 +
716 + $result = self::$sender->do_sync_and_set_delays( self::$sender->get_sync_queue() );
717 +
718 + if ( is_wp_error( $result ) && in_array( $result->get_error_code(), array( 'unclosed_buffer', 'sync_throttled' ), true ) ) {
719 + $result = true; // Give it some time.
720 + }
721 + // # of send actions performed.
722 + ++$executions;
723 +
724 + } while ( $result && ! is_wp_error( $result ) && ( $start_time + $time_limit ) > time() );
725 +
726 + if ( $lock_id ) {
727 + Dedicated_Sender::try_release_lock_spawn_request( $lock_id );
728 + }
729 +
730 + return $executions;
648 731 }
649 732
650 733 /**
651 734 * Starts a full sync via cron.
@@ -653,9 +736,33 @@
653 736 * @access public
654 737 * @static
655 738 */
656 739 public static function do_cron_full_sync() {
657 - self::do_cron_sync_by_type( 'full_sync' );
740 + if ( ! self::sync_allowed() ) {
741 + return;
742 + }
743 +
744 + self::initialize_sender();
745 +
746 + $executions = 0;
747 +
748 + $next_sync_time = self::$sender->get_next_sync_time( 'full_sync' );
749 +
750 + if ( $next_sync_time ) {
751 + $delay = $next_sync_time - time() + 1;
752 + if ( $delay > 15 ) {
753 + return;
754 + } elseif ( $delay > 0 ) {
755 + sleep( (int) $delay );
756 + }
757 + }
758 +
759 + // Explicitly only allow 1 do_full_sync call until issue with Immediate Full Sync is resolved.
760 + // For more context see p1HpG7-9pe-p2.
761 + self::$sender->do_full_sync();
762 + ++$executions;
763 +
764 + return $executions;
658 765 }
659 766
660 767 /**
661 768 * Try to send actions until we run out of things to send,
@@ -685,9 +792,9 @@
685 792 $delay = $next_sync_time - time() + 1;
686 793 if ( $delay > 15 ) {
687 794 break;
688 795 } elseif ( $delay > 0 ) {
689 - sleep( $delay );
796 + sleep( (int) $delay );
690 797 }
691 798 }
692 799
693 800 // Explicitly only allow 1 do_full_sync call until issue with Immediate Full Sync is resolved.
@@ -741,9 +848,9 @@
741 848 * @access public
742 849 * @static
743 850 */
744 851 public static function initialize_woocommerce() {
745 - if ( false === class_exists( 'WooCommerce' ) ) {
852 + if ( ! class_exists( 'WooCommerce' ) ) {
746 853 return;
747 854 }
748 855 add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
749 856
@@ -756,21 +863,30 @@
756 863 }
757 864 }
758 865
759 866 /**
760 - * Initializes sync for Instant Search.
867 + * Initializes sync for Jetpack Search.
761 868 *
869 + * The Search sync module owns the option-whitelist entries for
870 + * `instant_search_enabled` and `jetpack_search_experience`. Registration
871 + * is unconditional whenever the Search package is present — gating on
872 + * either `is_instant_search_enabled()` or `is_active()` reintroduces a
873 + * chicken-and-egg, because the very request that flips Search on (or
874 + * flips Instant Search on) must already have the option whitelist in
875 + * place to enqueue the write.
876 + *
877 + * The `class_exists()` guard below tracks package presence (autoloader
878 + * concern), not module activation — `Module_Control` is autoloaded as
879 + * long as the Search package is installed, even when the module is off.
880 + *
762 881 * @access public
763 882 * @static
764 883 */
765 884 public static function initialize_search() {
766 - if ( false === class_exists( 'Automattic\\Jetpack\\Search\\Module_Control' ) ) {
885 + if ( ! class_exists( 'Automattic\\Jetpack\\Search\\Module_Control' ) ) {
767 886 return;
768 887 }
769 - $search_module = new \Automattic\Jetpack\Search\Module_Control();
770 - if ( $search_module->is_instant_search_enabled() ) {
771 - add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_search_sync_module' ) );
772 - }
888 + add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_search_sync_module' ) );
773 889 }
774 890
775 891 /**
776 892 * Add Search updates to Sync Filters.
@@ -815,8 +931,25 @@
815 931 return $sync_modules;
816 932 }
817 933
818 934 /**
935 + * Adds Woo's Products sync module to existing modules for sending.
936 + *
937 + * Note: This module is currently used for WooCommerce Analytics only.
938 + *
939 + * @param array $sync_modules The list of sync modules declared prior to this filter.
940 + *
941 + * @access public
942 + * @static
943 + *
944 + * @return array A list of sync modules that now includes Woo's Products module.
945 + */
946 + public static function add_woocommerce_products_sync_module( $sync_modules ) {
947 + $sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce_Products';
948 + return $sync_modules;
949 + }
950 +
951 + /**
819 952 * Initializes sync for WP Super Cache.
820 953 *
821 954 * @access public
822 955 * @static
@@ -821,9 +954,9 @@
821 954 * @access public
822 955 * @static
823 956 */
824 957 public static function initialize_wp_super_cache() {
825 - if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
958 + if ( ! function_exists( 'wp_cache_is_enabled' ) ) {
826 959 return;
827 960 }
828 961 add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
829 962 }
@@ -1008,9 +1141,10 @@
1008 1141 public static function get_sync_status( $fields = null ) {
1009 1142 self::initialize_sender();
1010 1143
1011 1144 $sync_module = Modules::get_module( 'full-sync' );
1012 - $queue = self::$sender->get_sync_queue();
1145 + '@phan-var Modules\Full_Sync_Immediately|Modules\Full_Sync $sync_module';
1146 + $queue = self::$sender->get_sync_queue();
1013 1147
1014 1148 // _get_cron_array can be false
1015 1149 $cron_timestamps = ( _get_cron_array() ) ? array_keys( _get_cron_array() ) : array();
1016 1150 $next_cron = ( ! empty( $cron_timestamps ) ) ? $cron_timestamps[0] - time() : '';
@@ -1058,9 +1192,9 @@
1058 1192 )
1059 1193 );
1060 1194
1061 1195 // Verify $sync_module is not false.
1062 - if ( ( $sync_module ) && ! str_contains( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
1196 + if ( $sync_module && ! $sync_module instanceof Modules\Full_Sync_Immediately ) {
1063 1197 $result['full_queue_size'] = $full_queue->size();
1064 1198 $result['full_queue_lag'] = $full_queue->lag();
1065 1199 }
1066 1200 return $result;
@@ -1083,10 +1217,16 @@
1083 1217 // Retry after locks.
1084 1218 delete_option( self::RETRY_AFTER_PREFIX . 'sync' );
1085 1219 delete_option( self::RETRY_AFTER_PREFIX . 'full_sync' );
1086 1220 // Dedicated sync locks.
1087 - \Jetpack_Options::delete_raw_option( Dedicated_Sender::DEDICATED_SYNC_REQUEST_LOCK_OPTION_NAME );
1221 + $dedicated_sync_lock_option = Dedicated_Sender::DEDICATED_SYNC_REQUEST_LOCK_OPTION_NAME;
1222 + $dedicated_sync_lock_expires_option = $dedicated_sync_lock_option . '_expires';
1223 + \Jetpack_Options::delete_raw_option( $dedicated_sync_lock_option );
1224 + \Jetpack_Options::delete_raw_option( $dedicated_sync_lock_expires_option );
1225 +
1088 1226 delete_transient( Dedicated_Sender::DEDICATED_SYNC_TEMPORARY_DISABLE_FLAG );
1227 + // Lock for disabling Sync sending temporarily.
1228 + delete_transient( Sender::TEMP_SYNC_DISABLE_TRANSIENT_NAME );
1089 1229
1090 1230 // Queue locks.
1091 1231 // Note that we are just unlocking the queues here, not reseting them.
1092 1232 if ( $unlock_queues ) {
@@ -1094,7 +1234,62 @@
1094 1234 $sync_queue->unlock();
1095 1235
1096 1236 $full_sync_queue = new Queue( 'full_sync' );
1097 1237 $full_sync_queue->unlock();
1238 + }
1239 + }
1240 +
1241 + /**
1242 + * Prepare JSONL data.
1243 + *
1244 + * @param mixed $data The data to be prepared.
1245 + *
1246 + * @return string The prepared JSONL data.
1247 + */
1248 + private static function prepare_jsonl_data( $data ) {
1249 + $jsonl_data = implode(
1250 + "\n",
1251 + array_map(
1252 + function ( $key, $value ) {
1253 + return wp_json_encode( array( $key => $value ), JSON_UNESCAPED_SLASHES );
1254 + },
1255 + array_keys( (array) $data ),
1256 + array_values( (array) $data )
1257 + )
1258 + );
1259 + return $jsonl_data;
1260 + }
1261 +
1262 + /**
1263 + * Helper method to process the API response.
1264 + *
1265 + * @param mixed $response The response from the API.
1266 + * @return array|Wp_Error Array for successful response or a WP_Error object.
1267 + */
1268 + private static function process_rest_api_response( $response ) {
1269 +
1270 + $response_code = wp_remote_retrieve_response_code( $response );
1271 + $response_body = wp_remote_retrieve_body( $response );
1272 + if ( is_wp_error( $response ) ) {
1273 + return $response;
1274 + }
1275 + $decoded_response = json_decode( $response_body, true );
1276 +
1277 + if ( ! is_array( $decoded_response ) ) {
1278 + return new WP_Error( 'sync_rest_api_response_decoding_failed', 'Sync REST API response decoding failed', $response_body );
1279 + }
1280 +
1281 + if ( $response_code !== 200 || ! isset( $decoded_response['processed_items'] ) ) {
1282 + if ( isset( $decoded_response['code'] ) && isset( $decoded_response['message'] ) ) {
1283 + return new WP_Error(
1284 + 'jetpack_sync_send_error_' . $decoded_response['code'],
1285 + $decoded_response['message'],
1286 + $decoded_response['data'] ?? null
1287 + );
1288 + } else {
1289 + return new WP_Error( $response_code, 'Sync REST API request failed', $response_body );
1290 + }
1291 + } else {
1292 + return $decoded_response;
1098 1293 }
1099 1294 }
1100 1295 }