Completed
Push — add/gdpr-ads-compliance ( c52a1e...cda5c5 )
by
unknown
26:32 queued 14:46
created

delete_batch_metadata()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 3
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
require_once dirname( __FILE__ ) . '/interface.jetpack-sync-replicastore.php';
4
require_once dirname( __FILE__ ) . '/class.jetpack-sync-defaults.php';
5
6
/**
7
 * An implementation of iJetpack_Sync_Replicastore which returns data stored in a WordPress.org DB.
8
 * This is useful to compare values in the local WP DB to values in the synced replica store
9
 */
10
class Jetpack_Sync_WP_Replicastore implements iJetpack_Sync_Replicastore {
11
12
13
	public function reset() {
14
		global $wpdb;
15
16
		$wpdb->query( "DELETE FROM $wpdb->posts" );
17
		$wpdb->query( "DELETE FROM $wpdb->comments" );
18
19
		// also need to delete terms from cache
20
		$term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
21
		foreach ( $term_ids as $term_id ) {
22
			wp_cache_delete( $term_id, 'terms' );
23
		}
24
25
		$wpdb->query( "DELETE FROM $wpdb->terms" );
26
27
		$wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
28
		$wpdb->query( "DELETE FROM $wpdb->term_relationships" );
29
30
		// callables and constants
31
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
32
		$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
33
	}
34
35
	function full_sync_start( $config ) {
36
		$this->reset();
37
	}
38
39
	function full_sync_end( $checksum ) {
40
		// noop right now
41
	}
42
43 View Code Duplication
	public function post_count( $status = null, $min_id = null, $max_id = null ) {
44
		global $wpdb;
45
46
		$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...
47
48
		if ( $status ) {
49
			$where = "post_status = '" . esc_sql( $status ) . "'";
50
		} else {
51
			$where = '1=1';
52
		}
53
54
		if ( null != $min_id ) {
55
			$where .= ' AND ID >= ' . intval( $min_id );
56
		}
57
58
		if ( null != $max_id ) {
59
			$where .= ' AND ID <= ' . intval( $max_id );
60
		}
61
62
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
63
	}
64
65
	// TODO: actually use max_id/min_id
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
66
	public function get_posts( $status = null, $min_id = null, $max_id = null ) {
67
		$args = array( 'orderby' => 'ID', 'posts_per_page' => -1 );
68
69
		if ( $status ) {
70
			$args['post_status'] = $status;
71
		} else {
72
			$args['post_status'] = 'any';
73
		}
74
75
		return get_posts( $args );
76
	}
77
78
	public function get_post( $id ) {
79
		return get_post( $id );
80
	}
81
82
	public function upsert_post( $post, $silent = false ) {
83
		global $wpdb;
84
85
		// reject the post if it's not a WP_Post
86
		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...
87
			return;
88
		}
89
90
		$post = $post->to_array();
91
92
		// reject posts without an ID
93
		if ( ! isset( $post['ID'] ) ) {
94
			return;
95
		}
96
97
		$now     = current_time( 'mysql' );
98
		$now_gmt = get_gmt_from_date( $now );
99
100
		$defaults = array(
101
			'ID'                    => 0,
102
			'post_author'           => '0',
103
			'post_content'          => '',
104
			'post_content_filtered' => '',
105
			'post_title'            => '',
106
			'post_name'             => '',
107
			'post_excerpt'          => '',
108
			'post_status'           => 'draft',
109
			'post_type'             => 'post',
110
			'comment_status'        => 'closed',
111
			'comment_count'         => '0',
112
			'ping_status'           => '',
113
			'post_password'         => '',
114
			'to_ping'               => '',
115
			'pinged'                => '',
116
			'post_parent'           => 0,
117
			'menu_order'            => 0,
118
			'guid'                  => '',
119
			'post_date'             => $now,
120
			'post_date_gmt'         => $now_gmt,
121
			'post_modified'         => $now,
122
			'post_modified_gmt'     => $now_gmt,
123
		);
124
125
		$post = array_intersect_key( $post, $defaults );
126
127
		$post = sanitize_post( $post, 'db' );
128
129
		unset( $post['filter'] );
130
131
		$exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
132
133
		if ( $exists ) {
134
			$wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
135
		} else {
136
			$wpdb->insert( $wpdb->posts, $post );
137
		}
138
139
		clean_post_cache( $post['ID'] );
140
	}
141
142
	public function delete_post( $post_id ) {
143
		wp_delete_post( $post_id, true );
144
	}
145
146
	public function posts_checksum( $min_id = null, $max_id = null ) {
147
		global $wpdb;
148
		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...
149
	}
150
151
	public function post_meta_checksum( $min_id = null, $max_id = null ) {
152
		global $wpdb;
153
		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...
154
	}
155
156 View Code Duplication
	public function comment_count( $status = null, $min_id = null, $max_id = null ) {
157
		global $wpdb;
158
159
		$comment_approved = $this->comment_status_to_approval_value( $status );
160
161
		if ( $comment_approved !== false ) {
162
			$where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
163
		} else {
164
			$where = '1=1';
165
		}
166
167
		if ( $min_id != null ) {
168
			$where .= ' AND comment_ID >= ' . intval( $min_id );
169
		}
170
171
		if ( $max_id != null ) {
172
			$where .= ' AND comment_ID <= ' . intval( $max_id );
173
		}
174
175
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
176
	}
177
178
	private function comment_status_to_approval_value( $status ) {
179
		switch ( $status ) {
180
			case 'approve':
181
				return '1';
182
			case 'hold':
183
				return '0';
184
			case 'spam':
185
				return 'spam';
186
			case 'trash':
187
				return 'trash';
188
			case 'any':
189
				return false;
190
			case 'all':
191
				return false;
192
			default:
193
				return false;
194
		}
195
	}
196
197
	// TODO: actually use max_id/min_id
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
198
	public function get_comments( $status = null, $min_id = null, $max_id = null ) {
199
		$args = array( 'orderby' => 'ID', 'status' => 'all' );
200
201
		if ( $status ) {
202
			$args['status'] = $status;
203
		}
204
205
		return get_comments( $args );
206
	}
207
208
	public function get_comment( $id ) {
209
		return WP_Comment::get_instance( $id );
210
	}
211
212
	public function upsert_comment( $comment ) {
213
		global $wpdb, $wp_version;
214
215
		if ( version_compare( $wp_version, '4.4', '<' ) ) {
216
			$comment = (array) $comment;
217
		} else {
218
			// WP 4.4 introduced the WP_Comment Class
219
			$comment = $comment->to_array();
220
		}
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( $wpdb->prepare(
349
			"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
350
			$meta_id
351
		) );
352
353
		if ( $exists ) {
354
			$wpdb->update( $table, array(
355
				'meta_key'   => $meta_key,
356
				'meta_value' => maybe_serialize( $meta_value ),
357
			), array( 'meta_id' => $meta_id ) );
358
		} else {
359
			$object_id_field = $type . '_id';
360
			$wpdb->insert( $table, array(
361
				'meta_id'        => $meta_id,
362
				$object_id_field => $object_id,
363
				'meta_key'       => $meta_key,
364
				'meta_value'     => maybe_serialize( $meta_value ),
365
			) );
366
		}
367
368
		wp_cache_delete( $object_id, $type . '_meta' );
369
370
		return true;
371
	}
372
373
	public function delete_metadata( $type, $object_id, $meta_ids ) {
374
		global $wpdb;
375
376
		$table = _get_meta_table( $type );
377
		if ( ! $table ) {
378
			return false;
379
		}
380
381
		foreach ( $meta_ids as $meta_id ) {
382
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
383
		}
384
385
		// if we don't have an object ID what do we do - invalidate ALL meta?
386
		if ( $object_id ) {
387
			wp_cache_delete( $object_id, $type . '_meta' );
388
		}
389
	}
390
391
	// todo: test this out to make sure it works as expected.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
392
	public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
393
		global $wpdb;
394
395
		$table = _get_meta_table( $type );
396
		if ( ! $table ) {
397
			return false;
398
		}
399
		$column = sanitize_key($type . '_id' );
400
		$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ),  $meta_key ) );
401
402
		// if we don't have an object ID what do we do - invalidate ALL meta?
403
		foreach ( $object_ids as $object_id ) {
404
			wp_cache_delete( $object_id, $type . '_meta' );
405
		}
406
	}
407
408
	// constants
409
	public function get_constant( $constant ) {
410
		$value = get_option( 'jetpack_constant_' . $constant );
411
412
		if ( $value ) {
413
			return $value;
414
		}
415
416
		return null;
417
	}
418
419
	public function set_constant( $constant, $value ) {
420
		update_option( 'jetpack_constant_' . $constant, $value );
421
	}
422
423
	public function get_updates( $type ) {
424
		$all_updates = get_option( 'jetpack_updates', array() );
425
426
		if ( isset( $all_updates[ $type ] ) ) {
427
			return $all_updates[ $type ];
428
		} else {
429
			return null;
430
		}
431
	}
432
433
	public function set_updates( $type, $updates ) {
434
		$all_updates          = get_option( 'jetpack_updates', array() );
435
		$all_updates[ $type ] = $updates;
436
		update_option( 'jetpack_updates', $all_updates );
437
	}
438
439
	// functions
440
	public function get_callable( $name ) {
441
		$value = get_option( 'jetpack_' . $name );
442
443
		if ( $value ) {
444
			return $value;
445
		}
446
447
		return null;
448
	}
449
450
	public function set_callable( $name, $value ) {
451
		update_option( 'jetpack_' . $name, $value );
452
	}
453
454
	// network options
455
	public function get_site_option( $option ) {
456
		return get_option( 'jetpack_network_' . $option );
457
	}
458
459
	public function update_site_option( $option, $value ) {
460
		return update_option( 'jetpack_network_' . $option, $value );
461
	}
462
463
	public function delete_site_option( $option ) {
464
		return delete_option( 'jetpack_network_' . $option );
465
	}
466
467
	// terms
468
	// terms
469
	public function get_terms( $taxonomy ) {
470
		return get_terms( $taxonomy );
471
	}
472
473
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
474
		$t = $this->ensure_taxonomy( $taxonomy );
475
		if ( ! $t || is_wp_error( $t ) ) {
476
			return $t;
477
		}
478
479
		return get_term( $term_id, $taxonomy );
480
	}
481
482
	private function ensure_taxonomy( $taxonomy ) {
483
		if ( ! taxonomy_exists( $taxonomy ) ) {
484
			// try re-registering synced taxonomies
485
			$taxonomies = $this->get_callable( 'taxonomies' );
486
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
487
				// doesn't exist, or somehow hasn't been synced
488
				return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
489
			}
490
			$t = $taxonomies[ $taxonomy ];
491
492
			return register_taxonomy(
493
				$taxonomy,
494
				$t->object_type,
495
				(array) $t
496
			);
497
		}
498
499
		return true;
500
	}
501
502
	public function get_the_terms( $object_id, $taxonomy ) {
503
		return get_the_terms( $object_id, $taxonomy );
504
	}
505
506
	public function update_term( $term_object ) {
507
		$taxonomy = $term_object->taxonomy;
508
		global $wpdb;
509
		$exists = $wpdb->get_var( $wpdb->prepare(
510
			"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
511
			$term_object->term_id
512
		) );
513
		if ( ! $exists ) {
514
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
515
			$term          = array(
516
				'term_id'    => $term_object->term_id,
517
				'name'       => $term_object->name,
518
				'slug'       => $term_object->slug,
519
				'term_group' => $term_object->term_group,
520
			);
521
			$term_taxonomy = array(
522
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
523
				'term_id'          => $term_object->term_id,
524
				'taxonomy'         => $term_object->taxonomy,
525
				'description'      => $term_object->description,
526
				'parent'           => (int) $term_object->parent,
527
				'count'            => (int) $term_object->count,
528
			);
529
			$wpdb->insert( $wpdb->terms, $term );
530
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
531
532
			return true;
533
		}
534
535
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
536
	}
537
538
	public function delete_term( $term_id, $taxonomy ) {
539
		return wp_delete_term( $term_id, $taxonomy );
540
	}
541
542
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
543
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
544
	}
545
546
	public function delete_object_terms( $object_id, $tt_ids ) {
547
		global $wpdb;
548
549
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
550
			// escape
551
			$tt_ids_sanitized = array_map( 'intval', $tt_ids );
552
553
			$taxonomies = array();
554
			foreach ( $tt_ids_sanitized as $tt_id ) {
555
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
556
				$taxonomies[ $term->taxonomy ][] = $tt_id;
557
			}
558
			$in_tt_ids = implode( ", ", $tt_ids_sanitized );
559
560
			/**
561
			 * Fires immediately before an object-term relationship is deleted.
562
			 *
563
			 * @since 2.9.0
564
			 *
565
			 * @param int $object_id Object ID.
566
			 * @param array $tt_ids An array of term taxonomy IDs.
567
			 */
568
			do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
569
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
570
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
571
				$this->ensure_taxonomy( $taxonomy );
572
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
573
				/**
574
				 * Fires immediately after an object-term relationship is deleted.
575
				 *
576
				 * @since 2.9.0
577
				 *
578
				 * @param int $object_id Object ID.
579
				 * @param array $tt_ids An array of term taxonomy IDs.
580
				 */
581
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
582
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
583
			}
584
585
			return (bool) $deleted;
586
		}
587
588
		return false;
589
	}
590
591
	// users
592
	public function user_count() {
593
594
	}
595
596
	public function get_user( $user_id ) {
597
		return WP_User::get_instance( $user_id );
598
	}
599
600
	public function upsert_user( $user ) {
601
		$this->invalid_call();
602
	}
603
604
	public function delete_user( $user_id ) {
605
		$this->invalid_call();
606
	}
607
608
	public function upsert_user_locale( $user_id, $local ) {
609
		$this->invalid_call();
610
	}
611
612
	public function delete_user_locale( $user_id ) {
613
		$this->invalid_call();
614
	}
615
616
	public function get_user_locale( $user_id ) {
617
		return jetpack_get_user_locale( $user_id );
0 ignored issues
show
Unused Code introduced by
The call to jetpack_get_user_locale() has too many arguments starting with $user_id.

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...
618
	}
619
620
	public function get_allowed_mime_types( $user_id ) {
621
622
	}
623
624
	public function checksum_all() {
625
		$post_meta_checksum = $this->checksum_histogram( 'post_meta', 1 );
626
		$comment_meta_checksum = $this->checksum_histogram( 'comment_meta', 1 );
627
628
		return array(
629
			'posts'    => $this->posts_checksum(),
630
			'comments' => $this->comments_checksum(),
631
			'post_meta'=> reset( $post_meta_checksum ),
632
			'comment_meta'=> reset( $comment_meta_checksum ),
633
		);
634
	}
635
636
	function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true ) {
637
		global $wpdb;
638
639
		$wpdb->queries = array();
640
641
		switch( $object_type ) {
642
			case "posts":
643
				$object_count = $this->post_count( null, $start_id, $end_id );
644
				$object_table = $wpdb->posts;
645
				$id_field     = 'ID';
646
				$where_sql    = Jetpack_Sync_Settings::get_blacklisted_post_types_sql();
647
				if ( empty( $columns ) ) {
648
					$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...
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
649
				}
650
				break;
651 View Code Duplication
			case "post_meta":
652
				$object_table = $wpdb->postmeta;
653
				$where_sql    = Jetpack_Sync_Settings::get_whitelisted_post_meta_sql();
654
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
655
				$id_field     = 'meta_id';
656
657
				if ( empty( $columns ) ) {
658
					$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...
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
659
				}
660
				break;
661
			case "comments":
662
				$object_count = $this->comment_count( null, $start_id, $end_id );
663
				$object_table = $wpdb->comments;
664
				$id_field     = 'comment_ID';
665
				$where_sql    = Jetpack_Sync_Settings::get_comments_filter_sql();
666
				if ( empty( $columns ) ) {
667
					$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...
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
668
				}
669
				break;
670 View Code Duplication
			case "comment_meta":
671
				$object_table = $wpdb->commentmeta;
672
				$where_sql    = Jetpack_Sync_Settings::get_whitelisted_comment_meta_sql();
673
				$object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
674
				$id_field     = 'meta_id';
675
				if ( empty( $columns ) ) {
676
					$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...
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
677
				}
678
				break;
679
			default:
680
				return false;
681
		}
682
683
		$bucket_size  = intval( ceil( $object_count / $buckets ) );
684
		$previous_max_id = 0;
685
		$histogram    = array();
686
687
		$where = '1=1';
688
689
		if ( $start_id ) {
690
			$where .= " AND $id_field >= " . intval( $start_id );
691
		}
692
693
		if ( $end_id ) {
694
			$where .= " AND $id_field <= " . intval( $end_id );
695
		}
696
697
		do {
698
			list( $first_id, $last_id ) = $wpdb->get_row(
699
				"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",
700
				ARRAY_N
701
			);
702
703
			// get the checksum value
704
			$value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $first_id, $last_id, $strip_non_ascii );
705
706
			if ( is_wp_error( $value ) ) {
707
				return $value;
708
			}
709
710
			if ( $first_id === null || $last_id === null ) {
711
				break;
712
			} elseif ( $first_id === $last_id ) {
713
				$histogram[ $first_id ] = $value;
714
			} else {
715
				$histogram[ "{$first_id}-{$last_id}" ] = $value;
716
			}
717
718
			$previous_max_id = $last_id;
719
		} while ( true );
720
721
		return $histogram;
722
	}
723
724
	private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true ) {
725
		global $wpdb;
726
727
		// sanitize to just valid MySQL column names
728
		$sanitized_columns = preg_grep ( '/^[0-9,a-z,A-Z$_]+$/i', $columns );
729
730
		if ( $strip_non_ascii ) {
731
			$columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) );
732
		} else {
733
			$columns_sql = implode( ',', $sanitized_columns );
734
		}
735
736
		if ( $min_id !== null ) {
737
			$min_id = intval( $min_id );
738
			$where_sql .= " AND $id_column >= $min_id";
739
		}
740
741
		if ( $max_id !== null ) {
742
			$max_id = intval( $max_id );
743
			$where_sql .= " AND $id_column <= $max_id";
744
		}
745
746
		$query = <<<ENDSQL
747
			SELECT CONV(BIT_XOR(CRC32(CONCAT({$columns_sql}))), 10, 16)
748
				FROM $table
749
				WHERE $where_sql
750
ENDSQL;
751
		$result = $wpdb->get_var( $query );
752
753
		if ( $wpdb->last_error ) {
754
			return new WP_Error( 'database_error', $wpdb->last_error );
755
		}
756
757
		return $result;
758
759
	}
760
761
	private function meta_count( $table, $where_sql, $min_id, $max_id ) {
762
		global $wpdb;
763
764
		if ( $min_id != null ) {
765
			$where_sql .= ' AND meta_id >= ' . intval( $min_id );
766
		}
767
768
		if ( $max_id != null ) {
769
			$where_sql .= ' AND meta_id <= ' . intval( $max_id );
770
		}
771
772
		return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where_sql" );
773
	}
774
775
	/**
776
	 * Wraps a column name in SQL which strips non-ASCII chars.
777
	 * This helps normalize data to avoid checksum differences caused by
778
	 * badly encoded data in the DB
779
	 */
780
	function strip_non_ascii_sql( $column_name ) {
781
		return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )";
782
	}
783
784
	private function invalid_call() {
785
		$backtrace = debug_backtrace();
786
		$caller    = $backtrace[1]['function'];
787
		throw new Exception( "This function $caller is not supported on the WP Replicastore" );
788
	}
789
}
790