Completed
Push — add/sync-partial-sync-checksum... ( c3d856...c14db7 )
by
unknown
77:39 queued 69:42
created

Replicastore::checksum_histogram()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 7
nop 7
dl 0
loc 54
rs 7.7591
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Sync replicastore.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync;
9
10
use Automattic\Jetpack\Sync\Replicastore\Table_Checksum;
11
12
/**
13
 * An implementation of Replicastore Interface which returns data stored in a WordPress.org DB.
14
 * This is useful to compare values in the local WP DB to values in the synced replica store
15
 */
16
class Replicastore implements Replicastore_Interface {
17
	/**
18
	 * Empty and reset the replicastore.
19
	 *
20
	 * @access public
21
	 */
22
	public function reset() {
23
		global $wpdb;
24
25
		$wpdb->query( "DELETE FROM $wpdb->posts" );
26
27
		// Delete comments from cache.
28
		$comment_ids = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments" );
29
		if ( ! empty( $comment_ids ) ) {
30
			clean_comment_cache( $comment_ids );
31
		}
32
		$wpdb->query( "DELETE FROM $wpdb->comments" );
33
34
		// Also need to delete terms from cache.
35
		$term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
36
		foreach ( $term_ids as $term_id ) {
37
			wp_cache_delete( $term_id, 'terms' );
38
		}
39
40
		$wpdb->query( "DELETE FROM $wpdb->terms" );
41
42
		$wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
43
		$wpdb->query( "DELETE FROM $wpdb->term_relationships" );
44
45
		// Callables and constants.
46
		$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
47
		$wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
48
	}
49
50
	/**
51
	 * Ran when full sync has just started.
52
	 *
53
	 * @access public
54
	 *
55
	 * @param array $config Full sync configuration for this sync module.
56
	 */
57
	public function full_sync_start( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
58
		$this->reset();
59
	}
60
61
	/**
62
	 * Ran when full sync has just finished.
63
	 *
64
	 * @access public
65
	 *
66
	 * @param string $checksum Deprecated since 7.3.0.
67
	 */
68
	public function full_sync_end( $checksum ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
69
		// Noop right now.
70
	}
71
72
	/**
73
	 * Retrieve the number of terms.
74
	 *
75
	 * @access public
76
	 *
77
	 * @return int Number of terms.
78
	 */
79
	public function term_count() {
80
		global $wpdb;
81
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
82
	}
83
84
	/**
85
	 * Retrieve the number of rows in the `term_taxonomy` table.
86
	 *
87
	 * @access public
88
	 *
89
	 * @return int Number of terms.
90
	 */
91
	public function term_taxonomy_count() {
92
		global $wpdb;
93
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
94
	}
95
96
	/**
97
	 * Retrieve the number of term relationships.
98
	 *
99
	 * @access public
100
	 *
101
	 * @return int Number of rows in the term relationships table.
102
	 */
103
	public function term_relationship_count() {
104
		global $wpdb;
105
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
106
	}
107
108
	/**
109
	 * Retrieve the number of posts with a particular post status within a certain range.
110
	 *
111
	 * @access public
112
	 *
113
	 * @todo Prepare the SQL query before executing it.
114
	 *
115
	 * @param string $status Post status.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $status not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
116
	 * @param int    $min_id Minimum post ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
117
	 * @param int    $max_id Maximum post ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
118
	 * @return int Number of posts.
119
	 */
120 View Code Duplication
	public function post_count( $status = null, $min_id = null, $max_id = null ) {
121
		global $wpdb;
122
123
		$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...
124
125
		if ( $status ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $status of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
126
			$where = "post_status = '" . esc_sql( $status ) . "'";
127
		} else {
128
			$where = '1=1';
129
		}
130
131
		if ( ! empty( $min_id ) ) {
132
			$where .= ' AND ID >= ' . (int) $min_id;
133
		}
134
135
		if ( ! empty( $max_id ) ) {
136
			$where .= ' AND ID <= ' . (int) $max_id;
137
		}
138
139
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
140
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
141
	}
142
143
	/**
144
	 * Retrieve the posts with a particular post status.
145
	 *
146
	 * @access public
147
	 *
148
	 * @todo Implement range and actually use max_id/min_id arguments.
149
	 *
150
	 * @param string $status Post status.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $status not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
151
	 * @param int    $min_id Minimum post ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
152
	 * @param int    $max_id Maximum post ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
153
	 * @return array Array of posts.
154
	 */
155
	public function get_posts( $status = null, $min_id = null, $max_id = null ) {
156
		$args = array(
157
			'orderby'        => 'ID',
158
			'posts_per_page' => -1,
159
		);
160
161
		if ( $status ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $status of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
162
			$args['post_status'] = $status;
163
		} else {
164
			$args['post_status'] = 'any';
165
		}
166
167
		return get_posts( $args );
168
	}
169
170
	/**
171
	 * Retrieve a post object by the post ID.
172
	 *
173
	 * @access public
174
	 *
175
	 * @param int $id Post ID.
176
	 * @return \WP_Post Post object.
177
	 */
178
	public function get_post( $id ) {
179
		return get_post( $id );
180
	}
181
182
	/**
183
	 * Update or insert a post.
184
	 *
185
	 * @access public
186
	 *
187
	 * @param \WP_Post $post   Post object.
188
	 * @param bool     $silent Whether to perform a silent action. Not used in this implementation.
189
	 */
190
	public function upsert_post( $post, $silent = false ) {
191
		global $wpdb;
192
193
		// Reject the post if it's not a \WP_Post.
194
		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...
195
			return;
196
		}
197
198
		$post = $post->to_array();
199
200
		// Reject posts without an ID.
201
		if ( ! isset( $post['ID'] ) ) {
202
			return;
203
		}
204
205
		$now     = current_time( 'mysql' );
206
		$now_gmt = get_gmt_from_date( $now );
207
208
		$defaults = array(
209
			'ID'                    => 0,
210
			'post_author'           => '0',
211
			'post_content'          => '',
212
			'post_content_filtered' => '',
213
			'post_title'            => '',
214
			'post_name'             => '',
215
			'post_excerpt'          => '',
216
			'post_status'           => 'draft',
217
			'post_type'             => 'post',
218
			'comment_status'        => 'closed',
219
			'comment_count'         => '0',
220
			'ping_status'           => '',
221
			'post_password'         => '',
222
			'to_ping'               => '',
223
			'pinged'                => '',
224
			'post_parent'           => 0,
225
			'menu_order'            => 0,
226
			'guid'                  => '',
227
			'post_date'             => $now,
228
			'post_date_gmt'         => $now_gmt,
229
			'post_modified'         => $now,
230
			'post_modified_gmt'     => $now_gmt,
231
		);
232
233
		$post = array_intersect_key( $post, $defaults );
234
235
		$post = sanitize_post( $post, 'db' );
236
237
		unset( $post['filter'] );
238
239
		$exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
240
241
		if ( $exists ) {
242
			$wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
243
		} else {
244
			$wpdb->insert( $wpdb->posts, $post );
245
		}
246
247
		clean_post_cache( $post['ID'] );
248
	}
249
250
	/**
251
	 * Delete a post by the post ID.
252
	 *
253
	 * @access public
254
	 *
255
	 * @param int $post_id Post ID.
256
	 */
257
	public function delete_post( $post_id ) {
258
		wp_delete_post( $post_id, true );
259
	}
260
261
	/**
262
	 * Retrieve the checksum for posts within a range.
263
	 *
264
	 * @access public
265
	 *
266
	 * @param int $min_id Minimum post ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
267
	 * @param int $max_id Maximum post ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
268
	 * @return int The checksum.
269
	 */
270
	public function posts_checksum( $min_id = null, $max_id = null ) {
271
		return array_sum( $this->checksum_histogram( 'posts', $this->calculate_buckets( 'posts' ) ) );
272
	}
273
274
	/**
275
	 * Retrieve the checksum for post meta within a range.
276
	 *
277
	 * @access public
278
	 *
279
	 * @param int $min_id Minimum post meta ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
280
	 * @param int $max_id Maximum post meta ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
281
	 * @return int The checksum.
282
	 */
283
	public function post_meta_checksum( $min_id = null, $max_id = null ) {
284
		return array_sum( $this->checksum_histogram( 'post_meta', $this->calculate_buckets( 'post_meta' ) ) );
285
	}
286
287
	/**
288
	 * Retrieve the number of comments with a particular comment status within a certain range.
289
	 *
290
	 * @access public
291
	 *
292
	 * @todo Prepare the SQL query before executing it.
293
	 *
294
	 * @param string $status Comment status.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $status not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
295
	 * @param int    $min_id Minimum comment ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
296
	 * @param int    $max_id Maximum comment ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
297
	 * @return int Number of comments.
298
	 */
299 View Code Duplication
	public function comment_count( $status = null, $min_id = null, $max_id = null ) {
300
		global $wpdb;
301
302
		$comment_approved = $this->comment_status_to_approval_value( $status );
303
304
		if ( false !== $comment_approved ) {
305
			$where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
306
		} else {
307
			$where = '1=1';
308
		}
309
310
		if ( ! empty( $min_id ) ) {
311
			$where .= ' AND comment_ID >= ' . (int) $min_id;
312
		}
313
314
		if ( ! empty( $max_id ) ) {
315
			$where .= ' AND comment_ID <= ' . (int) $max_id;
316
		}
317
318
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
319
		return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
320
	}
321
322
	/**
323
	 * Translate a comment status to a value of the comment_approved field.
324
	 *
325
	 * @access private
326
	 *
327
	 * @param string $status Comment status.
328
	 * @return string|bool New comment_approved value, false if the status doesn't affect it.
329
	 */
330
	private function comment_status_to_approval_value( $status ) {
331
		switch ( $status ) {
332
			case 'approve':
333
				return '1';
334
			case 'hold':
335
				return '0';
336
			case 'spam':
337
				return 'spam';
338
			case 'trash':
339
				return 'trash';
340
			case 'any':
341
				return false;
342
			case 'all':
343
				return false;
344
			default:
345
				return false;
346
		}
347
	}
348
349
	/**
350
	 * Retrieve the comments with a particular comment status.
351
	 *
352
	 * @access public
353
	 *
354
	 * @todo Implement range and actually use max_id/min_id arguments.
355
	 *
356
	 * @param string $status Comment status.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $status not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
357
	 * @param int    $min_id Minimum comment ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
358
	 * @param int    $max_id Maximum comment ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
359
	 * @return array Array of comments.
360
	 */
361
	public function get_comments( $status = null, $min_id = null, $max_id = null ) {
362
		$args = array(
363
			'orderby' => 'ID',
364
			'status'  => 'all',
365
		);
366
367
		if ( $status ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $status of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
368
			$args['status'] = $status;
369
		}
370
371
		return get_comments( $args );
372
	}
373
374
	/**
375
	 * Retrieve a comment object by the comment ID.
376
	 *
377
	 * @access public
378
	 *
379
	 * @param int $id Comment ID.
380
	 * @return \WP_Comment Comment object.
381
	 */
382
	public function get_comment( $id ) {
383
		return \WP_Comment::get_instance( $id );
384
	}
385
386
	/**
387
	 * Update or insert a comment.
388
	 *
389
	 * @access public
390
	 *
391
	 * @param \WP_Comment $comment Comment object.
392
	 */
393
	public function upsert_comment( $comment ) {
394
		global $wpdb;
395
396
		$comment = $comment->to_array();
397
398
		// Filter by fields on comment table.
399
		$comment_fields_whitelist = array(
400
			'comment_ID',
401
			'comment_post_ID',
402
			'comment_author',
403
			'comment_author_email',
404
			'comment_author_url',
405
			'comment_author_IP',
406
			'comment_date',
407
			'comment_date_gmt',
408
			'comment_content',
409
			'comment_karma',
410
			'comment_approved',
411
			'comment_agent',
412
			'comment_type',
413
			'comment_parent',
414
			'user_id',
415
		);
416
417
		foreach ( $comment as $key => $value ) {
418
			if ( ! in_array( $key, $comment_fields_whitelist, true ) ) {
419
				unset( $comment[ $key ] );
420
			}
421
		}
422
423
		$exists = $wpdb->get_var(
424
			$wpdb->prepare(
425
				"SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
426
				$comment['comment_ID']
427
			)
428
		);
429
430
		if ( $exists ) {
431
			$wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
432
		} else {
433
			$wpdb->insert( $wpdb->comments, $comment );
434
		}
435
		// Remove comment from cache.
436
		clean_comment_cache( $comment['comment_ID'] );
437
438
		wp_update_comment_count( $comment['comment_post_ID'] );
439
	}
440
441
	/**
442
	 * Trash a comment by the comment ID.
443
	 *
444
	 * @access public
445
	 *
446
	 * @param int $comment_id Comment ID.
447
	 */
448
	public function trash_comment( $comment_id ) {
449
		wp_delete_comment( $comment_id );
450
	}
451
452
	/**
453
	 * Delete a comment by the comment ID.
454
	 *
455
	 * @access public
456
	 *
457
	 * @param int $comment_id Comment ID.
458
	 */
459
	public function delete_comment( $comment_id ) {
460
		wp_delete_comment( $comment_id, true );
461
	}
462
463
	/**
464
	 * Mark a comment by the comment ID as spam.
465
	 *
466
	 * @access public
467
	 *
468
	 * @param int $comment_id Comment ID.
469
	 */
470
	public function spam_comment( $comment_id ) {
471
		wp_spam_comment( $comment_id );
472
	}
473
474
	/**
475
	 * Trash the comments of a post.
476
	 *
477
	 * @access public
478
	 *
479
	 * @param int   $post_id  Post ID.
480
	 * @param array $statuses Post statuses. Not used in this implementation.
481
	 */
482
	public function trashed_post_comments( $post_id, $statuses ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
483
		wp_trash_post_comments( $post_id );
484
	}
485
486
	/**
487
	 * Untrash the comments of a post.
488
	 *
489
	 * @access public
490
	 *
491
	 * @param int $post_id Post ID.
492
	 */
493
	public function untrashed_post_comments( $post_id ) {
494
		wp_untrash_post_comments( $post_id );
495
	}
496
497
	/**
498
	 * Retrieve the checksum for comments within a range.
499
	 *
500
	 * @access public
501
	 *
502
	 * @param int $min_id Minimum comment ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
503
	 * @param int $max_id Maximum comment ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
504
	 * @return int The checksum.
505
	 */
506
	public function comments_checksum( $min_id = null, $max_id = null ) {
507
		$checksum_table = new Table_Checksum( 'comments' );
508
		return $checksum_table->calculate_checksum( $min_id, $max_id );
0 ignored issues
show
Bug introduced by
It seems like $min_id defined by parameter $min_id on line 506 can also be of type integer; however, Automattic\Jetpack\Sync\...m::calculate_checksum() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $max_id defined by parameter $max_id on line 506 can also be of type integer; however, Automattic\Jetpack\Sync\...m::calculate_checksum() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
509
	}
510
511
	/**
512
	 * Retrieve the checksum for comment meta within a range.
513
	 *
514
	 * @access public
515
	 *
516
	 * @param int $min_id Minimum comment meta ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $min_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
517
	 * @param int $max_id Maximum comment meta ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $max_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
518
	 * @return int The checksum.
519
	 */
520
	public function comment_meta_checksum( $min_id = null, $max_id = null ) {
521
		$checksum_table = new Table_Checksum( 'commentmeta' );
522
		return $checksum_table->calculate_checksum( $min_id, $max_id );
0 ignored issues
show
Bug introduced by
It seems like $min_id defined by parameter $min_id on line 520 can also be of type integer; however, Automattic\Jetpack\Sync\...m::calculate_checksum() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $max_id defined by parameter $max_id on line 520 can also be of type integer; however, Automattic\Jetpack\Sync\...m::calculate_checksum() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
523
	}
524
525
	/**
526
	 * Update the value of an option.
527
	 *
528
	 * @access public
529
	 *
530
	 * @param string $option Option name.
531
	 * @param mixed  $value  Option value.
532
	 * @return bool False if value was not updated and true if value was updated.
533
	 */
534
	public function update_option( $option, $value ) {
535
		return update_option( $option, $value );
536
	}
537
538
	/**
539
	 * Retrieve an option value based on an option name.
540
	 *
541
	 * @access public
542
	 *
543
	 * @param string $option  Name of option to retrieve.
544
	 * @param mixed  $default Optional. Default value to return if the option does not exist.
545
	 * @return mixed Value set for the option.
546
	 */
547
	public function get_option( $option, $default = false ) {
548
		return get_option( $option, $default );
549
	}
550
551
	/**
552
	 * Remove an option by name.
553
	 *
554
	 * @access public
555
	 *
556
	 * @param string $option Name of option to remove.
557
	 * @return bool True, if option is successfully deleted. False on failure.
558
	 */
559
	public function delete_option( $option ) {
560
		return delete_option( $option );
561
	}
562
563
	/**
564
	 * Change the features that the current theme supports.
565
	 * Intentionally not implemented in this replicastore.
566
	 *
567
	 * @access public
568
	 *
569
	 * @param array $theme_support Features that the theme supports.
570
	 */
571
	public function set_theme_support( $theme_support ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
572
		// Noop.
573
	}
574
575
	/**
576
	 * Whether the current theme supports a certain feature.
577
	 *
578
	 * @access public
579
	 *
580
	 * @param string $feature Name of the feature.
581
	 */
582
	public function current_theme_supports( $feature ) {
583
		return current_theme_supports( $feature );
584
	}
585
586
	/**
587
	 * Retrieve metadata for the specified object.
588
	 *
589
	 * @access public
590
	 *
591
	 * @param string $type       Meta type.
592
	 * @param int    $object_id  ID of the object.
593
	 * @param string $meta_key   Meta key.
594
	 * @param bool   $single     If true, return only the first value of the specified meta_key.
595
	 *
596
	 * @return mixed Single metadata value, or array of values.
597
	 */
598
	public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
599
		return get_metadata( $type, $object_id, $meta_key, $single );
600
	}
601
602
	/**
603
	 * Stores remote meta key/values alongside an ID mapping key.
604
	 *
605
	 * @access public
606
	 *
607
	 * @todo Refactor to not use interpolated values when preparing the SQL query.
608
	 *
609
	 * @param string $type       Meta type.
610
	 * @param int    $object_id  ID of the object.
611
	 * @param string $meta_key   Meta key.
612
	 * @param mixed  $meta_value Meta value.
613
	 * @param int    $meta_id    ID of the meta.
614
	 *
615
	 * @return bool False if meta table does not exist, true otherwise.
616
	 */
617
	public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
618
		$table = _get_meta_table( $type );
619
		if ( ! $table ) {
620
			return false;
621
		}
622
623
		global $wpdb;
624
625
		$exists = $wpdb->get_var(
626
			$wpdb->prepare(
627
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
628
				"SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
629
				$meta_id
630
			)
631
		);
632
633
		if ( $exists ) {
634
			$wpdb->update(
635
				$table,
636
				array(
637
					'meta_key'   => $meta_key,
638
					'meta_value' => maybe_serialize( $meta_value ),
639
				),
640
				array( 'meta_id' => $meta_id )
641
			);
642
		} else {
643
			$object_id_field = $type . '_id';
644
			$wpdb->insert(
645
				$table,
646
				array(
647
					'meta_id'        => $meta_id,
648
					$object_id_field => $object_id,
649
					'meta_key'       => $meta_key,
650
					'meta_value'     => maybe_serialize( $meta_value ),
651
				)
652
			);
653
		}
654
655
		wp_cache_delete( $object_id, $type . '_meta' );
656
657
		return true;
658
	}
659
660
	/**
661
	 * Delete metadata for the specified object.
662
	 *
663
	 * @access public
664
	 *
665
	 * @todo Refactor to not use interpolated values when preparing the SQL query.
666
	 *
667
	 * @param string $type      Meta type.
668
	 * @param int    $object_id ID of the object.
669
	 * @param array  $meta_ids  IDs of the meta objects to delete.
670
	 */
671
	public function delete_metadata( $type, $object_id, $meta_ids ) {
672
		global $wpdb;
673
674
		$table = _get_meta_table( $type );
675
		if ( ! $table ) {
676
			return false;
677
		}
678
679
		foreach ( $meta_ids as $meta_id ) {
680
			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
681
			$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
682
		}
683
684
		// If we don't have an object ID what do we do - invalidate ALL meta?
685
		if ( $object_id ) {
686
			wp_cache_delete( $object_id, $type . '_meta' );
687
		}
688
	}
689
690
	/**
691
	 * Delete metadata with a certain key for the specified objects.
692
	 *
693
	 * @access public
694
	 *
695
	 * @todo Test this out to make sure it works as expected.
696
	 * @todo Refactor to not use interpolated values when preparing the SQL query.
697
	 *
698
	 * @param string $type       Meta type.
699
	 * @param array  $object_ids IDs of the objects.
700
	 * @param string $meta_key   Meta key.
701
	 */
702
	public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
703
		global $wpdb;
704
705
		$table = _get_meta_table( $type );
706
		if ( ! $table ) {
707
			return false;
708
		}
709
		$column = sanitize_key( $type . '_id' );
710
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
711
		$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ), $meta_key ) );
712
713
		// If we don't have an object ID what do we do - invalidate ALL meta?
714
		foreach ( $object_ids as $object_id ) {
715
			wp_cache_delete( $object_id, $type . '_meta' );
716
		}
717
	}
718
719
	/**
720
	 * Retrieve value of a constant based on the constant name.
721
	 *
722
	 * @access public
723
	 *
724
	 * @param string $constant Name of constant to retrieve.
725
	 * @return mixed Value set for the constant.
726
	 */
727
	public function get_constant( $constant ) {
728
		$value = get_option( 'jetpack_constant_' . $constant );
729
730
		if ( $value ) {
731
			return $value;
732
		}
733
734
		return null;
735
	}
736
737
	/**
738
	 * Set the value of a constant.
739
	 *
740
	 * @access public
741
	 *
742
	 * @param string $constant Name of constant to retrieve.
743
	 * @param mixed  $value    Value set for the constant.
744
	 */
745
	public function set_constant( $constant, $value ) {
746
		update_option( 'jetpack_constant_' . $constant, $value );
747
	}
748
749
	/**
750
	 * Retrieve the number of the available updates of a certain type.
751
	 * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
752
	 *
753
	 * @access public
754
	 *
755
	 * @param string $type Type of updates to retrieve.
756
	 * @return int|null Number of updates available, `null` if type is invalid or missing.
757
	 */
758
	public function get_updates( $type ) {
759
		$all_updates = get_option( 'jetpack_updates', array() );
760
761
		if ( isset( $all_updates[ $type ] ) ) {
762
			return $all_updates[ $type ];
763
		} else {
764
			return null;
765
		}
766
	}
767
768
	/**
769
	 * Set the available updates of a certain type.
770
	 * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
771
	 *
772
	 * @access public
773
	 *
774
	 * @param string $type    Type of updates to set.
775
	 * @param int    $updates Total number of updates.
776
	 */
777
	public function set_updates( $type, $updates ) {
778
		$all_updates          = get_option( 'jetpack_updates', array() );
779
		$all_updates[ $type ] = $updates;
780
		update_option( 'jetpack_updates', $all_updates );
781
	}
782
783
	/**
784
	 * Retrieve a callable value based on its name.
785
	 *
786
	 * @access public
787
	 *
788
	 * @param string $name Name of the callable to retrieve.
789
	 * @return mixed Value of the callable.
790
	 */
791
	public function get_callable( $name ) {
792
		$value = get_option( 'jetpack_' . $name );
793
794
		if ( $value ) {
795
			return $value;
796
		}
797
798
		return null;
799
	}
800
801
	/**
802
	 * Update the value of a callable.
803
	 *
804
	 * @access public
805
	 *
806
	 * @param string $name  Callable name.
807
	 * @param mixed  $value Callable value.
808
	 */
809
	public function set_callable( $name, $value ) {
810
		update_option( 'jetpack_' . $name, $value );
811
	}
812
813
	/**
814
	 * Retrieve a network option value based on a network option name.
815
	 *
816
	 * @access public
817
	 *
818
	 * @param string $option Name of network option to retrieve.
819
	 * @return mixed Value set for the network option.
820
	 */
821
	public function get_site_option( $option ) {
822
		return get_option( 'jetpack_network_' . $option );
823
	}
824
825
	/**
826
	 * Update the value of a network option.
827
	 *
828
	 * @access public
829
	 *
830
	 * @param string $option Network option name.
831
	 * @param mixed  $value  Network option value.
832
	 * @return bool False if value was not updated and true if value was updated.
833
	 */
834
	public function update_site_option( $option, $value ) {
835
		return update_option( 'jetpack_network_' . $option, $value );
836
	}
837
838
	/**
839
	 * Remove a network option by name.
840
	 *
841
	 * @access public
842
	 *
843
	 * @param string $option Name of option to remove.
844
	 * @return bool True, if option is successfully deleted. False on failure.
845
	 */
846
	public function delete_site_option( $option ) {
847
		return delete_option( 'jetpack_network_' . $option );
848
	}
849
850
	/**
851
	 * Retrieve the terms from a particular taxonomy.
852
	 *
853
	 * @access public
854
	 *
855
	 * @param string $taxonomy Taxonomy slug.
856
	 * @return array Array of terms.
857
	 */
858
	public function get_terms( $taxonomy ) {
859
		return get_terms( $taxonomy );
860
	}
861
862
	/**
863
	 * Retrieve a particular term.
864
	 *
865
	 * @access public
866
	 *
867
	 * @param string $taxonomy   Taxonomy slug.
868
	 * @param int    $term_id    ID of the term.
869
	 * @param bool   $is_term_id Whether this is a `term_id` or a `term_taxonomy_id`.
870
	 * @return \WP_Term|\WP_Error Term object on success, \WP_Error object on failure.
871
	 */
872
	public function get_term( $taxonomy, $term_id, $is_term_id = true ) {
873
		$t = $this->ensure_taxonomy( $taxonomy );
874
		if ( ! $t || is_wp_error( $t ) ) {
875
			return $t;
876
		}
877
878
		return get_term( $term_id, $taxonomy );
879
	}
880
881
	/**
882
	 * Verify a taxonomy is legitimate and register it if necessary.
883
	 *
884
	 * @access private
885
	 *
886
	 * @param string $taxonomy Taxonomy slug.
887
	 * @return bool|void|\WP_Error True if already exists; void if it was registered; \WP_Error on error.
888
	 */
889
	private function ensure_taxonomy( $taxonomy ) {
890
		if ( ! taxonomy_exists( $taxonomy ) ) {
891
			// Try re-registering synced taxonomies.
892
			$taxonomies = $this->get_callable( 'taxonomies' );
893
			if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
894
				// Doesn't exist, or somehow hasn't been synced.
895
				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...
896
			}
897
			$t = $taxonomies[ $taxonomy ];
898
899
			return register_taxonomy(
900
				$taxonomy,
901
				$t->object_type,
902
				(array) $t
903
			);
904
		}
905
906
		return true;
907
	}
908
909
	/**
910
	 * Retrieve all terms from a taxonomy that are related to an object with a particular ID.
911
	 *
912
	 * @access public
913
	 *
914
	 * @param int    $object_id Object ID.
915
	 * @param string $taxonomy  Taxonomy slug.
916
	 * @return array|bool|\WP_Error Array of terms on success, `false` if no terms or post doesn't exist, \WP_Error on failure.
917
	 */
918
	public function get_the_terms( $object_id, $taxonomy ) {
919
		return get_the_terms( $object_id, $taxonomy );
920
	}
921
922
	/**
923
	 * Insert or update a term.
924
	 *
925
	 * @access public
926
	 *
927
	 * @param \WP_Term $term_object Term object.
928
	 * @return array|bool|\WP_Error Array of term_id and term_taxonomy_id if updated, true if inserted, \WP_Error on failure.
929
	 */
930
	public function update_term( $term_object ) {
931
		$taxonomy = $term_object->taxonomy;
932
		global $wpdb;
933
		$exists = $wpdb->get_var(
934
			$wpdb->prepare(
935
				"SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
936
				$term_object->term_id
937
			)
938
		);
939
		if ( ! $exists ) {
940
			$term_object   = sanitize_term( clone( $term_object ), $taxonomy, 'db' );
941
			$term          = array(
942
				'term_id'    => $term_object->term_id,
943
				'name'       => $term_object->name,
944
				'slug'       => $term_object->slug,
945
				'term_group' => $term_object->term_group,
946
			);
947
			$term_taxonomy = array(
948
				'term_taxonomy_id' => $term_object->term_taxonomy_id,
949
				'term_id'          => $term_object->term_id,
950
				'taxonomy'         => $term_object->taxonomy,
951
				'description'      => $term_object->description,
952
				'parent'           => (int) $term_object->parent,
953
				'count'            => (int) $term_object->count,
954
			);
955
			$wpdb->insert( $wpdb->terms, $term );
956
			$wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
957
958
			return true;
959
		}
960
961
		return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
962
	}
963
964
	/**
965
	 * Delete a term by the term ID and its corresponding taxonomy.
966
	 *
967
	 * @access public
968
	 *
969
	 * @param int    $term_id  Term ID.
970
	 * @param string $taxonomy Taxonomy slug.
971
	 * @return bool|int|\WP_Error True on success, false if term doesn't exist. Zero if trying with default category. \WP_Error on invalid taxonomy.
972
	 */
973
	public function delete_term( $term_id, $taxonomy ) {
974
		return wp_delete_term( $term_id, $taxonomy );
975
	}
976
977
	/**
978
	 * Add/update terms of a particular taxonomy of an object with the specified ID.
979
	 *
980
	 * @access public
981
	 *
982
	 * @param int              $object_id The object to relate to.
983
	 * @param string           $taxonomy  The context in which to relate the term to the object.
984
	 * @param string|int|array $terms     A single term slug, single term id, or array of either term slugs or ids.
985
	 * @param bool             $append    Optional. If false will delete difference of terms. Default false.
986
	 */
987
	public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
988
		wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
989
	}
990
991
	/**
992
	 * Remove certain term relationships from the specified object.
993
	 *
994
	 * @access public
995
	 *
996
	 * @todo Refactor to not use interpolated values when preparing the SQL query.
997
	 *
998
	 * @param int   $object_id ID of the object.
999
	 * @param array $tt_ids    Term taxonomy IDs.
1000
	 * @return bool True on success, false on failure.
1001
	 */
1002
	public function delete_object_terms( $object_id, $tt_ids ) {
1003
		global $wpdb;
1004
1005
		if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
1006
			// Escape.
1007
			$tt_ids_sanitized = array_map( 'intval', $tt_ids );
1008
1009
			$taxonomies = array();
1010
			foreach ( $tt_ids_sanitized as $tt_id ) {
1011
				$term                            = get_term_by( 'term_taxonomy_id', $tt_id );
1012
				$taxonomies[ $term->taxonomy ][] = $tt_id;
1013
			}
1014
			$in_tt_ids = implode( ', ', $tt_ids_sanitized );
1015
1016
			/**
1017
			 * Fires immediately before an object-term relationship is deleted.
1018
			 *
1019
			 * @since 2.9.0
1020
			 *
1021
			 * @param int   $object_id Object ID.
1022
			 * @param array $tt_ids    An array of term taxonomy IDs.
1023
			 */
1024
			do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
1025
			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1026
			$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
1027
			foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
1028
				$this->ensure_taxonomy( $taxonomy );
1029
				wp_cache_delete( $object_id, $taxonomy . '_relationships' );
1030
				/**
1031
				 * Fires immediately after an object-term relationship is deleted.
1032
				 *
1033
				 * @since 2.9.0
1034
				 *
1035
				 * @param int   $object_id Object ID.
1036
				 * @param array $tt_ids    An array of term taxonomy IDs.
1037
				 */
1038
				do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
1039
				wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
1040
			}
1041
1042
			return (bool) $deleted;
1043
		}
1044
1045
		return false;
1046
	}
1047
1048
	/**
1049
	 * Retrieve the number of users.
1050
	 * Not supported in this replicastore.
1051
	 *
1052
	 * @access public
1053
	 */
1054
	public function user_count() {
1055
		// Noop.
1056
	}
1057
1058
	/**
1059
	 * Retrieve a user object by the user ID.
1060
	 *
1061
	 * @access public
1062
	 *
1063
	 * @param int $user_id User ID.
1064
	 * @return \WP_User User object.
1065
	 */
1066
	public function get_user( $user_id ) {
1067
		return \WP_User::get_instance( $user_id );
1068
	}
1069
1070
	/**
1071
	 * Insert or update a user.
1072
	 * Not supported in this replicastore.
1073
	 *
1074
	 * @access public
1075
	 * @throws \Exception If this method is invoked.
1076
	 *
1077
	 * @param \WP_User $user User object.
1078
	 */
1079
	public function upsert_user( $user ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1080
		$this->invalid_call();
1081
	}
1082
1083
	/**
1084
	 * Delete a user.
1085
	 * Not supported in this replicastore.
1086
	 *
1087
	 * @access public
1088
	 * @throws \Exception If this method is invoked.
1089
	 *
1090
	 * @param int $user_id User ID.
1091
	 */
1092
	public function delete_user( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1093
		$this->invalid_call();
1094
	}
1095
1096
	/**
1097
	 * Update/insert user locale.
1098
	 * Not supported in this replicastore.
1099
	 *
1100
	 * @access public
1101
	 * @throws \Exception If this method is invoked.
1102
	 *
1103
	 * @param int    $user_id User ID.
1104
	 * @param string $local   The user locale.
1105
	 */
1106
	public function upsert_user_locale( $user_id, $local ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1107
		$this->invalid_call();
1108
	}
1109
1110
	/**
1111
	 * Delete user locale.
1112
	 * Not supported in this replicastore.
1113
	 *
1114
	 * @access public
1115
	 * @throws \Exception If this method is invoked.
1116
	 *
1117
	 * @param int $user_id User ID.
1118
	 */
1119
	public function delete_user_locale( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1120
		$this->invalid_call();
1121
	}
1122
1123
	/**
1124
	 * Retrieve the user locale.
1125
	 *
1126
	 * @access public
1127
	 *
1128
	 * @param int $user_id User ID.
1129
	 * @return string The user locale.
1130
	 */
1131
	public function get_user_locale( $user_id ) {
1132
		return get_user_locale( $user_id );
1133
	}
1134
1135
	/**
1136
	 * Retrieve the allowed mime types for the user.
1137
	 * Not supported in this replicastore.
1138
	 *
1139
	 * @access public
1140
	 *
1141
	 * @param int $user_id User ID.
1142
	 */
1143
	public function get_allowed_mime_types( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1144
		// Noop.
1145
	}
1146
1147
	/**
1148
	 * Retrieve all the checksums we are interested in.
1149
	 * Currently that is posts, comments, post meta and comment meta.
1150
	 *
1151
	 * @access public
1152
	 *
1153
	 * @return array Checksums.
1154
	 */
1155
	public function checksum_all() {
1156
1157
		$post_checksum               = $this->checksum_histogram( 'posts', $this->calculate_buckets( 'posts' ) );
1158
		$comments_checksum           = $this->checksum_histogram( 'comments', $this->calculate_buckets( 'comments' ) );
1159
		$post_meta_checksum          = $this->checksum_histogram( 'post_meta', $this->calculate_buckets( 'post_meta' ) );
1160
		$comment_meta_checksum       = $this->checksum_histogram( 'comment_meta', $this->calculate_buckets( 'comment_meta' ) );
1161
		$terms_checksum              = $this->checksum_histogram( 'terms', $this->calculate_buckets( 'terms' ) );
1162
		$term_relationships_checksum = $this->checksum_histogram( 'term_relationships', $this->calculate_buckets( 'term_relationships' ) );
1163
		$term_taxonomy_checksum      = $this->checksum_histogram( 'term_taxonomy', $this->calculate_buckets( 'term_taxonomy' ) );
1164
1165
		return array(
1166
			'posts'              => array_sum( $post_checksum ),
1167
			'comments'           => array_sum( $comments_checksum ),
1168
			'post_meta'          => array_sum( $post_meta_checksum ),
1169
			'comment_meta'       => array_sum( $comment_meta_checksum ),
1170
			'terms'              => array_sum( $terms_checksum ),
1171
			'term_relationships' => array_sum( $term_relationships_checksum ),
1172
			'term_taxonomy'      => array_sum( $term_taxonomy_checksum ),
1173
		);
1174
	}
1175
1176
	/**
1177
	 * Grabs the minimum and maximum object ids for the given parameters.
1178
	 *
1179
	 * @access public
1180
	 *
1181
	 * @param string $id_field     The id column in the table to query.
1182
	 * @param string $object_table The table to query.
1183
	 * @param string $where        A sql where clause without 'WHERE'.
1184
	 * @param int    $bucket_size  The maximum amount of objects to include in the query.
1185
	 *                             For `term_relationships` table, the bucket size will refer to the amount
1186
	 *                             of distinct object ids. This will likely include more database rows than
1187
	 *                             the bucket size implies.
1188
	 *
1189
	 * @return object An object with min_id and max_id properties.
1190
	 */
1191
	public function get_min_max_object_id( $id_field, $object_table, $where, $bucket_size ) {
1192
		global $wpdb;
1193
1194
		// The term relationship table's unique key is a combination of 2 columns. `DISTINCT` helps us get a more acurate query.
1195
		$distinct_sql = ( $wpdb->term_relationships === $object_table ) ? 'DISTINCT' : '';
1196
		$where_sql    = $where ? "WHERE $where" : '';
1197
1198
		// Since MIN() and MAX() do not work with LIMIT, we'll need to adjust the dataset we query if a limit is present.
1199
		// With a limit present, we'll look at a dataset consisting of object_ids that meet the constructs of the $where clause.
1200
		// Without a limit, we can use the actual table as a dataset.
1201
		$from = $bucket_size ?
1202
			"( SELECT $distinct_sql $id_field FROM $object_table $where_sql ORDER BY $id_field ASC LIMIT $bucket_size ) as ids" :
1203
			"$object_table $where_sql ORDER BY $id_field ASC";
1204
1205
		return $wpdb->get_row(
1206
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1207
			"SELECT MIN($id_field) as min, MAX($id_field) as max FROM $from"
1208
		);
1209
	}
1210
1211
	/**
1212
	 * Retrieve the checksum histogram for a specific object type.
1213
	 *
1214
	 * @access public
1215
	 *
1216
	 * @todo Refactor to not use interpolated values and properly prepare the SQL query.
1217
	 *
1218
	 * @param string $object_type     Object type.
1219
	 * @param int    $buckets         Number of buckets to split the objects to.
1220
	 * @param int    $start_id        Minimum object ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $start_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1221
	 * @param int    $end_id          Maximum object ID.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $end_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1222
	 * @param array  $columns         Table columns to calculate the checksum from.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $columns not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1223
	 * @param bool   $strip_non_ascii Whether to strip non-ASCII characters.
1224
	 * @param string $salt            Salt, used for $wpdb->prepare()'s args.
1225
	 * @return array The checksum histogram.
1226
	 */
1227
	public function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '' ) {
1228
		global $wpdb;
1229
1230
		$wpdb->queries = array();
1231
1232
		/**
1233
		 * TODO TEMPORARY!
1234
		 *
1235
		 * Translate $object_type to $table
1236
		 */
1237
		$table = $this->translate_object_type( $object_type );
1238
		if ( false === $table ) {
1239
			return false; // early return if unknown object_type.
1240
		}
1241
1242
		$checksum_table = new Table_Checksum( $table, $salt );
1243
		$range_edges    = $checksum_table->get_range_edges();
1244
1245
		$object_count = $range_edges['item_count'];
1246
1247
		$bucket_size     = (int) ceil( $object_count / $buckets );
1248
		$previous_max_id = 0;
1249
		$histogram       = array();
1250
1251
		do {
1252
			$ids_range = $checksum_table->get_range_edges( $previous_max_id, null, $bucket_size );
1253
1254
			if ( empty( $ids_range['min_range'] ) || empty( $ids_range['max_range'] ) ) {
1255
				// Nothing to checksum here...
1256
				break;
1257
			}
1258
1259
			// Get the checksum value.
1260
			$batch_checksum = $checksum_table->calculate_checksum( $ids_range['min_range'], $ids_range['max_range'] );
1261
1262
			if ( is_wp_error( $batch_checksum ) ) {
1263
				return $batch_checksum;
1264
			}
1265
1266
			if ( $ids_range['min_range'] === $ids_range['max_range'] ) {
1267
				$histogram[ $ids_range['min_range'] ] = $batch_checksum;
1268
			} else {
1269
				$histogram[ "{$ids_range[ 'min_range' ]}-{$ids_range[ 'max_range' ]}" ] = $batch_checksum;
1270
			}
1271
1272
			$previous_max_id = $ids_range['max_range'] + 1;
1273
			// If we've reached the max_range lets bail out.
1274
			if ( $previous_max_id >= $range_edges['max_range'] ) {
1275
				break;
1276
			}
1277
		} while ( true );
1278
1279
		return $histogram;
1280
	}
1281
1282
	/**
1283
	 * Retrieve the type of the checksum.
1284
	 *
1285
	 * @access public
1286
	 *
1287
	 * @return string Type of the checksum.
1288
	 */
1289
	public function get_checksum_type() {
1290
		return 'sum';
1291
	}
1292
1293
	/**
1294
	 * Used in methods that are not implemented and shouldn't be invoked.
1295
	 *
1296
	 * @access private
1297
	 * @throws \Exception If this method is invoked.
1298
	 */
1299
	private function invalid_call() {
1300
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
1301
		$backtrace = debug_backtrace();
1302
		$caller    = $backtrace[1]['function'];
1303
		throw new \Exception( "This function $caller is not supported on the WP Replicastore" );
1304
	}
1305
1306
	/**
1307
	 * Determine number of buckets to use in full table checksum.
1308
	 *
1309
	 * @param string $object_type Object Type.
1310
	 * @return int Number of Buckets to use.
1311
	 */
1312
	private function calculate_buckets( $object_type ) {
1313
1314
		// TODO : Temporary map object to table.
1315
		$table = $this->translate_object_type( $object_type );
1316
		if ( false === $table ) {
1317
			return 1; // default to 0 if unknown table.
1318
		}
1319
1320
		// Get # of objects.
1321
		$checksum_table = new Table_Checksum( $table );
1322
		$range_edges    = $checksum_table->get_range_edges();
1323
		$object_count   = $range_edges['item_count'];
1324
1325
		// Ensure no division by 0.
1326
		if ( 0 === (int) $object_count ) {
1327
			return 1;
1328
		}
1329
1330
		// Default Bucket sizes.
1331
		$bucket_size = 10000; // Default bucket size is 10,000 items.
1332
		switch ( $table ) {
1333
			case 'postmeta':
1334
			case 'commentmeta':
1335
				$bucket_size = 5000; // Meta bucket size is restricted to 5,000 items.
1336
		}
1337
1338
		return (int) ceil( $object_count / $bucket_size );
1339
	}
1340
1341
	/**
1342
	 * Translate the object_type to the table name.
1343
	 *
1344
	 * @param string $object_type Object Type.
1345
	 * @return bool|string
1346
	 */
1347
	private function translate_object_type( $object_type ) {
1348
1349
		switch ( $object_type ) {
1350
			case 'posts':
1351
			case 'term_taxonomy':
1352
			case 'comments':
1353
			case 'terms':
1354
			case 'term_relationships':
1355
				$table = $object_type;
1356
				break;
1357
			case 'post_meta':
1358
				$table = 'postmeta';
1359
				break;
1360
			case 'comment_meta':
1361
				$table = 'commentmeta';
1362
				break;
1363
			default:
1364
				return false;
1365
		}
1366
		return $table;
1367
	}
1368
1369
}
1370