Completed
Push — add/protect-module-sync-packag... ( ad899a...19deca )
by
unknown
133:23 queued 125:08
created

Replicastore::get_callable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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