PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-sync / src / class-replicastore.php

class-replicastore.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.5, at jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php

1,469 lines 41.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 use Automattic\Jetpack\Sync\Replicastore\Table_Checksum_Usermeta;
12 use Automattic\Jetpack\Sync\Replicastore\Table_Checksum_Users;
13 use Exception;
14 use WP_Error;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit( 0 );
18 }
19
20 /**
21 * An implementation of Replicastore Interface which returns data stored in a WordPress.org DB.
22 * This is useful to compare values in the local WP DB to values in the synced replica store
23 */
24 class Replicastore implements Replicastore_Interface {
25 /**
26 * Empty and reset the replicastore.
27 *
28 * @access public
29 */
30 public function reset() {
31 global $wpdb;
32
33 $wpdb->query( "DELETE FROM $wpdb->posts" );
34
35 // Delete comments from cache.
36 $comment_ids = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments" );
37 if ( ! empty( $comment_ids ) ) {
38 clean_comment_cache( $comment_ids );
39 }
40 $wpdb->query( "DELETE FROM $wpdb->comments" );
41
42 // Also need to delete terms from cache.
43 $term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
44 foreach ( $term_ids as $term_id ) {
45 wp_cache_delete( $term_id, 'terms' );
46 }
47
48 $wpdb->query( "DELETE FROM $wpdb->terms" );
49
50 $wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
51 $wpdb->query( "DELETE FROM $wpdb->term_relationships" );
52
53 // Callables and constants.
54 $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
55 $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
56 }
57
58 /**
59 * Ran when full sync has just started.
60 *
61 * @access public
62 *
63 * @param array $config Full sync configuration for this sync module.
64 */
65 public function full_sync_start( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
66 $this->reset();
67 }
68
69 /**
70 * Ran when full sync has just finished.
71 *
72 * @access public
73 *
74 * @param string $checksum Deprecated since 7.3.0.
75 */
76 public function full_sync_end( $checksum ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
77 // Noop right now.
78 }
79
80 /**
81 * Retrieve the number of terms.
82 *
83 * @access public
84 *
85 * @return int Number of terms.
86 */
87 public function term_count() {
88 global $wpdb;
89 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
90 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
91 }
92
93 /**
94 * Retrieve the number of rows in the `term_taxonomy` table.
95 *
96 * @access public
97 *
98 * @return int Number of terms.
99 */
100 public function term_taxonomy_count() {
101 global $wpdb;
102 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
103 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
104 }
105
106 /**
107 * Retrieve the number of term relationships.
108 *
109 * @access public
110 *
111 * @return int Number of rows in the term relationships table.
112 */
113 public function term_relationship_count() {
114 global $wpdb;
115 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
116 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
117 }
118
119 /**
120 * Retrieve the number of posts with a particular post status within a certain range.
121 *
122 * @access public
123 *
124 * @todo Prepare the SQL query before executing it.
125 *
126 * @param string $status Post status.
127 * @param int $min_id Minimum post ID.
128 * @param int $max_id Maximum post ID.
129 * @return int Number of posts.
130 */
131 public function post_count( $status = null, $min_id = null, $max_id = null ) {
132 global $wpdb;
133
134 $where = '';
135
136 if ( $status ) {
137 $where = "post_status = '" . esc_sql( $status ) . "'";
138 } else {
139 $where = '1=1';
140 }
141
142 if ( ! empty( $min_id ) ) {
143 $where .= ' AND ID >= ' . (int) $min_id;
144 }
145
146 if ( ! empty( $max_id ) ) {
147 $where .= ' AND ID <= ' . (int) $max_id;
148 }
149
150 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
151 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
152 }
153
154 /**
155 * Retrieve the posts with a particular post status.
156 *
157 * @access public
158 *
159 * @todo Implement range and actually use max_id/min_id arguments.
160 *
161 * @param string $status Post status.
162 * @param int $min_id Minimum post ID.
163 * @param int $max_id Maximum post ID.
164 * @return array Array of posts.
165 */
166 public function get_posts( $status = null, $min_id = null, $max_id = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
167 $args = array(
168 'orderby' => 'ID',
169 'posts_per_page' => -1,
170 );
171
172 if ( $status ) {
173 $args['post_status'] = $status;
174 } else {
175 $args['post_status'] = 'any';
176 }
177
178 return get_posts( $args );
179 }
180
181 /**
182 * Retrieve a post object by the post ID.
183 *
184 * @access public
185 *
186 * @param int $id Post ID.
187 * @return \WP_Post Post object.
188 */
189 public function get_post( $id ) {
190 return get_post( $id );
191 }
192
193 /**
194 * Update or insert a post.
195 *
196 * @access public
197 *
198 * @param \WP_Post $post Post object.
199 * @param bool $silent Whether to perform a silent action. Not used in this implementation.
200 */
201 public function upsert_post( $post, $silent = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
202 global $wpdb;
203
204 // Reject the post if it's not a \WP_Post.
205 if ( ! $post instanceof \WP_Post ) {
206 return;
207 }
208
209 $post = $post->to_array();
210
211 // Reject posts without an ID.
212 if ( ! isset( $post['ID'] ) ) {
213 return;
214 }
215
216 $now = current_time( 'mysql' );
217 $now_gmt = get_gmt_from_date( $now );
218
219 $defaults = array(
220 'ID' => 0,
221 'post_author' => '0',
222 'post_content' => '',
223 'post_content_filtered' => '',
224 'post_title' => '',
225 'post_name' => '',
226 'post_excerpt' => '',
227 'post_status' => 'draft',
228 'post_type' => 'post',
229 'comment_status' => 'closed',
230 'comment_count' => '0',
231 'ping_status' => '',
232 'post_password' => '',
233 'to_ping' => '',
234 'pinged' => '',
235 'post_parent' => 0,
236 'menu_order' => 0,
237 'guid' => '',
238 'post_date' => $now,
239 'post_date_gmt' => $now_gmt,
240 'post_modified' => $now,
241 'post_modified_gmt' => $now_gmt,
242 );
243
244 $post = array_intersect_key( $post, $defaults );
245
246 $post = sanitize_post( $post, 'db' );
247
248 unset( $post['filter'] );
249
250 $exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
251
252 if ( $exists ) {
253 $wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
254 } else {
255 $wpdb->insert( $wpdb->posts, $post );
256 }
257
258 clean_post_cache( $post['ID'] );
259 }
260
261 /**
262 * Delete a post by the post ID.
263 *
264 * @access public
265 *
266 * @param int $post_id Post ID.
267 */
268 public function delete_post( $post_id ) {
269 wp_delete_post( $post_id, true );
270 }
271
272 /**
273 * Retrieve the checksum for posts within a range.
274 *
275 * @access public
276 *
277 * @param int $min_id Minimum post ID.
278 * @param int $max_id Maximum post ID.
279 * @return int The checksum.
280 */
281 public function posts_checksum( $min_id = null, $max_id = null ) {
282 return $this->summarize_checksum_histogram( $this->checksum_histogram( 'posts', null, $min_id, $max_id ) );
283 }
284
285 /**
286 * Retrieve the checksum for post meta within a range.
287 *
288 * @access public
289 *
290 * @param int $min_id Minimum post meta ID.
291 * @param int $max_id Maximum post meta ID.
292 * @return int The checksum.
293 */
294 public function post_meta_checksum( $min_id = null, $max_id = null ) {
295 return $this->summarize_checksum_histogram( $this->checksum_histogram( 'postmeta', null, $min_id, $max_id ) );
296 }
297
298 /**
299 * Retrieve the number of comments with a particular comment status within a certain range.
300 *
301 * @access public
302 *
303 * @todo Prepare the SQL query before executing it.
304 *
305 * @param string $status Comment status.
306 * @param int $min_id Minimum comment ID.
307 * @param int $max_id Maximum comment ID.
308 * @return int Number of comments.
309 */
310 public function comment_count( $status = null, $min_id = null, $max_id = null ) {
311 global $wpdb;
312
313 $comment_approved = $this->comment_status_to_approval_value( $status );
314
315 if ( false !== $comment_approved ) {
316 $where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
317 } else {
318 $where = '1=1';
319 }
320
321 if ( ! empty( $min_id ) ) {
322 $where .= ' AND comment_ID >= ' . (int) $min_id;
323 }
324
325 if ( ! empty( $max_id ) ) {
326 $where .= ' AND comment_ID <= ' . (int) $max_id;
327 }
328
329 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
330 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
331 }
332
333 /**
334 * Translate a comment status to a value of the comment_approved field.
335 *
336 * @access protected
337 *
338 * @param string $status Comment status.
339 * @return string|bool New comment_approved value, false if the status doesn't affect it.
340 */
341 protected function comment_status_to_approval_value( $status ) {
342 switch ( (string) $status ) {
343 case 'approve':
344 case '1':
345 return '1';
346 case 'hold':
347 case '0':
348 return '0';
349 case 'spam':
350 return 'spam';
351 case 'trash':
352 return 'trash';
353 case 'post-trashed':
354 return 'post-trashed';
355 case 'any':
356 case 'all':
357 default:
358 return false;
359 }
360 }
361
362 /**
363 * Retrieve the comments with a particular comment status.
364 *
365 * @access public
366 *
367 * @todo Implement range and actually use max_id/min_id arguments.
368 *
369 * @param string $status Comment status.
370 * @param int $min_id Minimum comment ID.
371 * @param int $max_id Maximum comment ID.
372 * @return array Array of comments.
373 */
374 public function get_comments( $status = null, $min_id = null, $max_id = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
375 $args = array(
376 'orderby' => 'ID',
377 'status' => 'all',
378 );
379
380 if ( $status ) {
381 $args['status'] = $status;
382 }
383
384 return get_comments( $args );
385 }
386
387 /**
388 * Retrieve a comment object by the comment ID.
389 *
390 * @access public
391 *
392 * @param int $id Comment ID.
393 * @return \WP_Comment Comment object.
394 */
395 public function get_comment( $id ) {
396 return \WP_Comment::get_instance( $id );
397 }
398
399 /**
400 * Update or insert a comment.
401 *
402 * @access public
403 *
404 * @param \WP_Comment $comment Comment object.
405 */
406 public function upsert_comment( $comment ) {
407 global $wpdb;
408
409 $comment = $comment->to_array();
410
411 // Filter by fields on comment table.
412 $comment_fields_whitelist = array(
413 'comment_ID',
414 'comment_post_ID',
415 'comment_author',
416 'comment_author_email',
417 'comment_author_url',
418 'comment_author_IP',
419 'comment_date',
420 'comment_date_gmt',
421 'comment_content',
422 'comment_karma',
423 'comment_approved',
424 'comment_agent',
425 'comment_type',
426 'comment_parent',
427 'user_id',
428 );
429
430 foreach ( $comment as $key => $value ) {
431 if ( ! in_array( $key, $comment_fields_whitelist, true ) ) {
432 unset( $comment[ $key ] );
433 }
434 }
435
436 $exists = $wpdb->get_var(
437 $wpdb->prepare(
438 "SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
439 $comment['comment_ID']
440 )
441 );
442
443 if ( $exists ) {
444 $wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
445 } else {
446 $wpdb->insert( $wpdb->comments, $comment );
447 }
448 // Remove comment from cache.
449 clean_comment_cache( $comment['comment_ID'] );
450
451 wp_update_comment_count( $comment['comment_post_ID'] );
452 }
453
454 /**
455 * Trash a comment by the comment ID.
456 *
457 * @access public
458 *
459 * @param int $comment_id Comment ID.
460 */
461 public function trash_comment( $comment_id ) {
462 wp_delete_comment( $comment_id );
463 }
464
465 /**
466 * Delete a comment by the comment ID.
467 *
468 * @access public
469 *
470 * @param int $comment_id Comment ID.
471 */
472 public function delete_comment( $comment_id ) {
473 wp_delete_comment( $comment_id, true );
474 }
475
476 /**
477 * Mark a comment by the comment ID as spam.
478 *
479 * @access public
480 *
481 * @param int $comment_id Comment ID.
482 */
483 public function spam_comment( $comment_id ) {
484 wp_spam_comment( $comment_id );
485 }
486
487 /**
488 * Trash the comments of a post.
489 *
490 * @access public
491 *
492 * @param int $post_id Post ID.
493 * @param array $statuses Post statuses. Not used in this implementation.
494 */
495 public function trashed_post_comments( $post_id, $statuses ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
496 wp_trash_post_comments( $post_id );
497 }
498
499 /**
500 * Untrash the comments of a post.
501 *
502 * @access public
503 *
504 * @param int $post_id Post ID.
505 */
506 public function untrashed_post_comments( $post_id ) {
507 wp_untrash_post_comments( $post_id );
508 }
509
510 /**
511 * Retrieve the checksum for comments within a range.
512 *
513 * @access public
514 *
515 * @param int $min_id Minimum comment ID.
516 * @param int $max_id Maximum comment ID.
517 * @return int The checksum.
518 */
519 public function comments_checksum( $min_id = null, $max_id = null ) {
520 return $this->summarize_checksum_histogram( $this->checksum_histogram( 'comments', null, $min_id, $max_id ) );
521 }
522
523 /**
524 * Retrieve the checksum for comment meta within a range.
525 *
526 * @access public
527 *
528 * @param int $min_id Minimum comment meta ID.
529 * @param int $max_id Maximum comment meta ID.
530 * @return int The checksum.
531 */
532 public function comment_meta_checksum( $min_id = null, $max_id = null ) {
533 return $this->summarize_checksum_histogram( $this->checksum_histogram( 'commentmeta', null, $min_id, $max_id ) );
534 }
535
536 /**
537 * Update the value of an option.
538 *
539 * @access public
540 *
541 * @param string $option Option name.
542 * @param mixed $value Option value.
543 * @return bool False if value was not updated and true if value was updated.
544 */
545 public function update_option( $option, $value ) {
546 return update_option( $option, $value );
547 }
548
549 /**
550 * Retrieve an option value based on an option name.
551 *
552 * @access public
553 *
554 * @param string $option Name of option to retrieve.
555 * @param mixed $default Optional. Default value to return if the option does not exist.
556 * @return mixed Value set for the option.
557 */
558 public function get_option( $option, $default = false ) {
559 return get_option( $option, $default );
560 }
561
562 /**
563 * Remove an option by name.
564 *
565 * @access public
566 *
567 * @param string $option Name of option to remove.
568 * @return bool True, if option is successfully deleted. False on failure.
569 */
570 public function delete_option( $option ) {
571 return delete_option( $option );
572 }
573
574 /**
575 * Change the info of the current theme.
576 *
577 * @access public
578 *
579 * @param array $theme_info Theme info array.
580 */
581 public function set_theme_info( $theme_info ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
582 // Noop.
583 }
584
585 /**
586 * Whether the current theme supports a certain feature.
587 *
588 * @access public
589 *
590 * @param string $feature Name of the feature.
591 */
592 public function current_theme_supports( $feature ) {
593 return current_theme_supports( $feature );
594 }
595
596 /**
597 * Retrieve metadata for the specified object.
598 *
599 * @access public
600 *
601 * @param string $type Meta type.
602 * @param int $object_id ID of the object.
603 * @param string $meta_key Meta key.
604 * @param bool $single If true, return only the first value of the specified meta_key.
605 *
606 * @return mixed Single metadata value, or array of values.
607 */
608 public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
609 return get_metadata( $type, $object_id, $meta_key, $single );
610 }
611
612 /**
613 * Stores remote meta key/values alongside an ID mapping key.
614 *
615 * @access public
616 *
617 * @todo Refactor to not use interpolated values when preparing the SQL query.
618 *
619 * @param string $type Meta type.
620 * @param int $object_id ID of the object.
621 * @param string $meta_key Meta key.
622 * @param mixed $meta_value Meta value.
623 * @param int $meta_id ID of the meta.
624 *
625 * @return bool False if meta table does not exist, true otherwise.
626 */
627 public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
628 $table = _get_meta_table( $type );
629 if ( ! $table ) {
630 return false;
631 }
632
633 global $wpdb;
634
635 $exists = $wpdb->get_var(
636 $wpdb->prepare(
637 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
638 "SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
639 $meta_id
640 )
641 );
642
643 if ( $exists ) {
644 $wpdb->update(
645 $table,
646 array(
647 'meta_key' => $meta_key,
648 'meta_value' => maybe_serialize( $meta_value ),
649 ),
650 array( 'meta_id' => $meta_id )
651 );
652 } else {
653 $object_id_field = $type . '_id';
654 $wpdb->insert(
655 $table,
656 array(
657 'meta_id' => $meta_id,
658 $object_id_field => $object_id,
659 'meta_key' => $meta_key,
660 'meta_value' => maybe_serialize( $meta_value ),
661 )
662 );
663 }
664
665 wp_cache_delete( $object_id, $type . '_meta' );
666
667 return true;
668 }
669
670 /**
671 * Delete metadata for the specified object.
672 *
673 * @access public
674 *
675 * @todo Refactor to not use interpolated values when preparing the SQL query.
676 *
677 * @param string $type Meta type.
678 * @param int $object_id ID of the object.
679 * @param array $meta_ids IDs of the meta objects to delete.
680 */
681 public function delete_metadata( $type, $object_id, $meta_ids ) {
682 global $wpdb;
683
684 $table = _get_meta_table( $type );
685 if ( ! $table ) {
686 return false;
687 }
688
689 foreach ( $meta_ids as $meta_id ) {
690 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
691 $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
692 }
693
694 // If we don't have an object ID what do we do - invalidate ALL meta?
695 if ( $object_id ) {
696 wp_cache_delete( $object_id, $type . '_meta' );
697 }
698 }
699
700 /**
701 * Delete metadata with a certain key for the specified objects.
702 *
703 * @access public
704 *
705 * @todo Test this out to make sure it works as expected.
706 * @todo Refactor to not use interpolated values when preparing the SQL query.
707 *
708 * @param string $type Meta type.
709 * @param array $object_ids IDs of the objects.
710 * @param string $meta_key Meta key.
711 */
712 public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
713 global $wpdb;
714
715 $table = _get_meta_table( $type );
716 if ( ! $table ) {
717 return false;
718 }
719 $column = sanitize_key( $type . '_id' );
720 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
721 $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ), $meta_key ) );
722
723 // If we don't have an object ID what do we do - invalidate ALL meta?
724 foreach ( $object_ids as $object_id ) {
725 wp_cache_delete( $object_id, $type . '_meta' );
726 }
727 }
728
729 /**
730 * Delete metadata with a certain key and value for all objects.
731 *
732 * @access public
733 *
734 * @param string $type Meta type.
735 * @param string $meta_key Meta key.
736 * @param mixed $meta_value Meta value to match.
737 */
738 public function delete_metadata_by_key_value( $type, $meta_key, $meta_value ) {
739 global $wpdb;
740
741 $table = _get_meta_table( $type );
742 if ( ! $table ) {
743 return false;
744 }
745
746 if ( '' === $meta_value || null === $meta_value || false === $meta_value ) {
747 return false;
748 }
749
750 $column = sanitize_key( $type . '_id' );
751 $meta_value = maybe_serialize( $meta_value );
752
753 $object_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT %i FROM %i WHERE meta_key = %s AND meta_value = %s', $column, $table, $meta_key, $meta_value ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct replica query is needed to identify caches invalidated below.
754 $wpdb->delete( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Direct replica mutation.
755 $table,
756 array(
757 'meta_key' => $meta_key,
758 'meta_value' => $meta_value,
759 ),
760 array( '%s', '%s' )
761 );
762
763 foreach ( $object_ids as $object_id ) {
764 wp_cache_delete( $object_id, $type . '_meta' );
765 }
766 }
767
768 /**
769 * Retrieve value of a constant based on the constant name.
770 *
771 * We explicitly return null instead of false if the constant doesn't exist.
772 *
773 * @access public
774 *
775 * @param string $constant Name of constant to retrieve.
776 * @return mixed Value set for the constant.
777 */
778 public function get_constant( $constant ) {
779 $value = get_option( 'jetpack_constant_' . $constant );
780
781 if ( $value ) {
782 return $value;
783 }
784
785 return null;
786 }
787
788 /**
789 * Set the value of a constant.
790 *
791 * @access public
792 *
793 * @param string $constant Name of constant to retrieve.
794 * @param mixed $value Value set for the constant.
795 */
796 public function set_constant( $constant, $value ) {
797 update_option( 'jetpack_constant_' . $constant, $value );
798 }
799
800 /**
801 * Retrieve the number of the available updates of a certain type.
802 * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
803 *
804 * @access public
805 *
806 * @param string $type Type of updates to retrieve.
807 * @return int|null Number of updates available, `null` if type is invalid or missing.
808 */
809 public function get_updates( $type ) {
810 $all_updates = get_option( 'jetpack_updates', array() );
811
812 if ( isset( $all_updates[ $type ] ) ) {
813 return $all_updates[ $type ];
814 } else {
815 return null;
816 }
817 }
818
819 /**
820 * Set the available updates of a certain type.
821 * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
822 *
823 * @access public
824 *
825 * @param string $type Type of updates to set.
826 * @param int $updates Total number of updates.
827 */
828 public function set_updates( $type, $updates ) {
829 $all_updates = get_option( 'jetpack_updates', array() );
830 $all_updates[ $type ] = $updates;
831 update_option( 'jetpack_updates', $all_updates );
832 }
833
834 /**
835 * Retrieve a callable value based on its name.
836 *
837 * @access public
838 *
839 * @param string $name Name of the callable to retrieve.
840 * @return mixed Value of the callable.
841 */
842 public function get_callable( $name ) {
843 $value = get_option( 'jetpack_' . $name );
844
845 if ( $value ) {
846 return $value;
847 }
848
849 return null;
850 }
851
852 /**
853 * Update the value of a callable.
854 *
855 * @access public
856 *
857 * @param string $name Callable name.
858 * @param mixed $value Callable value.
859 */
860 public function set_callable( $name, $value ) {
861 update_option( 'jetpack_' . $name, $value );
862 }
863
864 /**
865 * Retrieve a network option value based on a network option name.
866 *
867 * @access public
868 *
869 * @param string $option Name of network option to retrieve.
870 * @return mixed Value set for the network option.
871 */
872 public function get_site_option( $option ) {
873 return get_option( 'jetpack_network_' . $option );
874 }
875
876 /**
877 * Update the value of a network option.
878 *
879 * @access public
880 *
881 * @param string $option Network option name.
882 * @param mixed $value Network option value.
883 * @return bool False if value was not updated and true if value was updated.
884 */
885 public function update_site_option( $option, $value ) {
886 return update_option( 'jetpack_network_' . $option, $value );
887 }
888
889 /**
890 * Remove a network option by name.
891 *
892 * @access public
893 *
894 * @param string $option Name of option to remove.
895 * @return bool True, if option is successfully deleted. False on failure.
896 */
897 public function delete_site_option( $option ) {
898 return delete_option( 'jetpack_network_' . $option );
899 }
900
901 /**
902 * Retrieve the terms from a particular taxonomy.
903 *
904 * @access public
905 *
906 * @param string $taxonomy Taxonomy slug.
907 *
908 * @return array|WP_Error Array of terms or WP_Error object on failure.
909 */
910 public function get_terms( $taxonomy ) {
911 $t = $this->ensure_taxonomy( $taxonomy );
912 if ( ! $t || is_wp_error( $t ) ) {
913 return $t;
914 }
915 return get_terms( $taxonomy );
916 }
917
918 /**
919 * Retrieve a particular term.
920 *
921 * @access public
922 *
923 * @param string|false $taxonomy Taxonomy slug.
924 * @param int $term_id ID of the term.
925 * @param string $term_key ID Field `term_id` or `term_taxonomy_id`.
926 *
927 * @return \WP_Term|WP_Error Term object on success, \WP_Error object on failure.
928 */
929 public function get_term( $taxonomy, $term_id, $term_key = 'term_id' ) {
930
931 // Full Sync will pass false for the $taxonomy so a check for term_taxonomy_id is needed before ensure_taxonomy.
932 if ( 'term_taxonomy_id' === $term_key ) {
933 return get_term_by( 'term_taxonomy_id', $term_id );
934 }
935
936 $t = $this->ensure_taxonomy( $taxonomy );
937 if ( ! $t || is_wp_error( $t ) ) {
938 return $t;
939 }
940
941 return get_term( $term_id, $taxonomy );
942 }
943
944 /**
945 * Verify a taxonomy is legitimate and register it if necessary.
946 *
947 * @access private
948 *
949 * @param string $taxonomy Taxonomy slug.
950 *
951 * @return bool|void|WP_Error True if already exists; void if it was registered; \WP_Error on error.
952 */
953 private function ensure_taxonomy( $taxonomy ) {
954 if ( ! taxonomy_exists( $taxonomy ) ) {
955 // Try re-registering synced taxonomies.
956 $taxonomies = $this->get_callable( 'taxonomies' );
957 if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
958 // Doesn't exist, or somehow hasn't been synced.
959 return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
960 }
961 $t = $taxonomies[ $taxonomy ];
962
963 return register_taxonomy(
964 $taxonomy,
965 $t->object_type,
966 (array) $t
967 );
968 }
969
970 return true;
971 }
972
973 /**
974 * Retrieve all terms from a taxonomy that are related to an object with a particular ID.
975 *
976 * @access public
977 *
978 * @param int $object_id Object ID.
979 * @param string $taxonomy Taxonomy slug.
980 *
981 * @return array|bool|WP_Error Array of terms on success, `false` if no terms or post doesn't exist, \WP_Error on failure.
982 */
983 public function get_the_terms( $object_id, $taxonomy ) {
984 return get_the_terms( $object_id, $taxonomy );
985 }
986
987 /**
988 * Insert or update a term.
989 *
990 * @access public
991 *
992 * @param \WP_Term $term_object Term object.
993 *
994 * @return array|bool|WP_Error Array of term_id and term_taxonomy_id if updated, true if inserted, \WP_Error on failure.
995 */
996 public function update_term( $term_object ) {
997 $taxonomy = $term_object->taxonomy;
998 global $wpdb;
999 $exists = $wpdb->get_var(
1000 $wpdb->prepare(
1001 "SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
1002 $term_object->term_id
1003 )
1004 );
1005 if ( ! $exists ) {
1006 $term_object = sanitize_term( clone $term_object, $taxonomy, 'db' );
1007 $term = array(
1008 'term_id' => $term_object->term_id,
1009 'name' => $term_object->name,
1010 'slug' => $term_object->slug,
1011 'term_group' => $term_object->term_group,
1012 );
1013 $term_taxonomy = array(
1014 'term_taxonomy_id' => $term_object->term_taxonomy_id,
1015 'term_id' => $term_object->term_id,
1016 'taxonomy' => $term_object->taxonomy,
1017 'description' => $term_object->description,
1018 'parent' => (int) $term_object->parent,
1019 'count' => (int) $term_object->count,
1020 );
1021 $wpdb->insert( $wpdb->terms, $term );
1022 $wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
1023
1024 return true;
1025 }
1026
1027 return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
1028 }
1029
1030 /**
1031 * Delete a term by the term ID and its corresponding taxonomy.
1032 *
1033 * @access public
1034 *
1035 * @param int $term_id Term ID.
1036 * @param string $taxonomy Taxonomy slug.
1037 *
1038 * @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.
1039 */
1040 public function delete_term( $term_id, $taxonomy ) {
1041 $this->ensure_taxonomy( $taxonomy );
1042 return wp_delete_term( $term_id, $taxonomy );
1043 }
1044
1045 /**
1046 * Add/update terms of a particular taxonomy of an object with the specified ID.
1047 *
1048 * @access public
1049 *
1050 * @param int $object_id The object to relate to.
1051 * @param string $taxonomy The context in which to relate the term to the object.
1052 * @param string|int|array $terms A single term slug, single term id, or array of either term slugs or ids.
1053 * @param bool $append Optional. If false will delete difference of terms. Default false.
1054 */
1055 public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
1056 $this->ensure_taxonomy( $taxonomy );
1057 wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
1058 }
1059
1060 /**
1061 * Remove certain term relationships from the specified object.
1062 *
1063 * @access public
1064 *
1065 * @todo Refactor to not use interpolated values when preparing the SQL query.
1066 *
1067 * @param int $object_id ID of the object.
1068 * @param array $tt_ids Term taxonomy IDs.
1069 * @return bool True on success, false on failure.
1070 */
1071 public function delete_object_terms( $object_id, $tt_ids ) {
1072 global $wpdb;
1073
1074 if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
1075 // Escape.
1076 $tt_ids_sanitized = array_map( 'intval', $tt_ids );
1077
1078 $taxonomies = array();
1079 foreach ( $tt_ids_sanitized as $tt_id ) {
1080 $term = get_term_by( 'term_taxonomy_id', $tt_id );
1081 $taxonomies[ $term->taxonomy ][] = $tt_id;
1082 }
1083 $in_tt_ids = implode( ', ', $tt_ids_sanitized );
1084
1085 /**
1086 * Fires immediately before an object-term relationship is deleted.
1087 *
1088 * @since 1.6.3
1089 * @since-jetpack 2.9.0
1090 *
1091 * @param int $object_id Object ID.
1092 * @param array $tt_ids An array of term taxonomy IDs.
1093 */
1094 do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
1095 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1096 $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
1097 foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
1098 $this->ensure_taxonomy( $taxonomy );
1099 wp_cache_delete( $object_id, $taxonomy . '_relationships' );
1100 /**
1101 * Fires immediately after an object-term relationship is deleted.
1102 *
1103 * @since 1.6.3
1104 * @since-jetpack 2.9.0
1105 *
1106 * @param int $object_id Object ID.
1107 * @param array $tt_ids An array of term taxonomy IDs.
1108 */
1109 do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
1110 wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
1111 }
1112
1113 return (bool) $deleted;
1114 }
1115
1116 return false;
1117 }
1118
1119 /**
1120 * Retrieve the number of users.
1121 * Not supported in this replicastore.
1122 *
1123 * @access public
1124 */
1125 public function user_count() {
1126 // Noop.
1127 }
1128
1129 /**
1130 * Retrieve a user object by the user ID.
1131 *
1132 * @access public
1133 *
1134 * @param int $user_id User ID.
1135 * @return \WP_User|null User object, or `null` if user invalid/not found.
1136 */
1137 public function get_user( $user_id ) {
1138 $user = get_user_by( 'id', $user_id );
1139 return $user instanceof \WP_User ? $user : null;
1140 }
1141
1142 /**
1143 * Insert or update a user.
1144 * Not supported in this replicastore.
1145 *
1146 * @access public
1147 * @throws Exception If this method is invoked.
1148 *
1149 * @param \WP_User $user User object.
1150 */
1151 public function upsert_user( $user ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1152 $this->invalid_call();
1153 }
1154
1155 /**
1156 * Delete a user.
1157 * Not supported in this replicastore.
1158 *
1159 * @access public
1160 * @throws Exception If this method is invoked.
1161 *
1162 * @param int $user_id User ID.
1163 */
1164 public function delete_user( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1165 $this->invalid_call();
1166 }
1167
1168 /**
1169 * Update/insert user locale.
1170 * Not supported in this replicastore.
1171 *
1172 * @access public
1173 * @throws Exception If this method is invoked.
1174 *
1175 * @param int $user_id User ID.
1176 * @param string $local The user locale.
1177 */
1178 public function upsert_user_locale( $user_id, $local ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1179 $this->invalid_call();
1180 }
1181
1182 /**
1183 * Delete user locale.
1184 * Not supported in this replicastore.
1185 *
1186 * @access public
1187 * @throws Exception If this method is invoked.
1188 *
1189 * @param int $user_id User ID.
1190 */
1191 public function delete_user_locale( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1192 $this->invalid_call();
1193 }
1194
1195 /**
1196 * Retrieve the user locale.
1197 *
1198 * @access public
1199 *
1200 * @param int $user_id User ID.
1201 * @return string The user locale.
1202 */
1203 public function get_user_locale( $user_id ) {
1204 return get_user_locale( $user_id );
1205 }
1206
1207 /**
1208 * Retrieve the allowed mime types for the user.
1209 * Not supported in this replicastore.
1210 *
1211 * @access public
1212 *
1213 * @param int $user_id User ID.
1214 */
1215 public function get_allowed_mime_types( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1216 // Noop.
1217 }
1218
1219 /**
1220 * Retrieve all the checksums we are interested in.
1221 *
1222 * @access public
1223 *
1224 * @param boolean $perform_text_conversion If text fields should be latin1 converted.
1225 *
1226 * @return array Checksums.
1227 */
1228 public function checksum_all( $perform_text_conversion = false ) {
1229 $all_checksum_tables = Table_Checksum::get_allowed_tables();
1230
1231 unset( $all_checksum_tables['users'] ); // Handled separately - TODO.
1232 unset( $all_checksum_tables['usermeta'] ); // Handled separately - TODO.
1233 unset( $all_checksum_tables['termmeta'] ); // Handled separately - TODO.
1234 unset( $all_checksum_tables['links'] ); // Not supported yet. Consider removing from default config.
1235 unset( $all_checksum_tables['options'] ); // Not supported yet. Consider removing from default config.
1236
1237 $all_checksum_tables = array_unique( array_keys( $all_checksum_tables ) );
1238
1239 $result = array();
1240
1241 foreach ( $all_checksum_tables as $table ) {
1242 $result_key = in_array( $table, array( 'postmeta', 'commentmeta' ), true ) ? str_replace( 'meta', '_meta', $table ) : $table;
1243 try {
1244 $checksum = $this->checksum_histogram( $table, null, null, null, null, true, '', false, false, $perform_text_conversion );
1245 $result[ $result_key ] = $this->summarize_checksum_histogram( $checksum );
1246 } catch ( Exception $ex ) {
1247 $result[ $result_key ] = null;
1248 }
1249 }
1250
1251 return $result;
1252 }
1253
1254 /**
1255 * Return the summarized checksum from buckets or the WP_Error.
1256 *
1257 * @param array $histogram checksum_histogram result.
1258 *
1259 * @return int|WP_Error checksum or Error.
1260 */
1261 protected function summarize_checksum_histogram( $histogram ) {
1262 if ( is_wp_error( $histogram ) ) {
1263 return $histogram;
1264 } else {
1265 return array_sum( $histogram );
1266 }
1267 }
1268
1269 /**
1270 * Grabs the minimum and maximum object ids for the given parameters.
1271 *
1272 * @access public
1273 *
1274 * @param string $id_field The id column in the table to query.
1275 * @param string $object_table The table to query.
1276 * @param string $where A sql where clause without 'WHERE'.
1277 * @param int $bucket_size The maximum amount of objects to include in the query.
1278 * For `term_relationships` table, the bucket size will refer to the amount
1279 * of distinct object ids. This will likely include more database rows than
1280 * the bucket size implies.
1281 *
1282 * @return object An object with min_id and max_id properties.
1283 */
1284 public function get_min_max_object_id( $id_field, $object_table, $where, $bucket_size ) {
1285 global $wpdb;
1286
1287 // The term relationship table's unique key is a combination of 2 columns. `DISTINCT` helps us get a more acurate query.
1288 $distinct_sql = ( $wpdb->term_relationships === $object_table ) ? 'DISTINCT' : '';
1289 $where_sql = $where ? "WHERE $where" : '';
1290
1291 // Since MIN() and MAX() do not work with LIMIT, we'll need to adjust the dataset we query if a limit is present.
1292 // With a limit present, we'll look at a dataset consisting of object_ids that meet the constructs of the $where clause.
1293 // Without a limit, we can use the actual table as a dataset.
1294 $from = $bucket_size ?
1295 "( SELECT $distinct_sql $id_field FROM $object_table $where_sql ORDER BY $id_field ASC LIMIT " . ( (int) $bucket_size ) . ' ) as ids' :
1296 "$object_table $where_sql ORDER BY $id_field ASC";
1297
1298 return $wpdb->get_row(
1299 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1300 "SELECT MIN($id_field) as min, MAX($id_field) as max FROM $from"
1301 );
1302 }
1303
1304 /**
1305 * Retrieve the checksum histogram for a specific object type.
1306 *
1307 * @access public
1308 *
1309 * @param string $table Object type.
1310 * @param null $buckets Number of buckets to split the objects to.
1311 * @param null $start_id Minimum object ID.
1312 * @param null $end_id Maximum object ID.
1313 * @param null $columns Table columns to calculate the checksum from.
1314 * @param bool $strip_non_ascii Whether to strip non-ASCII characters.
1315 * @param string $salt Salt, used for $wpdb->prepare()'s args.
1316 * @param bool $only_range_edges Only return the range edges and not the actual checksums.
1317 * @param bool $detailed_drilldown If the call should return a detailed drilldown for the checksum or only the checksum.
1318 * @param bool $perform_text_conversion If text fields should be converted to latin1 during the checksum calculation.
1319 *
1320 * @return array|WP_Error The checksum histogram.
1321 */
1322 public function checksum_histogram( $table, $buckets = null, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '', $only_range_edges = false, $detailed_drilldown = false, $perform_text_conversion = false ) {
1323 global $wpdb;
1324
1325 $wpdb->queries = array();
1326 try {
1327 $checksum_table = $this->get_table_checksum_instance( $table, $salt, $perform_text_conversion, $columns );
1328 } catch ( Exception $ex ) {
1329 return new WP_Error( 'checksum_disabled', $ex->getMessage() );
1330 }
1331
1332 try {
1333 $range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
1334 } catch ( Exception $ex ) {
1335 return new WP_Error( 'invalid_range_edges', '[' . ( $start_id ?? 'null' ) . '-' . ( $end_id ?? 'null' ) . ']: ' . $ex->getMessage() );
1336 }
1337
1338 if ( $only_range_edges ) {
1339 return $range_edges;
1340 }
1341
1342 $object_count = (int) $range_edges['item_count'];
1343
1344 if ( 0 === $object_count ) {
1345 return array();
1346 }
1347
1348 // Validate / Determine Buckets.
1349 if ( $buckets === null || $buckets < 1 ) {
1350 $buckets = $this->calculate_buckets( $table, $object_count );
1351 }
1352
1353 $bucket_size = (int) ceil( $object_count / $buckets );
1354 $previous_max_id = max( 0, $range_edges['min_range'] );
1355 $histogram = array();
1356
1357 do {
1358 try {
1359 $ids_range = $checksum_table->get_range_edges( $previous_max_id, null, $bucket_size );
1360 } catch ( Exception $ex ) {
1361 return new WP_Error( 'invalid_range_edges', '[' . $previous_max_id . '- ]: ' . $ex->getMessage() );
1362 }
1363
1364 if ( empty( $ids_range['min_range'] ) || empty( $ids_range['max_range'] ) ) {
1365 // Nothing to checksum here...
1366 break;
1367 }
1368
1369 // Get the checksum value.
1370 $batch_checksum = $checksum_table->calculate_checksum( $ids_range['min_range'], $ids_range['max_range'], null, $detailed_drilldown );
1371
1372 if ( is_wp_error( $batch_checksum ) ) {
1373 return $batch_checksum;
1374 }
1375
1376 if ( $ids_range['min_range'] === $ids_range['max_range'] ) {
1377 $histogram[ $ids_range['min_range'] ] = $batch_checksum;
1378 } else {
1379 $histogram[ "{$ids_range[ 'min_range' ]}-{$ids_range[ 'max_range' ]}" ] = $batch_checksum;
1380 }
1381 // If ids_range['max_range'] is PHP_INT_MAX, we've reached the end of the table. Edge case causing the loop to never end.
1382 if ( PHP_INT_MAX === (int) $ids_range['max_range'] ) {
1383 break;
1384 }
1385 $previous_max_id = $ids_range['max_range'] + 1;
1386 // If we've reached the max_range lets bail out.
1387 if ( $previous_max_id > $range_edges['max_range'] ) {
1388 break;
1389 }
1390 } while ( true );
1391
1392 return $histogram;
1393 }
1394
1395 /**
1396 * Retrieve the type of the checksum.
1397 *
1398 * @access public
1399 *
1400 * @return string Type of the checksum.
1401 */
1402 public function get_checksum_type() {
1403 return 'sum';
1404 }
1405
1406 /**
1407 * Used in methods that are not implemented and shouldn't be invoked.
1408 *
1409 * @access private
1410 * @return never
1411 * @throws Exception If this method is invoked.
1412 */
1413 private function invalid_call() {
1414 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
1415 $backtrace = debug_backtrace();
1416 $caller = $backtrace[1]['function'];
1417 throw new Exception( "This function $caller is not supported on the WP Replicastore" );
1418 }
1419
1420 /**
1421 * Determine number of buckets to use in full table checksum.
1422 *
1423 * @param string $table Object Type.
1424 * @param int $object_count Object count.
1425 * @return int Number of Buckets to use.
1426 */
1427 private function calculate_buckets( $table, $object_count ) {
1428 // Ensure no division by 0.
1429 if ( 0 === (int) $object_count ) {
1430 return 1;
1431 }
1432
1433 // Default Bucket sizes.
1434 $bucket_size = 10000; // Default bucket size is 10,000 items.
1435 switch ( $table ) {
1436 case 'postmeta':
1437 case 'commentmeta':
1438 case 'order_itemmeta':
1439 $bucket_size = 1000; // Meta bucket size is restricted to 1000 items.
1440 }
1441
1442 return (int) ceil( $object_count / $bucket_size );
1443 }
1444
1445 /**
1446 * Return an instance for `Table_Checksum`, depending on the table.
1447 *
1448 * Some tables require custom instances, due to different checksum logic.
1449 *
1450 * @param string $table The table that we want to get the instance for.
1451 * @param string $salt Salt to be used when generating the checksums.
1452 * @param bool $perform_text_conversion Should we perform text encoding conversion when calculating the checksum.
1453 * @param array $additional_columns Additional columns to add to the checksum calculation.
1454 *
1455 * @return Table_Checksum|Table_Checksum_Usermeta
1456 * @throws Exception Might throw an exception if any of the input parameters were invalid.
1457 */
1458 public function get_table_checksum_instance( $table, $salt = null, $perform_text_conversion = false, $additional_columns = null ) {
1459 if ( 'users' === $table ) {
1460 return new Table_Checksum_Users( $table, $salt, $perform_text_conversion, $additional_columns );
1461 }
1462 if ( 'usermeta' === $table ) {
1463 return new Table_Checksum_Usermeta( $table, $salt, $perform_text_conversion, $additional_columns );
1464 }
1465
1466 return new Table_Checksum( $table, $salt, $perform_text_conversion, $additional_columns );
1467 }
1468 }
1469