Completed
Push — renovate/glob-7.x ( 697d78...f7fc07 )
by
unknown
18:21 queued 12:01
created

Replicastore::checksum_histogram()   D

Complexity

Conditions 16
Paths 202

Size

Total Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
nc 202
nop 7
dl 0
loc 89
rs 4.0066
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 get_checksum_columns_for_object_type( $object_type ) {
651
		switch ( $object_type ) {
652
			case 'posts':
653
				return 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...
654
			case 'post_meta':
655
				return 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...
656
			case 'comments':
657
				return 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...
658
			case 'comment_meta':
659
				return 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...
660
			case 'terms':
661
				return Defaults::$default_term_checksum_columns;
0 ignored issues
show
Bug introduced by
The property default_term_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...
662
			default:
663
				return false;
664
		}
665
	}
666
667
	function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '' ) {
668
		global $wpdb;
669
670
		$wpdb->queries = array();
671
672
		if ( empty( $columns ) ) {
673
			$columns = $this->get_checksum_columns_for_object_type( $object_type );
674
		}
675
676
		switch ( $object_type ) {
677
			case 'posts':
678
				$object_count = $this->post_count( null, $start_id, $end_id );
679
				$object_table = $wpdb->posts;
680
				$id_field     = 'ID';
681
				$where_sql    = Settings::get_blacklisted_post_types_sql();
682
				break;
683
			case 'post_meta':
684
				$object_table = $wpdb->postmeta;
685
				$where_sql    = Settings::get_whitelisted_post_meta_sql();
686
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
687
				$id_field     = 'meta_id';
688
				break;
689
			case 'comments':
690
				$object_count = $this->comment_count( null, $start_id, $end_id );
691
				$object_table = $wpdb->comments;
692
				$id_field     = 'comment_ID';
693
				$where_sql    = Settings::get_comments_filter_sql();
694
				break;
695
			case 'comment_meta':
696
				$object_table = $wpdb->commentmeta;
697
				$where_sql    = Settings::get_whitelisted_comment_meta_sql();
698
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
699
				$id_field     = 'meta_id';
700
				break;
701
			case 'terms':
702
				$object_table = $wpdb->terms;
703
				$object_count = $this->term_count();
704
				$id_field     = 'term_id';
705
				$where_sql    = '1=1';
706
				break;
707
			default:
708
				return false;
709
		}
710
711
		$bucket_size     = intval( ceil( $object_count / $buckets ) );
712
		$previous_max_id = 0;
713
		$histogram       = array();
714
715
		$where = '1=1';
716
717
		if ( $start_id ) {
718
			$where .= " AND $id_field >= " . intval( $start_id );
719
		}
720
721
		if ( $end_id ) {
722
			$where .= " AND $id_field <= " . intval( $end_id );
723
		}
724
725
		do {
726
			list( $first_id, $last_id ) = $wpdb->get_row(
727
				"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",
728
				ARRAY_N
729
			);
730
731
			if ( null === $first_id || null === $last_id ) {
732
				// Nothing to checksum here...
733
				break;
734
			}
735
736
			// get the checksum value
737
			$value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $first_id, $last_id, $strip_non_ascii, $salt );
738
739
			if ( is_wp_error( $value ) ) {
740
				return $value;
741
			}
742
743
			if ( null === $first_id || null === $last_id ) {
744
				break;
745
			} elseif ( $first_id === $last_id ) {
746
				$histogram[ $first_id ] = $value;
747
			} else {
748
				$histogram[ "{$first_id}-{$last_id}" ] = $value;
749
			}
750
751
			$previous_max_id = $last_id;
752
		} while ( true );
753
754
		return $histogram;
755
	}
756
757
	private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true, $salt = '' ) {
758
		global $wpdb;
759
760
		// sanitize to just valid MySQL column names
761
		$sanitized_columns = preg_grep( '/^[0-9,a-z,A-Z$_]+$/i', $columns );
762
763
		if ( $strip_non_ascii ) {
764
			$columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) );
765
		} else {
766
			$columns_sql = implode( ',', $sanitized_columns );
767
		}
768
769
		if ( null !== $min_id && null !== $max_id ) {
770
			if ( $min_id === $max_id ) {
771
				$min_id     = intval( $min_id );
772
				$where_sql .= " AND $id_column = $min_id LIMIT 1";
773
			} else {
774
				$min_id     = intval( $min_id );
775
				$max_id     = intval( $max_id );
776
				$size       = $max_id - $min_id;
777
				$where_sql .= " AND $id_column >= $min_id AND $id_column <= $max_id LIMIT $size";
778
			}
779
		} else {
780
			if ( null !== $min_id ) {
781
				$min_id     = intval( $min_id );
782
				$where_sql .= " AND $id_column >= $min_id";
783
			}
784
785
			if ( null !== $max_id ) {
786
				$max_id     = intval( $max_id );
787
				$where_sql .= " AND $id_column <= $max_id";
788
			}
789
		}
790
791
		$query  = <<<ENDSQL
792
			SELECT CAST( SUM( CRC32( CONCAT_WS( '#', '%s', {$columns_sql} ) ) ) AS UNSIGNED INT )
793
			FROM $table
794
			WHERE $where_sql;
795
ENDSQL;
796
		$result = $wpdb->get_var( $wpdb->prepare( $query, $salt ) );
797
		if ( $wpdb->last_error ) {
798
			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...
799
		}
800
801
		return $result;
802
	}
803
804
	public function get_checksum_type() {
805
		return 'sum';
806
	}
807
808
	private function meta_count( $table, $where_sql, $min_id, $max_id ) {
809
		global $wpdb;
810
811
		if ( $min_id != null ) {
812
			$where_sql .= ' AND meta_id >= ' . intval( $min_id );
813
		}
814
815
		if ( $max_id != null ) {
816
			$where_sql .= ' AND meta_id <= ' . intval( $max_id );
817
		}
818
819
		return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where_sql" );
820
	}
821
822
	/**
823
	 * Wraps a column name in SQL which strips non-ASCII chars.
824
	 * This helps normalize data to avoid checksum differences caused by
825
	 * badly encoded data in the DB
826
	 */
827
	function strip_non_ascii_sql( $column_name ) {
828
		return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )";
829
	}
830
831
	private function invalid_call() {
832
		$backtrace = debug_backtrace();
833
		$caller    = $backtrace[1]['function'];
834
		throw new \Exception( "This function $caller is not supported on the WP Replicastore" );
835
	}
836
}
837