Completed
Push — update/gitignore ( 7f3e2e...30ec70 )
by
unknown
497:33 queued 488:24
created

Jetpack_Sync_Full::set_status_queuing_started()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
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
require_once 'class.jetpack-sync-wp-replicastore.php';
16
17
class Jetpack_Sync_Full {
18
	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...
19
	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...
20
	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...
21
	static $modules = array(
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...
22
		'wp_version',
23
		'constants',
24
		'functions',
25
		'options',
26
		'posts',
27
		'comments',
28
		'themes',
29
		'updates',
30
		'users',
31
		'terms',
32
		'network_options',
33
	);
34
35
	// singleton functions
36
	private static $instance;
37
	private $client;
38
39
	public static function getInstance() {
40
		if ( null === self::$instance ) {
41
			self::$instance = new self();
42
		}
43
44
		return self::$instance;
45
	}
46
47
	protected function __construct() {
48
		$this->init();
49
	}
50
51
	function init() {
52
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_post_ids' ) );
53
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_comments', array( $this, 'expand_comment_ids' ) );
54
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_options', array( $this, 'expand_options' ) );
55
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
56
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_network_options', array(
57
			$this,
58
			'expand_network_options'
59
		) );
60
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_terms', array( $this, 'expand_term_ids' ) );
61
	}
62
63
	function start() {
64
		do_action( 'jetpack_full_sync_start' );
65
66
		$this->set_status_queuing_started();
67
68
		$this->enqueue_wp_version();
69
		$this->enqueue_all_constants();
70
		$this->enqueue_all_functions();
71
		$this->enqueue_all_options();
72
73
		if ( is_multisite() ) {
74
			$this->enqueue_all_network_options();
75
		}
76
77
		$this->enqueue_all_terms();
78
		$this->enqueue_all_theme_info();
79
		$this->enqueue_all_users();
80
		$this->enqueue_all_posts();
81
		$this->enqueue_all_comments();
82
		$this->enqueue_all_updates();
83
84
		$this->set_status_queuing_finished();
85
86
		$store = new Jetpack_Sync_WP_Replicastore();
87
		do_action( 'jetpack_full_sync_end', $store->checksum_all() );
88
	}
89
90
	private function get_client() {
91
		if ( ! $this->client ) {
92
			$this->client = Jetpack_Sync_Client::getInstance();
93
		}
94
95
		return $this->client;
96
	}
97
98
	private function enqueue_wp_version() {
99
		$this->set_status( 'wp_version', 0 );
100
		global $wp_version;
101
		do_action( 'jetpack_sync_wp_version', $wp_version );
102
		$this->set_status( 'wp_version', 100 );
103
	}
104
105
	private function enqueue_all_constants() {
106
		$this->set_status( 'constants', 0 );
107
		$this->get_client()->force_sync_constants();
108
		$this->set_status( 'constants', 100 );
109
	}
110
111
	private function enqueue_all_functions() {
112
		$this->set_status( 'functions', 0 );
113
		$this->get_client()->force_sync_callables();
114
		$this->set_status( 'functions', 100 );
115
	}
116
117
	private function enqueue_all_options() {
118
		$this->set_status( 'options', 0 );
119
		$this->get_client()->force_sync_options();
120
		$this->set_status( 'options', 100 );
121
	}
122
123
	private function enqueue_all_network_options() {
124
		$this->set_status( 'network_options', 0 );
125
		$this->get_client()->force_sync_network_options();
126
		$this->set_status( 'network_options', 100 );
127
	}
128
129
	private function enqueue_all_terms() {
130
		$this->set_status( 'terms', 0 );
131
		global $wpdb;
132
133
		$taxonomies = get_taxonomies();
134
135
		$taxonomy_counter = 0;
136
		$total_count      = count( $taxonomies );
137
138
		foreach ( $taxonomies as $taxonomy ) {
139
140
			// I hope this is never bigger than RAM...
141
			$term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s", $taxonomy ) ); // Should we set a limit here?
142
			// Request posts in groups of N for efficiency
143
			$chunked_term_ids = array_chunk( $term_ids, self::$array_chunk_size );
144
145
			$total_chunks  = count( $chunked_term_ids );
146
			$chunk_counter = 0;
147
			// Send each chunk as an array of objects
148
			foreach ( $chunked_term_ids as $chunk ) {
149
				$this->set_status( 'terms', ( ( $taxonomy_counter / $total_count ) + ( ( $chunk_counter / $total_chunks ) / $total_count ) ) * 100 );
150
				do_action( 'jetpack_full_sync_terms', $chunk, $taxonomy );
151
				$chunk_counter ++;
152
			}
153
			$taxonomy_counter ++;
154
		}
155
		$this->set_status( 'terms', 100 );
156
	}
157
158 View Code Duplication
	private function enqueue_all_posts() {
159
		$this->set_status( 'posts', 0 );
160
		global $wpdb;
161
162
		// I hope this is never bigger than RAM...
163
		$post_type_sql = Jetpack_Sync_Defaults::get_blacklisted_post_types_sql();
164
		$post_ids      = $wpdb->get_col( "SELECT id FROM $wpdb->posts WHERE $post_type_sql" ); // Should we set a limit here?
165
166
		// Request posts in groups of N for efficiency
167
		$chunked_post_ids = array_chunk( $post_ids, self::$array_chunk_size );
168
169
		$counter = 0;
170
		$total   = count( $chunked_post_ids );
171
172
		// Send each chunk as an array of objects
173
		foreach ( $chunked_post_ids as $chunk ) {
174
			$this->set_status( 'posts', ( $counter / $total ) * 100 );
175
			do_action( 'jetpack_full_sync_posts', $chunk );
176
			$counter += 1;
177
		}
178
179
		$this->set_status( 'posts', 100 );
180
	}
181
182
	public function expand_post_ids( $args ) {
183
		$post_ids = $args[0];
184
185
		$posts = array_map( array( 'WP_Post', 'get_instance' ), $post_ids );
186
		$posts = array_map( array( $this->get_client(), 'filter_post_content_and_add_links' ), $posts );
187
188
		return array(
189
			'posts'      => $posts,
190
			'post_metas' => $this->get_metadata( $post_ids, 'post' ),
191
			'terms'      => $this->get_term_relationships( $post_ids )
192
		);
193
	}
194
195 View Code Duplication
	private function enqueue_all_comments() {
196
		$this->set_status( 'comments', 0 );
197
198
		global $wpdb;
199
200
		$comment_ids         = $wpdb->get_col( "SELECT comment_id FROM $wpdb->comments" ); // Should we set a limit here?
201
		$chunked_comment_ids = array_chunk( $comment_ids, self::$array_chunk_size );
202
203
		$counter = 0;
204
		$total   = count( $chunked_comment_ids );
205
206
		foreach ( $chunked_comment_ids as $chunk ) {
207
			$this->set_status( 'comments', ( $counter / $total ) * 100 );
208
			do_action( 'jetpack_full_sync_comments', $chunk );
209
			$counter += 1;
210
		}
211
212
		$this->set_status( 'comments', 100 );
213
	}
214
215
	public function expand_comment_ids( $args ) {
216
		$comment_ids = $args[0];
217
		$comments    = get_comments( array(
218
			'include_unapproved' => true,
219
			'comment__in'        => $comment_ids,
220
		) );
221
222
		return array(
223
			'comments'      => $comments,
224
			'comment_metas' => $this->get_metadata( $comment_ids, 'comment' ),
225
		);
226
	}
227
228
	public function expand_term_ids( $args ) {
229
		global $wp_version;
230
		$term_ids = $args[0];
231
		$taxonomy = $args[1];
232
		// version 4.5 or higher
233
		if ( version_compare( $wp_version, 4.5, '>=' ) ) {
234
			$terms = get_terms( array(
235
				'taxonomy'   => $taxonomy,
236
				'hide_empty' => false,
237
				'include'    => $term_ids
238
			) );
239
		} else {
240
			$terms = get_terms( $taxonomy, array(
241
				'hide_empty' => false,
242
				'include'    => $term_ids
243
			) );
244
		}
245
246
		return $terms;
247
	}
248
249
	public function expand_options( $args ) {
250
		if ( $args[0] ) {
251
			return $this->get_client()->get_all_options();
252
		}
253
254
		return $args;
255
	}
256
257
	private function enqueue_all_users() {
258
		$this->set_status( 'users', 0 );
259
260
		$user_ids          = get_users( array( 'fields' => 'ID' ) );
261
		$chunked_users_ids = array_chunk( $user_ids, self::$array_chunk_size );
262
263
		$counter = 0;
264
		$total   = count( $chunked_users_ids );
265
266
		foreach ( $chunked_users_ids as $chunk ) {
267
			$this->set_status( 'users', ( $counter / $total ) * 100 );
268
			do_action( 'jetpack_full_sync_users', $chunk );
269
			$counter += 1;
270
		}
271
272
		$this->set_status( 'users', 100 );
273
	}
274
275
	public function expand_users( $args ) {
276
		$user_ids = $args[0];
277
278
		return array_map( array( $this->get_client(), 'sanitize_user' ), get_users( array( 'include' => $user_ids ) ) );
279
	}
280
281
	public function expand_network_options( $args ) {
282
		if ( $args[0] ) {
283
			return $this->get_client()->get_all_network_options();
284
		}
285
286
		return $args;
287
	}
288
289
	private function get_metadata( $ids, $meta_type ) {
290
		global $wpdb;
291
		$table = _get_meta_table( $meta_type );
292
		$id    = $meta_type . '_id';
293
		if ( ! $table ) {
294
			return array();
295
		}
296
297
		return $wpdb->get_results( "SELECT * FROM $table WHERE $id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . " )", OBJECT );
298
	}
299
300
	private function get_term_relationships( $ids ) {
301
		global $wpdb;
302
303
		return $wpdb->get_results( "SELECT * FROM $wpdb->term_relationships WHERE object_id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . " )", OBJECT );
304
	}
305
306
	// 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...
307
	private function enqueue_all_theme_info() {
308
		$this->set_status( 'themes', 0 );
309
		$this->get_client()->send_theme_info();
310
		$this->set_status( 'themes', 100 );
311
	}
312
313
	private function enqueue_all_updates() {
314
		$this->set_status( 'updates', 0 );
315
		// check for updates
316
		wp_update_plugins();
317
		wp_update_themes();
318
		_maybe_update_core();
319
		$this->set_status( 'updates', 100 );
320
	}
321
322
	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...
323
		set_transient( self::$status_transient_name . '_' . $name,
324
			array(
325
				'progress' => $percent,
326
				// 'count' => $count, 
327
				// 'total' => $total 
328
			),
329
			self::$transient_timeout
330
		);
331
	}
332
333
	private function set_status_queuing_started() {
334
		set_transient( self::$status_transient_name, array( 'phase' => 'queuing started' ), self::$transient_timeout );
335
	}
336
337
	private function set_status_queuing_finished() {
338
		set_transient( self::$status_transient_name, array( 'phase' => 'queuing finished' ), self::$transient_timeout );
339
	}
340
341
	// these are called by the Sync Client when it sees that the full sync start/end actions have actually been transmitted
342
	public function set_status_sending_started() {
343
		do_action( 'jetpack_full_sync_start_sent' );
344
		set_transient( self::$status_transient_name, array( 'phase' => 'sending started' ), self::$transient_timeout );
345
	}
346
347
	public function set_status_sending_finished() {
348
		do_action( 'jetpack_full_sync_end_sent' );
349
		set_transient( self::$status_transient_name, array( 'phase' => 'sending finished' ), self::$transient_timeout );
350
	}
351
352
	public function get_status() {
353
		$status = get_transient( self::$status_transient_name );
354
		if ( ! is_array( $status ) ) {
355
			return array( 'phase' => 'not started' );
356
		}
357
358
		return $status;
359
	}
360
361
	public function get_module_status( $module ) {
362
		return get_transient( self::$status_transient_name . '_' . $module );
363
	}
364
365
	public function get_complete_status() {
366
		return array_merge(
367
			$this->get_status(),
368
			array_combine(
369
				Jetpack_Sync_Full::$modules,
370
				array_map( array( $this, 'get_module_status' ), Jetpack_Sync_Full::$modules )
371
			)
372
		);
373
	}
374
}
375