Completed
Push — master-stable ( c9231e...1bac3c )
by
unknown
676:01 queued 666:50
created

Jetpack_Sync_WP_Replicastore::get_updates()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 9
rs 9.6666
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 View Code Duplication
	public function comment_count( $status = null, $min_id = null, $max_id = null ) {
152
		global $wpdb;
153
154
		$comment_approved = $this->comment_status_to_approval_value( $status );
155
156
		if ( $comment_approved !== false ) {
157
			$where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
158
		} else {
159
			$where = '1=1';
160
		}
161
162
		if ( $min_id != null ) {
163
			$where .= ' AND comment_ID >= ' . intval( $min_id );
164
		}
165
166
		if ( $max_id != null ) {
167
			$where .= ' AND comment_ID <= ' . intval( $max_id );
168
		}
169
170
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
171
	}
172
173
	private function comment_status_to_approval_value( $status ) {
174
		switch ( $status ) {
175
			case 'approve':
176
				return '1';
177
			case 'hold':
178
				return '0';
179
			case 'spam':
180
				return 'spam';
181
			case 'trash':
182
				return 'trash';
183
			case 'any':
184
				return false;
185
			case 'all':
186
				return false;
187
			default:
188
				return false;
189
		}
190
	}
191
192
	// 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...
193
	public function get_comments( $status = null, $min_id = null, $max_id = null ) {
194
		$args = array( 'orderby' => 'ID', 'status' => 'all' );
195
196
		if ( $status ) {
197
			$args['status'] = $status;
198
		}
199
200
		return get_comments( $args );
201
	}
202
203
	public function get_comment( $id ) {
204
		return WP_Comment::get_instance( $id );
205
	}
206
207
	public function upsert_comment( $comment ) {
208
		global $wpdb, $wp_version;
209
210
		if ( version_compare( $wp_version, '4.4', '<' ) ) {
211
			$comment = (array) $comment;
212
		} else {
213
			// WP 4.4 introduced the WP_Comment Class
214
			$comment = $comment->to_array();
215
		}
216
217
		// filter by fields on comment table
218
		$comment_fields_whitelist = array(
219
			'comment_ID',
220
			'comment_post_ID',
221
			'comment_author',
222
			'comment_author_email',
223
			'comment_author_url',
224
			'comment_author_IP',
225
			'comment_date',
226
			'comment_date_gmt',
227
			'comment_content',
228
			'comment_karma',
229
			'comment_approved',
230
			'comment_agent',
231
			'comment_type',
232
			'comment_parent',
233
			'user_id',
234
		);
235
236
		foreach ( $comment as $key => $value ) {
237
			if ( ! in_array( $key, $comment_fields_whitelist ) ) {
238
				unset( $comment[ $key ] );
239
			}
240
		}
241
242
		$exists = $wpdb->get_var(
243
			$wpdb->prepare(
244
				"SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
245
				$comment['comment_ID']
246
			)
247
		);
248
249
		if ( $exists ) {
250
			$wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
251
		} else {
252
			$wpdb->insert( $wpdb->comments, $comment );
253
		}
254
255
		wp_update_comment_count( $comment['comment_post_ID'] );
256
	}
257
258
	public function trash_comment( $comment_id ) {
259
		wp_delete_comment( $comment_id );
260
	}
261
262
	public function delete_comment( $comment_id ) {
263
		wp_delete_comment( $comment_id, true );
264
	}
265
266
	public function spam_comment( $comment_id ) {
267
		wp_spam_comment( $comment_id );
268
	}
269
270
	public function trashed_post_comments( $post_id, $statuses ) {
271
		wp_trash_post_comments( $post_id );
272
	}
273
274
	public function untrashed_post_comments( $post_id ) {
275
		wp_untrash_post_comments( $post_id );
276
	}
277
278
	public function comments_checksum( $min_id = null, $max_id = null ) {
279
		global $wpdb;
280
		return $this->table_checksum( $wpdb->comments, Jetpack_Sync_Defaults::$default_comment_checksum_columns, 'comment_ID', "comment_approved <> 'spam'", $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...
281
	}
282
283
	public function options_checksum() {
284
		global $wpdb;
285
286
		$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...
287
		$where_sql = "option_name IN ( $options_whitelist )";
288
289
		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...
290
	}
291
292
293
	public function update_option( $option, $value ) {
294
		return update_option( $option, $value );
295
	}
296
297
	public function get_option( $option, $default = false ) {
298
		return get_option( $option, $default );
299
	}
300
301
	public function delete_option( $option ) {
302
		return delete_option( $option );
303
	}
304
305
	public function set_theme_support( $theme_support ) {
306
		// noop
307
	}
308
309
	public function current_theme_supports( $feature ) {
310
		return current_theme_supports( $feature );
311
	}
312
313
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
314
		return get_metadata( $type, $object_id, $meta_key, $single );
315
	}
316
317
	/**
318
	 *
319
	 * Stores remote meta key/values alongside an ID mapping key
320
	 *
321
	 * @param $type
322
	 * @param $object_id
323
	 * @param $meta_key
324
	 * @param $meta_value
325
	 * @param $meta_id
326
	 *
327
	 * @return bool
328
	 */
329
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
330
331
		$table = _get_meta_table( $type );
332
		if ( ! $table ) {
333
			return false;
334
		}
335
336
		global $wpdb;
337
338
		$exists = $wpdb->get_var( $wpdb->prepare(
339
			"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
340
			$meta_id
341
		) );
342
343
		if ( $exists ) {
344
			$wpdb->update( $table, array(
345
				'meta_key'   => $meta_key,
346
				'meta_value' => serialize( $meta_value ),
347
			), array( 'meta_id' => $meta_id ) );
348
		} else {
349
			$object_id_field = $type . '_id';
350
			$wpdb->insert( $table, array(
351
				'meta_id'        => $meta_id,
352
				$object_id_field => $object_id,
353
				'meta_key'       => $meta_key,
354
				'meta_value'     => serialize( $meta_value ),
355
			) );
356
		}
357
358
		wp_cache_delete( $object_id, $type . '_meta' );
359
360
		return true;
361
	}
362
363
	public function delete_metadata( $type, $object_id, $meta_ids ) {
364
		global $wpdb;
365
366
		$table = _get_meta_table( $type );
367
		if ( ! $table ) {
368
			return false;
369
		}
370
371
		foreach ( $meta_ids as $meta_id ) {
372
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
373
		}
374
375
		// if we don't have an object ID what do we do - invalidate ALL meta?
376
		if ( $object_id ) {
377
			wp_cache_delete( $object_id, $type . '_meta' );
378
		}
379
	}
380
381
	// constants
382
	public function get_constant( $constant ) {
383
		$value = get_option( 'jetpack_constant_' . $constant );
384
385
		if ( $value ) {
386
			return $value;
387
		}
388
389
		return null;
390
	}
391
392
	public function set_constant( $constant, $value ) {
393
		update_option( 'jetpack_constant_' . $constant, $value );
394
	}
395
396
	public function get_updates( $type ) {
397
		$all_updates = get_option( 'jetpack_updates', array() );
398
399
		if ( isset( $all_updates[ $type ] ) ) {
400
			return $all_updates[ $type ];
401
		} else {
402
			return null;
403
		}
404
	}
405
406
	public function set_updates( $type, $updates ) {
407
		$all_updates          = get_option( 'jetpack_updates', array() );
408
		$all_updates[ $type ] = $updates;
409
		update_option( 'jetpack_updates', $all_updates );
410
	}
411
412
	// functions
413
	public function get_callable( $name ) {
414
		$value = get_option( 'jetpack_' . $name );
415
416
		if ( $value ) {
417
			return $value;
418
		}
419
420
		return null;
421
	}
422
423
	public function set_callable( $name, $value ) {
424
		update_option( 'jetpack_' . $name, $value );
425
	}
426
427
	// network options
428
	public function get_site_option( $option ) {
429
		return get_option( 'jetpack_network_' . $option );
430
	}
431
432
	public function update_site_option( $option, $value ) {
433
		return update_option( 'jetpack_network_' . $option, $value );
434
	}
435
436
	public function delete_site_option( $option ) {
437
		return delete_option( 'jetpack_network_' . $option );
438
	}
439
440
	// terms
441
	// terms
442
	public function get_terms( $taxonomy ) {
443
		return get_terms( $taxonomy );
444
	}
445
446
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
447
		$t = $this->ensure_taxonomy( $taxonomy );
448
		if ( ! $t || is_wp_error( $t ) ) {
449
			return $t;
450
		}
451
452
		return get_term( $term_id, $taxonomy );
453
	}
454
455
	private function ensure_taxonomy( $taxonomy ) {
456
		if ( ! taxonomy_exists( $taxonomy ) ) {
457
			// try re-registering synced taxonomies
458
			$taxonomies = $this->get_callable( 'taxonomies' );
459
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
460
				// doesn't exist, or somehow hasn't been synced
461
				return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
462
			}
463
			$t = $taxonomies[ $taxonomy ];
464
465
			return register_taxonomy(
466
				$taxonomy,
467
				$t->object_type,
468
				(array) $t
469
			);
470
		}
471
472
		return true;
473
	}
474
475
	public function get_the_terms( $object_id, $taxonomy ) {
476
		return get_the_terms( $object_id, $taxonomy );
477
	}
478
479
	public function update_term( $term_object ) {
480
		$taxonomy = $term_object->taxonomy;
481
		global $wpdb;
482
		$exists = $wpdb->get_var( $wpdb->prepare(
483
			"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
484
			$term_object->term_id
485
		) );
486
		if ( ! $exists ) {
487
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
488
			$term          = array(
489
				'term_id'    => $term_object->term_id,
490
				'name'       => $term_object->name,
491
				'slug'       => $term_object->slug,
492
				'term_group' => $term_object->term_group,
493
			);
494
			$term_taxonomy = array(
495
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
496
				'term_id'          => $term_object->term_id,
497
				'taxonomy'         => $term_object->taxonomy,
498
				'description'      => $term_object->description,
499
				'parent'           => (int) $term_object->parent,
500
				'count'            => (int) $term_object->count,
501
			);
502
			$wpdb->insert( $wpdb->terms, $term );
503
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
504
505
			return true;
506
		}
507
508
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
509
	}
510
511
	public function delete_term( $term_id, $taxonomy ) {
512
		return wp_delete_term( $term_id, $taxonomy );
513
	}
514
515
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
516
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
517
	}
518
519
	public function delete_object_terms( $object_id, $tt_ids ) {
520
		global $wpdb;
521
522
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
523
			$taxonomies = array();
524
			foreach ( $tt_ids as $tt_id ) {
525
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
526
				$taxonomies[ $term->taxonomy ][] = $tt_id;
527
			}
528
			$in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'";
529
530
			/**
531
			 * Fires immediately before an object-term relationship is deleted.
532
			 *
533
			 * @since 2.9.0
534
			 *
535
			 * @param int $object_id Object ID.
536
			 * @param array $tt_ids An array of term taxonomy IDs.
537
			 */
538
			do_action( 'delete_term_relationships', $object_id, $tt_ids );
539
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
540
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
541
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
542
				/**
543
				 * Fires immediately after an object-term relationship is deleted.
544
				 *
545
				 * @since 2.9.0
546
				 *
547
				 * @param int $object_id Object ID.
548
				 * @param array $tt_ids An array of term taxonomy IDs.
549
				 */
550
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
551
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
552
			}
553
554
			return (bool) $deleted;
555
		}
556
557
		return false;
558
	}
559
560
	// users
561
	public function user_count() {
562
563
	}
564
565
	public function get_user( $user_id ) {
566
		return WP_User::get_instance( $user_id );
567
	}
568
569
	public function upsert_user( $user ) {
570
		$this->invalid_call();
571
	}
572
573
	public function delete_user( $user_id ) {
574
		$this->invalid_call();
575
	}
576
577
	public function get_allowed_mime_types( $user_id ) {
578
579
	}
580
581
	public function checksum_all() {
582
		return array(
583
			'posts'    => $this->posts_checksum(),
584
			'comments' => $this->comments_checksum()
585
		);
586
	}
587
588
	function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null ) {
589
		global $wpdb;
590
591
		$wpdb->queries = array();
592
593
		switch( $object_type ) {
594
			case "posts":
595
				$object_count = $this->post_count( null, $start_id, $end_id );
596
				$object_table = $wpdb->posts;
597
				$id_field     = 'ID';
598
				if ( empty( $columns ) ) {
599
					$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...
600
				}
601
				break;
602
			case "comments":
603
				$object_count = $this->comment_count( null, $start_id, $end_id );
604
				$object_table = $wpdb->comments;
605
				$id_field     = 'comment_ID';
606
				if ( empty( $columns ) ) {
607
					$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...
608
				}
609
				break;
610
			default:
611
				return false;
612
		}
613
614
		$bucket_size  = intval( ceil( $object_count / $buckets ) );
615
		$previous_max_id = 0;
616
		$histogram    = array();
617
618
		$where = '1=1';
619
620
		if ( $start_id ) {
621
			$where .= " AND $id_field >= " . intval( $start_id );
622
		}
623
624
		if ( $end_id ) {
625
			$where .= " AND $id_field <= " . intval( $end_id );
626
		}
627
628
		do {
629
			list( $first_id, $last_id ) = $wpdb->get_row(
630
				"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",
631
				ARRAY_N
632
			);
633
634
			// get the checksum value
635
			$value = $this->table_checksum( $object_table, $columns, $id_field, '1=1', $first_id, $last_id );
636
637
			if ( is_wp_error( $value ) ) {
638
				return $value;
639
			}
640
641
			if ( $first_id === null || $last_id === null ) {
642
				break;
643
			} elseif ( $first_id === $last_id ) {
644
				$histogram[ $first_id ] = $value;
645
			} else {
646
				$histogram[ "{$first_id}-{$last_id}" ] = $value;
647
			}
648
649
			$previous_max_id = $last_id;
650
		} while ( true );
651
652
		return $histogram;
653
	}
654
655
	private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null ) {
656
		global $wpdb;
657
658
		// sanitize to just valid MySQL column names
659
		$sanitized_columns = preg_grep ( '/^[0-9,a-z,A-Z$_]+$/i', $columns );
660
		$columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) );
661
662
		if ( $min_id !== null ) {
663
			$min_id = intval( $min_id );
664
			$where_sql .= " AND $id_column >= $min_id";
665
		}
666
667
		if ( $max_id !== null ) {
668
			$max_id = intval( $max_id );
669
			$where_sql .= " AND $id_column <= $max_id";
670
		}
671
672
		$query = <<<ENDSQL
673
			SELECT CONV(BIT_XOR(CRC32(CONCAT({$columns_sql}))), 10, 16)
674
				FROM $table
675
				WHERE $where_sql
676
ENDSQL;
677
678
		$result = $wpdb->get_var( $query );
679
680
		if ( $wpdb->last_error ) {
681
			return new WP_Error( 'database_error', $wpdb->last_error );
682
		}
683
684
		return $result;
685
686
	}
687
688
	/**
689
	 * Wraps a column name in SQL which strips non-ASCII chars.
690
	 * This helps normalize data to avoid checksum differences caused by
691
	 * badly encoded data in the DB
692
	 */
693
	function strip_non_ascii_sql( $column_name ) {
694
		return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )";
695
	}
696
697
	private function invalid_call() {
698
		$backtrace = debug_backtrace();
699
		$caller    = $backtrace[1]['function'];
700
		throw new Exception( "This function $caller is not supported on the WP Replicastore" );
701
	}
702
}
703