Completed
Push — add/sync-action ( faa3ed...fff2f2 )
by
unknown
09:59
created

Jetpack_Sync_WP_Replicastore   F

Complexity

Total Complexity 82

Size/Duplication

Total Lines 502
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 502
rs 1.5789
wmc 82
lcom 0
cbo 0

51 Methods

Rating   Name   Duplication   Size   Complexity  
A set_wp_version() 0 3 1
A get_wp_version() 0 4 1
A reset() 0 21 2
A full_sync_start() 0 3 1
A full_sync_end() 0 3 1
A post_count() 0 3 1
A get_posts() 0 11 2
A get_post() 0 3 1
B upsert_post() 0 60 4
A delete_post() 0 3 1
A posts_checksum() 0 11 1
A comment_count() 0 14 2
B comment_status_to_approval_value() 0 18 7
A get_comments() 0 9 2
A get_comment() 0 3 1
B upsert_comment() 0 50 5
A trash_comment() 0 3 1
A delete_comment() 0 3 1
A spam_comment() 0 3 1
A comments_checksum() 0 9 1
A update_option() 0 3 1
A get_option() 0 3 1
A delete_option() 0 3 1
A set_theme_support() 0 3 1
A current_theme_supports() 0 3 1
A get_metadata() 0 3 1
A upsert_metadata() 0 3 1
A delete_metadata() 0 4 1
A get_constant() 0 9 2
A set_constants() 0 9 2
A get_updates() 0 9 2
A set_updates() 0 5 1
A get_callable() 0 9 2
A set_callables() 0 5 2
A get_site_option() 0 3 1
A update_site_option() 0 3 1
A delete_site_option() 0 3 1
A get_terms() 0 3 1
A get_term() 0 8 3
A ensure_taxonomy() 0 19 3
A get_the_terms() 0 3 1
B update_term() 0 33 2
A delete_term() 0 3 1
A update_object_terms() 0 3 1
B delete_object_terms() 0 40 5
A user_count() 0 3 1
A get_user() 0 3 1
A upsert_user() 0 3 1
A delete_user() 0 3 1
A checksum_all() 0 6 1
A invalid_call() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Jetpack_Sync_WP_Replicastore often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Sync_WP_Replicastore, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
require_once 'interface.jetpack-sync-replicastore.php';
4
5
/**
6
 * An implementation of iJetpack_Sync_Replicastore which returns data stored in a WordPress.org DB.
7
 * This is useful to compare values in the local WP DB to values in the synced replica store
8
 */
9
class Jetpack_Sync_WP_Replicastore implements iJetpack_Sync_Replicastore {
10
	public function set_wp_version( $version ) {
11
		// makes no sense here?
12
	}
13
	public function get_wp_version() {
14
		global $wp_version;
15
		return $wp_version;
16
	}
17
18
	public function reset() {
19
		global $wpdb;
20
21
		$wpdb->query( "DELETE FROM $wpdb->posts" );
22
		$wpdb->query( "DELETE FROM $wpdb->comments" );
23
24
		// also need to delete terms from cache
25
		$term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
26
		foreach( $term_ids as $term_id ) {
27
			wp_cache_delete( $term_id, 'terms' );
28
		}
29
		
30
		$wpdb->query( "DELETE FROM $wpdb->terms" );
31
32
		$wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
33
		$wpdb->query( "DELETE FROM $wpdb->term_relationships" );
34
35
		// callables and constants
36
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
37
		$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '_%'" ); //TODO: delete by special prefix?
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...
38
	}
39
40
	function full_sync_start() {
41
		$this->reset();
42
	}
43
	
44
	function full_sync_end() {
45
		// noop right now
46
	}
47
	
48
	public function post_count( $status = null ) {
49
		return count( $this->get_posts( $status ) );
50
	}
51
52
	public function get_posts( $status = null ) {
53
		$args = array( 'orderby' => 'ID' );
54
55
		if ( $status ) {
56
			$args['post_status'] = $status;
57
		} else {
58
			$args['post_status'] = 'any';
59
		}
60
61
		return get_posts( $args );
62
	}
63
64
	public function get_post( $id ) {
65
		return get_post( $id );
66
	}
67
68
	public function upsert_post( $post ) {
69
		global $blog_id, $wpdb;
70
71
		// reject the post if it's not a WP_Post
72
		if ( ! $post instanceof WP_Post ) {
0 ignored issues
show
Bug introduced by
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
73
			return;
74
		}
75
76
		$post = $post->to_array();
77
78
		// reject posts without an ID
79
		if ( ! isset( $post['ID'] ) ) {
80
			return;
81
		}
82
83
		$now     = current_time( 'mysql' );
84
		$now_gmt = get_gmt_from_date( $now );
85
86
		$defaults = array(
87
			'ID'                    => 0,
88
			'post_author'           => '0',
89
			'post_content'          => '',
90
			'post_content_filtered' => '',
91
			'post_title'            => '',
92
			'post_name'             => '',
93
			'post_excerpt'          => '',
94
			'post_status'           => 'draft',
95
			'post_type'             => 'post',
96
			'comment_status'        => '',
97
			'comment_status'        => 'closed',
98
			'comment_count'         => '0',
99
			'ping_status'           => '',
100
			'post_password'         => '',
101
			'to_ping'               => '',
102
			'pinged'                => '',
103
			'post_parent'           => 0,
104
			'menu_order'            => 0,
105
			'guid'                  => '',
106
			'post_date'             => $now,
107
			'post_date_gmt'         => $now_gmt,
108
			'post_modified'         => $now,
109
			'post_modified_gmt'     => $now_gmt,
110
		);
111
112
		$post = array_intersect_key( $post, $defaults );
113
114
		$post = sanitize_post( $post, 'db' );
115
116
		unset( $post['filter'] );
117
118
		$exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
119
120
		if ( $exists ) {
121
			$affected_rows = $wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
0 ignored issues
show
Unused Code introduced by
$affected_rows is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
122
		} else {
123
			$affected_rows = $wpdb->insert( $wpdb->posts, $post );
0 ignored issues
show
Unused Code introduced by
$affected_rows is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
124
		}
125
126
		clean_post_cache( $post['ID'] );
127
	}
128
129
	public function delete_post( $post_id ) {
130
		wp_delete_post( $post_id, true );
131
	}
132
133
	public function posts_checksum() {
134
		global $wpdb;
135
136
		$query = <<<ENDSQL
137
			SELECT CONV(BIT_XOR(CRC32(CONCAT(ID,post_modified))), 10, 16) 
138
				FROM $wpdb->posts
139
				WHERE post_type <> 'revision'
140
ENDSQL;
141
142
		return $wpdb->get_var($query);
143
	}
144
145
	public function comment_count( $status = null ) {
146
		global $wpdb;
147
148
		$comment_approved = $this->comment_status_to_approval_value( $status );
149
150
		if ( $comment_approved !== false ) {
151
			return $wpdb->get_var( $wpdb->prepare(
152
				"SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = %s",
153
				$comment_approved
154
			) );
155
		} else {
156
			return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments" );
157
		}
158
	}
159
160
	private function comment_status_to_approval_value( $status ) {
161
		switch ( $status ) {
162
			case 'approve':
163
				return "1";
164
			case 'hold':
165
				return "0";
166
			case 'spam':
167
				return 'spam';
168
			case 'trash':
169
				return 'trash';
170
			case 'any':
171
				return false;
172
			case 'all':
173
				return false;
174
			default:
175
				return false;
176
		}
177
	}
178
179
	public function get_comments( $status = null ) {
180
		$args = array( 'orderby' => 'ID', 'status' => 'all' );
181
182
		if ( $status ) {
183
			$args['status'] = $status;
184
		}
185
186
		return get_comments( $args );
187
	}
188
189
	public function get_comment( $id ) {
190
		return WP_Comment::get_instance( $id );
191
	}
192
193
	public function upsert_comment( $comment ) {
194
		global $wpdb, $wp_version;
195
196
		if ( version_compare( $wp_version, '4.4', '<' ) ) {
197
			$comment = (array) $comment;
198
		} else {
199
			// WP 4.4 introduced the WP_Comment Class
200
			$comment = $comment->to_array();
201
		}
202
		
203
		// filter by fields on comment table
204
		$comment_fields_whitelist = array(
205
			'comment_ID',
206
			'comment_post_ID',
207
			'comment_author',
208
			'comment_author_email',
209
			'comment_author_url',
210
			'comment_author_IP',
211
			'comment_date',
212
			'comment_date_gmt',
213
			'comment_content',
214
			'comment_karma',
215
			'comment_approved',
216
			'comment_agent',
217
			'comment_type',
218
			'comment_parent',
219
			'user_id'
220
		);
221
222
		foreach ( $comment as $key => $value ) {
223
			if ( ! in_array( $key, $comment_fields_whitelist ) ) {
224
				unset( $comment[ $key ] );
225
			}
226
		}
227
228
		$exists = $wpdb->get_var(
229
			$wpdb->prepare(
230
				"SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
231
				$comment['comment_ID']
232
			)
233
		);
234
235
		if ( $exists ) {
236
			$wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
237
		} else {
238
			$wpdb->insert( $wpdb->comments, $comment );
239
		}
240
241
		wp_update_comment_count( $comment['comment_post_ID'] );
242
	}
243
244
	public function trash_comment( $comment_id ) {
245
		wp_delete_comment( $comment_id );
246
	}
247
248
	public function delete_comment( $comment_id ) {
249
		wp_delete_comment( $comment_id, true );
250
	}
251
252
	public function spam_comment( $comment_id ) {
253
		wp_spam_comment( $comment_id );
254
	}
255
256
	public function comments_checksum() {
257
		global $wpdb;
258
259
		$query = <<<ENDSQL
260
			SELECT CONV(BIT_XOR(CRC32(CONCAT(comment_ID,comment_content))), 10, 16) FROM $wpdb->comments
261
ENDSQL;
262
263
		return $wpdb->get_var($query);
264
	}
265
266
	public function update_option( $option, $value ) {
267
		return update_option( $option, $value );
268
	}
269
270
	public function get_option( $option ) {
271
		return get_option( $option );
272
	}
273
274
	public function delete_option( $option ) {
275
		return delete_option( $option );
276
	}
277
278
	public function set_theme_support( $theme_support ) {
279
		// noop
280
	}
281
282
	public function current_theme_supports( $feature ) {
283
		return current_theme_supports( $feature );
284
	}
285
286
	// meta
287
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
288
		return get_metadata( $type, $object_id, $meta_key, $single );
289
	}
290
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
291
		// add_metadata( $type, $object_id, $meta_key, $value, $unique );
292
	}
293
	public function delete_metadata( $type, $object_id, $meta_ids ) {
294
		// TODO: SQL delete
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...
295
		// delete_metadata( $meta_type, $object_id, $meta_key, $meta_value, $delete_all );
296
	}
297
298
	// constants
299
	public function get_constant( $constant ) {
300
		$value = get_option( 'jetpack_constant_' . $constant );
301
302
		if ( $value ) {
303
			return $value;
304
		}
305
306
		return null;
307
	}
308
	public function set_constants( $constants ) {
309
		global $wpdb;
310
311
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_constant_%'" );
312
313
		foreach ( $constants as $key => $value ) {
314
			update_option( 'jetpack_constant_' . $key, $value );
315
		}
316
	}
317
318
	public function get_updates( $type ) {
319
		$all_updates = get_option( 'jetpack_updates', array() );
320
321
		if ( isset( $all_updates[ $type ] ) ) {
322
			return $all_updates[ $type ];
323
		} else {
324
			return null;
325
		}
326
	}
327
328
	public function set_updates( $type, $updates ) {
329
		$all_updates          = get_option( 'jetpack_updates', array() );
330
		$all_updates[ $type ] = $updates;
331
		update_option( 'jetpack_updates', $all_updates );
332
	}
333
334
	// functions
335
	public function get_callable( $constant ) {
336
		$value = get_option( 'jetpack_' . $constant );
337
338
		if ( $value ) {
339
			return $value;
340
		}
341
342
		return null;
343
	}
344
	public function set_callables( $constants ) {
345
		foreach ( $constants as $key => $value ) {
346
			update_option( 'jetpack_' . $key, $value );
347
		}
348
	}
349
350
	// network options
351
	public function get_site_option( $option ) {
352
		return get_option( 'jetpack_network_' . $option );
353
	}
354
355
	public function update_site_option( $option, $value ) {
356
		return update_option( 'jetpack_network_' . $option, $value );
357
	}
358
359
	public function delete_site_option( $option ) {
360
		return delete_option( 'jetpack_network_' . $option );
361
	}
362
363
	// terms
364
	// terms
365
	public function get_terms( $taxonomy ) {
366
		return get_terms( $taxonomy );
367
	}
368
369
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
370
		$t = $this->ensure_taxonomy( $taxonomy );
371
		if ( ! $t || is_wp_error( $t ) ) {
372
			return $t;
373
		}
374
375
		return get_term( $term_id, $taxonomy );
376
	}
377
378
	private function ensure_taxonomy( $taxonomy ) {
379
		if ( ! taxonomy_exists( $taxonomy ) ) {
380
			// try re-registering synced taxonomies
381
			$taxonomies = $this->get_callable( 'taxonomies' );
382
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
383
				// doesn't exist, or somehow hasn't been synced
384
				return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
385
			}
386
			$t = $taxonomies[ $taxonomy ];
387
388
			return register_taxonomy(
389
				$taxonomy,
390
				$t->object_type,
391
				(array) $t
392
			);
393
		}
394
395
		return true;
396
	}
397
398
	public function get_the_terms( $object_id, $taxonomy ) {
399
		return get_the_terms( $object_id, $taxonomy );
400
	}
401
402
	public function update_term( $term_object ) {
403
		$taxonomy = $term_object->taxonomy;
404
		global $wpdb;
405
		$exists = $wpdb->get_var( $wpdb->prepare(
406
			"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
407
			$term_object->term_id
408
		) );
409
		if ( ! $exists ) {
410
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
411
			$term          = array(
412
				'term_id'    => $term_object->term_id,
413
				'name'       => $term_object->name,
414
				'slug'       => $term_object->slug,
415
				'term_group' => $term_object->term_group,
416
			);
417
			$term_taxonomy = array(
418
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
419
				'term_id'          => $term_object->term_id,
420
				'taxonomy'         => $term_object->taxonomy,
421
				'description'      => $term_object->description,
422
				'parent'           => (int) $term_object->parent,
423
				'count'            => (int) $term_object->count,
424
			);
425
			$wpdb->insert( $wpdb->terms, $term );
426
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
427
428
//			clean_term_cache( $term_object->term_id, $taxonomy );
429
430
			return true;
431
		}
432
433
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
434
	}
435
436
	public function delete_term( $term_id, $taxonomy ) {
437
		return wp_delete_term( $term_id, $taxonomy );
438
	}
439
440
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
441
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
442
	}
443
444
	public function delete_object_terms( $object_id, $tt_ids ) {
445
		global $wpdb;
446
447
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
448
			$taxonomies = array();
449
			foreach ( $tt_ids as $tt_id ) {
450
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
451
				$taxonomies[ $term->taxonomy ][] = $tt_id;
452
			}
453
			$in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'";
454
455
			/**
456
			 * Fires immediately before an object-term relationship is deleted.
457
			 *
458
			 * @since 2.9.0
459
			 *
460
			 * @param int $object_id Object ID.
461
			 * @param array $tt_ids An array of term taxonomy IDs.
462
			 */
463
			do_action( 'delete_term_relationships', $object_id, $tt_ids );
464
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
465
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
466
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
467
				/**
468
				 * Fires immediately after an object-term relationship is deleted.
469
				 *
470
				 * @since 2.9.0
471
				 *
472
				 * @param int $object_id Object ID.
473
				 * @param array $tt_ids An array of term taxonomy IDs.
474
				 */
475
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
476
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
477
			}
478
479
			return (bool) $deleted;
480
		}
481
482
		return false;
483
	}
484
485
	// users
486
	public function user_count() {
487
488
	}
489
	public function get_user( $user_id ) {
490
		return WP_User::get_instance( $user_id );
491
	}
492
	public function upsert_user( $user ) {
493
		return $this->invalid_call();
494
	}
495
	public function delete_user( $user_id ) {
496
		return $this->invalid_call();
497
	}
498
499
	public function checksum_all() {
500
		return array(
501
			'posts' => $this->posts_checksum(),
502
			'comments' => $this->comments_checksum()
503
		);
504
	}
505
506
	private function invalid_call() {
507
		$caller = debug_backtrace()[1]['function'];
508
		throw new Exception("This function $caller is not supported on the WP Replicastore");
509
	}
510
}
511