Completed
Push — add/pathinfo-sitemap ( eb9d33...02fba8 )
by Jeremy
14:23 queued 04:10
created

Jetpack_Sync_WP_Replicastore   F

Complexity

Total Complexity 85

Size/Duplication

Total Lines 557
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 557
rs 1.5789
wmc 85
lcom 0
cbo 1

51 Methods

Rating   Name   Duplication   Size   Complexity  
A set_wp_version() 0 3 1
A get_wp_version() 0 5 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 59 4
A delete_post() 0 3 1
A posts_checksum() 0 13 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_constant() 0 9 2
A set_updates() 0 5 1
A get_callable() 0 9 2
A set_callable() 0 3 1
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 5 1
A get_metadata() 0 3 1
A delete_metadata() 0 17 4
B upsert_metadata() 0 31 3
A set_constant() 0 3 1
A get_updates() 0 9 2

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
14
	public function get_wp_version() {
15
		global $wp_version;
16
17
		return $wp_version;
18
	}
19
20
	public function reset() {
21
		global $wpdb;
22
23
		$wpdb->query( "DELETE FROM $wpdb->posts" );
24
		$wpdb->query( "DELETE FROM $wpdb->comments" );
25
26
		// also need to delete terms from cache
27
		$term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
28
		foreach ( $term_ids as $term_id ) {
29
			wp_cache_delete( $term_id, 'terms' );
30
		}
31
32
		$wpdb->query( "DELETE FROM $wpdb->terms" );
33
34
		$wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
35
		$wpdb->query( "DELETE FROM $wpdb->term_relationships" );
36
37
		// callables and constants
38
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
39
		$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
40
	}
41
42
	function full_sync_start() {
43
		$this->reset();
44
	}
45
46
	function full_sync_end( $checksum ) {
47
		// noop right now
48
	}
49
50
	public function post_count( $status = null ) {
51
		return count( $this->get_posts( $status ) );
52
	}
53
54
	public function get_posts( $status = null ) {
55
		$args = array( 'orderby' => 'ID' );
56
57
		if ( $status ) {
58
			$args['post_status'] = $status;
59
		} else {
60
			$args['post_status'] = 'any';
61
		}
62
63
		return get_posts( $args );
64
	}
65
66
	public function get_post( $id ) {
67
		return get_post( $id );
68
	}
69
70
	public function upsert_post( $post, $silent = false ) {
71
		global $wpdb;
72
73
		// reject the post if it's not a WP_Post
74
		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...
75
			return;
76
		}
77
78
		$post = $post->to_array();
79
80
		// reject posts without an ID
81
		if ( ! isset( $post['ID'] ) ) {
82
			return;
83
		}
84
85
		$now     = current_time( 'mysql' );
86
		$now_gmt = get_gmt_from_date( $now );
87
88
		$defaults = array(
89
			'ID'                    => 0,
90
			'post_author'           => '0',
91
			'post_content'          => '',
92
			'post_content_filtered' => '',
93
			'post_title'            => '',
94
			'post_name'             => '',
95
			'post_excerpt'          => '',
96
			'post_status'           => 'draft',
97
			'post_type'             => 'post',
98
			'comment_status'        => 'closed',
99
			'comment_count'         => '0',
100
			'ping_status'           => '',
101
			'post_password'         => '',
102
			'to_ping'               => '',
103
			'pinged'                => '',
104
			'post_parent'           => 0,
105
			'menu_order'            => 0,
106
			'guid'                  => '',
107
			'post_date'             => $now,
108
			'post_date_gmt'         => $now_gmt,
109
			'post_modified'         => $now,
110
			'post_modified_gmt'     => $now_gmt,
111
		);
112
113
		$post = array_intersect_key( $post, $defaults );
114
115
		$post = sanitize_post( $post, 'db' );
116
117
		unset( $post['filter'] );
118
119
		$exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
120
121
		if ( $exists ) {
122
			$wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
123
		} else {
124
			$wpdb->insert( $wpdb->posts, $post );
125
		}
126
127
		clean_post_cache( $post['ID'] );
128
	}
129
130
	public function delete_post( $post_id ) {
131
		wp_delete_post( $post_id, true );
132
	}
133
134
	public function posts_checksum() {
135
		global $wpdb;
136
137
		$post_type_sql = Jetpack_Sync_Defaults::get_blacklisted_post_types_sql();
138
139
		$query = <<<ENDSQL
140
			SELECT CONV(BIT_XOR(CRC32(CONCAT(ID,post_modified))), 10, 16) 
141
				FROM $wpdb->posts
142
				WHERE $post_type_sql
143
ENDSQL;
144
145
		return $wpdb->get_var( $query );
146
	}
147
148
	public function comment_count( $status = null ) {
149
		global $wpdb;
150
151
		$comment_approved = $this->comment_status_to_approval_value( $status );
152
153
		if ( $comment_approved !== false ) {
154
			return $wpdb->get_var( $wpdb->prepare(
155
				"SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = %s",
156
				$comment_approved
157
			) );
158
		} else {
159
			return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments" );
160
		}
161
	}
162
163
	private function comment_status_to_approval_value( $status ) {
164
		switch ( $status ) {
165
			case 'approve':
166
				return "1";
167
			case 'hold':
168
				return "0";
169
			case 'spam':
170
				return 'spam';
171
			case 'trash':
172
				return 'trash';
173
			case 'any':
174
				return false;
175
			case 'all':
176
				return false;
177
			default:
178
				return false;
179
		}
180
	}
181
182
	public function get_comments( $status = null ) {
183
		$args = array( 'orderby' => 'ID', 'status' => 'all' );
184
185
		if ( $status ) {
186
			$args['status'] = $status;
187
		}
188
189
		return get_comments( $args );
190
	}
191
192
	public function get_comment( $id ) {
193
		return WP_Comment::get_instance( $id );
194
	}
195
196
	public function upsert_comment( $comment ) {
197
		global $wpdb, $wp_version;
198
199
		if ( version_compare( $wp_version, '4.4', '<' ) ) {
200
			$comment = (array) $comment;
201
		} else {
202
			// WP 4.4 introduced the WP_Comment Class
203
			$comment = $comment->to_array();
204
		}
205
206
		// filter by fields on comment table
207
		$comment_fields_whitelist = array(
208
			'comment_ID',
209
			'comment_post_ID',
210
			'comment_author',
211
			'comment_author_email',
212
			'comment_author_url',
213
			'comment_author_IP',
214
			'comment_date',
215
			'comment_date_gmt',
216
			'comment_content',
217
			'comment_karma',
218
			'comment_approved',
219
			'comment_agent',
220
			'comment_type',
221
			'comment_parent',
222
			'user_id'
223
		);
224
225
		foreach ( $comment as $key => $value ) {
226
			if ( ! in_array( $key, $comment_fields_whitelist ) ) {
227
				unset( $comment[ $key ] );
228
			}
229
		}
230
231
		$exists = $wpdb->get_var(
232
			$wpdb->prepare(
233
				"SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
234
				$comment['comment_ID']
235
			)
236
		);
237
238
		if ( $exists ) {
239
			$wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
240
		} else {
241
			$wpdb->insert( $wpdb->comments, $comment );
242
		}
243
244
		wp_update_comment_count( $comment['comment_post_ID'] );
245
	}
246
247
	public function trash_comment( $comment_id ) {
248
		wp_delete_comment( $comment_id );
249
	}
250
251
	public function delete_comment( $comment_id ) {
252
		wp_delete_comment( $comment_id, true );
253
	}
254
255
	public function spam_comment( $comment_id ) {
256
		wp_spam_comment( $comment_id );
257
	}
258
259
	public function comments_checksum() {
260
		global $wpdb;
261
262
		$query = <<<ENDSQL
263
			SELECT CONV(BIT_XOR(CRC32(CONCAT(comment_ID,comment_content))), 10, 16) FROM $wpdb->comments
264
ENDSQL;
265
266
		return $wpdb->get_var( $query );
267
	}
268
269
	public function update_option( $option, $value ) {
270
		return update_option( $option, $value );
271
	}
272
273
	public function get_option( $option ) {
274
		return get_option( $option );
275
	}
276
277
	public function delete_option( $option ) {
278
		return delete_option( $option );
279
	}
280
281
	public function set_theme_support( $theme_support ) {
282
		// noop
283
	}
284
285
	public function current_theme_supports( $feature ) {
286
		return current_theme_supports( $feature );
287
	}
288
289
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
290
		return get_metadata( $type, $object_id, $meta_key, $single );
291
	}
292
293
	/**
294
	 *
295
	 * Stores remote meta key/values alongside an ID mapping key
296
	 *
297
	 * @param $type
298
	 * @param $object_id
299
	 * @param $meta_key
300
	 * @param $meta_value
301
	 * @param $meta_id
302
	 *
303
	 * @return bool
304
	 */
305
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
306
307
		$table = _get_meta_table( $type );
308
		if ( ! $table ) {
309
			return false;
310
		}
311
312
		global $wpdb;
313
314
		$exists = $wpdb->get_var( $wpdb->prepare(
315
			"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
316
			$meta_id
317
		) );
318
319
		if ( $exists ) {
320
			$wpdb->update( $table, array( 'meta_key'   => $meta_key,
321
			                              'meta_value' => serialize( $meta_value )
322
			), array( 'meta_id' => $meta_id ) );
323
		} else {
324
			$object_id_field = $type . '_id';
325
			$wpdb->insert( $table, array( 'meta_id'        => $meta_id,
326
			                              $object_id_field => $object_id,
327
			                              'meta_key'       => $meta_key,
328
			                              'meta_value'     => serialize( $meta_value )
329
			) );
330
		}
331
332
		wp_cache_delete( $object_id, $type . '_meta' );
333
334
		return true;
335
	}
336
337
	public function delete_metadata( $type, $object_id, $meta_ids ) {
338
		global $wpdb;
339
340
		$table = _get_meta_table( $type );
341
		if ( ! $table ) {
342
			return false;
343
		}
344
345
		foreach ( $meta_ids as $meta_id ) {
346
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
347
		}
348
349
		// if we don't have an object ID what do we do - invalidate ALL meta?
350
		if ( $object_id ) {
351
			wp_cache_delete( $object_id, $type . '_meta' );
352
		}
353
	}
354
355
	// constants
356
	public function get_constant( $constant ) {
357
		$value = get_option( 'jetpack_constant_' . $constant );
358
359
		if ( $value ) {
360
			return $value;
361
		}
362
363
		return null;
364
	}
365
366
	public function set_constant( $constant, $value ) {
367
		update_option( 'jetpack_constant_' . $constant, $value );
368
	}
369
370
	public function get_updates( $type ) {
371
		$all_updates = get_option( 'jetpack_updates', array() );
372
373
		if ( isset( $all_updates[ $type ] ) ) {
374
			return $all_updates[ $type ];
375
		} else {
376
			return null;
377
		}
378
	}
379
380
	public function set_updates( $type, $updates ) {
381
		$all_updates          = get_option( 'jetpack_updates', array() );
382
		$all_updates[ $type ] = $updates;
383
		update_option( 'jetpack_updates', $all_updates );
384
	}
385
386
	// functions
387
	public function get_callable( $name ) {
388
		$value = get_option( 'jetpack_' . $name );
389
390
		if ( $value ) {
391
			return $value;
392
		}
393
394
		return null;
395
	}
396
397
	public function set_callable( $name, $value ) {
398
		update_option( 'jetpack_' . $name, $value );
399
	}
400
401
	// network options
402
	public function get_site_option( $option ) {
403
		return get_option( 'jetpack_network_' . $option );
404
	}
405
406
	public function update_site_option( $option, $value ) {
407
		return update_option( 'jetpack_network_' . $option, $value );
408
	}
409
410
	public function delete_site_option( $option ) {
411
		return delete_option( 'jetpack_network_' . $option );
412
	}
413
414
	// terms
415
	// terms
416
	public function get_terms( $taxonomy ) {
417
		return get_terms( $taxonomy );
418
	}
419
420
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
421
		$t = $this->ensure_taxonomy( $taxonomy );
422
		if ( ! $t || is_wp_error( $t ) ) {
423
			return $t;
424
		}
425
426
		return get_term( $term_id, $taxonomy );
427
	}
428
429
	private function ensure_taxonomy( $taxonomy ) {
430
		if ( ! taxonomy_exists( $taxonomy ) ) {
431
			// try re-registering synced taxonomies
432
			$taxonomies = $this->get_callable( 'taxonomies' );
433
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
434
				// doesn't exist, or somehow hasn't been synced
435
				return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
436
			}
437
			$t = $taxonomies[ $taxonomy ];
438
439
			return register_taxonomy(
440
				$taxonomy,
441
				$t->object_type,
442
				(array) $t
443
			);
444
		}
445
446
		return true;
447
	}
448
449
	public function get_the_terms( $object_id, $taxonomy ) {
450
		return get_the_terms( $object_id, $taxonomy );
451
	}
452
453
	public function update_term( $term_object ) {
454
		$taxonomy = $term_object->taxonomy;
455
		global $wpdb;
456
		$exists = $wpdb->get_var( $wpdb->prepare(
457
			"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
458
			$term_object->term_id
459
		) );
460
		if ( ! $exists ) {
461
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
462
			$term          = array(
463
				'term_id'    => $term_object->term_id,
464
				'name'       => $term_object->name,
465
				'slug'       => $term_object->slug,
466
				'term_group' => $term_object->term_group,
467
			);
468
			$term_taxonomy = array(
469
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
470
				'term_id'          => $term_object->term_id,
471
				'taxonomy'         => $term_object->taxonomy,
472
				'description'      => $term_object->description,
473
				'parent'           => (int) $term_object->parent,
474
				'count'            => (int) $term_object->count,
475
			);
476
			$wpdb->insert( $wpdb->terms, $term );
477
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
478
479
//			clean_term_cache( $term_object->term_id, $taxonomy );
480
481
			return true;
482
		}
483
484
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
485
	}
486
487
	public function delete_term( $term_id, $taxonomy ) {
488
		return wp_delete_term( $term_id, $taxonomy );
489
	}
490
491
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
492
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
493
	}
494
495
	public function delete_object_terms( $object_id, $tt_ids ) {
496
		global $wpdb;
497
498
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
499
			$taxonomies = array();
500
			foreach ( $tt_ids as $tt_id ) {
501
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
502
				$taxonomies[ $term->taxonomy ][] = $tt_id;
503
			}
504
			$in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'";
505
506
			/**
507
			 * Fires immediately before an object-term relationship is deleted.
508
			 *
509
			 * @since 2.9.0
510
			 *
511
			 * @param int $object_id Object ID.
512
			 * @param array $tt_ids An array of term taxonomy IDs.
513
			 */
514
			do_action( 'delete_term_relationships', $object_id, $tt_ids );
515
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
516
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
517
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
518
				/**
519
				 * Fires immediately after an object-term relationship is deleted.
520
				 *
521
				 * @since 2.9.0
522
				 *
523
				 * @param int $object_id Object ID.
524
				 * @param array $tt_ids An array of term taxonomy IDs.
525
				 */
526
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
527
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
528
			}
529
530
			return (bool) $deleted;
531
		}
532
533
		return false;
534
	}
535
536
	// users
537
	public function user_count() {
538
539
	}
540
541
	public function get_user( $user_id ) {
542
		return WP_User::get_instance( $user_id );
543
	}
544
545
	public function upsert_user( $user ) {
546
		$this->invalid_call();
547
	}
548
549
	public function delete_user( $user_id ) {
550
		$this->invalid_call();
551
	}
552
553
	public function checksum_all() {
554
		return array(
555
			'posts'    => $this->posts_checksum(),
556
			'comments' => $this->comments_checksum()
557
		);
558
	}
559
560
	private function invalid_call() {
561
		$backtrace = debug_backtrace();
562
		$caller    = $backtrace[1]['function'];
563
		throw new Exception( "This function $caller is not supported on the WP Replicastore" );
564
	}
565
}
566