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 +146 -40 13.3.3 → 16.3-beta View file →
@@ -56,9 +56,9 @@
56 56 *
57 57 * @access public
58 58 * @static
59 59 *
60 - * @var Automattic\Jetpack\Sync\Sender
60 + * @var \Automattic\Jetpack\Sync\Sender
61 61 */
62 62 public static $sender = null;
63 63
64 64 /**
@@ -66,9 +66,9 @@
66 66 *
67 67 * @access public
68 68 * @static
69 69 *
70 - * @var Automattic\Jetpack\Sync\Listener
70 + * @var \Automattic\Jetpack\Sync\Listener
71 71 */
72 72 public static $listener = null;
73 73
74 74 /**
@@ -115,9 +115,9 @@
115 115 return;
116 116 }
117 117
118 118 if ( self::sync_via_cron_allowed() ) {
119 - self::init_sync_cron_jobs();
119 + add_action( 'init', array( __CLASS__, 'init_sync_cron_jobs' ), 1 );
120 120 } elseif ( wp_next_scheduled( 'jetpack_sync_cron' ) ) {
121 121 self::clear_sync_cron_jobs();
122 122 }
123 123 // When importing via cron, do not sync.
@@ -174,9 +174,12 @@
174 174 self::should_initialize_sender()
175 175 ) ) {
176 176 self::initialize_sender();
177 177 add_action( 'shutdown', array( self::$sender, 'do_sync' ), 9998 );
178 - 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 + }
179 182 }
180 183 }
181 184
182 185 /**
@@ -211,11 +214,13 @@
211 214 *
212 215 * @access public
213 216 * @static
214 217 *
218 + * @param bool $full_sync Whether the Full Sync sender should run on shutdown for this request.
219 + *
215 220 * @return bool
216 221 */
217 - public static function should_initialize_sender() {
222 + public static function should_initialize_sender( $full_sync = false ) {
218 223
219 224 // Allow for explicit disable of Sync from request param jetpack_sync_read_only.
220 225 if ( isset( $_REQUEST['jetpack_sync_read_only'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
221 226 self::mark_sync_read_only();
@@ -226,11 +231,12 @@
226 231 return self::sync_via_cron_allowed();
227 232 }
228 233
229 234 /**
230 - * 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.
231 237 */
232 - if ( Settings::is_dedicated_sync_enabled() ) {
238 + if ( false === $full_sync && Settings::is_dedicated_sync_enabled() ) {
233 239 return true;
234 240 }
235 241
236 242 if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
@@ -303,12 +309,8 @@
303 309 if ( ( new Status() )->is_offline_mode() ) {
304 310 return false;
305 311 }
306 312
307 - if ( ( new Status() )->is_staging_site() ) {
308 - return false;
309 - }
310 -
311 313 $connection = new Jetpack_Connection();
312 314 if ( ! $connection->is_connected() ) {
313 315 if ( ! doing_action( 'jetpack_site_registered' ) ) {
314 316 return false;
@@ -314,8 +316,13 @@
314 316 return false;
315 317 }
316 318 }
317 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 +
318 325 return true;
319 326 }
320 327
321 328 /**
@@ -341,10 +348,10 @@
341 348 }
342 349 if ( ( new Status() )->is_offline_mode() ) {
343 350 $debug['debug_details']['is_offline_mode'] = true;
344 351 }
345 - if ( ( new Status() )->is_staging_site() ) {
346 - $debug['debug_details']['is_staging_site'] = true;
352 + if ( ( new Status() )->in_safe_mode() ) {
353 + $debug['debug_details']['in_safe_mode'] = true;
347 354 }
348 355 $connection = new Jetpack_Connection();
349 356 if ( ! $connection->is_connected() ) {
350 357 $debug['debug_details']['active_connection'] = false;
@@ -355,8 +362,12 @@
355 362 $debug['debug_details']['dedicated_sync_enabled'] = Settings::is_dedicated_sync_enabled();
356 363
357 364 $queue = self::$sender->get_sync_queue();
358 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';
359 370
360 371 $debug['debug_details']['sync_locks'] = array(
361 372 'retry_time_sync' => get_option( self::RETRY_AFTER_PREFIX . 'sync' ),
362 373 'retry_time_full_sync' => get_option( self::RETRY_AFTER_PREFIX . 'full_sync' ),
@@ -363,9 +374,9 @@
363 374 'next_sync_time_sync' => self::$sender->get_next_sync_time( 'sync' ),
364 375 'next_sync_time_full_sync' => self::$sender->get_next_sync_time( 'full_sync' ),
365 376 'queue_locked_sync' => $queue->is_locked(),
366 377 'queue_locked_full_sync' => $full_queue->is_locked(),
367 - '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 ),
368 379 'dedicated_sync_temporary_disable_flag' => get_transient( Dedicated_Sender::DEDICATED_SYNC_TEMPORARY_DISABLE_FLAG ),
369 380 );
370 381
371 382 // Sync Logs.
@@ -588,8 +599,9 @@
588 599 }
589 600
590 601 // Don't start new sync if a full sync is in process.
591 602 $full_sync_module = Modules::get_module( 'full-sync' );
603 + '@phan-var Modules\Full_Sync_Immediately|Modules\Full_Sync $full_sync_module';
592 604 if ( $full_sync_module && $full_sync_module->is_started() && ! $full_sync_module->is_finished() ) {
593 605 return false;
594 606 }
595 607
@@ -600,9 +612,9 @@
600 612 'users' => array( get_current_user_id() ),
601 613 'network_options' => true,
602 614 );
603 615
604 - self::do_full_sync( $initial_sync_config );
616 + self::do_full_sync( $initial_sync_config, 'initial_sync' );
605 617 }
606 618
607 619 /**
608 620 * Do an initial full sync only if one has not already been started.
@@ -610,8 +622,9 @@
610 622 * @return bool|null False if the initial full sync was already started, otherwise null.
611 623 */
612 624 public static function do_only_first_initial_sync() {
613 625 $full_sync_module = Modules::get_module( 'full-sync' );
626 + '@phan-var Modules\Full_Sync_Immediately|Modules\Full_Sync $full_sync_module';
614 627 if ( $full_sync_module && $full_sync_module->is_started() ) {
615 628 return false;
616 629 }
617 630
@@ -624,16 +637,18 @@
624 637 * @access public
625 638 * @static
626 639 *
627 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.
628 642 * @return bool True if full sync was successfully started.
629 643 */
630 - public static function do_full_sync( $modules = null ) {
644 + public static function do_full_sync( $modules = null, $context = null ) {
631 645 if ( ! self::sync_allowed() ) {
632 646 return false;
633 647 }
634 648
635 649 $full_sync_module = Modules::get_module( 'full-sync' );
650 + '@phan-var Modules\Full_Sync_Immediately|Modules\Full_Sync $full_sync_module';
636 651
637 652 if ( ! $full_sync_module ) {
638 653 return false;
639 654 }
@@ -639,9 +654,9 @@
639 654 }
640 655
641 656 self::initialize_listener();
642 657
643 - $full_sync_module->start( $modules );
658 + $full_sync_module->start( $modules, $context );
644 659
645 660 return true;
646 661 }
647 662
@@ -655,13 +670,11 @@
655 670 * @return array A list of WordPress cron schedules with the Jetpack sync interval added.
656 671 */
657 672 public static function jetpack_cron_schedule( $schedules ) {
658 673 if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
659 - $minutes = (int) ( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
660 - $display = ( 1 === $minutes ) ?
661 - __( 'Every minute', 'jetpack-sync' ) :
662 - /* translators: %d is an integer indicating the number of minutes. */
663 - 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 );
664 677 $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
665 678 'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
666 679 'display' => $display,
667 680 );
@@ -675,9 +688,47 @@
675 688 * @access public
676 689 * @static
677 690 */
678 691 public static function do_cron_sync() {
679 - 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;
680 731 }
681 732
682 733 /**
683 734 * Starts a full sync via cron.
@@ -685,9 +736,33 @@
685 736 * @access public
686 737 * @static
687 738 */
688 739 public static function do_cron_full_sync() {
689 - 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;
690 765 }
691 766
692 767 /**
693 768 * Try to send actions until we run out of things to send,
@@ -717,9 +792,9 @@
717 792 $delay = $next_sync_time - time() + 1;
718 793 if ( $delay > 15 ) {
719 794 break;
720 795 } elseif ( $delay > 0 ) {
721 - sleep( $delay );
796 + sleep( (int) $delay );
722 797 }
723 798 }
724 799
725 800 // Explicitly only allow 1 do_full_sync call until issue with Immediate Full Sync is resolved.
@@ -773,9 +848,9 @@
773 848 * @access public
774 849 * @static
775 850 */
776 851 public static function initialize_woocommerce() {
777 - if ( false === class_exists( 'WooCommerce' ) ) {
852 + if ( ! class_exists( 'WooCommerce' ) ) {
778 853 return;
779 854 }
780 855 add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
781 856
@@ -788,21 +863,30 @@
788 863 }
789 864 }
790 865
791 866 /**
792 - * Initializes sync for Instant Search.
867 + * Initializes sync for Jetpack Search.
793 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 + *
794 881 * @access public
795 882 * @static
796 883 */
797 884 public static function initialize_search() {
798 - if ( false === class_exists( 'Automattic\\Jetpack\\Search\\Module_Control' ) ) {
885 + if ( ! class_exists( 'Automattic\\Jetpack\\Search\\Module_Control' ) ) {
799 886 return;
800 887 }
801 - $search_module = new \Automattic\Jetpack\Search\Module_Control();
802 - if ( $search_module->is_instant_search_enabled() ) {
803 - add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_search_sync_module' ) );
804 - }
888 + add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_search_sync_module' ) );
805 889 }
806 890
807 891 /**
808 892 * Add Search updates to Sync Filters.
@@ -847,8 +931,25 @@
847 931 return $sync_modules;
848 932 }
849 933
850 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 + /**
851 952 * Initializes sync for WP Super Cache.
852 953 *
853 954 * @access public
854 955 * @static
@@ -853,9 +954,9 @@
853 954 * @access public
854 955 * @static
855 956 */
856 957 public static function initialize_wp_super_cache() {
857 - if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
958 + if ( ! function_exists( 'wp_cache_is_enabled' ) ) {
858 959 return;
859 960 }
860 961 add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
861 962 }
@@ -1040,9 +1141,10 @@
1040 1141 public static function get_sync_status( $fields = null ) {
1041 1142 self::initialize_sender();
1042 1143
1043 1144 $sync_module = Modules::get_module( 'full-sync' );
1044 - $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();
1045 1147
1046 1148 // _get_cron_array can be false
1047 1149 $cron_timestamps = ( _get_cron_array() ) ? array_keys( _get_cron_array() ) : array();
1048 1150 $next_cron = ( ! empty( $cron_timestamps ) ) ? $cron_timestamps[0] - time() : '';
@@ -1090,9 +1192,9 @@
1090 1192 )
1091 1193 );
1092 1194
1093 1195 // Verify $sync_module is not false.
1094 - if ( ( $sync_module ) && ! str_contains( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
1196 + if ( $sync_module && ! $sync_module instanceof Modules\Full_Sync_Immediately ) {
1095 1197 $result['full_queue_size'] = $full_queue->size();
1096 1198 $result['full_queue_lag'] = $full_queue->lag();
1097 1199 }
1098 1200 return $result;
@@ -1115,9 +1217,13 @@
1115 1217 // Retry after locks.
1116 1218 delete_option( self::RETRY_AFTER_PREFIX . 'sync' );
1117 1219 delete_option( self::RETRY_AFTER_PREFIX . 'full_sync' );
1118 1220 // Dedicated sync locks.
1119 - \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 +
1120 1226 delete_transient( Dedicated_Sender::DEDICATED_SYNC_TEMPORARY_DISABLE_FLAG );
1121 1227 // Lock for disabling Sync sending temporarily.
1122 1228 delete_transient( Sender::TEMP_SYNC_DISABLE_TRANSIENT_NAME );
1123 1229
@@ -1143,9 +1249,9 @@
1143 1249 $jsonl_data = implode(
1144 1250 "\n",
1145 1251 array_map(
1146 1252 function ( $key, $value ) {
1147 - return wp_json_encode( array( $key => $value ) );
1253 + return wp_json_encode( array( $key => $value ), JSON_UNESCAPED_SLASHES );
1148 1254 },
1149 1255 array_keys( (array) $data ),
1150 1256 array_values( (array) $data )
1151 1257 )
@@ -1167,14 +1273,14 @@
1167 1273 return $response;
1168 1274 }
1169 1275 $decoded_response = json_decode( $response_body, true );
1170 1276
1171 - if ( false === is_array( $decoded_response ) ) {
1277 + if ( ! is_array( $decoded_response ) ) {
1172 1278 return new WP_Error( 'sync_rest_api_response_decoding_failed', 'Sync REST API response decoding failed', $response_body );
1173 1279 }
1174 1280
1175 - if ( $response_code !== 200 || false === isset( $decoded_response['processed_items'] ) ) {
1176 - if ( is_array( $decoded_response ) && isset( $decoded_response['code'] ) && isset( $decoded_response['message'] ) ) {
1281 + if ( $response_code !== 200 || ! isset( $decoded_response['processed_items'] ) ) {
1282 + if ( isset( $decoded_response['code'] ) && isset( $decoded_response['message'] ) ) {
1177 1283 return new WP_Error(
1178 1284 'jetpack_sync_send_error_' . $decoded_response['code'],
1179 1285 $decoded_response['message'],
1180 1286 $decoded_response['data'] ?? null