PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 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 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php +104 -93 12.4.2 → 16.3-beta View file →
@@ -12,8 +12,12 @@
12 12 use Automattic\Jetpack\Sync\Replicastore\Table_Checksum_Users;
13 13 use Exception;
14 14 use WP_Error;
15 15
16 +if ( ! defined( 'ABSPATH' ) ) {
17 + exit( 0 );
18 +}
19 +
16 20 /**
17 21 * An implementation of Replicastore Interface which returns data stored in a WordPress.org DB.
18 22 * This is useful to compare values in the local WP DB to values in the synced replica store
19 23 */
@@ -81,9 +85,10 @@
81 85 * @return int Number of terms.
82 86 */
83 87 public function term_count() {
84 88 global $wpdb;
85 - return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
89 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
90 + return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
86 91 }
87 92
88 93 /**
89 94 * Retrieve the number of rows in the `term_taxonomy` table.
@@ -93,9 +98,10 @@
93 98 * @return int Number of terms.
94 99 */
95 100 public function term_taxonomy_count() {
96 101 global $wpdb;
97 - return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
102 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
103 + return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
98 104 }
99 105
100 106 /**
101 107 * Retrieve the number of term relationships.
@@ -105,9 +111,10 @@
105 111 * @return int Number of rows in the term relationships table.
106 112 */
107 113 public function term_relationship_count() {
108 114 global $wpdb;
109 - return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
115 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
116 + return (int) $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
110 117 }
111 118
112 119 /**
113 120 * Retrieve the number of posts with a particular post status within a certain range.
@@ -139,10 +146,10 @@
139 146 if ( ! empty( $max_id ) ) {
140 147 $where .= ' AND ID <= ' . (int) $max_id;
141 148 }
142 149
143 - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
144 - return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
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" );
145 152 }
146 153
147 154 /**
148 155 * Retrieve the posts with a particular post status.
@@ -318,10 +325,10 @@
318 325 if ( ! empty( $max_id ) ) {
319 326 $where .= ' AND comment_ID <= ' . (int) $max_id;
320 327 }
321 328
322 - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
323 - return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
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" );
324 331 }
325 332
326 333 /**
327 334 * Translate a comment status to a value of the comment_approved field.
@@ -719,8 +726,47 @@
719 726 }
720 727 }
721 728
722 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 + /**
723 769 * Retrieve value of a constant based on the constant name.
724 770 *
725 771 * We explicitly return null instead of false if the constant doesn't exist.
726 772 *
@@ -873,11 +919,11 @@
873 919 * Retrieve a particular term.
874 920 *
875 921 * @access public
876 922 *
877 - * @param string $taxonomy Taxonomy slug.
878 - * @param int $term_id ID of the term.
879 - * @param string $term_key ID Field `term_id` or `term_taxonomy_id`.
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`.
880 926 *
881 927 * @return \WP_Term|WP_Error Term object on success, \WP_Error object on failure.
882 928 */
883 929 public function get_term( $taxonomy, $term_id, $term_key = 'term_id' ) {
@@ -1085,12 +1131,13 @@
1085 1131 *
1086 1132 * @access public
1087 1133 *
1088 1134 * @param int $user_id User ID.
1089 - * @return \WP_User User object.
1135 + * @return \WP_User|null User object, or `null` if user invalid/not found.
1090 1136 */
1091 1137 public function get_user( $user_id ) {
1092 - return \WP_User::get_instance( $user_id );
1138 + $user = get_user_by( 'id', $user_id );
1139 + return $user instanceof \WP_User ? $user : null;
1093 1140 }
1094 1141
1095 1142 /**
1096 1143 * Insert or update a user.
@@ -1170,9 +1217,8 @@
1170 1217 }
1171 1218
1172 1219 /**
1173 1220 * Retrieve all the checksums we are interested in.
1174 - * Currently that is posts, comments, post meta and comment meta.
1175 1221 *
1176 1222 * @access public
1177 1223 *
1178 1224 * @param boolean $perform_text_conversion If text fields should be latin1 converted.
@@ -1179,62 +1225,28 @@
1179 1225 *
1180 1226 * @return array Checksums.
1181 1227 */
1182 1228 public function checksum_all( $perform_text_conversion = false ) {
1183 - $post_checksum = $this->checksum_histogram( 'posts', null, null, null, null, true, '', false, false, $perform_text_conversion );
1184 - $comments_checksum = $this->checksum_histogram( 'comments', null, null, null, null, true, '', false, false, $perform_text_conversion );
1185 - $post_meta_checksum = $this->checksum_histogram( 'postmeta', null, null, null, null, true, '', false, false, $perform_text_conversion );
1186 - $comment_meta_checksum = $this->checksum_histogram( 'commentmeta', null, null, null, null, true, '', false, false, $perform_text_conversion );
1187 - $terms_checksum = $this->checksum_histogram( 'terms', null, null, null, null, true, '', false, false, $perform_text_conversion );
1188 - $term_relationships_checksum = $this->checksum_histogram( 'term_relationships', null, null, null, null, true, '', false, false, $perform_text_conversion );
1189 - $term_taxonomy_checksum = $this->checksum_histogram( 'term_taxonomy', null, null, null, null, true, '', false, false, $perform_text_conversion );
1229 + $all_checksum_tables = Table_Checksum::get_allowed_tables();
1190 1230
1191 - $result = array(
1192 - 'posts' => $this->summarize_checksum_histogram( $post_checksum ),
1193 - 'comments' => $this->summarize_checksum_histogram( $comments_checksum ),
1194 - 'post_meta' => $this->summarize_checksum_histogram( $post_meta_checksum ),
1195 - 'comment_meta' => $this->summarize_checksum_histogram( $comment_meta_checksum ),
1196 - 'terms' => $this->summarize_checksum_histogram( $terms_checksum ),
1197 - 'term_relationships' => $this->summarize_checksum_histogram( $term_relationships_checksum ),
1198 - 'term_taxonomy' => $this->summarize_checksum_histogram( $term_taxonomy_checksum ),
1199 - );
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.
1200 1236
1201 - /**
1202 - * WooCommerce tables
1203 - */
1237 + $all_checksum_tables = array_unique( array_keys( $all_checksum_tables ) );
1204 1238
1205 - /**
1206 - * On WordPress.com, we can't directly check if the site has support for WooCommerce.
1207 - * Having the option to override the functionality here helps with syncing WooCommerce tables.
1208 - *
1209 - * @since 10.1
1210 - *
1211 - * @param bool If we should we force-enable WooCommerce tables support.
1212 - */
1213 - $force_woocommerce_support = apply_filters( 'jetpack_table_checksum_force_enable_woocommerce', false );
1239 + $result = array();
1214 1240
1215 - if ( $force_woocommerce_support || class_exists( 'WooCommerce' ) ) {
1216 - /**
1217 - * Guard in Try/Catch as it's possible for the WooCommerce class to exist, but
1218 - * the tables to not. If we don't do this, the response will be just the exception, without
1219 - * returning any valid data. This will prevent us from ever performing a checksum/fix
1220 - * for sites like this.
1221 - * It's better to just skip the tables in the response, instead of completely failing.
1222 - */
1223 -
1241 + foreach ( $all_checksum_tables as $table ) {
1242 + $result_key = in_array( $table, array( 'postmeta', 'commentmeta' ), true ) ? str_replace( 'meta', '_meta', $table ) : $table;
1224 1243 try {
1225 - $woocommerce_order_items_checksum = $this->checksum_histogram( 'woocommerce_order_items' );
1226 - $result['woocommerce_order_items'] = $this->summarize_checksum_histogram( $woocommerce_order_items_checksum );
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 );
1227 1246 } catch ( Exception $ex ) {
1228 - $result['woocommerce_order_items'] = null;
1247 + $result[ $result_key ] = null;
1229 1248 }
1230 -
1231 - try {
1232 - $woocommerce_order_itemmeta_checksum = $this->checksum_histogram( 'woocommerce_order_itemmeta' );
1233 - $result['woocommerce_order_itemmeta'] = $this->summarize_checksum_histogram( $woocommerce_order_itemmeta_checksum );
1234 - } catch ( Exception $ex ) {
1235 - $result['woocommerce_order_itemmeta'] = null;
1236 - }
1237 1249 }
1238 1250
1239 1251 return $result;
1240 1252 }
@@ -1279,9 +1291,9 @@
1279 1291 // Since MIN() and MAX() do not work with LIMIT, we'll need to adjust the dataset we query if a limit is present.
1280 1292 // With a limit present, we'll look at a dataset consisting of object_ids that meet the constructs of the $where clause.
1281 1293 // Without a limit, we can use the actual table as a dataset.
1282 1294 $from = $bucket_size ?
1283 - "( SELECT $distinct_sql $id_field FROM $object_table $where_sql ORDER BY $id_field ASC LIMIT $bucket_size ) as ids" :
1295 + "( SELECT $distinct_sql $id_field FROM $object_table $where_sql ORDER BY $id_field ASC LIMIT " . ( (int) $bucket_size ) . ' ) as ids' :
1284 1296 "$object_table $where_sql ORDER BY $id_field ASC";
1285 1297
1286 1298 return $wpdb->get_row(
1287 1299 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
@@ -1305,9 +1317,8 @@
1305 1317 * @param bool $detailed_drilldown If the call should return a detailed drilldown for the checksum or only the checksum.
1306 1318 * @param bool $perform_text_conversion If text fields should be converted to latin1 during the checksum calculation.
1307 1319 *
1308 1320 * @return array|WP_Error The checksum histogram.
1309 - * @throws Exception Throws an exception if data validation fails inside `Table_Checksum` calls.
1310 1321 */
1311 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 ) {
1312 1323 global $wpdb;
1313 1324
@@ -1312,23 +1323,19 @@
1312 1323 global $wpdb;
1313 1324
1314 1325 $wpdb->queries = array();
1315 1326 try {
1316 - $checksum_table = $this->get_table_checksum_instance( $table, $salt, $perform_text_conversion );
1327 + $checksum_table = $this->get_table_checksum_instance( $table, $salt, $perform_text_conversion, $columns );
1317 1328 } catch ( Exception $ex ) {
1318 1329 return new WP_Error( 'checksum_disabled', $ex->getMessage() );
1319 1330 }
1320 1331
1321 - // Validate / Determine Buckets.
1322 - if ( $buckets === null || $buckets < 1 ) {
1323 - $buckets = $this->calculate_buckets( $table, $start_id, $end_id );
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() );
1324 1336 }
1325 - if ( is_wp_error( $buckets ) ) {
1326 - return $buckets;
1327 - }
1328 1337
1329 - $range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
1330 -
1331 1338 if ( $only_range_edges ) {
1332 1339 return $range_edges;
1333 1340 }
1334 1341
@@ -1337,14 +1344,23 @@
1337 1344 if ( 0 === $object_count ) {
1338 1345 return array();
1339 1346 }
1340 1347
1348 + // Validate / Determine Buckets.
1349 + if ( $buckets === null || $buckets < 1 ) {
1350 + $buckets = $this->calculate_buckets( $table, $object_count );
1351 + }
1352 +
1341 1353 $bucket_size = (int) ceil( $object_count / $buckets );
1342 1354 $previous_max_id = max( 0, $range_edges['min_range'] );
1343 1355 $histogram = array();
1344 1356
1345 1357 do {
1346 - $ids_range = $checksum_table->get_range_edges( $previous_max_id, null, $bucket_size );
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 + }
1347 1363
1348 1364 if ( empty( $ids_range['min_range'] ) || empty( $ids_range['max_range'] ) ) {
1349 1365 // Nothing to checksum here...
1350 1366 break;
@@ -1361,9 +1377,12 @@
1361 1377 $histogram[ $ids_range['min_range'] ] = $batch_checksum;
1362 1378 } else {
1363 1379 $histogram[ "{$ids_range[ 'min_range' ]}-{$ids_range[ 'max_range' ]}" ] = $batch_checksum;
1364 1380 }
1365 -
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 + }
1366 1385 $previous_max_id = $ids_range['max_range'] + 1;
1367 1386 // If we've reached the max_range lets bail out.
1368 1387 if ( $previous_max_id > $range_edges['max_range'] ) {
1369 1388 break;
@@ -1387,8 +1406,9 @@
1387 1406 /**
1388 1407 * Used in methods that are not implemented and shouldn't be invoked.
1389 1408 *
1390 1409 * @access private
1410 + * @return never
1391 1411 * @throws Exception If this method is invoked.
1392 1412 */
1393 1413 private function invalid_call() {
1394 1414 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
@@ -1400,22 +1420,12 @@
1400 1420 /**
1401 1421 * Determine number of buckets to use in full table checksum.
1402 1422 *
1403 1423 * @param string $table Object Type.
1404 - * @param int $start_id Min Object ID.
1405 - * @param int $end_id Max Object ID.
1406 - * @return int|WP_Error Number of Buckets to use.
1424 + * @param int $object_count Object count.
1425 + * @return int Number of Buckets to use.
1407 1426 */
1408 - private function calculate_buckets( $table, $start_id = null, $end_id = null ) {
1409 - // Get # of objects.
1410 - try {
1411 - $checksum_table = $this->get_table_checksum_instance( $table );
1412 - } catch ( Exception $ex ) {
1413 - return new WP_Error( 'checksum_disabled', $ex->getMessage() );
1414 - }
1415 - $range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
1416 - $object_count = $range_edges['item_count'];
1417 -
1427 + private function calculate_buckets( $table, $object_count ) {
1418 1428 // Ensure no division by 0.
1419 1429 if ( 0 === (int) $object_count ) {
1420 1430 return 1;
1421 1431 }
@@ -1436,22 +1446,23 @@
1436 1446 * Return an instance for `Table_Checksum`, depending on the table.
1437 1447 *
1438 1448 * Some tables require custom instances, due to different checksum logic.
1439 1449 *
1440 - * @param string $table The table that we want to get the instance for.
1441 - * @param null $salt Salt to be used when generating the checksums.
1442 - * @param false $perform_text_conversion Should we perform text encoding conversion when calculating the checksum.
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.
1443 1454 *
1444 1455 * @return Table_Checksum|Table_Checksum_Usermeta
1445 1456 * @throws Exception Might throw an exception if any of the input parameters were invalid.
1446 1457 */
1447 - public function get_table_checksum_instance( $table, $salt = null, $perform_text_conversion = false ) {
1458 + public function get_table_checksum_instance( $table, $salt = null, $perform_text_conversion = false, $additional_columns = null ) {
1448 1459 if ( 'users' === $table ) {
1449 - return new Table_Checksum_Users( $table, $salt, $perform_text_conversion );
1460 + return new Table_Checksum_Users( $table, $salt, $perform_text_conversion, $additional_columns );
1450 1461 }
1451 1462 if ( 'usermeta' === $table ) {
1452 - return new Table_Checksum_Usermeta( $table, $salt, $perform_text_conversion );
1463 + return new Table_Checksum_Usermeta( $table, $salt, $perform_text_conversion, $additional_columns );
1453 1464 }
1454 1465
1455 - return new Table_Checksum( $table, $salt, $perform_text_conversion );
1466 + return new Table_Checksum( $table, $salt, $perform_text_conversion, $additional_columns );
1456 1467 }
1457 1468 }