Completed
Push — add/sync-rest-2 ( ae2fc3...dfd2b5 )
by
unknown
137:46 queued 127:52
created

Jetpack_Sync_Client::set_codec()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
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
15
	private $sync_queue;
16
	private $full_sync_client;
17
	private $codec;
18
	private $options_whitelist;
19
	private $constants_whitelist;
20
	private $meta_types = array( 'post' );
21
	private $callable_whitelist;
22
	private $network_options_whitelist;
23
24
	// singleton functions
25
	private static $instance;
26
27
	public static function getInstance() {
28
		if ( null === self::$instance ) {
29
			self::$instance = new self();
30
		}
31
		return self::$instance;
32
	}
33
34
	// this is necessary because you can't use "new" when you declare instance properties >:(
35
	protected function __construct() {
36
		$this->sync_queue          = new Jetpack_Sync_Queue( 'sync', self::$default_send_buffer_size );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 10 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
37
		$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...
38
		$this->codec               = new Jetpack_Sync_Deflate_Codec();
39
		$this->constants_whitelist = self::$default_constants_whitelist;
40
		$this->options_whitelist   = self::$default_options_whitelist;
41
		$this->callable_whitelist  = self::$default_callable_whitelist;
42
		$this->network_options_whitelist = self::$default_network_options_whitelist;
43
		$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...
44
		$this->init();
45
	}
46
47
	private function init() {
48
49
		$handler = array( $this, 'action_handler' );
50
51
		// constants
52
		add_action( 'jetpack_sync_current_constants', $handler, 10 );
53
54
		// functions
55
		add_action( 'jetpack_sync_current_callables', $handler, 10 );
56
57
		// posts
58
		add_action( 'wp_insert_post', $handler, 10, 3 );
59
		add_action( 'deleted_post', $handler, 10 );
60
61
		// comments
62
		add_action( 'wp_insert_comment', $handler, 10, 2 );
63
		add_action( 'deleted_comment', $handler, 10 );
64
		add_action( 'trashed_comment', $handler, 10 );
65
		add_action( 'spammed_comment', $handler, 10 );
66
67
		// even though it's messy, we implement these hooks because 
68
		// the edit_comment hook doesn't include the data
69
		// so this saves us a DB read for every comment event
70
		foreach ( array( '', 'trackback', 'pingback' ) as $comment_type ) {
71
			foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
72
				add_action( "comment_{$comment_status}_{$comment_type}", $handler, 10, 2 );
73
			}
74
		}
75
76
		// options
77
		add_action( 'added_option', $handler, 10, 2 );
78
		add_action( 'updated_option', $handler, 10, 3 );
79
		add_action( 'deleted_option', $handler, 10, 1 );
80
81
		// themes
82
		add_action( 'jetpack_sync_current_theme_support', $handler, 10 ); // custom hook, see meta-hooks below
83
84
		// post-meta, and in the future - other meta?
85
		foreach ( $this->meta_types as $meta_type ) {
86
			// we need to make sure we don't commit before we receive these,
87
			// because they're invoked after meta changes are saved to the DB
88
			add_action( "added_{$meta_type}_meta", $handler, 99, 4 );
89
			add_action( "updated_{$meta_type}_meta", $handler, 99, 4 );
90
			add_action( "deleted_{$meta_type}_meta", $handler, 99, 4 );
91
		}
92
93
		// synthetic actions for full sync
94
		add_action( 'jp_full_sync_post', $handler, 10, 1 );
95
96
		/**
97
		 * Other hooks - fire synthetic hooks for all the properties we need to sync,
98
		 * e.g. when a theme changes
99
		 */
100
101
		// themes
102
		add_action( 'switch_theme', array( $this, 'switch_theme_handler' ) );
103
104
		add_action( 'set_site_transient_update_plugins', $handler, 10, 1 );
105
		add_action( 'set_site_transient_update_themes', $handler, 10, 1 );
106
		add_action( 'set_site_transient_update_core', $handler, 10, 1 );
107
108
		// multi site network options
109
		if ( $this->is_multisite ) {
110
			add_action( 'add_site_option', $handler, 10, 2 );
111
			add_action( 'update_site_option', $handler, 10, 3 );
112
			add_action( 'delete_site_option', $handler, 10, 1 );
113
		}
114
115
		/**
116
		 * Sync all pending actions with server
117
		 */
118
		add_action( 'jetpack_sync_actions', array( $this, 'do_sync' ) );
119
	}
120
121
	function set_options_whitelist( $options ) {
122
		$this->options_whitelist = $options;
123
	}
124
125
	function set_constants_whitelist( $constants ) {
126
		$this->constants_whitelist = $constants;
127
	}
128
129
	function set_callable_whitelist( $functions ) {
130
		$this->callable_whitelist = $functions;
131
	}
132
133
	function set_network_options_whitelist( $options ) {
134
		$this->network_options_whitelist = $options;
135
	}
136
137
	function set_send_buffer_size( $size ) {
138
		$this->sync_queue->set_checkout_size( $size );
139
	}
140
141
	function is_whitelisted_option( $option ) {
142
		foreach ( $this->options_whitelist as $whitelisted_option ) {
143
			if ( $whitelisted_option[0] === '/' && preg_match( $whitelisted_option, $option ) ) {
144
				return true;
145
			} elseif ( $whitelisted_option === $option ) {
146
				return true;
147
			}
148
		}
149
150
		return false;
151
	}
152
153
	function is_whitelisted_network_option( $option ) {
154
		return $this->is_multisite && in_array( $option, $this->network_options_whitelist );
155
	}
156
157
	function set_codec( iJetpack_Sync_Codec $codec ) {
158
		$this->codec = $codec;
159
	}
160
161
	function set_full_sync_client( $full_sync_client ) {
162
		if ( $this->full_sync_client ) {
163
			remove_action( 'jetpack_sync_full', array( $this->full_sync_client, 'start' ) );
164
		}
165
166
		$this->full_sync_client = $full_sync_client;
167
168
		/**
169
		 * Sync all objects in the database with the server
170
		 */
171
		add_action( 'jetpack_sync_full', array( $this->full_sync_client, 'start' ) );
172
	}
173
174
	function action_handler() {
175
		// 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...
176
		// wherever we initialize the action listeners or we're just wasting cycles
177
		if ( Jetpack::is_development_mode() || Jetpack::is_staging_site() ) {
178
			return false;
179
		}
180
181
		$current_filter = current_filter();
182
		$args           = func_get_args();
183
184
		if ( $current_filter === 'wp_insert_post' && $args[1]->post_type === 'revision' ) {
185
			return;
186
		}
187
188
		if ( in_array( $current_filter, array( 'deleted_option', 'added_option', 'updated_option' ) )
189
		     &&
190
		     ! $this->is_whitelisted_option( $args[0] )
191
		) {
192
			return;
193
		}
194
195
		if ( in_array( $current_filter, array( 'delete_site_option', 'add_site_option', 'update_site_option' ) )
196
		     &&
197
		     ! $this->is_whitelisted_network_option( $args[0] )
198
		) {
199
			return;
200
		}
201
202
		$this->sync_queue->add( array(
203
			$current_filter,
204
			$args
205
		) );
206
	}
207
208
	function switch_theme_handler() {
209
		global $_wp_theme_features;
210
211
		do_action( 'jetpack_sync_current_theme_support', $_wp_theme_features );
212
	}
213
214
	function do_sync() {
215
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
216
			$this->schedule_sync( "+1 minute" );
217
			return false;
218
		}
219
220
		$this->maybe_sync_constants();
221
		$this->maybe_sync_callables();
222
223
		$buffer = $this->sync_queue->checkout();
224
225
		if ( ! $buffer ) {
226
			// buffer has no items
227
			return;
228
		}
229
230
		if ( is_wp_error( $buffer ) ) {
231
			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...
232
233
			return;
234
		}
235
236
		$data = $this->codec->encode( $buffer->get_items() );
237
238
		/**
239
		 * Fires when data is ready to send to the server.
240
		 * Return false or WP_Error to abort the sync (e.g. if there's an error)
241
		 * The items will be automatically re-sent later
242
		 *
243
		 * @since 4.1
244
		 *
245
		 * @param array $data The action buffer
246
		 */
247
		$result = apply_filters( 'jetpack_sync_client_send_data', $data );
248
249
		if ( ! $result || is_wp_error( $result ) ) {
250
			$this->sync_queue->checkin( $buffer );
251
			// try again in 1 minute
252
			$this->schedule_sync( "+1 minute" );
253
		} else {
254
			$this->sync_queue->close( $buffer );
255
			// check if there are any more events in the buffer
256
			// if so, schedule a cron job to happen soon
257
			if ( $this->sync_queue->has_any_items() ) {
258
				$this->schedule_sync( "+1 minute" );
259
			}
260
		}
261
	}
262
263
	private function schedule_sync( $when ) {
264
		wp_schedule_single_event( strtotime( $when ), 'jetpack_sync_actions' );
265
	}
266
267 View Code Duplication
	private function maybe_sync_constants() {
268
		$constants           = $this->get_all_constants();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 11 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
269
		if ( empty( $constants ) ) {
270
			return;
271
		}
272
		$constants_check_sum = $this->get_check_sum( $constants );
273
		if ( $constants_check_sum !== get_option( self::$constants_checksum_option_name ) ) {
274
			do_action( 'jetpack_sync_current_constants', $constants );
275
			update_option( self::$constants_checksum_option_name, $constants_check_sum );
276
		}
277
	}
278
279
	private function get_all_constants() {
280
		return array_combine(
281
			$this->constants_whitelist,
282
			array_map( array( $this, 'get_constant' ), $this->constants_whitelist )
283
		);
284
	}
285
286
	private function get_constant( $constant ) {
287
		if ( defined( $constant ) ) {
288
			return constant( $constant );
289
		}
290
291
		return null;
292
	}
293
294 View Code Duplication
	private function maybe_sync_callables() {
295
		$callables           = $this->get_all_callables();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 11 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
296
		if ( empty( $callables ) ) {
297
			return;
298
		}
299
		$callables_check_sum = $this->get_check_sum( $callables );
300
301
		if ( $callables_check_sum !== get_option( self::$functions_checksum_option_name ) ) {
302
			do_action( 'jetpack_sync_current_callables', $callables  );
303
			update_option( self::$functions_checksum_option_name, $callables_check_sum );
304
		}
305
	}
306
307
	private function get_all_callables() {
308
		return array_combine(
309
			$this->callable_whitelist,
310
			array_map( array( $this, 'get_callable' ), $this->callable_whitelist )
311
		);
312
	}
313
	
314
	private function get_callable( $callable ) {
315
		return call_user_func( $callable );
316
	}
317
318
	private function get_check_sum( $values ) {
319
		return crc32( serialize( $values ) );
320
	}
321
322
	function get_actions() {
323
		// 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...
324
		return $this->sync_queue->flush_all();
325
	}
326
327
	function get_all_actions() {
328
		return $this->sync_queue->get_all();
329
	}
330
331
	function get_sync_queue() {
332
		return $this->sync_queue;
333
	}
334
335
	function reset_state() {
336
		$this->codec               = new Jetpack_Sync_Deflate_Codec();
337
		$this->constants_whitelist = self::$default_constants_whitelist;
338
		$this->options_whitelist   = self::$default_options_whitelist;
339
		$this->set_send_buffer_size( self::$default_send_buffer_size );
340
		delete_option( self::$constants_checksum_option_name );
341
		$this->sync_queue->reset();
342
	}
343
}
344