Completed
Push — add/checksum-histogram-api ( da85cc...fcb325 )
by
unknown
09:04
created

Jetpack_Sync_WP_Replicastore::get_user()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
11
12
	public function reset() {
13
		global $wpdb;
14
15
		$wpdb->query( "DELETE FROM $wpdb->posts" );
16
		$wpdb->query( "DELETE FROM $wpdb->comments" );
17
18
		// also need to delete terms from cache
19
		$term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
20
		foreach ( $term_ids as $term_id ) {
21
			wp_cache_delete( $term_id, 'terms' );
22
		}
23
24
		$wpdb->query( "DELETE FROM $wpdb->terms" );
25
26
		$wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
27
		$wpdb->query( "DELETE FROM $wpdb->term_relationships" );
28
29
		// callables and constants
30
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
31
		$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
32
	}
33
34
	function full_sync_start() {
35
		$this->reset();
36
	}
37
38
	function full_sync_end( $checksum ) {
39
		// noop right now
40
	}
41
42
	public function post_count( $status = null ) {
43
		return count( $this->get_posts( $status ) );
44
	}
45
46
	public function get_posts( $status = null ) {
47
		$args = array( 'orderby' => 'ID', 'posts_per_page' => -1 );
48
49
		if ( $status ) {
50
			$args['post_status'] = $status;
51
		} else {
52
			$args['post_status'] = 'any';
53
		}
54
55
		return get_posts( $args );
56
	}
57
58
	public function get_post( $id ) {
59
		return get_post( $id );
60
	}
61
62
	public function upsert_post( $post, $silent = false ) {
63
		global $wpdb;
64
65
		// reject the post if it's not a WP_Post
66
		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...
67
			return;
68
		}
69
70
		$post = $post->to_array();
71
72
		// reject posts without an ID
73
		if ( ! isset( $post['ID'] ) ) {
74
			return;
75
		}
76
77
		$now     = current_time( 'mysql' );
78
		$now_gmt = get_gmt_from_date( $now );
79
80
		$defaults = array(
81
			'ID'                    => 0,
82
			'post_author'           => '0',
83
			'post_content'          => '',
84
			'post_content_filtered' => '',
85
			'post_title'            => '',
86
			'post_name'             => '',
87
			'post_excerpt'          => '',
88
			'post_status'           => 'draft',
89
			'post_type'             => 'post',
90
			'comment_status'        => 'closed',
91
			'comment_count'         => '0',
92
			'ping_status'           => '',
93
			'post_password'         => '',
94
			'to_ping'               => '',
95
			'pinged'                => '',
96
			'post_parent'           => 0,
97
			'menu_order'            => 0,
98
			'guid'                  => '',
99
			'post_date'             => $now,
100
			'post_date_gmt'         => $now_gmt,
101
			'post_modified'         => $now,
102
			'post_modified_gmt'     => $now_gmt,
103
		);
104
105
		$post = array_intersect_key( $post, $defaults );
106
107
		$post = sanitize_post( $post, 'db' );
108
109
		unset( $post['filter'] );
110
111
		$exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
112
113
		if ( $exists ) {
114
			$wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
115
		} else {
116
			$wpdb->insert( $wpdb->posts, $post );
117
		}
118
119
		clean_post_cache( $post['ID'] );
120
	}
121
122
	public function delete_post( $post_id ) {
123
		wp_delete_post( $post_id, true );
124
	}
125
126
	public function posts_checksum( $min_id = null, $max_id = null ) {
127
		global $wpdb;
128
129
		$where_sql = Jetpack_Sync_Defaults::get_blacklisted_post_types_sql();
130
131
		if ( $min_id !== null ) {
132
			$min_id = intval( $min_id );
133
			$where_sql .= " AND ID >= $min_id";
134
		}
135
136
		if ( $max_id !== null ) {
137
			$max_id = intval( $max_id );
138
			$where_sql .= " AND ID <= $max_id";
139
		}
140
141
		$query = <<<ENDSQL
142
			SELECT CONV(BIT_XOR(CRC32(CONCAT(ID,post_modified))), 10, 16)
143
				FROM $wpdb->posts
144
				WHERE $where_sql
145
ENDSQL;
146
147
		return $wpdb->get_var( $query );
148
	}
149
150
	public function comment_count( $status = null ) {
151
		global $wpdb;
152
153
		$comment_approved = $this->comment_status_to_approval_value( $status );
154
155
		if ( $comment_approved !== false ) {
156
			return $wpdb->get_var( $wpdb->prepare(
157
				"SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = %s",
158
				$comment_approved
159
			) );
160
		} else {
161
			return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments" );
162
		}
163
	}
164
165
	private function comment_status_to_approval_value( $status ) {
166
		switch ( $status ) {
167
			case 'approve':
168
				return "1";
169
			case 'hold':
170
				return "0";
171
			case 'spam':
172
				return 'spam';
173
			case 'trash':
174
				return 'trash';
175
			case 'any':
176
				return false;
177
			case 'all':
178
				return false;
179
			default:
180
				return false;
181
		}
182
	}
183
184
	public function get_comments( $status = null ) {
185
		$args = array( 'orderby' => 'ID', 'status' => 'all' );
186
187
		if ( $status ) {
188
			$args['status'] = $status;
189
		}
190
191
		return get_comments( $args );
192
	}
193
194
	public function get_comment( $id ) {
195
		return WP_Comment::get_instance( $id );
196
	}
197
198
	public function upsert_comment( $comment ) {
199
		global $wpdb, $wp_version;
200
201
		if ( version_compare( $wp_version, '4.4', '<' ) ) {
202
			$comment = (array) $comment;
203
		} else {
204
			// WP 4.4 introduced the WP_Comment Class
205
			$comment = $comment->to_array();
206
		}
207
208
		// filter by fields on comment table
209
		$comment_fields_whitelist = array(
210
			'comment_ID',
211
			'comment_post_ID',
212
			'comment_author',
213
			'comment_author_email',
214
			'comment_author_url',
215
			'comment_author_IP',
216
			'comment_date',
217
			'comment_date_gmt',
218
			'comment_content',
219
			'comment_karma',
220
			'comment_approved',
221
			'comment_agent',
222
			'comment_type',
223
			'comment_parent',
224
			'user_id'
225
		);
226
227
		foreach ( $comment as $key => $value ) {
228
			if ( ! in_array( $key, $comment_fields_whitelist ) ) {
229
				unset( $comment[ $key ] );
230
			}
231
		}
232
233
		$exists = $wpdb->get_var(
234
			$wpdb->prepare(
235
				"SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
236
				$comment['comment_ID']
237
			)
238
		);
239
240
		if ( $exists ) {
241
			$wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
242
		} else {
243
			$wpdb->insert( $wpdb->comments, $comment );
244
		}
245
246
		wp_update_comment_count( $comment['comment_post_ID'] );
247
	}
248
249
	public function trash_comment( $comment_id ) {
250
		wp_delete_comment( $comment_id );
251
	}
252
253
	public function delete_comment( $comment_id ) {
254
		wp_delete_comment( $comment_id, true );
255
	}
256
257
	public function spam_comment( $comment_id ) {
258
		wp_spam_comment( $comment_id );
259
	}
260
261
	public function comments_checksum() {
262
		global $wpdb;
263
264
		$query = <<<ENDSQL
265
			SELECT CONV(BIT_XOR(CRC32(CONCAT(comment_ID,comment_content))), 10, 16) FROM $wpdb->comments
266
ENDSQL;
267
268
		return $wpdb->get_var( $query );
269
	}
270
271
	public function options_checksum() {
272
		global $wpdb;
273
274
		$options_whitelist = "'" . implode( "', '", Jetpack_Sync_Defaults::$default_options_whitelist ) . "'";
0 ignored issues
show
Bug introduced by
The property default_options_whitelist cannot be accessed from this context as it is declared private in class Jetpack_Sync_Defaults.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
275
		$query = <<<ENDSQL
276
			SELECT CONV(BIT_XOR(CRC32(CONCAT(option_name,option_value))), 10, 16) FROM $wpdb->options WHERE option_name IN ( $options_whitelist ) 
277
ENDSQL;
278
279
		return $wpdb->get_var( $query );
280
	}
281
	
282
283
	public function update_option( $option, $value ) {
284
		return update_option( $option, $value );
285
	}
286
287
	public function get_option( $option, $default = false ) {
288
		return get_option( $option, $default );
289
	}
290
291
	public function delete_option( $option ) {
292
		return delete_option( $option );
293
	}
294
295
	public function set_theme_support( $theme_support ) {
296
		// noop
297
	}
298
299
	public function current_theme_supports( $feature ) {
300
		return current_theme_supports( $feature );
301
	}
302
303
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
304
		return get_metadata( $type, $object_id, $meta_key, $single );
305
	}
306
307
	/**
308
	 *
309
	 * Stores remote meta key/values alongside an ID mapping key
310
	 *
311
	 * @param $type
312
	 * @param $object_id
313
	 * @param $meta_key
314
	 * @param $meta_value
315
	 * @param $meta_id
316
	 *
317
	 * @return bool
318
	 */
319
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
320
321
		$table = _get_meta_table( $type );
322
		if ( ! $table ) {
323
			return false;
324
		}
325
326
		global $wpdb;
327
328
		$exists = $wpdb->get_var( $wpdb->prepare(
329
			"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
330
			$meta_id
331
		) );
332
333
		if ( $exists ) {
334
			$wpdb->update( $table, array( 'meta_key'   => $meta_key,
335
			                              'meta_value' => serialize( $meta_value )
336
			), array( 'meta_id' => $meta_id ) );
337
		} else {
338
			$object_id_field = $type . '_id';
339
			$wpdb->insert( $table, array( 'meta_id'        => $meta_id,
340
			                              $object_id_field => $object_id,
341
			                              'meta_key'       => $meta_key,
342
			                              'meta_value'     => serialize( $meta_value )
343
			) );
344
		}
345
346
		wp_cache_delete( $object_id, $type . '_meta' );
347
348
		return true;
349
	}
350
351
	public function delete_metadata( $type, $object_id, $meta_ids ) {
352
		global $wpdb;
353
354
		$table = _get_meta_table( $type );
355
		if ( ! $table ) {
356
			return false;
357
		}
358
359
		foreach ( $meta_ids as $meta_id ) {
360
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
361
		}
362
363
		// if we don't have an object ID what do we do - invalidate ALL meta?
364
		if ( $object_id ) {
365
			wp_cache_delete( $object_id, $type . '_meta' );
366
		}
367
	}
368
369
	// constants
370
	public function get_constant( $constant ) {
371
		$value = get_option( 'jetpack_constant_' . $constant );
372
373
		if ( $value ) {
374
			return $value;
375
		}
376
377
		return null;
378
	}
379
380
	public function set_constant( $constant, $value ) {
381
		update_option( 'jetpack_constant_' . $constant, $value );
382
	}
383
384
	public function get_updates( $type ) {
385
		$all_updates = get_option( 'jetpack_updates', array() );
386
387
		if ( isset( $all_updates[ $type ] ) ) {
388
			return $all_updates[ $type ];
389
		} else {
390
			return null;
391
		}
392
	}
393
394
	public function set_updates( $type, $updates ) {
395
		$all_updates          = get_option( 'jetpack_updates', array() );
396
		$all_updates[ $type ] = $updates;
397
		update_option( 'jetpack_updates', $all_updates );
398
	}
399
400
	// functions
401
	public function get_callable( $name ) {
402
		$value = get_option( 'jetpack_' . $name );
403
404
		if ( $value ) {
405
			return $value;
406
		}
407
408
		return null;
409
	}
410
411
	public function set_callable( $name, $value ) {
412
		update_option( 'jetpack_' . $name, $value );
413
	}
414
415
	// network options
416
	public function get_site_option( $option ) {
417
		return get_option( 'jetpack_network_' . $option );
418
	}
419
420
	public function update_site_option( $option, $value ) {
421
		return update_option( 'jetpack_network_' . $option, $value );
422
	}
423
424
	public function delete_site_option( $option ) {
425
		return delete_option( 'jetpack_network_' . $option );
426
	}
427
428
	// terms
429
	// terms
430
	public function get_terms( $taxonomy ) {
431
		return get_terms( $taxonomy );
432
	}
433
434
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
435
		$t = $this->ensure_taxonomy( $taxonomy );
436
		if ( ! $t || is_wp_error( $t ) ) {
437
			return $t;
438
		}
439
440
		return get_term( $term_id, $taxonomy );
441
	}
442
443
	private function ensure_taxonomy( $taxonomy ) {
444
		if ( ! taxonomy_exists( $taxonomy ) ) {
445
			// try re-registering synced taxonomies
446
			$taxonomies = $this->get_callable( 'taxonomies' );
447
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
448
				// doesn't exist, or somehow hasn't been synced
449
				return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
450
			}
451
			$t = $taxonomies[ $taxonomy ];
452
453
			return register_taxonomy(
454
				$taxonomy,
455
				$t->object_type,
456
				(array) $t
457
			);
458
		}
459
460
		return true;
461
	}
462
463
	public function get_the_terms( $object_id, $taxonomy ) {
464
		return get_the_terms( $object_id, $taxonomy );
465
	}
466
467
	public function update_term( $term_object ) {
468
		$taxonomy = $term_object->taxonomy;
469
		global $wpdb;
470
		$exists = $wpdb->get_var( $wpdb->prepare(
471
			"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
472
			$term_object->term_id
473
		) );
474
		if ( ! $exists ) {
475
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
476
			$term          = array(
477
				'term_id'    => $term_object->term_id,
478
				'name'       => $term_object->name,
479
				'slug'       => $term_object->slug,
480
				'term_group' => $term_object->term_group,
481
			);
482
			$term_taxonomy = array(
483
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
484
				'term_id'          => $term_object->term_id,
485
				'taxonomy'         => $term_object->taxonomy,
486
				'description'      => $term_object->description,
487
				'parent'           => (int) $term_object->parent,
488
				'count'            => (int) $term_object->count,
489
			);
490
			$wpdb->insert( $wpdb->terms, $term );
491
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
492
493
//			clean_term_cache( $term_object->term_id, $taxonomy );
494
495
			return true;
496
		}
497
498
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
499
	}
500
501
	public function delete_term( $term_id, $taxonomy ) {
502
		return wp_delete_term( $term_id, $taxonomy );
503
	}
504
505
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
506
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
507
	}
508
509
	public function delete_object_terms( $object_id, $tt_ids ) {
510
		global $wpdb;
511
512
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
513
			$taxonomies = array();
514
			foreach ( $tt_ids as $tt_id ) {
515
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
516
				$taxonomies[ $term->taxonomy ][] = $tt_id;
517
			}
518
			$in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'";
519
520
			/**
521
			 * Fires immediately before an object-term relationship is deleted.
522
			 *
523
			 * @since 2.9.0
524
			 *
525
			 * @param int $object_id Object ID.
526
			 * @param array $tt_ids An array of term taxonomy IDs.
527
			 */
528
			do_action( 'delete_term_relationships', $object_id, $tt_ids );
529
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
530
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
531
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
532
				/**
533
				 * Fires immediately after an object-term relationship is deleted.
534
				 *
535
				 * @since 2.9.0
536
				 *
537
				 * @param int $object_id Object ID.
538
				 * @param array $tt_ids An array of term taxonomy IDs.
539
				 */
540
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
541
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
542
			}
543
544
			return (bool) $deleted;
545
		}
546
547
		return false;
548
	}
549
550
	// users
551
	public function user_count() {
552
553
	}
554
555
	public function get_user( $user_id ) {
556
		return WP_User::get_instance( $user_id );
557
	}
558
559
	public function upsert_user( $user ) {
560
		$this->invalid_call();
561
	}
562
563
	public function delete_user( $user_id ) {
564
		$this->invalid_call();
565
	}
566
567
	public function get_allowed_mime_types( $user_id ) {
568
569
	}
570
571
	public function checksum_all() {
572
		return array(
573
			'posts'    => $this->posts_checksum(),
574
			'comments' => $this->comments_checksum(),
575
			'options' => $this->options_checksum(),
576
		);
577
	}
578
579
	function checksum_histogram( $object_type, $buckets, $start_id, $end_id ) {
580
		global $wpdb;
581
582
		switch( $object_type ) {
583
			case "posts":
584
				$object_count = $this->post_count();
585
				$object_table = $wpdb->posts;
586
				$id_field = 'ID';
587
				$checksum_method = array( $this, 'posts_checksum' );
588
				break;
589
			case "comments":
590
				$object_count = $this->comment_count();
591
				$object_table = $wpdb->comments;
592
				$id_field = 'comment_ID';
593
				$checksum_method = array( $this, 'comments_checksum' );
594
				break;
595
			default:
596
				return false;
597
		}
598
599
		$bucket_size = intval( ceil( $object_count / $buckets ) );
600
		$query_offset = 0;
601
		$histogram = array();
602
603
		do {
604
			list( $first_id, $last_id ) = $wpdb->get_row( 
605
				"SELECT MIN($id_field) as min_id, MAX($id_field) as max_id FROM ( SELECT $id_field FROM $object_table ORDER BY $id_field ASC LIMIT $query_offset, $bucket_size ) as ids", 
606
				ARRAY_N 
607
			);
608
609
			// get the checksum value
610
			$value = call_user_func( $checksum_method, $first_id, $last_id );
611
612
			if ( $first_id === null || $last_id === null ) {
613
				break;
614
			} elseif ( $first_id === $last_id ) {
615
				$histogram[$first_id] = $value;
616
			} else {
617
				$histogram["{$first_id}-{$last_id}"] = $value;
618
			}
619
620
			$query_offset += $bucket_size;
621
		} while ( true );
622
623
		return $histogram;
624
	}
625
626
	private function invalid_call() {
627
		$backtrace = debug_backtrace();
628
		$caller    = $backtrace[1]['function'];
629
		throw new Exception( "This function $caller is not supported on the WP Replicastore" );
630
	}
631
}
632