Completed
Push — add/sync-rest-2 ( c74e68...05b575 )
by
unknown
09:11
created

Jetpack_Sync_Client::maybe_sync_constants()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 11
Ratio 100 %

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 11
loc 11
rs 9.4285
cc 3
eloc 8
nc 3
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
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 $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...
10
	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...
11
12
	private $sync_queue;
13
	private $codec;
14
	private $options_whitelist;
15
	private $constants_whitelist;
16
	private $meta_types = array( 'post' );
17
	private $callable_whitelist;
18
19
	// singleton functions
20
	private static $instance;
21
22
	public static function getInstance() {
23
		if ( null === static::$instance ) {
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
24
			static::$instance = new static();
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
25
		}
26
		return static::$instance;
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
27
	}
28
29
	// this is necessary because you can't use "new" when you declare instance properties >:(
30
	protected function __construct() {
31
		$this->sync_queue          = new Jetpack_Sync_Queue( 'sync', 100 );
32
		$this->codec               = new Jetpack_Sync_Deflate_Codec();
33
		$this->constants_whitelist = self::$default_constants_whitelist;
34
		$this->options_whitelist   = self::$default_options_whitelist;
35
		$this->callable_whitelist  = self::$default_callable_whitelist;
36
		$this->init();
37
	}
38
39
	private function init() {
40
		$handler = array( $this, 'action_handler' );
41
42
		// constants
43
		add_action( 'jetpack_sync_current_constants', $handler, 10 );
44
45
		// functions
46
		add_action( 'jetpack_sync_current_callables', $handler, 10 );
47
48
		// posts
49
		add_action( 'wp_insert_post', $handler, 10, 3 );
50
		add_action( 'deleted_post', $handler, 10 );
51
52
		// comments
53
		add_action( 'wp_insert_comment', $handler, 10, 2 );
54
		add_action( 'deleted_comment', $handler, 10 );
55
		add_action( 'trashed_comment', $handler, 10 );
56
		add_action( 'spammed_comment', $handler, 10 );
57
58
		// even though it's messy, we implement these hooks because 
59
		// the edit_comment hook doesn't include the data
60
		// so this saves us a DB read for every comment event
61
		foreach ( array( '', 'trackback', 'pingback' ) as $comment_type ) {
62
			foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
63
				add_action( "comment_{$comment_status}_{$comment_type}", $handler, 10, 2 );
64
			}
65
		}
66
67
		// options
68
		add_action( 'added_option', $handler, 10, 2 );
69
		add_action( 'updated_option', $handler, 10, 3 );
70
		add_action( 'deleted_option', $handler, 10, 1 );
71
72
		// themes
73
		add_action( 'jetpack_sync_current_theme_support', $handler, 10 ); // custom hook, see meta-hooks below
74
75
		// post-meta, and in the future - other meta?
76
		foreach ( $this->meta_types as $meta_type ) {
77
			// we need to make sure we don't commit before we receive these,
78
			// because they're invoked after meta changes are saved to the DB
79
			add_action( "added_{$meta_type}_meta", $handler, 99, 4 );
80
			add_action( "updated_{$meta_type}_meta", $handler, 99, 4 );
81
			add_action( "deleted_{$meta_type}_meta", $handler, 99, 4 );
82
		}
83
84
		/**
85
		 * Other hooks - fire synthetic hooks for all the properties we need to sync,
86
		 * e.g. when a theme changes
87
		 */
88
89
		// themes
90
		add_action( 'switch_theme', array( $this, 'switch_theme_handler' ) );
91
92
		add_action( 'set_site_transient_update_plugins', $handler, 10, 1 );
93
		add_action( 'set_site_transient_update_themes', $handler, 10, 1 );
94
		add_action( 'set_site_transient_update_core', $handler, 10, 1 );
95
96
	}
97
98
	function set_options_whitelist( $options ) {
99
		$this->options_whitelist = $options;
100
	}
101
102
	function set_constants_whitelist( $constants ) {
103
		$this->constants_whitelist = $constants;
104
	}
105
106
	function set_callable_whitelist( $functions ) {
107
		$this->callable_whitelist = $functions;
108
	}
109
110
	function is_whitelisted_option( $option ) {
111
		foreach ( $this->options_whitelist as $whitelisted_option ) {
112
			if ( $whitelisted_option[0] === '/' && preg_match( $whitelisted_option, $option ) ) {
113
				return true;
114
			} elseif ( $whitelisted_option === $option ) {
115
				return true;
116
			}
117
		}
118
119
		return false;
120
	}
121
122
	function set_codec( iJetpack_Sync_Codec $codec ) {
123
		$this->codec = $codec;
124
	}
125
126
	function set_sync_queue( $queue ) {
127
		$this->sync_queue = $queue;
128
	}
129
130
	function action_handler() {
131
		$current_filter = current_filter();
132
		$args           = func_get_args();
133
134
		if ( $current_filter === 'wp_insert_post' && $args[1]->post_type === 'revision' ) {
135
			return;
136
		}
137
138
		if ( in_array( $current_filter, array( 'deleted_option', 'added_option', 'updated_option' ) )
139
		     &&
140
		     ! $this->is_whitelisted_option( $args[0] )
141
		) {
142
			return;
143
		}
144
		Jetpack_Sync::schedule_sync();
145
		$this->sync_queue->add( array(
146
			$current_filter,
147
			$args
148
		) );
149
	}
150
151
	function switch_theme_handler() {
152
		global $_wp_theme_features;
153
154
		do_action( 'jetpack_sync_current_theme_support', $_wp_theme_features );
155
	}
156
157
	function do_sync() {
158
		$this->maybe_sync_constants();
159
160
		$this->maybe_sync_callables();
161
162
		// TODO: only send buffer once, then do the rest in a cron job
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...
163
		$iters = 0;
164
		while ( ( $buffer = $this->sync_queue->checkout() ) && $iters < 100 ) {
165
166
			if ( ! $buffer ) {
167
				// buffer has no items
168
				return;
169
			}
170
171
			if ( is_wp_error( $buffer ) ) {
172
				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...
173
174
				return;
175
			}
176
177
			$data = $this->codec->encode( $buffer->get_items() );
178
179
			/**
180
			 * Fires when data is ready to send to the server.
181
			 * Return false or WP_Error to abort the sync (e.g. if there's an error)
182
			 * The items will be automatically re-sent later
183
			 *
184
			 * @since 4.1
185
			 *
186
			 * @param array $data The action buffer
187
			 */
188
			$result = apply_filters( 'jetpack_sync_client_send_data', $data );
189
190
			if ( ! $result || is_wp_error( $result ) ) {
191
				$this->sync_queue->checkin( $buffer );
192
			} else {
193
				$this->sync_queue->close( $buffer );
194
			}
195
			$iters += 1;
196
		}
197
	}
198
199 View Code Duplication
	private function maybe_sync_constants() {
200
		$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...
201
		if ( empty( $constants ) ) {
202
			return;
203
		}
204
		$constants_check_sum = $this->get_check_sum( $constants );
205
		if ( $constants_check_sum !== get_option( self::$constants_checksum_option_name ) ) {
206
			do_action( 'jetpack_sync_current_constants', $constants );
207
			update_option( self::$constants_checksum_option_name, $constants_check_sum );
208
		}
209
	}
210
211
	private function get_all_constants() {
212
		return array_combine(
213
			$this->constants_whitelist,
214
			array_map( array( $this, 'get_constant' ), $this->constants_whitelist )
215
		);
216
	}
217
218
	private function get_constant( $constant ) {
219
		if ( defined( $constant ) ) {
220
			return constant( $constant );
221
		}
222
223
		return null;
224
	}
225
226 View Code Duplication
	private function maybe_sync_callables() {
227
		$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...
228
		if ( empty( $callables ) ) {
229
			return;
230
		}
231
		$callables_check_sum = $this->get_check_sum( $callables );
232
233
		if ( $callables_check_sum !== get_option( self::$functions_checksum_option_name ) ) {
234
			do_action( 'jetpack_sync_current_callables', $callables  );
235
			update_option( self::$functions_checksum_option_name, $callables_check_sum );
236
		}
237
	}
238
239
	private function get_all_callables() {
240
		return array_combine(
241
			$this->callable_whitelist,
242
			array_map( array( $this, 'get_callable' ), $this->callable_whitelist )
243
		);
244
	}
245
	
246
	private function get_callable( $callable ) {
247
		return call_user_func( $callable );
248
	}
249
250
	private function get_check_sum( $values ) {
251
		return crc32( serialize( $values ) );
252
	}
253
254
	function get_actions() {
255
		// 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...
256
		return $this->sync_queue->flush_all();
257
	}
258
259
	function get_all_actions() {
260
		return $this->sync_queue->get_all();
261
	}
262
263
	function reset_state() {
264
		$this->codec               = new Jetpack_Sync_Deflate_Codec();
265
		$this->constants_whitelist = self::$default_constants_whitelist;
266
		$this->options_whitelist   = self::$default_options_whitelist;
267
		delete_option( self::$constants_checksum_option_name );
268
		$this->sync_queue->reset();
269
	}
270
271
272
}
273