Completed
Push — add/sync-rest-2 ( 658618...e8aaa0 )
by
unknown
09:35
created

Jetpack_Sync_Client::get_constant()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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