Completed
Push — try/statically-access-asset-to... ( e50fad...74c9e7 )
by
unknown
126:59 queued 118:11
created

Replicastore::comment_count()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 21

Duplication

Lines 21
Ratio 100 %

Importance

Changes 0
Metric Value
cc 4
nc 8
nop 3
dl 21
loc 21
rs 9.584
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
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( $config ) {
35
		$this->reset();
36
	}
37
38
	function full_sync_end( $checksum ) {
39
		// noop right now
40
	}
41
42 View Code Duplication
	public function post_count( $status = null, $min_id = null, $max_id = null ) {
43
		global $wpdb;
44
45
		$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...
46
47
		if ( $status ) {
48
			$where = "post_status = '" . esc_sql( $status ) . "'";
49
		} else {
50
			$where = '1=1';
51
		}
52
53
		if ( null != $min_id ) {
54
			$where .= ' AND ID >= ' . intval( $min_id );
55
		}
56
57
		if ( null != $max_id ) {
58
			$where .= ' AND ID <= ' . intval( $max_id );
59
		}
60
61
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
62
	}
63
64
	// TODO: actually use max_id/min_id
65
	public function get_posts( $status = null, $min_id = null, $max_id = null ) {
66
		$args = array(
67
			'orderby'        => 'ID',
68
			'posts_per_page' => -1,
69
		);
70
71
		if ( $status ) {
72
			$args['post_status'] = $status;
73
		} else {
74
			$args['post_status'] = 'any';
75
		}
76
77
		return get_posts( $args );
78
	}
79
80
	public function get_post( $id ) {
81
		return get_post( $id );
82
	}
83
84
	public function upsert_post( $post, $silent = false ) {
85
		global $wpdb;
86
87
		// reject the post if it's not a WP_Post
88
		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...
89
			return;
90
		}
91
92
		$post = $post->to_array();
93
94
		// reject posts without an ID
95
		if ( ! isset( $post['ID'] ) ) {
96
			return;
97
		}
98
99
		$now     = current_time( 'mysql' );
100
		$now_gmt = get_gmt_from_date( $now );
101
102
		$defaults = array(
103
			'ID'                    => 0,
104
			'post_author'           => '0',
105
			'post_content'          => '',
106
			'post_content_filtered' => '',
107
			'post_title'            => '',
108
			'post_name'             => '',
109
			'post_excerpt'          => '',
110
			'post_status'           => 'draft',
111
			'post_type'             => 'post',
112
			'comment_status'        => 'closed',
113
			'comment_count'         => '0',
114
			'ping_status'           => '',
115
			'post_password'         => '',
116
			'to_ping'               => '',
117
			'pinged'                => '',
118
			'post_parent'           => 0,
119
			'menu_order'            => 0,
120
			'guid'                  => '',
121
			'post_date'             => $now,
122
			'post_date_gmt'         => $now_gmt,
123
			'post_modified'         => $now,
124
			'post_modified_gmt'     => $now_gmt,
125
		);
126
127
		$post = array_intersect_key( $post, $defaults );
128
129
		$post = sanitize_post( $post, 'db' );
130
131
		unset( $post['filter'] );
132
133
		$exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
134
135
		if ( $exists ) {
136
			$wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
137
		} else {
138
			$wpdb->insert( $wpdb->posts, $post );
139
		}
140
141
		clean_post_cache( $post['ID'] );
142
	}
143
144
	public function delete_post( $post_id ) {
145
		wp_delete_post( $post_id, true );
146
	}
147
148
	public function posts_checksum( $min_id = null, $max_id = null ) {
149
		global $wpdb;
150
		return $this->table_checksum( $wpdb->posts, \Jetpack_Sync_Defaults::$default_post_checksum_columns, 'ID', \Jetpack_Sync_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 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...
151
	}
152
153
	public function post_meta_checksum( $min_id = null, $max_id = null ) {
154
		global $wpdb;
155
		return $this->table_checksum( $wpdb->postmeta, \Jetpack_Sync_Defaults::$default_post_meta_checksum_columns, 'meta_id', \Jetpack_Sync_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 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...
156
	}
157
158 View Code Duplication
	public function comment_count( $status = null, $min_id = null, $max_id = null ) {
159
		global $wpdb;
160
161
		$comment_approved = $this->comment_status_to_approval_value( $status );
162
163
		if ( $comment_approved !== false ) {
164
			$where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
165
		} else {
166
			$where = '1=1';
167
		}
168
169
		if ( $min_id != null ) {
170
			$where .= ' AND comment_ID >= ' . intval( $min_id );
171
		}
172
173
		if ( $max_id != null ) {
174
			$where .= ' AND comment_ID <= ' . intval( $max_id );
175
		}
176
177
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
178
	}
179
180
	private function comment_status_to_approval_value( $status ) {
181
		switch ( $status ) {
182
			case 'approve':
183
				return '1';
184
			case 'hold':
185
				return '0';
186
			case 'spam':
187
				return 'spam';
188
			case 'trash':
189
				return 'trash';
190
			case 'any':
191
				return false;
192
			case 'all':
193
				return false;
194
			default:
195
				return false;
196
		}
197
	}
198
199
	// TODO: actually use max_id/min_id
200
	public function get_comments( $status = null, $min_id = null, $max_id = null ) {
201
		$args = array(
202
			'orderby' => 'ID',
203
			'status'  => 'all',
204
		);
205
206
		if ( $status ) {
207
			$args['status'] = $status;
208
		}
209
210
		return get_comments( $args );
211
	}
212
213
	public function get_comment( $id ) {
214
		return \WP_Comment::get_instance( $id );
215
	}
216
217
	public function upsert_comment( $comment ) {
218
		global $wpdb;
219
220
		$comment = $comment->to_array();
221
222
		// filter by fields on comment table
223
		$comment_fields_whitelist = array(
224
			'comment_ID',
225
			'comment_post_ID',
226
			'comment_author',
227
			'comment_author_email',
228
			'comment_author_url',
229
			'comment_author_IP',
230
			'comment_date',
231
			'comment_date_gmt',
232
			'comment_content',
233
			'comment_karma',
234
			'comment_approved',
235
			'comment_agent',
236
			'comment_type',
237
			'comment_parent',
238
			'user_id',
239
		);
240
241
		foreach ( $comment as $key => $value ) {
242
			if ( ! in_array( $key, $comment_fields_whitelist ) ) {
243
				unset( $comment[ $key ] );
244
			}
245
		}
246
247
		$exists = $wpdb->get_var(
248
			$wpdb->prepare(
249
				"SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
250
				$comment['comment_ID']
251
			)
252
		);
253
254
		if ( $exists ) {
255
			$wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
256
		} else {
257
			$wpdb->insert( $wpdb->comments, $comment );
258
		}
259
260
		wp_update_comment_count( $comment['comment_post_ID'] );
261
	}
262
263
	public function trash_comment( $comment_id ) {
264
		wp_delete_comment( $comment_id );
265
	}
266
267
	public function delete_comment( $comment_id ) {
268
		wp_delete_comment( $comment_id, true );
269
	}
270
271
	public function spam_comment( $comment_id ) {
272
		wp_spam_comment( $comment_id );
273
	}
274
275
	public function trashed_post_comments( $post_id, $statuses ) {
276
		wp_trash_post_comments( $post_id );
277
	}
278
279
	public function untrashed_post_comments( $post_id ) {
280
		wp_untrash_post_comments( $post_id );
281
	}
282
283
	public function comments_checksum( $min_id = null, $max_id = null ) {
284
		global $wpdb;
285
		return $this->table_checksum( $wpdb->comments, \Jetpack_Sync_Defaults::$default_comment_checksum_columns, 'comment_ID', \Jetpack_Sync_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 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...
286
	}
287
288
	public function comment_meta_checksum( $min_id = null, $max_id = null ) {
289
		global $wpdb;
290
		return $this->table_checksum( $wpdb->commentmeta, \Jetpack_Sync_Defaults::$default_comment_meta_checksum_columns, 'meta_id', \Jetpack_Sync_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 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...
291
	}
292
293
	public function options_checksum() {
294
		global $wpdb;
295
296
		$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...
297
		$where_sql         = "option_name IN ( $options_whitelist )";
298
299
		return $this->table_checksum( $wpdb->options, \Jetpack_Sync_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 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
	}
301
302
303
	public function update_option( $option, $value ) {
304
		return update_option( $option, $value );
305
	}
306
307
	public function get_option( $option, $default = false ) {
308
		return get_option( $option, $default );
309
	}
310
311
	public function delete_option( $option ) {
312
		return delete_option( $option );
313
	}
314
315
	public function set_theme_support( $theme_support ) {
316
		// noop
317
	}
318
319
	public function current_theme_supports( $feature ) {
320
		return current_theme_supports( $feature );
321
	}
322
323
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
324
		return get_metadata( $type, $object_id, $meta_key, $single );
325
	}
326
327
	/**
328
	 *
329
	 * Stores remote meta key/values alongside an ID mapping key
330
	 *
331
	 * @param $type
332
	 * @param $object_id
333
	 * @param $meta_key
334
	 * @param $meta_value
335
	 * @param $meta_id
336
	 *
337
	 * @return bool
338
	 */
339
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
340
341
		$table = _get_meta_table( $type );
342
		if ( ! $table ) {
343
			return false;
344
		}
345
346
		global $wpdb;
347
348
		$exists = $wpdb->get_var(
349
			$wpdb->prepare(
350
				"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
351
				$meta_id
352
			)
353
		);
354
355
		if ( $exists ) {
356
			$wpdb->update(
357
				$table,
358
				array(
359
					'meta_key'   => $meta_key,
360
					'meta_value' => maybe_serialize( $meta_value ),
361
				),
362
				array( 'meta_id' => $meta_id )
363
			);
364
		} else {
365
			$object_id_field = $type . '_id';
366
			$wpdb->insert(
367
				$table,
368
				array(
369
					'meta_id'        => $meta_id,
370
					$object_id_field => $object_id,
371
					'meta_key'       => $meta_key,
372
					'meta_value'     => maybe_serialize( $meta_value ),
373
				)
374
			);
375
		}
376
377
		wp_cache_delete( $object_id, $type . '_meta' );
378
379
		return true;
380
	}
381
382
	public function delete_metadata( $type, $object_id, $meta_ids ) {
383
		global $wpdb;
384
385
		$table = _get_meta_table( $type );
386
		if ( ! $table ) {
387
			return false;
388
		}
389
390
		foreach ( $meta_ids as $meta_id ) {
391
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
392
		}
393
394
		// if we don't have an object ID what do we do - invalidate ALL meta?
395
		if ( $object_id ) {
396
			wp_cache_delete( $object_id, $type . '_meta' );
397
		}
398
	}
399
400
	// todo: test this out to make sure it works as expected.
401
	public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
402
		global $wpdb;
403
404
		$table = _get_meta_table( $type );
405
		if ( ! $table ) {
406
			return false;
407
		}
408
		$column = sanitize_key( $type . '_id' );
409
		$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ), $meta_key ) );
410
411
		// if we don't have an object ID what do we do - invalidate ALL meta?
412
		foreach ( $object_ids as $object_id ) {
413
			wp_cache_delete( $object_id, $type . '_meta' );
414
		}
415
	}
416
417
	// constants
418
	public function get_constant( $constant ) {
419
		$value = get_option( 'jetpack_constant_' . $constant );
420
421
		if ( $value ) {
422
			return $value;
423
		}
424
425
		return null;
426
	}
427
428
	public function set_constant( $constant, $value ) {
429
		update_option( 'jetpack_constant_' . $constant, $value );
430
	}
431
432
	public function get_updates( $type ) {
433
		$all_updates = get_option( 'jetpack_updates', array() );
434
435
		if ( isset( $all_updates[ $type ] ) ) {
436
			return $all_updates[ $type ];
437
		} else {
438
			return null;
439
		}
440
	}
441
442
	public function set_updates( $type, $updates ) {
443
		$all_updates          = get_option( 'jetpack_updates', array() );
444
		$all_updates[ $type ] = $updates;
445
		update_option( 'jetpack_updates', $all_updates );
446
	}
447
448
	// functions
449
	public function get_callable( $name ) {
450
		$value = get_option( 'jetpack_' . $name );
451
452
		if ( $value ) {
453
			return $value;
454
		}
455
456
		return null;
457
	}
458
459
	public function set_callable( $name, $value ) {
460
		update_option( 'jetpack_' . $name, $value );
461
	}
462
463
	// network options
464
	public function get_site_option( $option ) {
465
		return get_option( 'jetpack_network_' . $option );
466
	}
467
468
	public function update_site_option( $option, $value ) {
469
		return update_option( 'jetpack_network_' . $option, $value );
470
	}
471
472
	public function delete_site_option( $option ) {
473
		return delete_option( 'jetpack_network_' . $option );
474
	}
475
476
	// terms
477
	// terms
478
	public function get_terms( $taxonomy ) {
479
		return get_terms( $taxonomy );
480
	}
481
482
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
483
		$t = $this->ensure_taxonomy( $taxonomy );
484
		if ( ! $t || is_wp_error( $t ) ) {
485
			return $t;
486
		}
487
488
		return get_term( $term_id, $taxonomy );
489
	}
490
491
	private function ensure_taxonomy( $taxonomy ) {
492
		if ( ! taxonomy_exists( $taxonomy ) ) {
493
			// try re-registering synced taxonomies
494
			$taxonomies = $this->get_callable( 'taxonomies' );
495
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
496
				// doesn't exist, or somehow hasn't been synced
497
				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...
498
			}
499
			$t = $taxonomies[ $taxonomy ];
500
501
			return register_taxonomy(
502
				$taxonomy,
503
				$t->object_type,
504
				(array) $t
505
			);
506
		}
507
508
		return true;
509
	}
510
511
	public function get_the_terms( $object_id, $taxonomy ) {
512
		return get_the_terms( $object_id, $taxonomy );
513
	}
514
515
	public function update_term( $term_object ) {
516
		$taxonomy = $term_object->taxonomy;
517
		global $wpdb;
518
		$exists = $wpdb->get_var(
519
			$wpdb->prepare(
520
				"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
521
				$term_object->term_id
522
			)
523
		);
524
		if ( ! $exists ) {
525
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
526
			$term          = array(
527
				'term_id'    => $term_object->term_id,
528
				'name'       => $term_object->name,
529
				'slug'       => $term_object->slug,
530
				'term_group' => $term_object->term_group,
531
			);
532
			$term_taxonomy = array(
533
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
534
				'term_id'          => $term_object->term_id,
535
				'taxonomy'         => $term_object->taxonomy,
536
				'description'      => $term_object->description,
537
				'parent'           => (int) $term_object->parent,
538
				'count'            => (int) $term_object->count,
539
			);
540
			$wpdb->insert( $wpdb->terms, $term );
541
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
542
543
			return true;
544
		}
545
546
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
547
	}
548
549
	public function delete_term( $term_id, $taxonomy ) {
550
		return wp_delete_term( $term_id, $taxonomy );
551
	}
552
553
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
554
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
555
	}
556
557
	public function delete_object_terms( $object_id, $tt_ids ) {
558
		global $wpdb;
559
560
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
561
			// escape
562
			$tt_ids_sanitized = array_map( 'intval', $tt_ids );
563
564
			$taxonomies = array();
565
			foreach ( $tt_ids_sanitized as $tt_id ) {
566
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
567
				$taxonomies[ $term->taxonomy ][] = $tt_id;
568
			}
569
			$in_tt_ids = implode( ', ', $tt_ids_sanitized );
570
571
			/**
572
			 * Fires immediately before an object-term relationship is deleted.
573
			 *
574
			 * @since 2.9.0
575
			 *
576
			 * @param int $object_id Object ID.
577
			 * @param array $tt_ids An array of term taxonomy IDs.
578
			 */
579
			do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
580
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
581
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
582
				$this->ensure_taxonomy( $taxonomy );
583
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
584
				/**
585
				 * Fires immediately after an object-term relationship is deleted.
586
				 *
587
				 * @since 2.9.0
588
				 *
589
				 * @param int $object_id Object ID.
590
				 * @param array $tt_ids An array of term taxonomy IDs.
591
				 */
592
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
593
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
594
			}
595
596
			return (bool) $deleted;
597
		}
598
599
		return false;
600
	}
601
602
	// users
603
	public function user_count() {
604
605
	}
606
607
	public function get_user( $user_id ) {
608
		return \WP_User::get_instance( $user_id );
609
	}
610
611
	public function upsert_user( $user ) {
612
		$this->invalid_call();
613
	}
614
615
	public function delete_user( $user_id ) {
616
		$this->invalid_call();
617
	}
618
619
	public function upsert_user_locale( $user_id, $local ) {
620
		$this->invalid_call();
621
	}
622
623
	public function delete_user_locale( $user_id ) {
624
		$this->invalid_call();
625
	}
626
627
	public function get_user_locale( $user_id ) {
628
		return get_user_locale( $user_id );
629
	}
630
631
	public function get_allowed_mime_types( $user_id ) {
632
633
	}
634
635
	public function checksum_all() {
636
		$post_meta_checksum    = $this->checksum_histogram( 'post_meta', 1 );
637
		$comment_meta_checksum = $this->checksum_histogram( 'comment_meta', 1 );
638
639
		return array(
640
			'posts'        => $this->posts_checksum(),
641
			'comments'     => $this->comments_checksum(),
642
			'post_meta'    => reset( $post_meta_checksum ),
643
			'comment_meta' => reset( $comment_meta_checksum ),
644
		);
645
	}
646
647
	function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '' ) {
648
		global $wpdb;
649
650
		$wpdb->queries = array();
651
652
		switch ( $object_type ) {
653
			case 'posts':
654
				$object_count = $this->post_count( null, $start_id, $end_id );
655
				$object_table = $wpdb->posts;
656
				$id_field     = 'ID';
657
				$where_sql    = \Jetpack_Sync_Settings::get_blacklisted_post_types_sql();
658
				if ( empty( $columns ) ) {
659
					$columns = \Jetpack_Sync_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 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
				}
661
				break;
662 View Code Duplication
			case 'post_meta':
663
				$object_table = $wpdb->postmeta;
664
				$where_sql    = \Jetpack_Sync_Settings::get_whitelisted_post_meta_sql();
665
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
666
				$id_field     = 'meta_id';
667
668
				if ( empty( $columns ) ) {
669
					$columns = \Jetpack_Sync_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 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...
670
				}
671
				break;
672
			case 'comments':
673
				$object_count = $this->comment_count( null, $start_id, $end_id );
674
				$object_table = $wpdb->comments;
675
				$id_field     = 'comment_ID';
676
				$where_sql    = \Jetpack_Sync_Settings::get_comments_filter_sql();
677
				if ( empty( $columns ) ) {
678
					$columns = \Jetpack_Sync_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 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...
679
				}
680
				break;
681 View Code Duplication
			case 'comment_meta':
682
				$object_table = $wpdb->commentmeta;
683
				$where_sql    = \Jetpack_Sync_Settings::get_whitelisted_comment_meta_sql();
684
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
685
				$id_field     = 'meta_id';
686
				if ( empty( $columns ) ) {
687
					$columns = \Jetpack_Sync_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 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...
688
				}
689
				break;
690
			default:
691
				return false;
692
		}
693
694
		$bucket_size     = intval( ceil( $object_count / $buckets ) );
695
		$previous_max_id = 0;
696
		$histogram       = array();
697
698
		$where = '1=1';
699
700
		if ( $start_id ) {
701
			$where .= " AND $id_field >= " . intval( $start_id );
702
		}
703
704
		if ( $end_id ) {
705
			$where .= " AND $id_field <= " . intval( $end_id );
706
		}
707
708
		do {
709
			list( $first_id, $last_id ) = $wpdb->get_row(
710
				"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",
711
				ARRAY_N
712
			);
713
714
			if ( null === $first_id || null === $last_id ) {
715
				// Nothing to checksum here...
716
				break;
717
			}
718
719
			// get the checksum value
720
			$value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $first_id, $last_id, $strip_non_ascii, $salt );
721
722
			if ( is_wp_error( $value ) ) {
723
				return $value;
724
			}
725
726
			if ( null === $first_id || null === $last_id ) {
727
				break;
728
			} elseif ( $first_id === $last_id ) {
729
				$histogram[ $first_id ] = $value;
730
			} else {
731
				$histogram[ "{$first_id}-{$last_id}" ] = $value;
732
			}
733
734
			$previous_max_id = $last_id;
735
		} while ( true );
736
737
		return $histogram;
738
	}
739
740
	private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true, $salt = '' ) {
741
		global $wpdb;
742
743
		// sanitize to just valid MySQL column names
744
		$sanitized_columns = preg_grep( '/^[0-9,a-z,A-Z$_]+$/i', $columns );
745
746
		if ( $strip_non_ascii ) {
747
			$columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) );
748
		} else {
749
			$columns_sql = implode( ',', $sanitized_columns );
750
		}
751
752
		if ( null !== $min_id && null !== $max_id ) {
753
			if ( $min_id === $max_id ) {
754
				$min_id     = intval( $min_id );
755
				$where_sql .= " AND $id_column = $min_id LIMIT 1";
756
			} else {
757
				$min_id     = intval( $min_id );
758
				$max_id     = intval( $max_id );
759
				$size       = $max_id - $min_id;
760
				$where_sql .= " AND $id_column >= $min_id AND $id_column <= $max_id LIMIT $size";
761
			}
762
		} else {
763
			if ( null !== $min_id ) {
764
				$min_id     = intval( $min_id );
765
				$where_sql .= " AND $id_column >= $min_id";
766
			}
767
768
			if ( null !== $max_id ) {
769
				$max_id     = intval( $max_id );
770
				$where_sql .= " AND $id_column <= $max_id";
771
			}
772
		}
773
774
		$query  = <<<ENDSQL
775
			SELECT CAST( SUM( CRC32( CONCAT_WS( '#', '%s', {$columns_sql} ) ) ) AS UNSIGNED INT )
776
			FROM $table
777
			WHERE $where_sql;
778
ENDSQL;
779
		$result = $wpdb->get_var( $wpdb->prepare( $query, $salt ) );
780
		if ( $wpdb->last_error ) {
781
			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...
782
		}
783
784
		return $result;
785
	}
786
787
	public function get_checksum_type() {
788
		return 'sum';
789
	}
790
791
	private function meta_count( $table, $where_sql, $min_id, $max_id ) {
792
		global $wpdb;
793
794
		if ( $min_id != null ) {
795
			$where_sql .= ' AND meta_id >= ' . intval( $min_id );
796
		}
797
798
		if ( $max_id != null ) {
799
			$where_sql .= ' AND meta_id <= ' . intval( $max_id );
800
		}
801
802
		return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where_sql" );
803
	}
804
805
	/**
806
	 * Wraps a column name in SQL which strips non-ASCII chars.
807
	 * This helps normalize data to avoid checksum differences caused by
808
	 * badly encoded data in the DB
809
	 */
810
	function strip_non_ascii_sql( $column_name ) {
811
		return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )";
812
	}
813
814
	private function invalid_call() {
815
		$backtrace = debug_backtrace();
816
		$caller    = $backtrace[1]['function'];
817
		throw new \Exception( "This function $caller is not supported on the WP Replicastore" );
818
	}
819
}
820