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 +136 -34 13.6.2 → 16.3-beta View file →
@@ -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() )->in_safe_mode() ) {
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 /**
@@ -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.
@@ -601,9 +612,9 @@
601 612 'users' => array( get_current_user_id() ),
602 613 'network_options' => true,
603 614 );
604 615
605 - self::do_full_sync( $initial_sync_config );
616 + self::do_full_sync( $initial_sync_config, 'initial_sync' );
606 617 }
607 618
608 619 /**
609 620 * Do an initial full sync only if one has not already been started.
@@ -626,11 +637,12 @@
626 637 * @access public
627 638 * @static
628 639 *
629 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.
630 642 * @return bool True if full sync was successfully started.
631 643 */
632 - public static function do_full_sync( $modules = null ) {
644 + public static function do_full_sync( $modules = null, $context = null ) {
633 645 if ( ! self::sync_allowed() ) {
634 646 return false;
635 647 }
636 648
@@ -642,9 +654,9 @@
642 654 }
643 655
644 656 self::initialize_listener();
645 657
646 - $full_sync_module->start( $modules );
658 + $full_sync_module->start( $modules, $context );
647 659
648 660 return true;
649 661 }
650 662
@@ -658,13 +670,11 @@
658 670 * @return array A list of WordPress cron schedules with the Jetpack sync interval added.
659 671 */
660 672 public static function jetpack_cron_schedule( $schedules ) {
661 673 if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
662 - $minutes = (int) ( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
663 - $display = ( 1 === $minutes ) ?
664 - __( 'Every minute', 'jetpack-sync' ) :
665 - /* translators: %d is an integer indicating the number of minutes. */
666 - 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 );
667 677 $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
668 678 'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
669 679 'display' => $display,
670 680 );
@@ -678,9 +688,47 @@
678 688 * @access public
679 689 * @static
680 690 */
681 691 public static function do_cron_sync() {
682 - 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;
683 731 }
684 732
685 733 /**
686 734 * Starts a full sync via cron.
@@ -688,9 +736,33 @@
688 736 * @access public
689 737 * @static
690 738 */
691 739 public static function do_cron_full_sync() {
692 - 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;
693 765 }
694 766
695 767 /**
696 768 * Try to send actions until we run out of things to send,
@@ -720,9 +792,9 @@
720 792 $delay = $next_sync_time - time() + 1;
721 793 if ( $delay > 15 ) {
722 794 break;
723 795 } elseif ( $delay > 0 ) {
724 - sleep( $delay );
796 + sleep( (int) $delay );
725 797 }
726 798 }
727 799
728 800 // Explicitly only allow 1 do_full_sync call until issue with Immediate Full Sync is resolved.
@@ -776,9 +848,9 @@
776 848 * @access public
777 849 * @static
778 850 */
779 851 public static function initialize_woocommerce() {
780 - if ( false === class_exists( 'WooCommerce' ) ) {
852 + if ( ! class_exists( 'WooCommerce' ) ) {
781 853 return;
782 854 }
783 855 add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
784 856
@@ -791,21 +863,30 @@
791 863 }
792 864 }
793 865
794 866 /**
795 - * Initializes sync for Instant Search.
867 + * Initializes sync for Jetpack Search.
796 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 + *
797 881 * @access public
798 882 * @static
799 883 */
800 884 public static function initialize_search() {
801 - if ( false === class_exists( 'Automattic\\Jetpack\\Search\\Module_Control' ) ) {
885 + if ( ! class_exists( 'Automattic\\Jetpack\\Search\\Module_Control' ) ) {
802 886 return;
803 887 }
804 - $search_module = new \Automattic\Jetpack\Search\Module_Control();
805 - if ( $search_module->is_instant_search_enabled() ) {
806 - add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_search_sync_module' ) );
807 - }
888 + add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_search_sync_module' ) );
808 889 }
809 890
810 891 /**
811 892 * Add Search updates to Sync Filters.
@@ -850,8 +931,25 @@
850 931 return $sync_modules;
851 932 }
852 933
853 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 + /**
854 952 * Initializes sync for WP Super Cache.
855 953 *
856 954 * @access public
857 955 * @static
@@ -856,9 +954,9 @@
856 954 * @access public
857 955 * @static
858 956 */
859 957 public static function initialize_wp_super_cache() {
860 - if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
958 + if ( ! function_exists( 'wp_cache_is_enabled' ) ) {
861 959 return;
862 960 }
863 961 add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
864 962 }
@@ -1119,9 +1217,13 @@
1119 1217 // Retry after locks.
1120 1218 delete_option( self::RETRY_AFTER_PREFIX . 'sync' );
1121 1219 delete_option( self::RETRY_AFTER_PREFIX . 'full_sync' );
1122 1220 // Dedicated sync locks.
1123 - \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 +
1124 1226 delete_transient( Dedicated_Sender::DEDICATED_SYNC_TEMPORARY_DISABLE_FLAG );
1125 1227 // Lock for disabling Sync sending temporarily.
1126 1228 delete_transient( Sender::TEMP_SYNC_DISABLE_TRANSIENT_NAME );
1127 1229
@@ -1147,9 +1249,9 @@
1147 1249 $jsonl_data = implode(
1148 1250 "\n",
1149 1251 array_map(
1150 1252 function ( $key, $value ) {
1151 - return wp_json_encode( array( $key => $value ) );
1253 + return wp_json_encode( array( $key => $value ), JSON_UNESCAPED_SLASHES );
1152 1254 },
1153 1255 array_keys( (array) $data ),
1154 1256 array_values( (array) $data )
1155 1257 )
@@ -1171,14 +1273,14 @@
1171 1273 return $response;
1172 1274 }
1173 1275 $decoded_response = json_decode( $response_body, true );
1174 1276
1175 - if ( false === is_array( $decoded_response ) ) {
1277 + if ( ! is_array( $decoded_response ) ) {
1176 1278 return new WP_Error( 'sync_rest_api_response_decoding_failed', 'Sync REST API response decoding failed', $response_body );
1177 1279 }
1178 1280
1179 - if ( $response_code !== 200 || false === isset( $decoded_response['processed_items'] ) ) {
1180 - 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'] ) ) {
1181 1283 return new WP_Error(
1182 1284 'jetpack_sync_send_error_' . $decoded_response['code'],
1183 1285 $decoded_response['message'],
1184 1286 $decoded_response['data'] ?? null