Completed
Push — add/sync-rest-2 ( 49e56f...0a5cb1 )
by
unknown
40:39 queued 31:26
created

Jetpack_Sync_Client::init()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 85
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 25
Bugs 2 Features 9
Metric Value
c 25
b 2
f 9
dl 0
loc 85
rs 8.3367
cc 5
eloc 39
nc 12
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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-full.php';
5
6
class Jetpack_Sync_Client {
7
	static $default_options_whitelist = array( 'stylesheet', '/^theme_mods_.*$/' );
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...
8
	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...
9
	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...
10
	static $default_network_options_whitelist = array();
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...
11
	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...
12
	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...
13
	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...
14
	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...
15
16
	private $sync_queue;
17
	private $full_sync_client;
18
	private $codec;
19
	private $options_whitelist;
20
	private $constants_whitelist;
21
	private $meta_types = array( 'post' );
22
	private $callable_whitelist;
23
	private $network_options_whitelist;
24
	private $taxonomy_whitelist;
25
26
	// singleton functions
27
	private static $instance;
28
29
	public static function getInstance() {
30
		if ( null === self::$instance ) {
31
			self::$instance = new self();
32
		}
33
34
		return self::$instance;
35
	}
36
37
	// this is necessary because you can't use "new" when you declare instance properties >:(
38
	protected function __construct() {
39
		$this->sync_queue = new Jetpack_Sync_Queue( 'sync', self::$default_send_buffer_size );
40
		$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...
41
		$this->codec                     = new Jetpack_Sync_Deflate_Codec();
42
		$this->constants_whitelist       = self::$default_constants_whitelist;
43
		$this->options_whitelist         = self::$default_options_whitelist;
44
		$this->callable_whitelist        = self::$default_callable_whitelist;
45
		$this->network_options_whitelist = self::$default_network_options_whitelist;
46
		$this->taxonomy_whitelist        = self::$default_taxonomy_whitelist;
47
		$this->is_multisite              = 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...
48
		$this->init();
49
	}
50
51
	private function init() {
52
53
		$handler = array( $this, 'action_handler' );
54
55
		// constants
56
		add_action( 'jetpack_sync_current_constants', $handler, 10 );
57
58
		// functions
59
		add_action( 'jetpack_sync_current_callables', $handler, 10 );
60
61
		// posts
62
		add_action( 'wp_insert_post', $handler, 10, 3 );
63
		add_action( 'deleted_post', $handler, 10 );
64
65
		// comments
66
		add_action( 'wp_insert_comment', $handler, 10, 2 );
67
		add_action( 'deleted_comment', $handler, 10 );
68
		add_action( 'trashed_comment', $handler, 10 );
69
		add_action( 'spammed_comment', $handler, 10 );
70
71
		// even though it's messy, we implement these hooks because 
72
		// the edit_comment hook doesn't include the data
73
		// so this saves us a DB read for every comment event
74
		foreach ( array( '', 'trackback', 'pingback' ) as $comment_type ) {
75
			foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
76
				add_action( "comment_{$comment_status}_{$comment_type}", $handler, 10, 2 );
77
			}
78
		}
79
80
		// options
81
		add_action( 'added_option', $handler, 10, 2 );
82
		add_action( 'updated_option', $handler, 10, 3 );
83
		add_action( 'deleted_option', $handler, 10, 1 );
84
85
		// themes
86
		add_action( 'switch_theme', array( $this, 'send_theme_info' ) );
87
		add_action( 'jetpack_sync_current_theme_support', $handler, 10 ); // custom hook, see meta-hooks below
88
89
		// post-meta, and in the future - other meta?
90
		foreach ( $this->meta_types as $meta_type ) {
91
			// we need to make sure we don't commit before we receive these,
92
			// because they're invoked after meta changes are saved to the DB
93
			add_action( "added_{$meta_type}_meta", $handler, 99, 4 );
94
			add_action( "updated_{$meta_type}_meta", $handler, 99, 4 );
95
			add_action( "deleted_{$meta_type}_meta", $handler, 99, 4 );
96
		}
97
98
		// synthetic actions for full sync
99
		add_action( 'jp_full_sync_start', $handler );
100
		add_action( 'jp_full_sync_posts', $handler );
101
		add_action( 'jp_full_sync_comments', $handler );
102
		add_action( 'jp_full_sync_option', $handler, 10, 2 );
103
		add_action( 'jp_full_sync_postmeta', $handler, 10, 2 );
104
105
		/**
106
		 * Other hooks - fire synthetic hooks for all the properties we need to sync,
107
		 * e.g. when a theme changes
108
		 */
109
110
		// themes
111
		add_action( 'set_site_transient_update_plugins', $handler, 10, 1 );
112
		add_action( 'set_site_transient_update_themes', $handler, 10, 1 );
113
		add_action( 'set_site_transient_update_core', $handler, 10, 1 );
114
115
		// multi site network options
116
		if ( $this->is_multisite ) {
117
			add_action( 'add_site_option', $handler, 10, 2 );
118
			add_action( 'update_site_option', $handler, 10, 3 );
119
			add_action( 'delete_site_option', $handler, 10, 1 );
120
		}
121
122
123
		/**
124
		 * Sync all pending actions with server
125
		 */
126
		add_action( 'jetpack_sync_actions', array( $this, 'do_sync' ) );
127
128
		// terms
129
		add_action( 'created_term', array( $this, 'save_term_handler' ), 10, 3 );
130
		add_action( 'edited_term', array( $this, 'save_term_handler' ), 10, 3 );
131
		add_action( 'jetapack_sync_save_term', $handler, 10, 4 );
132
		add_action( 'delete_term', $handler, 10, 5 );
133
134
135
	}
136
137
	// 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...
138
	function set_options_whitelist( $options ) {
139
		$this->options_whitelist = $options;
140
	}
141
142
	function set_constants_whitelist( $constants ) {
143
		$this->constants_whitelist = $constants;
144
	}
145
146
	function set_callable_whitelist( $functions ) {
147
		$this->callable_whitelist = $functions;
148
	}
149
150
	function set_network_options_whitelist( $options ) {
151
		$this->network_options_whitelist = $options;
152
	}
153
154
	function set_send_buffer_size( $size ) {
155
		$this->sync_queue->set_checkout_size( $size );
156
	}
157
158
	function set_taxonomy_whitelist( $taxonomies ) {
159
		$this->taxonomy_whitelist = $taxonomies;
160
	}
161
162
	function is_whitelisted_option( $option ) {
163
		foreach ( $this->options_whitelist as $whitelisted_option ) {
164
			if ( $whitelisted_option[0] === '/' && preg_match( $whitelisted_option, $option ) ) {
165
				return true;
166
			} elseif ( $whitelisted_option === $option ) {
167
				return true;
168
			}
169
		}
170
171
		return false;
172
	}
173
174
	function is_whitelisted_network_option( $option ) {
175
		return $this->is_multisite && in_array( $option, $this->network_options_whitelist );
176
	}
177
178
	function set_codec( iJetpack_Sync_Codec $codec ) {
179
		$this->codec = $codec;
180
	}
181
182
	function set_full_sync_client( $full_sync_client ) {
183
		if ( $this->full_sync_client ) {
184
			remove_action( 'jetpack_sync_full', array( $this->full_sync_client, 'start' ) );
185
		}
186
187
		$this->full_sync_client = $full_sync_client;
188
189
		/**
190
		 * Sync all objects in the database with the server
191
		 */
192
		add_action( 'jetpack_sync_full', array( $this->full_sync_client, 'start' ) );
193
	}
194
195
	function action_handler() {
196
		// 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...
197
		// wherever we initialize the action listeners or we're just wasting cycles
198
		if ( Jetpack::is_development_mode() || Jetpack::is_staging_site() ) {
199
			return false;
200
		}
201
202
		$current_filter = current_filter();
203
		$args           = func_get_args();
204
205
		if ( $current_filter === 'wp_insert_post' && $args[1]->post_type === 'revision' ) {
206
			return;
207
		}
208
209
		if ( in_array( $current_filter, array( 'deleted_option', 'added_option', 'updated_option' ) )
210
		     &&
211
		     ! $this->is_whitelisted_option( $args[0] )
212
		) {
213
			return;
214
		}
215
216
		if ( in_array( $current_filter, array( 'delete_site_option', 'add_site_option', 'update_site_option' ) )
217
		     &&
218
		     ! $this->is_whitelisted_network_option( $args[0] )
219
		) {
220
			return;
221
		}
222
223
		$this->sync_queue->add( array(
224
			$current_filter,
225
			$args
226
		) );
227
	}
228
229
	function send_theme_info() {
230
		global $_wp_theme_features;
231
		do_action( 'jetpack_sync_current_theme_support', $_wp_theme_features );
232
	}
233
234
	function save_term_handler( $term_id, $tt_id, $taxonomy ) {
235
		$term_object = WP_Term::get_instance( $term_id, $taxonomy );
236
		do_action( 'jetapack_sync_save_term', $term_id, $tt_id, $taxonomy, $term_object );
237
	}
238
239
	function do_sync() {
240
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
241
			$this->schedule_sync( "+1 minute" );
242
243
			return false;
244
		}
245
246
		$this->maybe_sync_constants();
247
		$this->maybe_sync_callables();
248
249
		$buffer = $this->sync_queue->checkout();
250
251
		if ( ! $buffer ) {
252
			// buffer has no items
253
			return;
254
		}
255
256
		if ( is_wp_error( $buffer ) ) {
257
			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...
258
259
			return;
260
		}
261
262
		$data = $this->codec->encode( $buffer->get_items() );
263
264
		/**
265
		 * Fires when data is ready to send to the server.
266
		 * Return false or WP_Error to abort the sync (e.g. if there's an error)
267
		 * The items will be automatically re-sent later
268
		 *
269
		 * @since 4.1
270
		 *
271
		 * @param array $data The action buffer
272
		 */
273
		$result = apply_filters( 'jetpack_sync_client_send_data', $data );
274
275
		if ( ! $result || is_wp_error( $result ) ) {
276
			$this->sync_queue->checkin( $buffer );
277
			// try again in 1 minute
278
			$this->schedule_sync( "+1 minute" );
279
		} else {
280
			$this->sync_queue->close( $buffer );
281
			// check if there are any more events in the buffer
282
			// if so, schedule a cron job to happen soon
283
			if ( $this->sync_queue->has_any_items() ) {
284
				$this->schedule_sync( "+1 minute" );
285
			}
286
		}
287
	}
288
289
290
	private function schedule_sync( $when ) {
291
		wp_schedule_single_event( strtotime( $when ), 'jetpack_sync_actions' );
292
	}
293
294
	function force_sync_constants() {
295
		delete_option( self::$constants_checksum_option_name );
296
		$this->maybe_sync_constants();
297
	}
298
299 View Code Duplication
	private function maybe_sync_constants() {
300
		$constants = $this->get_all_constants();
301
		if ( empty( $constants ) ) {
302
			return;
303
		}
304
		$constants_check_sum = $this->get_check_sum( $constants );
305
		if ( $constants_check_sum !== get_option( self::$constants_checksum_option_name ) ) {
306
			do_action( 'jetpack_sync_current_constants', $constants );
307
			update_option( self::$constants_checksum_option_name, $constants_check_sum );
308
		}
309
	}
310
311
	private function get_all_constants() {
312
		return array_combine(
313
			$this->constants_whitelist,
314
			array_map( array( $this, 'get_constant' ), $this->constants_whitelist )
315
		);
316
	}
317
318
	private function get_constant( $constant ) {
319
		if ( defined( $constant ) ) {
320
			return constant( $constant );
321
		}
322
323
		return null;
324
	}
325
326
	public function force_sync_callables() {
327
		delete_option( self::$functions_checksum_option_name );
328
		$this->maybe_sync_callables();
329
	}
330
331 View Code Duplication
	private function maybe_sync_callables() {
332
		$callables = $this->get_all_callables();
333
		if ( empty( $callables ) ) {
334
			return;
335
		}
336
		$callables_check_sum = $this->get_check_sum( $callables );
337
338
		if ( $callables_check_sum !== get_option( self::$functions_checksum_option_name ) ) {
339
			do_action( 'jetpack_sync_current_callables', $callables );
340
			update_option( self::$functions_checksum_option_name, $callables_check_sum );
341
		}
342
	}
343
344
	private function get_all_callables() {
345
		return array_combine(
346
			$this->callable_whitelist,
347
			array_map( array( $this, 'get_callable' ), $this->callable_whitelist )
348
		);
349
	}
350
351
	private function get_callable( $callable ) {
352
		return call_user_func( $callable );
353
	}
354
355
	private function get_check_sum( $values ) {
356
		return crc32( serialize( $values ) );
357
	}
358
359
	function get_actions() {
360
		// TODO: we should only send a bit at a time, flush_all sends everything
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...
361
		return $this->sync_queue->flush_all();
362
	}
363
364
	function get_all_actions() {
365
		return $this->sync_queue->get_all();
366
	}
367
368
	function get_sync_queue() {
369
		return $this->sync_queue;
370
	}
371
372
	function reset_sync_queue() {
373
		$this->sync_queue->reset();
374
	}
375
376
	function reset_state() {
377
		$this->codec               = new Jetpack_Sync_Deflate_Codec();
378
		$this->constants_whitelist = self::$default_constants_whitelist;
379
		$this->options_whitelist   = self::$default_options_whitelist;
380
		$this->set_send_buffer_size( self::$default_send_buffer_size );
381
		delete_option( self::$constants_checksum_option_name );
382
		$this->reset_sync_queue();
383
	}
384
}
385