Completed
Push — dna-sync-settings ( 229df0...4ab5ef )
by
unknown
172:59 queued 164:54
created

Replicastore::user_count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
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
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, 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 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...
151
	}
152
153
	public function post_meta_checksum( $min_id = null, $max_id = null ) {
154
		global $wpdb;
155
		return $this->table_checksum( $wpdb->postmeta, 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 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...
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, 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 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...
286
	}
287
288
	public function comment_meta_checksum( $min_id = null, $max_id = null ) {
289
		global $wpdb;
290
		return $this->table_checksum( $wpdb->commentmeta, 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 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...
291
	}
292
293
	public function options_checksum() {
294
		global $wpdb;
295
		$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...
296
		$where_sql         = "option_name IN ( $options_whitelist )";
297
298
		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...
299
	}
300
301
302
	public function update_option( $option, $value ) {
303
		return update_option( $option, $value );
304
	}
305
306
	public function get_option( $option, $default = false ) {
307
		return get_option( $option, $default );
308
	}
309
310
	public function delete_option( $option ) {
311
		return delete_option( $option );
312
	}
313
314
	public function set_theme_support( $theme_support ) {
315
		// noop
316
	}
317
318
	public function current_theme_supports( $feature ) {
319
		return current_theme_supports( $feature );
320
	}
321
322
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
323
		return get_metadata( $type, $object_id, $meta_key, $single );
324
	}
325
326
	/**
327
	 *
328
	 * Stores remote meta key/values alongside an ID mapping key
329
	 *
330
	 * @param $type
331
	 * @param $object_id
332
	 * @param $meta_key
333
	 * @param $meta_value
334
	 * @param $meta_id
335
	 *
336
	 * @return bool
337
	 */
338
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
339
340
		$table = _get_meta_table( $type );
341
		if ( ! $table ) {
342
			return false;
343
		}
344
345
		global $wpdb;
346
347
		$exists = $wpdb->get_var(
348
			$wpdb->prepare(
349
				"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
350
				$meta_id
351
			)
352
		);
353
354
		if ( $exists ) {
355
			$wpdb->update(
356
				$table,
357
				array(
358
					'meta_key'   => $meta_key,
359
					'meta_value' => maybe_serialize( $meta_value ),
360
				),
361
				array( 'meta_id' => $meta_id )
362
			);
363
		} else {
364
			$object_id_field = $type . '_id';
365
			$wpdb->insert(
366
				$table,
367
				array(
368
					'meta_id'        => $meta_id,
369
					$object_id_field => $object_id,
370
					'meta_key'       => $meta_key,
371
					'meta_value'     => maybe_serialize( $meta_value ),
372
				)
373
			);
374
		}
375
376
		wp_cache_delete( $object_id, $type . '_meta' );
377
378
		return true;
379
	}
380
381
	public function delete_metadata( $type, $object_id, $meta_ids ) {
382
		global $wpdb;
383
384
		$table = _get_meta_table( $type );
385
		if ( ! $table ) {
386
			return false;
387
		}
388
389
		foreach ( $meta_ids as $meta_id ) {
390
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
391
		}
392
393
		// if we don't have an object ID what do we do - invalidate ALL meta?
394
		if ( $object_id ) {
395
			wp_cache_delete( $object_id, $type . '_meta' );
396
		}
397
	}
398
399
	// todo: test this out to make sure it works as expected.
400
	public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
401
		global $wpdb;
402
403
		$table = _get_meta_table( $type );
404
		if ( ! $table ) {
405
			return false;
406
		}
407
		$column = sanitize_key( $type . '_id' );
408
		$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ), $meta_key ) );
409
410
		// if we don't have an object ID what do we do - invalidate ALL meta?
411
		foreach ( $object_ids as $object_id ) {
412
			wp_cache_delete( $object_id, $type . '_meta' );
413
		}
414
	}
415
416
	// constants
417
	public function get_constant( $constant ) {
418
		$value = get_option( 'jetpack_constant_' . $constant );
419
420
		if ( $value ) {
421
			return $value;
422
		}
423
424
		return null;
425
	}
426
427
	public function set_constant( $constant, $value ) {
428
		update_option( 'jetpack_constant_' . $constant, $value );
429
	}
430
431
	public function get_updates( $type ) {
432
		$all_updates = get_option( 'jetpack_updates', array() );
433
434
		if ( isset( $all_updates[ $type ] ) ) {
435
			return $all_updates[ $type ];
436
		} else {
437
			return null;
438
		}
439
	}
440
441
	public function set_updates( $type, $updates ) {
442
		$all_updates          = get_option( 'jetpack_updates', array() );
443
		$all_updates[ $type ] = $updates;
444
		update_option( 'jetpack_updates', $all_updates );
445
	}
446
447
	// functions
448
	public function get_callable( $name ) {
449
		$value = get_option( 'jetpack_' . $name );
450
451
		if ( $value ) {
452
			return $value;
453
		}
454
455
		return null;
456
	}
457
458
	public function set_callable( $name, $value ) {
459
		update_option( 'jetpack_' . $name, $value );
460
	}
461
462
	// network options
463
	public function get_site_option( $option ) {
464
		return get_option( 'jetpack_network_' . $option );
465
	}
466
467
	public function update_site_option( $option, $value ) {
468
		return update_option( 'jetpack_network_' . $option, $value );
469
	}
470
471
	public function delete_site_option( $option ) {
472
		return delete_option( 'jetpack_network_' . $option );
473
	}
474
475
	// terms
476
	// terms
477
	public function get_terms( $taxonomy ) {
478
		return get_terms( $taxonomy );
479
	}
480
481
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
482
		$t = $this->ensure_taxonomy( $taxonomy );
483
		if ( ! $t || is_wp_error( $t ) ) {
484
			return $t;
485
		}
486
487
		return get_term( $term_id, $taxonomy );
488
	}
489
490
	private function ensure_taxonomy( $taxonomy ) {
491
		if ( ! taxonomy_exists( $taxonomy ) ) {
492
			// try re-registering synced taxonomies
493
			$taxonomies = $this->get_callable( 'taxonomies' );
494
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
495
				// doesn't exist, or somehow hasn't been synced
496
				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...
497
			}
498
			$t = $taxonomies[ $taxonomy ];
499
500
			return register_taxonomy(
501
				$taxonomy,
502
				$t->object_type,
503
				(array) $t
504
			);
505
		}
506
507
		return true;
508
	}
509
510
	public function get_the_terms( $object_id, $taxonomy ) {
511
		return get_the_terms( $object_id, $taxonomy );
512
	}
513
514
	public function update_term( $term_object ) {
515
		$taxonomy = $term_object->taxonomy;
516
		global $wpdb;
517
		$exists = $wpdb->get_var(
518
			$wpdb->prepare(
519
				"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
520
				$term_object->term_id
521
			)
522
		);
523
		if ( ! $exists ) {
524
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
525
			$term          = array(
526
				'term_id'    => $term_object->term_id,
527
				'name'       => $term_object->name,
528
				'slug'       => $term_object->slug,
529
				'term_group' => $term_object->term_group,
530
			);
531
			$term_taxonomy = array(
532
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
533
				'term_id'          => $term_object->term_id,
534
				'taxonomy'         => $term_object->taxonomy,
535
				'description'      => $term_object->description,
536
				'parent'           => (int) $term_object->parent,
537
				'count'            => (int) $term_object->count,
538
			);
539
			$wpdb->insert( $wpdb->terms, $term );
540
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
541
542
			return true;
543
		}
544
545
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
546
	}
547
548
	public function delete_term( $term_id, $taxonomy ) {
549
		return wp_delete_term( $term_id, $taxonomy );
550
	}
551
552
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
553
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
554
	}
555
556
	public function delete_object_terms( $object_id, $tt_ids ) {
557
		global $wpdb;
558
559
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
560
			// escape
561
			$tt_ids_sanitized = array_map( 'intval', $tt_ids );
562
563
			$taxonomies = array();
564
			foreach ( $tt_ids_sanitized as $tt_id ) {
565
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
566
				$taxonomies[ $term->taxonomy ][] = $tt_id;
567
			}
568
			$in_tt_ids = implode( ', ', $tt_ids_sanitized );
569
570
			/**
571
			 * Fires immediately before an object-term relationship is deleted.
572
			 *
573
			 * @since 2.9.0
574
			 *
575
			 * @param int $object_id Object ID.
576
			 * @param array $tt_ids An array of term taxonomy IDs.
577
			 */
578
			do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
579
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
580
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
581
				$this->ensure_taxonomy( $taxonomy );
582
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
583
				/**
584
				 * Fires immediately after an object-term relationship is deleted.
585
				 *
586
				 * @since 2.9.0
587
				 *
588
				 * @param int $object_id Object ID.
589
				 * @param array $tt_ids An array of term taxonomy IDs.
590
				 */
591
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
592
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
593
			}
594
595
			return (bool) $deleted;
596
		}
597
598
		return false;
599
	}
600
601
	// users
602
	public function user_count() {
603
604
	}
605
606
	public function get_user( $user_id ) {
607
		return \WP_User::get_instance( $user_id );
608
	}
609
610
	public function upsert_user( $user ) {
611
		$this->invalid_call();
612
	}
613
614
	public function delete_user( $user_id ) {
615
		$this->invalid_call();
616
	}
617
618
	public function upsert_user_locale( $user_id, $local ) {
619
		$this->invalid_call();
620
	}
621
622
	public function delete_user_locale( $user_id ) {
623
		$this->invalid_call();
624
	}
625
626
	public function get_user_locale( $user_id ) {
627
		return get_user_locale( $user_id );
628
	}
629
630
	public function get_allowed_mime_types( $user_id ) {
631
632
	}
633
634
	public function checksum_all() {
635
		$post_meta_checksum    = $this->checksum_histogram( 'post_meta', 1 );
636
		$comment_meta_checksum = $this->checksum_histogram( 'comment_meta', 1 );
637
638
		return array(
639
			'posts'        => $this->posts_checksum(),
640
			'comments'     => $this->comments_checksum(),
641
			'post_meta'    => reset( $post_meta_checksum ),
642
			'comment_meta' => reset( $comment_meta_checksum ),
643
		);
644
	}
645
646
	function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '' ) {
647
		global $wpdb;
648
649
		$wpdb->queries = array();
650
651
		switch ( $object_type ) {
652
			case 'posts':
653
				$object_count = $this->post_count( null, $start_id, $end_id );
654
				$object_table = $wpdb->posts;
655
				$id_field     = 'ID';
656
				$where_sql    = Settings::get_blacklisted_post_types_sql();
657
				if ( empty( $columns ) ) {
658
					$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...
659
				}
660
				break;
661 View Code Duplication
			case 'post_meta':
662
				$object_table = $wpdb->postmeta;
663
				$where_sql    = Settings::get_whitelisted_post_meta_sql();
664
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
665
				$id_field     = 'meta_id';
666
667
				if ( empty( $columns ) ) {
668
					$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...
669
				}
670
				break;
671
			case 'comments':
672
				$object_count = $this->comment_count( null, $start_id, $end_id );
673
				$object_table = $wpdb->comments;
674
				$id_field     = 'comment_ID';
675
				$where_sql    = Settings::get_comments_filter_sql();
676
				if ( empty( $columns ) ) {
677
					$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...
678
				}
679
				break;
680 View Code Duplication
			case 'comment_meta':
681
				$object_table = $wpdb->commentmeta;
682
				$where_sql    = Settings::get_whitelisted_comment_meta_sql();
683
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
684
				$id_field     = 'meta_id';
685
				if ( empty( $columns ) ) {
686
					$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...
687
				}
688
				break;
689
			default:
690
				return false;
691
		}
692
693
		$bucket_size     = intval( ceil( $object_count / $buckets ) );
694
		$previous_max_id = 0;
695
		$histogram       = array();
696
697
		$where = '1=1';
698
699
		if ( $start_id ) {
700
			$where .= " AND $id_field >= " . intval( $start_id );
701
		}
702
703
		if ( $end_id ) {
704
			$where .= " AND $id_field <= " . intval( $end_id );
705
		}
706
707
		do {
708
			list( $first_id, $last_id ) = $wpdb->get_row(
709
				"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",
710
				ARRAY_N
711
			);
712
713
			if ( null === $first_id || null === $last_id ) {
714
				// Nothing to checksum here...
715
				break;
716
			}
717
718
			// get the checksum value
719
			$value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $first_id, $last_id, $strip_non_ascii, $salt );
720
721
			if ( is_wp_error( $value ) ) {
722
				return $value;
723
			}
724
725
			if ( null === $first_id || null === $last_id ) {
726
				break;
727
			} elseif ( $first_id === $last_id ) {
728
				$histogram[ $first_id ] = $value;
729
			} else {
730
				$histogram[ "{$first_id}-{$last_id}" ] = $value;
731
			}
732
733
			$previous_max_id = $last_id;
734
		} while ( true );
735
736
		return $histogram;
737
	}
738
739
	private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true, $salt = '' ) {
740
		global $wpdb;
741
742
		// sanitize to just valid MySQL column names
743
		$sanitized_columns = preg_grep( '/^[0-9,a-z,A-Z$_]+$/i', $columns );
744
745
		if ( $strip_non_ascii ) {
746
			$columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) );
747
		} else {
748
			$columns_sql = implode( ',', $sanitized_columns );
749
		}
750
751
		if ( null !== $min_id && null !== $max_id ) {
752
			if ( $min_id === $max_id ) {
753
				$min_id     = intval( $min_id );
754
				$where_sql .= " AND $id_column = $min_id LIMIT 1";
755
			} else {
756
				$min_id     = intval( $min_id );
757
				$max_id     = intval( $max_id );
758
				$size       = $max_id - $min_id;
759
				$where_sql .= " AND $id_column >= $min_id AND $id_column <= $max_id LIMIT $size";
760
			}
761
		} else {
762
			if ( null !== $min_id ) {
763
				$min_id     = intval( $min_id );
764
				$where_sql .= " AND $id_column >= $min_id";
765
			}
766
767
			if ( null !== $max_id ) {
768
				$max_id     = intval( $max_id );
769
				$where_sql .= " AND $id_column <= $max_id";
770
			}
771
		}
772
773
		$query  = <<<ENDSQL
774
			SELECT CAST( SUM( CRC32( CONCAT_WS( '#', '%s', {$columns_sql} ) ) ) AS UNSIGNED INT )
775
			FROM $table
776
			WHERE $where_sql;
777
ENDSQL;
778
		$result = $wpdb->get_var( $wpdb->prepare( $query, $salt ) );
779
		if ( $wpdb->last_error ) {
780
			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...
781
		}
782
783
		return $result;
784
	}
785
786
	public function get_checksum_type() {
787
		return 'sum';
788
	}
789
790
	private function meta_count( $table, $where_sql, $min_id, $max_id ) {
791
		global $wpdb;
792
793
		if ( $min_id != null ) {
794
			$where_sql .= ' AND meta_id >= ' . intval( $min_id );
795
		}
796
797
		if ( $max_id != null ) {
798
			$where_sql .= ' AND meta_id <= ' . intval( $max_id );
799
		}
800
801
		return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where_sql" );
802
	}
803
804
	/**
805
	 * Wraps a column name in SQL which strips non-ASCII chars.
806
	 * This helps normalize data to avoid checksum differences caused by
807
	 * badly encoded data in the DB
808
	 */
809
	function strip_non_ascii_sql( $column_name ) {
810
		return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )";
811
	}
812
813
	private function invalid_call() {
814
		$backtrace = debug_backtrace();
815
		$caller    = $backtrace[1]['function'];
816
		throw new \Exception( "This function $caller is not supported on the WP Replicastore" );
817
	}
818
}
819