Completed
Push — update/sync-histogram-terms ( 432d1b )
by
unknown
07:05
created

Replicastore::term_count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Automattic\Jetpack\Sync;
4
5
/**
6
 * An implementation of Replicastore Interface 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 Replicastore implements Replicastore_Interface {
10
11
	public function reset() {
12
		global $wpdb;
13
14
		$wpdb->query( "DELETE FROM $wpdb->posts" );
15
		$wpdb->query( "DELETE FROM $wpdb->comments" );
16
17
		// also need to delete terms from cache
18
		$term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
19
		foreach ( $term_ids as $term_id ) {
20
			wp_cache_delete( $term_id, 'terms' );
21
		}
22
23
		$wpdb->query( "DELETE FROM $wpdb->terms" );
24
25
		$wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
26
		$wpdb->query( "DELETE FROM $wpdb->term_relationships" );
27
28
		// callables and constants
29
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
30
		$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
31
	}
32
33
	function full_sync_start( $config ) {
34
		$this->reset();
35
	}
36
37
	function full_sync_end( $checksum ) {
38
		// noop right now
39
	}
40
41
	public function term_count() {
42
		global $wpdb;
43
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
44
	}
45
46 View Code Duplication
	public function post_count( $status = null, $min_id = null, $max_id = null ) {
47
		global $wpdb;
48
49
		$where = '';
0 ignored issues
show
Unused Code introduced by
$where 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...
50
51
		if ( $status ) {
52
			$where = "post_status = '" . esc_sql( $status ) . "'";
53
		} else {
54
			$where = '1=1';
55
		}
56
57
		if ( null != $min_id ) {
58
			$where .= ' AND ID >= ' . intval( $min_id );
59
		}
60
61
		if ( null != $max_id ) {
62
			$where .= ' AND ID <= ' . intval( $max_id );
63
		}
64
65
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
66
	}
67
68
	// TODO: actually use max_id/min_id
69
	public function get_posts( $status = null, $min_id = null, $max_id = null ) {
70
		$args = array(
71
			'orderby'        => 'ID',
72
			'posts_per_page' => -1,
73
		);
74
75
		if ( $status ) {
76
			$args['post_status'] = $status;
77
		} else {
78
			$args['post_status'] = 'any';
79
		}
80
81
		return get_posts( $args );
82
	}
83
84
	public function get_post( $id ) {
85
		return get_post( $id );
86
	}
87
88
	public function upsert_post( $post, $silent = false ) {
89
		global $wpdb;
90
91
		// reject the post if it's not a WP_Post
92
		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...
93
			return;
94
		}
95
96
		$post = $post->to_array();
97
98
		// reject posts without an ID
99
		if ( ! isset( $post['ID'] ) ) {
100
			return;
101
		}
102
103
		$now     = current_time( 'mysql' );
104
		$now_gmt = get_gmt_from_date( $now );
105
106
		$defaults = array(
107
			'ID'                    => 0,
108
			'post_author'           => '0',
109
			'post_content'          => '',
110
			'post_content_filtered' => '',
111
			'post_title'            => '',
112
			'post_name'             => '',
113
			'post_excerpt'          => '',
114
			'post_status'           => 'draft',
115
			'post_type'             => 'post',
116
			'comment_status'        => 'closed',
117
			'comment_count'         => '0',
118
			'ping_status'           => '',
119
			'post_password'         => '',
120
			'to_ping'               => '',
121
			'pinged'                => '',
122
			'post_parent'           => 0,
123
			'menu_order'            => 0,
124
			'guid'                  => '',
125
			'post_date'             => $now,
126
			'post_date_gmt'         => $now_gmt,
127
			'post_modified'         => $now,
128
			'post_modified_gmt'     => $now_gmt,
129
		);
130
131
		$post = array_intersect_key( $post, $defaults );
132
133
		$post = sanitize_post( $post, 'db' );
134
135
		unset( $post['filter'] );
136
137
		$exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
138
139
		if ( $exists ) {
140
			$wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
141
		} else {
142
			$wpdb->insert( $wpdb->posts, $post );
143
		}
144
145
		clean_post_cache( $post['ID'] );
146
	}
147
148
	public function delete_post( $post_id ) {
149
		wp_delete_post( $post_id, true );
150
	}
151
152
	public function posts_checksum( $min_id = null, $max_id = null ) {
153
		global $wpdb;
154
		return $this->table_checksum( $wpdb->posts, Defaults::$default_post_checksum_columns, 'ID', Settings::get_blacklisted_post_types_sql(), $min_id, $max_id );
0 ignored issues
show
Bug introduced by
The property default_post_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
155
	}
156
157
	public function post_meta_checksum( $min_id = null, $max_id = null ) {
158
		global $wpdb;
159
		return $this->table_checksum( $wpdb->postmeta, Defaults::$default_post_meta_checksum_columns, 'meta_id', Settings::get_whitelisted_post_meta_sql(), $min_id, $max_id );
0 ignored issues
show
Bug introduced by
The property default_post_meta_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
160
	}
161
162 View Code Duplication
	public function comment_count( $status = null, $min_id = null, $max_id = null ) {
163
		global $wpdb;
164
165
		$comment_approved = $this->comment_status_to_approval_value( $status );
166
167
		if ( $comment_approved !== false ) {
168
			$where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
169
		} else {
170
			$where = '1=1';
171
		}
172
173
		if ( $min_id != null ) {
174
			$where .= ' AND comment_ID >= ' . intval( $min_id );
175
		}
176
177
		if ( $max_id != null ) {
178
			$where .= ' AND comment_ID <= ' . intval( $max_id );
179
		}
180
181
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
182
	}
183
184
	private function comment_status_to_approval_value( $status ) {
185
		switch ( $status ) {
186
			case 'approve':
187
				return '1';
188
			case 'hold':
189
				return '0';
190
			case 'spam':
191
				return 'spam';
192
			case 'trash':
193
				return 'trash';
194
			case 'any':
195
				return false;
196
			case 'all':
197
				return false;
198
			default:
199
				return false;
200
		}
201
	}
202
203
	// TODO: actually use max_id/min_id
204
	public function get_comments( $status = null, $min_id = null, $max_id = null ) {
205
		$args = array(
206
			'orderby' => 'ID',
207
			'status'  => 'all',
208
		);
209
210
		if ( $status ) {
211
			$args['status'] = $status;
212
		}
213
214
		return get_comments( $args );
215
	}
216
217
	public function get_comment( $id ) {
218
		return \WP_Comment::get_instance( $id );
219
	}
220
221
	public function upsert_comment( $comment ) {
222
		global $wpdb;
223
224
		$comment = $comment->to_array();
225
226
		// filter by fields on comment table
227
		$comment_fields_whitelist = array(
228
			'comment_ID',
229
			'comment_post_ID',
230
			'comment_author',
231
			'comment_author_email',
232
			'comment_author_url',
233
			'comment_author_IP',
234
			'comment_date',
235
			'comment_date_gmt',
236
			'comment_content',
237
			'comment_karma',
238
			'comment_approved',
239
			'comment_agent',
240
			'comment_type',
241
			'comment_parent',
242
			'user_id',
243
		);
244
245
		foreach ( $comment as $key => $value ) {
246
			if ( ! in_array( $key, $comment_fields_whitelist ) ) {
247
				unset( $comment[ $key ] );
248
			}
249
		}
250
251
		$exists = $wpdb->get_var(
252
			$wpdb->prepare(
253
				"SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
254
				$comment['comment_ID']
255
			)
256
		);
257
258
		if ( $exists ) {
259
			$wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
260
		} else {
261
			$wpdb->insert( $wpdb->comments, $comment );
262
		}
263
264
		wp_update_comment_count( $comment['comment_post_ID'] );
265
	}
266
267
	public function trash_comment( $comment_id ) {
268
		wp_delete_comment( $comment_id );
269
	}
270
271
	public function delete_comment( $comment_id ) {
272
		wp_delete_comment( $comment_id, true );
273
	}
274
275
	public function spam_comment( $comment_id ) {
276
		wp_spam_comment( $comment_id );
277
	}
278
279
	public function trashed_post_comments( $post_id, $statuses ) {
280
		wp_trash_post_comments( $post_id );
281
	}
282
283
	public function untrashed_post_comments( $post_id ) {
284
		wp_untrash_post_comments( $post_id );
285
	}
286
287
	public function comments_checksum( $min_id = null, $max_id = null ) {
288
		global $wpdb;
289
		return $this->table_checksum( $wpdb->comments, Defaults::$default_comment_checksum_columns, 'comment_ID', Settings::get_comments_filter_sql(), $min_id, $max_id );
0 ignored issues
show
Bug introduced by
The property default_comment_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
290
	}
291
292
	public function comment_meta_checksum( $min_id = null, $max_id = null ) {
293
		global $wpdb;
294
		return $this->table_checksum( $wpdb->commentmeta, Defaults::$default_comment_meta_checksum_columns, 'meta_id', Settings::get_whitelisted_comment_meta_sql(), $min_id, $max_id );
0 ignored issues
show
Bug introduced by
The property default_comment_meta_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
295
	}
296
297
	public function options_checksum() {
298
		global $wpdb;
299
		$options_whitelist = "'" . implode( "', '", 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 Automattic\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...
300
		$where_sql         = "option_name IN ( $options_whitelist )";
301
302
		return $this->table_checksum( $wpdb->options, Defaults::$default_option_checksum_columns, null, $where_sql, null, null );
0 ignored issues
show
Bug introduced by
The property default_option_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
303
	}
304
305
306
	public function update_option( $option, $value ) {
307
		return update_option( $option, $value );
308
	}
309
310
	public function get_option( $option, $default = false ) {
311
		return get_option( $option, $default );
312
	}
313
314
	public function delete_option( $option ) {
315
		return delete_option( $option );
316
	}
317
318
	public function set_theme_support( $theme_support ) {
319
		// noop
320
	}
321
322
	public function current_theme_supports( $feature ) {
323
		return current_theme_supports( $feature );
324
	}
325
326
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
327
		return get_metadata( $type, $object_id, $meta_key, $single );
328
	}
329
330
	/**
331
	 *
332
	 * Stores remote meta key/values alongside an ID mapping key
333
	 *
334
	 * @param $type
335
	 * @param $object_id
336
	 * @param $meta_key
337
	 * @param $meta_value
338
	 * @param $meta_id
339
	 *
340
	 * @return bool
341
	 */
342
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
343
344
		$table = _get_meta_table( $type );
345
		if ( ! $table ) {
346
			return false;
347
		}
348
349
		global $wpdb;
350
351
		$exists = $wpdb->get_var(
352
			$wpdb->prepare(
353
				"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
354
				$meta_id
355
			)
356
		);
357
358
		if ( $exists ) {
359
			$wpdb->update(
360
				$table,
361
				array(
362
					'meta_key'   => $meta_key,
363
					'meta_value' => maybe_serialize( $meta_value ),
364
				),
365
				array( 'meta_id' => $meta_id )
366
			);
367
		} else {
368
			$object_id_field = $type . '_id';
369
			$wpdb->insert(
370
				$table,
371
				array(
372
					'meta_id'        => $meta_id,
373
					$object_id_field => $object_id,
374
					'meta_key'       => $meta_key,
375
					'meta_value'     => maybe_serialize( $meta_value ),
376
				)
377
			);
378
		}
379
380
		wp_cache_delete( $object_id, $type . '_meta' );
381
382
		return true;
383
	}
384
385
	public function delete_metadata( $type, $object_id, $meta_ids ) {
386
		global $wpdb;
387
388
		$table = _get_meta_table( $type );
389
		if ( ! $table ) {
390
			return false;
391
		}
392
393
		foreach ( $meta_ids as $meta_id ) {
394
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
395
		}
396
397
		// if we don't have an object ID what do we do - invalidate ALL meta?
398
		if ( $object_id ) {
399
			wp_cache_delete( $object_id, $type . '_meta' );
400
		}
401
	}
402
403
	// todo: test this out to make sure it works as expected.
404
	public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
405
		global $wpdb;
406
407
		$table = _get_meta_table( $type );
408
		if ( ! $table ) {
409
			return false;
410
		}
411
		$column = sanitize_key( $type . '_id' );
412
		$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ), $meta_key ) );
413
414
		// if we don't have an object ID what do we do - invalidate ALL meta?
415
		foreach ( $object_ids as $object_id ) {
416
			wp_cache_delete( $object_id, $type . '_meta' );
417
		}
418
	}
419
420
	// constants
421
	public function get_constant( $constant ) {
422
		$value = get_option( 'jetpack_constant_' . $constant );
423
424
		if ( $value ) {
425
			return $value;
426
		}
427
428
		return null;
429
	}
430
431
	public function set_constant( $constant, $value ) {
432
		update_option( 'jetpack_constant_' . $constant, $value );
433
	}
434
435
	public function get_updates( $type ) {
436
		$all_updates = get_option( 'jetpack_updates', array() );
437
438
		if ( isset( $all_updates[ $type ] ) ) {
439
			return $all_updates[ $type ];
440
		} else {
441
			return null;
442
		}
443
	}
444
445
	public function set_updates( $type, $updates ) {
446
		$all_updates          = get_option( 'jetpack_updates', array() );
447
		$all_updates[ $type ] = $updates;
448
		update_option( 'jetpack_updates', $all_updates );
449
	}
450
451
	// functions
452
	public function get_callable( $name ) {
453
		$value = get_option( 'jetpack_' . $name );
454
455
		if ( $value ) {
456
			return $value;
457
		}
458
459
		return null;
460
	}
461
462
	public function set_callable( $name, $value ) {
463
		update_option( 'jetpack_' . $name, $value );
464
	}
465
466
	// network options
467
	public function get_site_option( $option ) {
468
		return get_option( 'jetpack_network_' . $option );
469
	}
470
471
	public function update_site_option( $option, $value ) {
472
		return update_option( 'jetpack_network_' . $option, $value );
473
	}
474
475
	public function delete_site_option( $option ) {
476
		return delete_option( 'jetpack_network_' . $option );
477
	}
478
479
	// terms
480
	// terms
481
	public function get_terms( $taxonomy ) {
482
		return get_terms( $taxonomy );
483
	}
484
485
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
486
		$t = $this->ensure_taxonomy( $taxonomy );
487
		if ( ! $t || is_wp_error( $t ) ) {
488
			return $t;
489
		}
490
491
		return get_term( $term_id, $taxonomy );
492
	}
493
494
	private function ensure_taxonomy( $taxonomy ) {
495
		if ( ! taxonomy_exists( $taxonomy ) ) {
496
			// try re-registering synced taxonomies
497
			$taxonomies = $this->get_callable( 'taxonomies' );
498
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
499
				// doesn't exist, or somehow hasn't been synced
500
				return new \WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_taxonomy'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
501
			}
502
			$t = $taxonomies[ $taxonomy ];
503
504
			return register_taxonomy(
505
				$taxonomy,
506
				$t->object_type,
507
				(array) $t
508
			);
509
		}
510
511
		return true;
512
	}
513
514
	public function get_the_terms( $object_id, $taxonomy ) {
515
		return get_the_terms( $object_id, $taxonomy );
516
	}
517
518
	public function update_term( $term_object ) {
519
		$taxonomy = $term_object->taxonomy;
520
		global $wpdb;
521
		$exists = $wpdb->get_var(
522
			$wpdb->prepare(
523
				"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
524
				$term_object->term_id
525
			)
526
		);
527
		if ( ! $exists ) {
528
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
529
			$term          = array(
530
				'term_id'    => $term_object->term_id,
531
				'name'       => $term_object->name,
532
				'slug'       => $term_object->slug,
533
				'term_group' => $term_object->term_group,
534
			);
535
			$term_taxonomy = array(
536
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
537
				'term_id'          => $term_object->term_id,
538
				'taxonomy'         => $term_object->taxonomy,
539
				'description'      => $term_object->description,
540
				'parent'           => (int) $term_object->parent,
541
				'count'            => (int) $term_object->count,
542
			);
543
			$wpdb->insert( $wpdb->terms, $term );
544
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
545
546
			return true;
547
		}
548
549
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
550
	}
551
552
	public function delete_term( $term_id, $taxonomy ) {
553
		return wp_delete_term( $term_id, $taxonomy );
554
	}
555
556
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
557
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
558
	}
559
560
	public function delete_object_terms( $object_id, $tt_ids ) {
561
		global $wpdb;
562
563
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
564
			// escape
565
			$tt_ids_sanitized = array_map( 'intval', $tt_ids );
566
567
			$taxonomies = array();
568
			foreach ( $tt_ids_sanitized as $tt_id ) {
569
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
570
				$taxonomies[ $term->taxonomy ][] = $tt_id;
571
			}
572
			$in_tt_ids = implode( ', ', $tt_ids_sanitized );
573
574
			/**
575
			 * Fires immediately before an object-term relationship is deleted.
576
			 *
577
			 * @since 2.9.0
578
			 *
579
			 * @param int $object_id Object ID.
580
			 * @param array $tt_ids An array of term taxonomy IDs.
581
			 */
582
			do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
583
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
584
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
585
				$this->ensure_taxonomy( $taxonomy );
586
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
587
				/**
588
				 * Fires immediately after an object-term relationship is deleted.
589
				 *
590
				 * @since 2.9.0
591
				 *
592
				 * @param int $object_id Object ID.
593
				 * @param array $tt_ids An array of term taxonomy IDs.
594
				 */
595
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
596
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
597
			}
598
599
			return (bool) $deleted;
600
		}
601
602
		return false;
603
	}
604
605
	// users
606
	public function user_count() {
607
608
	}
609
610
	public function get_user( $user_id ) {
611
		return \WP_User::get_instance( $user_id );
612
	}
613
614
	public function upsert_user( $user ) {
615
		$this->invalid_call();
616
	}
617
618
	public function delete_user( $user_id ) {
619
		$this->invalid_call();
620
	}
621
622
	public function upsert_user_locale( $user_id, $local ) {
623
		$this->invalid_call();
624
	}
625
626
	public function delete_user_locale( $user_id ) {
627
		$this->invalid_call();
628
	}
629
630
	public function get_user_locale( $user_id ) {
631
		return get_user_locale( $user_id );
632
	}
633
634
	public function get_allowed_mime_types( $user_id ) {
635
636
	}
637
638
	public function checksum_all() {
639
		$post_meta_checksum    = $this->checksum_histogram( 'post_meta', 1 );
640
		$comment_meta_checksum = $this->checksum_histogram( 'comment_meta', 1 );
641
642
		return array(
643
			'posts'        => $this->posts_checksum(),
644
			'comments'     => $this->comments_checksum(),
645
			'post_meta'    => reset( $post_meta_checksum ),
646
			'comment_meta' => reset( $comment_meta_checksum ),
647
		);
648
	}
649
650
	function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '' ) {
651
		global $wpdb;
652
653
		$wpdb->queries = array();
654
655
		switch ( $object_type ) {
656
			case 'posts':
657
				$object_count = $this->post_count( null, $start_id, $end_id );
658
				$object_table = $wpdb->posts;
659
				$id_field     = 'ID';
660
				$where_sql    = Settings::get_blacklisted_post_types_sql();
661
				if ( empty( $columns ) ) {
662
					$columns = Defaults::$default_post_checksum_columns;
0 ignored issues
show
Bug introduced by
The property default_post_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
663
				}
664
				break;
665 View Code Duplication
			case 'post_meta':
666
				$object_table = $wpdb->postmeta;
667
				$where_sql    = Settings::get_whitelisted_post_meta_sql();
668
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
669
				$id_field     = 'meta_id';
670
671
				if ( empty( $columns ) ) {
672
					$columns = Defaults::$default_post_meta_checksum_columns;
0 ignored issues
show
Bug introduced by
The property default_post_meta_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
673
				}
674
				break;
675
			case 'comments':
676
				$object_count = $this->comment_count( null, $start_id, $end_id );
677
				$object_table = $wpdb->comments;
678
				$id_field     = 'comment_ID';
679
				$where_sql    = Settings::get_comments_filter_sql();
680
				if ( empty( $columns ) ) {
681
					$columns = Defaults::$default_comment_checksum_columns;
0 ignored issues
show
Bug introduced by
The property default_comment_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
682
				}
683
				break;
684 View Code Duplication
			case 'comment_meta':
685
				$object_table = $wpdb->commentmeta;
686
				$where_sql    = Settings::get_whitelisted_comment_meta_sql();
687
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
688
				$id_field     = 'meta_id';
689
				if ( empty( $columns ) ) {
690
					$columns = Defaults::$default_post_meta_checksum_columns;
0 ignored issues
show
Bug introduced by
The property default_post_meta_checksum_columns cannot be accessed from this context as it is declared private in class Automattic\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...
691
				}
692
				break;
693
			case "terms":
694
				$object_table = $wpdb->terms;
695
				$object_count = $this->term_count();
696
				$id_field     = 'term_id';
697
				$where_sql    = '';
698
				if ( empty( $columns ) ) {
699
					$columns  = Jetpack_Sync_Defaults::$default_term_checksum_column;
700
				}
701
				break;
702
			default:
703
				return false;
704
		}
705
706
		$bucket_size     = intval( ceil( $object_count / $buckets ) );
707
		$previous_max_id = 0;
708
		$histogram       = array();
709
710
		$where = '1=1';
711
712
		if ( $start_id ) {
713
			$where .= " AND $id_field >= " . intval( $start_id );
714
		}
715
716
		if ( $end_id ) {
717
			$where .= " AND $id_field <= " . intval( $end_id );
718
		}
719
720
		do {
721
			list( $first_id, $last_id ) = $wpdb->get_row(
722
				"SELECT MIN($id_field) as min_id, MAX($id_field) as max_id FROM ( SELECT $id_field FROM $object_table WHERE $where AND $id_field > $previous_max_id ORDER BY $id_field ASC LIMIT $bucket_size ) as ids",
723
				ARRAY_N
724
			);
725
726
			if ( null === $first_id || null === $last_id ) {
727
				// Nothing to checksum here...
728
				break;
729
			}
730
731
			// get the checksum value
732
			$value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $first_id, $last_id, $strip_non_ascii, $salt );
733
734
			if ( is_wp_error( $value ) ) {
735
				return $value;
736
			}
737
738
			if ( null === $first_id || null === $last_id ) {
739
				break;
740
			} elseif ( $first_id === $last_id ) {
741
				$histogram[ $first_id ] = $value;
742
			} else {
743
				$histogram[ "{$first_id}-{$last_id}" ] = $value;
744
			}
745
746
			$previous_max_id = $last_id;
747
		} while ( true );
748
749
		return $histogram;
750
	}
751
752
	private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true, $salt = '' ) {
753
		global $wpdb;
754
755
		// sanitize to just valid MySQL column names
756
		$sanitized_columns = preg_grep( '/^[0-9,a-z,A-Z$_]+$/i', $columns );
757
758
		if ( $strip_non_ascii ) {
759
			$columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) );
760
		} else {
761
			$columns_sql = implode( ',', $sanitized_columns );
762
		}
763
764
		if ( null !== $min_id && null !== $max_id ) {
765
			if ( $min_id === $max_id ) {
766
				$min_id     = intval( $min_id );
767
				$where_sql .= " AND $id_column = $min_id LIMIT 1";
768
			} else {
769
				$min_id     = intval( $min_id );
770
				$max_id     = intval( $max_id );
771
				$size       = $max_id - $min_id;
772
				$where_sql .= " AND $id_column >= $min_id AND $id_column <= $max_id LIMIT $size";
773
			}
774
		} else {
775
			if ( null !== $min_id ) {
776
				$min_id     = intval( $min_id );
777
				$where_sql .= " AND $id_column >= $min_id";
778
			}
779
780
			if ( null !== $max_id ) {
781
				$max_id     = intval( $max_id );
782
				$where_sql .= " AND $id_column <= $max_id";
783
			}
784
		}
785
786
		$query  = <<<ENDSQL
787
			SELECT CAST( SUM( CRC32( CONCAT_WS( '#', '%s', {$columns_sql} ) ) ) AS UNSIGNED INT )
788
			FROM $table
789
			WHERE $where_sql;
790
ENDSQL;
791
		$result = $wpdb->get_var( $wpdb->prepare( $query, $salt ) );
792
		if ( $wpdb->last_error ) {
793
			return new \WP_Error( 'database_error', $wpdb->last_error );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'database_error'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
794
		}
795
796
		return $result;
797
	}
798
799
	public function get_checksum_type() {
800
		return 'sum';
801
	}
802
803
	private function meta_count( $table, $where_sql, $min_id, $max_id ) {
804
		global $wpdb;
805
806
		if ( $min_id != null ) {
807
			$where_sql .= ' AND meta_id >= ' . intval( $min_id );
808
		}
809
810
		if ( $max_id != null ) {
811
			$where_sql .= ' AND meta_id <= ' . intval( $max_id );
812
		}
813
814
		return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where_sql" );
815
	}
816
817
	/**
818
	 * Wraps a column name in SQL which strips non-ASCII chars.
819
	 * This helps normalize data to avoid checksum differences caused by
820
	 * badly encoded data in the DB
821
	 */
822
	function strip_non_ascii_sql( $column_name ) {
823
		return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )";
824
	}
825
826
	private function invalid_call() {
827
		$backtrace = debug_backtrace();
828
		$caller    = $backtrace[1]['function'];
829
		throw new \Exception( "This function $caller is not supported on the WP Replicastore" );
830
	}
831
}
832