Completed
Push — add/sync-rest-2 ( 26bda8...bb873d )
by
unknown
72:56 queued 63:47
created

Jetpack_Sync_Full::init()   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
3
/**
4
 * This class does a full resync of the database by 
5
 * enqueuing an outbound action for every single object
6
 * that we care about.
7
 * 
8
 * This class contains a few non-obvious optimisations that should be explained:
9
 * - we fire an action called jetpack_full_sync_start so that WPCOM can erase the contents of the cached database
10
 * - for each object type, we obtain a full list of object IDs to sync via a single API call (hoping that since they're ints, they can all fit in RAM)
11
 * - we load the full objects for those IDs in chunks of Jetpack_Sync_Full::$array_chunk_size (to reduce the number of MySQL calls)
12
 * - we fire a trigger for the entire array which the Jetpack_Sync_Client then serializes and queues.
13
 */
14
15
class Jetpack_Sync_Full {
16
	static $array_chunk_size = 5;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $array_chunk_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...
17
	static $status_transient_name = "jetpack_full_sync_progress";
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $status_transient_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...
18
	static $transient_timeout = 3600; // an hour
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $transient_timeout.

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...
19
	static $modules = array( 'wp_version', 'constants', 'functions', 'options', 'posts', 'comments', 'themes', 'updates' ); 
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $modules.

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...
20
21
	// singleton functions
22
	private static $instance;
23
24
	public static function getInstance() {
25
		if ( null === self::$instance ) {
26
			self::$instance = new self();
27
		}
28
29
		return self::$instance;
30
	}
31
32
	protected function __construct() {
33
		$this->init();
34
	}
35
36
	function init() {
37
		add_filter( "jetack_sync_before_send_jetpack_full_sync_posts", array( $this, 'expand_post_ids' ) );
38
		add_filter( "jetack_sync_before_send_jetpack_full_sync_comments", array( $this, 'expand_comment_ids' ) );
39
	}
40
41
	function start() {
42
		$this->client = Jetpack_Sync_Client::getInstance();
0 ignored issues
show
Bug introduced by
The property client 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...
43
		do_action( 'jetpack_full_sync_start' );
44
45
		$this->set_status_queuing_started();
46
47
		$this->enqueue_wp_version();
48
		$this->enqueue_all_constants();
49
		$this->enqueue_all_functions();
50
		$this->enqueue_all_options();
51
		$this->enqueue_all_theme_info();
52
		$this->enqueue_all_posts();
53
		$this->enqueue_all_comments();
54
		$this->enqueue_all_updates();
55
56
		$this->set_status_queuing_finished();
57
58
		do_action( 'jetpack_full_sync_end' );
59
	}
60
61
	private function enqueue_wp_version() {
62
		$this->set_status("wp_version", 0);
63
		global $wp_version;
64
		do_action( 'jetpack_sync_wp_version', $wp_version );
65
		$this->set_status("wp_version", 100);
66
	}
67
68
	private function enqueue_all_constants() {
69
		$this->set_status("constants", 0);
70
		$this->client->force_sync_constants();
71
		$this->set_status("constants", 100);
72
	}
73
74
	private function enqueue_all_functions() {
75
		$this->set_status("functions", 0);
76
		$this->client->force_sync_callables();
77
		$this->set_status("functions", 100);
78
	}
79
80
	private function enqueue_all_options() {
81
		$this->set_status("options", 0);
82
		global $wpdb;
83
84
		// Unfortunately, since our options whitelist includes regexes,
85
		// we need to load all option names and match them against the whitelist.
86
		// This could be pretty awful if we have huge queues, but it's the only way to 
87
		// be sure we're syncing everything that's whitelisted.
88
89
		// As per posts and comments, we do this in ID batches and hope the IDs *AND* names don't exceed RAM
90
91
		// In theory, MySQL has regex support. In practice, I wouldn't want to rely on it being compatible
92
		// with PHP's regexes.
93
94
		// Alternatively, rather than option regexes, we could use wildcards in the option and then just
95
		// use "LIKE" queries here, replacing * with %?
96
97
		$option_names = $wpdb->get_col( "SELECT option_name FROM $wpdb->options" );
98
99
		// filter by client option whitelist
100
		$option_names = array_filter( $option_names, array( $this->client, 'is_whitelisted_option' ) );
101
102
		$counter = 0;
103
		$total = count( $option_names );
104
105
		foreach ( $option_names as $option_name ) {
106
			$this->set_status( "options", ( $counter / $total ) * 100 );
107
			do_action( 'jetpack_full_sync_option', $option_name, get_option( $option_name ) );
108
			$counter += 1;
109
		}
110
111
		$this->set_status("options", 100);
112
	}
113
114 View Code Duplication
	private function enqueue_all_posts() {
115
		$this->set_status("posts", 0);
116
		global $wpdb;
117
118
		// I hope this is never bigger than RAM...
119
		$post_ids = $wpdb->get_col( "SELECT id FROM $wpdb->posts");
120
121
		// Request posts in groups of N for efficiency
122
		$chunked_post_ids = array_chunk( $post_ids, self::$array_chunk_size );
123
124
		$counter = 0;
125
		$total = count( $chunked_post_ids );
126
127
		// Send each chunk as an array of objects
128
		foreach ( $chunked_post_ids as $chunk ) {
129
			$this->set_status( "posts", ( $counter / $total ) * 100 );
130
			do_action( 'jetpack_full_sync_posts', $chunk );
131
			$counter += 1;
132
		}
133
134
		$this->set_status("posts", 100);
135
	}
136
137
	public function expand_post_ids( $args ) {
138
		$post_ids = $args[0];
139
		global $wpdb;
140
141
		$posts = get_posts( array(
142
	 		'include'          => $post_ids,
143
	 		'post_type'        => 'any',
144
	 		'post_status'      => 'any',
145
	 		'suppress_filters' => true ) );
146
147
		return array(
148
			'posts' => $posts,
149
			'postmetas' => $this->get_metadata( $post_ids, 'post' ),
150
		);
151
	}
152
153 View Code Duplication
	private function enqueue_all_comments() {
154
		$this->set_status("comments", 0);
155
156
		global $wpdb;
157
158
		$comment_ids = $wpdb->get_col( "SELECT comment_id FROM $wpdb->comments");
159
		$chunked_comment_ids = array_chunk( $comment_ids, self::$array_chunk_size );
160
161
		$counter = 0;
162
		$total = count( $chunked_comment_ids );
163
164
		foreach ( $chunked_comment_ids as $chunk ) {
165
			$this->set_status( "comments", ( $counter / $total ) * 100 );
166
			do_action( 'jetpack_full_sync_comments', $chunk);
167
			$counter += 1;
168
		}
169
170
		$this->set_status("comments", 100);
171
	}
172
173
	public function expand_comment_ids( $args ) {
174
		$comment_ids = $args[0];
175
		$comments = get_comments( array(
176
	 		'include_unapproved' => true,
177
	 		'comment__in' => $comment_ids,
178
 		) );
179
180
		return array(
181
			'comments' => $comments,
182
			'comment_metas' => $this->get_metadata( $comment_ids, 'comment' ),
183
	);
184
185
		return array(
0 ignored issues
show
Unused Code introduced by
return array('posts' => ...tmetas' => $postmetas); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
186
			'posts' => $posts,
187
			'commentmetas' => $postmetas
188
		);
189
	}
190
191
	private function get_metadata( $ids, $meta_type ) {
192
		global $wpdb;
193
		$table = _get_meta_table( $meta_type );
194
		$id = $meta_type . '_id';
195
		if ( ! $table ) {
196
			return array();
197
		}
198
		return $wpdb->get_results( "SELECT * FROM $table WHERE $id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . " )", OBJECT );
199
	}
200
201
	// TODO:
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
202
	private function enqueue_all_theme_info() {
203
		$this->set_status("themes", 0);
204
		$this->client->send_theme_info();
205
		$this->set_status("themes", 100);
206
	}
207
208
	private function enqueue_all_updates() {
209
		$this->set_status("updates", 0);
210
		// check for updates
211
		wp_update_plugins();
212
		wp_update_themes();
213
		_maybe_update_core();
214
		$this->set_status("updates", 100);
215
	}
216
	
217
	private function set_status( $name, $percent, $count = 1, $total =1 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $count 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...
Unused Code introduced by
The parameter $total 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...
218
		set_transient( self::$status_transient_name.'_'.$name, 
219
			array( 
220
				'progress' => $percent, 
221
				// 'count' => $count, 
222
				// 'total' => $total 
223
			),
224
			self::$transient_timeout
225
		);
226
	}
227
228
	private function set_status_queuing_started() {
229
		set_transient( self::$status_transient_name, array( 'phase' => 'queuing started' ), self::$transient_timeout );
230
	}
231
232
	private function set_status_queuing_finished() {
233
		set_transient( self::$status_transient_name, array( 'phase' => 'queuing finished' ), self::$transient_timeout );
234
	}
235
236
	// these are called by the Sync Client when it sees that the full sync start/end actions have actually been transmitted
237
	public function set_status_sending_started() {
238
		set_transient( self::$status_transient_name, array( 'phase' => 'sending started' ), self::$transient_timeout );
239
	}
240
241
	public function set_status_sending_finished() {
242
		set_transient( self::$status_transient_name, array( 'phase' => 'sending finished' ), self::$transient_timeout );
243
	}
244
245
	public function get_status() {
246
		return get_transient( self::$status_transient_name );
247
	}
248
249
	public function get_module_status( $module ) {
250
		return get_transient( self::$status_transient_name.'_'.$module );
251
	}
252
253
	public function get_complete_status() {
254
		return array_merge( 
255
			$this->get_status(),
256
			array_combine( 
257
				Jetpack_Sync_Full::$modules, 
258
				array_map( array( $this, 'get_module_status' ), Jetpack_Sync_Full::$modules )
259
			)
260
		);
261
	}
262
}