Completed
Push — add/sync-rest-2 ( 83f13e...e0eac0 )
by
unknown
246:52 queued 237:19
created

Jetpack_Sync_Client::reset_data()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
require_once dirname( __FILE__ ) . '/class.jetpack-sync-deflate-codec.php';
3
require_once dirname( __FILE__ ) . '/class.jetpack-sync-queue.php';
4
require_once dirname( __FILE__ ) . '/class.jetpack-sync-functions.php';
5
require_once dirname( __FILE__ ) . '/class.jetpack-sync-full.php';
6
7
class Jetpack_Sync_Client {
8
	static $default_options_whitelist = array( 
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $default_options_whitelist.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
9
		'stylesheet', 
10
		'/^theme_mods_.*$/',
11
		'blogname',
12
		'home',
13
		'siteurl',
14
		'blogdescription',
15
		'blog_charset',
16
		'permalink_structure',
17
		'category_base',
18
		'tag_base',
19
		'comment_moderation',
20
		'default_comment_status',
21
		'thread_comments',
22
		'thread_comments_depth',
23
		'jetpack_site_icon_url',
24
		'social_notifications_like',
25
		'page_on_front',
26
		'rss_use_excerpt',
27
		'subscription_options',
28
		'stb_enabled',
29
		'stc_enabled',
30
		'comment_registration',
31
		'require_name_email',
32
		'show_avatars',
33
		'avatar_default',
34
		'avatar_rating',
35
		'highlander_comment_form_prompt',
36
		'jetpack_comment_form_color_scheme',
37
		'stats_options',
38
		'gmt_offset',
39
		'timezone_string',
40
		'jetpack_sync_non_public_post_stati',
41
		'jetpack_options',
42
		'site_icon', // (int) - ID of core's Site Icon attachment ID
43
		'default_post_format',
44
		'default_category',
45
		'large_size_w',
46
		'large_size_h',
47
		'thumbnail_size_w',
48
		'thumbnail_size_h',
49
		'medium_size_w',
50
		'medium_size_h',
51
		'thumbnail_crop',
52
		'image_default_link_type',
53
		'site_logo',
54
		'sharing-options',
55
		'sharing-services',
56
		'post_count',
57
		'default_ping_status',
58
		'sticky_posts',
59
		'disabled_likes',
60
		'blog_public',
61
		'default_pingback_flag',
62
		'require_name_email',
63
		'close_comments_for_old_posts',
64
		'close_comments_days_old',
65
		'thread_comments',
66
		'thread_comments_depth',
67
		'page_comments',
68
		'comments_per_page',
69
		'default_comments_page',
70
		'comment_order',
71
		'comments_notify',
72
		'moderation_notify',
73
		'social_notifications_like',
74
		'social_notifications_reblog',
75
		'social_notifications_subscribe',
76
		'comment_whitelist',
77
		'comment_max_links',
78
		'moderation_keys',
79
		'blacklist_keys',
80
		'lang_id',
81
		'wga',
82
		'disabled_likes',
83
		'disabled_reblogs',
84
		'jetpack_comment_likes_enabled',
85
		'twitter_via',
86
		'twitter-cards-site-tag' );
87
88
	static $default_constants_whitelist = array(
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $default_constants_whitelist.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
89
		'EMPTY_TRASH_DAYS',
90
		'WP_POST_REVISIONS',
91
		'AUTOMATIC_UPDATER_DISABLED',
92
		'ABSPATH',
93
		'WP_CONTENT_DIR',
94
		'FS_METHOD',
95
		'DISALLOW_FILE_EDIT',
96
		'DISALLOW_FILE_MODS',
97
		'WP_AUTO_UPDATE_CORE',
98
		'WP_HTTP_BLOCK_EXTERNAL',
99
		'WP_ACCESSIBLE_HOSTS',
100
		'JETPACK__VERSION'
101
	);
102
103
	static $default_callable_whitelist = array(
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $default_callable_whitelist.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
104
		'wp_max_upload_size' => 'wp_max_upload_size',
105
		'is_main_network' => array( 'Jetpack', 'is_multi_network' ),
106
		'is_multi_site' => 'is_multisite',
107
		'main_network_site' => 'network_site_url',
108
		'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
109
		'has_file_system_write_access' => array( 'Jetpack_Sync_Functions', 'file_system_write_access' ),
110
		'is_version_controlled' => array( 'Jetpack_Sync_Functions', 'is_version_controlled' ),
111
		'modules' => array( 'Jetpack_Sync_Functions', 'get_modules' )
112
	);
113
114
	static $default_network_options_whitelist = array( 'site_name' );
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $default_network_options_whitelist.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
115
	static $constants_checksum_option_name = 'jetpack_constants_sync_checksum';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $constants_checksum_option_name.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
116
	static $functions_checksum_option_name = 'jetpack_functions_sync_checksum';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $functions_checksum_option_name.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
117
	static $default_send_buffer_size = 20;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $default_send_buffer_size.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
118
	static $default_taxonomy_whitelist = array();
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $default_taxonomy_whitelist.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
119
120
	private $sync_queue;
121
	private $full_sync_client;
122
	private $codec;
123
	private $options_whitelist;
124
	private $constants_whitelist;
125
	private $meta_types = array( 'post' );
126
	private $callable_whitelist;
127
	private $network_options_whitelist;
128
	private $taxonomy_whitelist;
129
130
	// singleton functions
131
	private static $instance;
132
133
	public static function getInstance() {
134
		if ( null === self::$instance ) {
135
			self::$instance = new self();
136
		}
137
138
		return self::$instance;
139
	}
140
141
	// this is necessary because you can't use "new" when you declare instance properties >:(
142
	protected function __construct() {
143
		$this->set_defaults();
144
		$this->init();
145
	}
146
147
	private function init() {
148
149
		$handler = array( $this, 'action_handler' );
150
151
		// constants
152
		add_action( 'jetpack_sync_current_constants', $handler, 10 );
153
154
		// functions
155
		add_action( 'jetpack_sync_current_callables', $handler, 10 );
156
157
		// posts
158
		add_action( 'wp_insert_post', $handler, 10, 3 );
159
		add_action( 'deleted_post', $handler, 10 );
160
161
		// comments
162
		add_action( 'wp_insert_comment', $handler, 10, 2 );
163
		add_action( 'deleted_comment', $handler, 10 );
164
		add_action( 'trashed_comment', $handler, 10 );
165
		add_action( 'spammed_comment', $handler, 10 );
166
167
		// even though it's messy, we implement these hooks because 
168
		// the edit_comment hook doesn't include the data
169
		// so this saves us a DB read for every comment event
170
		foreach ( array( '', 'trackback', 'pingback' ) as $comment_type ) {
171
			foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
172
				add_action( "comment_{$comment_status}_{$comment_type}", $handler, 10, 2 );
173
			}
174
		}
175
176
		// options
177
		add_action( 'added_option', $handler, 10, 2 );
178
		add_action( 'updated_option', $handler, 10, 3 );
179
		add_action( 'deleted_option', $handler, 10, 1 );
180
181
		// Sync Core Icon: Detect changes in Core's Site Icon and make it syncable.
182
		add_action( 'add_option_site_icon',    array( $this, 'jetpack_sync_core_icon' ) );
183
		add_action( 'update_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
184
		add_action( 'delete_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
185
186
		// themes
187
		add_action( 'switch_theme', array( $this, 'send_theme_info' ) );
188
		add_action( 'jetpack_sync_current_theme_support', $handler, 10 ); // custom hook, see meta-hooks below
189
190
		// post-meta, and in the future - other meta?
191
		foreach ( $this->meta_types as $meta_type ) {
192
			// we need to make sure we don't commit before we receive these,
193
			// because they're invoked after meta changes are saved to the DB
194
			add_action( "added_{$meta_type}_meta", $handler, 99, 4 );
195
			add_action( "updated_{$meta_type}_meta", $handler, 99, 4 );
196
			add_action( "deleted_{$meta_type}_meta", $handler, 99, 4 );
197
		}
198
199
		// synthetic actions for full sync
200
		add_action( 'jp_full_sync_start', $handler );
201
		add_action( 'jp_full_sync_posts', $handler );
202
		add_action( 'jp_full_sync_comments', $handler );
203
		add_action( 'jp_full_sync_option', $handler, 10, 2 );
204
		add_action( 'jp_full_sync_postmeta', $handler, 10, 2 );
205
206
		/**
207
		 * Other hooks - fire synthetic hooks for all the properties we need to sync,
208
		 * e.g. when a theme changes
209
		 */
210
211
		// themes
212
		add_action( 'set_site_transient_update_plugins', $handler, 10, 1 );
213
		add_action( 'set_site_transient_update_themes', $handler, 10, 1 );
214
		add_action( 'set_site_transient_update_core', $handler, 10, 1 );
215
216
		// multi site network options
217
		if ( $this->is_multisite ) {
0 ignored issues
show
Bug introduced by
The property is_multisite does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
218
			add_action( 'add_site_option', $handler, 10, 2 );
219
			add_action( 'update_site_option', $handler, 10, 3 );
220
			add_action( 'delete_site_option', $handler, 10, 1 );
221
		}
222
223
224
		/**
225
		 * Sync all pending actions with server
226
		 */
227
		add_action( 'jetpack_sync_actions', array( $this, 'do_sync' ) );
228
229
		// terms
230
		add_action( 'created_term', array( $this, 'save_term_handler' ), 10, 3 );
231
		add_action( 'edited_term', array( $this, 'save_term_handler' ), 10, 3 );
232
		add_action( 'jetapack_sync_save_term', $handler, 10, 4 );
233
		add_action( 'delete_term', $handler, 10, 5 );
234
235
236
	}
237
238
	// TODO: Refactor to use one set whitelist function, with one is_whitelisted.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
239
	function set_options_whitelist( $options ) {
240
		$this->options_whitelist = $options;
241
	}
242
243
	function set_constants_whitelist( $constants ) {
244
		$this->constants_whitelist = $constants;
245
	}
246
247
	function get_callable_whitelist( $functions ) {
0 ignored issues
show
Unused Code introduced by
The parameter $functions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
248
		return $this->callable_whitelist;
249
	}
250
251
	function set_callable_whitelist( $functions ) {
252
		$this->callable_whitelist = $functions;
253
	}
254
255
	function set_network_options_whitelist( $options ) {
256
		$this->network_options_whitelist = $options;
257
	}
258
259
	function set_send_buffer_size( $size ) {
260
		$this->sync_queue->set_checkout_size( $size );
261
	}
262
263
	function set_taxonomy_whitelist( $taxonomies ) {
264
		$this->taxonomy_whitelist = $taxonomies;
265
	}
266
267
	function is_whitelisted_option( $option ) {
268
		foreach ( $this->options_whitelist as $whitelisted_option ) {
269
			if ( $whitelisted_option[0] === '/' && preg_match( $whitelisted_option, $option ) ) {
270
				return true;
271
			} elseif ( $whitelisted_option === $option ) {
272
				return true;
273
			}
274
		}
275
		
276
		return false;
277
	}
278
279
	function is_whitelisted_network_option( $option ) {
280
		return $this->is_multisite && in_array( $option, $this->network_options_whitelist );
281
	}
282
283
	function set_codec( iJetpack_Sync_Codec $codec ) {
284
		$this->codec = $codec;
285
	}
286
287
	function set_full_sync_client( $full_sync_client ) {
288
		if ( $this->full_sync_client ) {
289
			remove_action( 'jetpack_sync_full', array( $this->full_sync_client, 'start' ) );
290
		}
291
292
		$this->full_sync_client = $full_sync_client;
293
294
		/**
295
		 * Sync all objects in the database with the server
296
		 */
297
		add_action( 'jetpack_sync_full', array( $this->full_sync_client, 'start' ) );
298
	}
299
300
	function action_handler() {
301
		// TODO: it's really silly to have this function here - it should be
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
302
		// wherever we initialize the action listeners or we're just wasting cycles
303
		if ( Jetpack::is_development_mode() || Jetpack::is_staging_site() ) {
304
			return false;
305
		}
306
307
		$current_filter = current_filter();
308
		$args           = func_get_args();
309
310
		if ( $current_filter === 'wp_insert_post' && $args[1]->post_type === 'revision' ) {
311
			return;
312
		}
313
314
		if ( in_array( $current_filter, array( 'deleted_option', 'added_option', 'updated_option' ) )
315
		     &&
316
		     ! $this->is_whitelisted_option( $args[0] )
317
		) {
318
			return;
319
		}
320
321
		if ( in_array( $current_filter, array( 'delete_site_option', 'add_site_option', 'update_site_option' ) )
322
		     &&
323
		     ! $this->is_whitelisted_network_option( $args[0] )
324
		) {
325
			return;
326
		}
327
328
		$this->sync_queue->add( array(
329
			$current_filter,
330
			$args
331
		) );
332
	}
333
334
	function send_theme_info() {
335
		global $_wp_theme_features;
336
		do_action( 'jetpack_sync_current_theme_support', $_wp_theme_features );
337
	}
338
339
	function save_term_handler( $term_id, $tt_id, $taxonomy ) {
340
		$term_object = WP_Term::get_instance( $term_id, $taxonomy );
341
		do_action( 'jetapack_sync_save_term', $term_id, $tt_id, $taxonomy, $term_object );
342
	}
343
344
	function do_sync() {
345
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
346
			$this->schedule_sync( "+1 minute" );
347
348
			return false;
349
		}
350
351
		$this->maybe_sync_constants();
352
		$this->maybe_sync_callables();
353
354
		$buffer = $this->sync_queue->checkout();
355
356
		if ( ! $buffer ) {
357
			// buffer has no items
358
			return;
359
		}
360
361
		if ( is_wp_error( $buffer ) ) {
362
			error_log( "Error fetching buffer: " . $buffer->get_error_message() );
0 ignored issues
show
Bug introduced by
The method get_error_message() does not seem to exist on object<Jetpack_Sync_Queue_Buffer>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
363
364
			return;
365
		}
366
367
		$data = $this->codec->encode( $buffer->get_items() );
368
369
		/**
370
		 * Fires when data is ready to send to the server.
371
		 * Return false or WP_Error to abort the sync (e.g. if there's an error)
372
		 * The items will be automatically re-sent later
373
		 *
374
		 * @since 4.1
375
		 *
376
		 * @param array $data The action buffer
377
		 */
378
		$result = apply_filters( 'jetpack_sync_client_send_data', $data );
379
380
		if ( ! $result || is_wp_error( $result ) ) {
381
			$this->sync_queue->checkin( $buffer );
382
			// try again in 1 minute
383
			$this->schedule_sync( "+1 minute" );
384
		} else {
385
			$this->sync_queue->close( $buffer );
386
			// check if there are any more events in the buffer
387
			// if so, schedule a cron job to happen soon
388
			if ( $this->sync_queue->has_any_items() ) {
389
				$this->schedule_sync( "+1 minute" );
390
			}
391
		}
392
	}
393
394
395
	private function schedule_sync( $when ) {
396
		wp_schedule_single_event( strtotime( $when ), 'jetpack_sync_actions' );
397
	}
398
399
	function force_sync_constants() {
400
		delete_option( self::$constants_checksum_option_name );
401
		$this->maybe_sync_constants();
402
	}
403
404 View Code Duplication
	private function maybe_sync_constants() {
405
		$constants = $this->get_all_constants();
406
		if ( empty( $constants ) ) {
407
			return;
408
		}
409
		$constants_check_sum = $this->get_check_sum( $constants );
410
		if ( $constants_check_sum !== get_option( self::$constants_checksum_option_name ) ) {
411
			do_action( 'jetpack_sync_current_constants', $constants );
412
			update_option( self::$constants_checksum_option_name, $constants_check_sum );
413
		}
414
	}
415
416
	private function get_all_constants() {
417
		return array_combine(
418
			$this->constants_whitelist,
419
			array_map( array( $this, 'get_constant' ), $this->constants_whitelist )
420
		);
421
	}
422
423
	private function get_constant( $constant ) {
424
		if ( defined( $constant ) ) {
425
			return constant( $constant );
426
		}
427
428
		return null;
429
	}
430
431
	public function force_sync_callables() {
432
		delete_option( self::$functions_checksum_option_name );
433
		$this->maybe_sync_callables();
434
	}
435
436 View Code Duplication
	private function maybe_sync_callables() {
437
		$callables = $this->get_all_callables();
438
		if ( empty( $callables ) ) {
439
			return;
440
		}
441
		$callables_check_sum = $this->get_check_sum( $callables );
442
443
		if ( $callables_check_sum !== get_option( self::$functions_checksum_option_name ) ) {
444
			do_action( 'jetpack_sync_current_callables', $callables );
445
			update_option( self::$functions_checksum_option_name, $callables_check_sum );
446
		}
447
	}
448
449
	private function get_all_callables() {
450
		return array_combine(
451
			array_keys( $this->callable_whitelist ),
452
			array_map( array( $this, 'get_callable' ), array_values( $this->callable_whitelist ) )
453
		);
454
	}
455
456
	private function get_callable( $callable ) {
457
		return call_user_func( $callable );
458
	}
459
460
	private function get_check_sum( $values ) {
461
		return crc32( serialize( $values ) );
462
	}
463
464 View Code Duplication
	function jetpack_sync_core_icon() {
465
		error_log("syncing core icon");
466
		if ( function_exists( 'get_site_icon_url' ) ) {
467
			$url = get_site_icon_url();
468
		} else {
469
			return;
470
		}
471
472
		require_once( JETPACK__PLUGIN_DIR . 'modules/site-icon/site-icon-functions.php' );
473
		// If there's a core icon, maybe update the option.  If not, fall back to Jetpack's.
474
		if ( ! empty( $url ) && $url !== jetpack_site_icon_url() ) {
475
			// This is the option that is synced with dotcom
476
			Jetpack_Options::update_option( 'site_icon_url', $url );
477
		} else if ( empty( $url ) && did_action( 'delete_option_site_icon' ) ) {
478
			Jetpack_Options::delete_option( 'site_icon_url' );
479
		}
480
	}
481
482
	function get_sync_queue() {
483
		return $this->sync_queue;
484
	}
485
486
	function reset_sync_queue() {
487
		$this->sync_queue->reset();
488
	}
489
490
	function set_defaults() {
491
		$this->sync_queue = new Jetpack_Sync_Queue( 'sync', self::$default_send_buffer_size );
492
		$this->set_full_sync_client( new Jetpack_Sync_Full( $this->sync_queue ) );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Sync_Full::__construct() has too many arguments starting with $this->sync_queue.

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...
493
		$this->codec                     = new Jetpack_Sync_Deflate_Codec();
494
		$this->constants_whitelist       = self::$default_constants_whitelist;
495
		$this->options_whitelist         = self::$default_options_whitelist;
496
		$this->callable_whitelist        = self::$default_callable_whitelist;
497
		$this->network_options_whitelist = self::$default_network_options_whitelist;
498
		$this->taxonomy_whitelist        = self::$default_taxonomy_whitelist;
499
		$this->is_multisite              = is_multisite();
500
	}
501
502
	function reset_data() {
503
		delete_option( self::$constants_checksum_option_name );
504
		$this->reset_sync_queue();
505
	}
506
}
507
508
if ( is_multisite() ) {
509
	$client = Jetpack_Sync_Client::getInstance();
510
	$existing_callables = $client->get_callable_whitelist();
511
	$client->set_callable_whitelist( array_merge( $existing_callables, array(
512
		'network_name'                        => array( 'Jetpack', 'network_name' ),
513
		'network_allow_new_registrations'     => array( 'Jetpack', 'network_allow_new_registrations' ),
514
		'network_add_new_users'               => array( 'Jetpack', 'network_add_new_users' ),
515
		'network_site_upload_space'           => array( 'Jetpack', 'network_site_upload_space' ),
516
		'network_upload_file_types'           => array( 'Jetpack', 'network_upload_file_types' ),
517
		'network_enable_administration_menus' => array( 'Jetpack', 'network_enable_administration_menus' ),
518
	) ) );
519
}
520