From e44051075ca370f8d6d4e5ff950798eefea9e6c4 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Fri, 23 May 2025 14:30:52 +0100 Subject: [PATCH 01/28] Replace direct cache operations with query-specific functions Introduced `wp_cache_get_query_data` and `wp_cache_set_query_data` to handle cached query data with freshness validation. Refactored various classes, functions, and queries to use these standardized methods for improved readability and maintainability while ensuring cache consistency. --- src/wp-includes/class-wp-comment-query.php | 14 ++-- src/wp-includes/class-wp-network-query.php | 6 +- src/wp-includes/class-wp-query.php | 29 ++++---- src/wp-includes/class-wp-site-query.php | 6 +- src/wp-includes/class-wp-term-query.php | 17 ++--- src/wp-includes/class-wp-user-query.php | 67 ++++++++--------- src/wp-includes/functions.php | 83 ++++++++++++++++++++++ src/wp-includes/general-template.php | 30 ++++---- src/wp-includes/link-template.php | 6 +- src/wp-includes/post.php | 6 +- src/wp-includes/query.php | 12 ++-- src/wp-includes/taxonomy.php | 6 +- src/wp-includes/user.php | 6 +- 13 files changed, 187 insertions(+), 101 deletions(-) diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index 6a72c0d209d5b..40df025946183 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -451,8 +451,8 @@ public function get_comments() { $key = md5( serialize( $_args ) ); $last_changed = wp_cache_get_last_changed( 'comment' ); - $cache_key = "get_comments:$key:$last_changed"; - $cache_value = wp_cache_get( $cache_key, 'comment-queries' ); + $cache_key = "get_comments:$key"; + $cache_value = wp_cache_get_query_data( $cache_key, 'comment-queries', $last_changed ); if ( false === $cache_value ) { $comment_ids = $this->get_comment_ids(); if ( $comment_ids ) { @@ -463,7 +463,7 @@ public function get_comments() { 'comment_ids' => $comment_ids, 'found_comments' => $this->found_comments, ); - wp_cache_add( $cache_key, $cache_value, 'comment-queries' ); + wp_cache_set_query_data( $cache_key, $cache_value, 'comment-queries', $last_changed ); } else { $comment_ids = $cache_value['comment_ids']; $this->found_comments = $cache_value['found_comments']; @@ -1046,9 +1046,9 @@ protected function fill_descendants( $comments ) { if ( $_parent_ids ) { $cache_keys = array(); foreach ( $_parent_ids as $parent_id ) { - $cache_keys[ $parent_id ] = "get_comment_child_ids:$parent_id:$key:$last_changed"; + $cache_keys[ $parent_id ] = "get_comment_child_ids:$parent_id:$key"; } - $cache_data = wp_cache_get_multiple( array_values( $cache_keys ), 'comment-queries' ); + $cache_data = wp_cache_get_multiple_query_data( array_values( $cache_keys ), 'comment-queries', $last_changed ); foreach ( $_parent_ids as $parent_id ) { $parent_child_ids = $cache_data[ $cache_keys[ $parent_id ] ]; if ( false !== $parent_child_ids ) { @@ -1082,10 +1082,10 @@ protected function fill_descendants( $comments ) { $data = array(); foreach ( $parent_map as $parent_id => $children ) { - $cache_key = "get_comment_child_ids:$parent_id:$key:$last_changed"; + $cache_key = "get_comment_child_ids:$parent_id:$key"; $data[ $cache_key ] = $children; } - wp_cache_set_multiple( $data, 'comment-queries' ); + wp_cache_set_multiple_query_data( $data, 'comment-queries', $last_changed ); } ++$level; diff --git a/src/wp-includes/class-wp-network-query.php b/src/wp-includes/class-wp-network-query.php index d69f1a98ed81d..dddcdef6fe725 100644 --- a/src/wp-includes/class-wp-network-query.php +++ b/src/wp-includes/class-wp-network-query.php @@ -249,8 +249,8 @@ public function get_networks() { $key = md5( serialize( $_args ) ); $last_changed = wp_cache_get_last_changed( 'networks' ); - $cache_key = "get_network_ids:$key:$last_changed"; - $cache_value = wp_cache_get( $cache_key, 'network-queries' ); + $cache_key = "get_network_ids:$key"; + $cache_value = wp_cache_get_query_data( $cache_key, 'network-queries', $last_changed ); if ( false === $cache_value ) { $network_ids = $this->get_network_ids(); @@ -262,7 +262,7 @@ public function get_networks() { 'network_ids' => $network_ids, 'found_networks' => $this->found_networks, ); - wp_cache_add( $cache_key, $cache_value, 'network-queries' ); + wp_cache_set_query_data( $cache_key, $cache_value, 'network-queries', $last_changed ); } else { $network_ids = $cache_value['network_ids']; $this->found_networks = $cache_value['found_networks']; diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 21127dc755f1c..70c67ecb265f6 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -2884,11 +2884,11 @@ public function get_posts() { $key = md5( $comments_request ); $last_changed = wp_cache_get_last_changed( 'comment' ) . ':' . wp_cache_get_last_changed( 'posts' ); - $cache_key = "comment_feed:$key:$last_changed"; - $comment_ids = wp_cache_get( $cache_key, 'comment-queries' ); + $cache_key = "comment_feed:$key"; + $comment_ids = wp_cache_get_query_data( $cache_key, 'comment-queries', $last_changed ); if ( false === $comment_ids ) { $comment_ids = $wpdb->get_col( $comments_request ); - wp_cache_add( $cache_key, $comment_ids, 'comment-queries' ); + wp_cache_set_query_data( $cache_key, $comment_ids, 'comment-queries', $last_changed ); } _prime_comment_caches( $comment_ids ); @@ -3246,15 +3246,21 @@ public function get_posts() { $id_query_is_cacheable = false; } + $last_changed = wp_cache_get_last_changed( 'posts' ); + if ( ! empty( $this->tax_query->queries ) ) { + $last_changed .= wp_cache_get_last_changed( 'terms' ); + } + if ( $q['cache_results'] && $id_query_is_cacheable ) { $new_request = str_replace( $fields, "{$wpdb->posts}.*", $this->request ); $cache_key = $this->generate_cache_key( $q, $new_request ); $cache_found = false; if ( null === $this->posts ) { - $cached_results = wp_cache_get( $cache_key, 'post-queries', false, $cache_found ); + $cached_results = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); if ( $cached_results ) { + $cache_found = true; /** @var int[] */ $post_ids = array_map( 'intval', $cached_results['posts'] ); @@ -3312,7 +3318,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set( $cache_key, $cache_value, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); } return $this->posts; @@ -3486,11 +3492,11 @@ public function get_posts() { $comment_key = md5( $comments_request ); $comment_last_changed = wp_cache_get_last_changed( 'comment' ); - $comment_cache_key = "comment_feed:$comment_key:$comment_last_changed"; - $comment_ids = wp_cache_get( $comment_cache_key, 'comment-queries' ); + $comment_cache_key = "comment_feed:$comment_key"; + $comment_ids = wp_cache_get_query_data( $comment_cache_key, 'comment-queries', $comment_last_changed ); if ( false === $comment_ids ) { $comment_ids = $wpdb->get_col( $comments_request ); - wp_cache_add( $comment_cache_key, $comment_ids, 'comment-queries' ); + wp_cache_set_query_data( $comment_cache_key, $comment_ids, 'comment-queries', $comment_last_changed ); } _prime_comment_caches( $comment_ids ); @@ -5062,12 +5068,7 @@ static function ( &$value ) use ( $wpdb, $placeholder ) { $sql = $wpdb->remove_placeholder_escape( $sql ); $key = md5( serialize( $args ) . $sql ); - $last_changed = wp_cache_get_last_changed( 'posts' ); - if ( ! empty( $this->tax_query->queries ) ) { - $last_changed .= wp_cache_get_last_changed( 'terms' ); - } - - $this->query_cache_key = "wp_query:$key:$last_changed"; + $this->query_cache_key = "wp_query:$key"; return $this->query_cache_key; } diff --git a/src/wp-includes/class-wp-site-query.php b/src/wp-includes/class-wp-site-query.php index 826bf76c784ad..46faa3bb0ba56 100644 --- a/src/wp-includes/class-wp-site-query.php +++ b/src/wp-includes/class-wp-site-query.php @@ -357,8 +357,8 @@ public function get_sites() { $key = md5( serialize( $_args ) ); $last_changed = wp_cache_get_last_changed( 'sites' ); - $cache_key = "get_sites:$key:$last_changed"; - $cache_value = wp_cache_get( $cache_key, 'site-queries' ); + $cache_key = "get_sites:$key"; + $cache_value = wp_cache_get_query_data( $cache_key, 'site-queries', $last_changed ); if ( false === $cache_value ) { $site_ids = $this->get_site_ids(); @@ -370,7 +370,7 @@ public function get_sites() { 'site_ids' => $site_ids, 'found_sites' => $this->found_sites, ); - wp_cache_add( $cache_key, $cache_value, 'site-queries' ); + wp_cache_set_query_data( $cache_key, $cache_value, 'site-queries', $last_changed ); } else { $site_ids = $cache_value['site_ids']; $this->found_sites = $cache_value['found_sites']; diff --git a/src/wp-includes/class-wp-term-query.php b/src/wp-includes/class-wp-term-query.php index d680fd602cb5d..f15a9d4ee6c7b 100644 --- a/src/wp-includes/class-wp-term-query.php +++ b/src/wp-includes/class-wp-term-query.php @@ -777,8 +777,9 @@ public function get_terms() { } if ( $args['cache_results'] ) { - $cache_key = $this->generate_cache_key( $args, $this->request ); - $cache = wp_cache_get( $cache_key, 'term-queries' ); + $cache_key = $this->generate_cache_key( $args, $this->request ); + $last_changed = wp_cache_get_last_changed( 'terms' ); + $cache = wp_cache_get_query_data( $cache_key, 'term-queries', $last_changed ); if ( false !== $cache ) { if ( 'ids' === $_fields ) { @@ -806,7 +807,7 @@ public function get_terms() { if ( 'count' === $_fields ) { $count = $wpdb->get_var( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared if ( $args['cache_results'] ) { - wp_cache_set( $cache_key, $count, 'term-queries' ); + wp_cache_set_query_data( $cache_key, $count, 'term-queries', $last_changed ); } return $count; } @@ -815,7 +816,7 @@ public function get_terms() { if ( empty( $terms ) ) { if ( $args['cache_results'] ) { - wp_cache_add( $cache_key, array(), 'term-queries' ); + wp_cache_set_query_data( $cache_key, array(), 'term-queries', $last_changed ); } return array(); } @@ -899,7 +900,7 @@ public function get_terms() { } if ( $args['cache_results'] ) { - wp_cache_add( $cache_key, $term_cache, 'term-queries' ); + wp_cache_set_query_data( $cache_key, $term_cache, 'term-queries', $last_changed ); } $this->terms = $this->format_terms( $term_objects, $_fields ); @@ -1171,8 +1172,8 @@ protected function generate_cache_key( array $args, $sql ) { // Replace wpdb placeholder in the SQL statement used by the cache key. $sql = $wpdb->remove_placeholder_escape( $sql ); - $key = md5( serialize( $cache_args ) . $sql ); - $last_changed = wp_cache_get_last_changed( 'terms' ); - return "get_terms:$key:$last_changed"; + $key = md5( serialize( $cache_args ) . $sql ); + + return "get_terms:$key"; } } diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index fd35182eab9f5..340b4e253fef7 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -830,8 +830,38 @@ public function query() { $cache_value = false; $cache_key = $this->generate_cache_key( $qv, $this->request ); $cache_group = 'user-queries'; + $last_changed = wp_cache_get_last_changed( 'users' ); + + if ( empty( $qv['orderby'] ) ) { + // Default order is by 'user_login'. + $ordersby = array( 'user_login' => '' ); + } elseif ( is_array( $qv['orderby'] ) ) { + $ordersby = $qv['orderby']; + } else { + // 'orderby' values may be a comma- or space-separated list. + $ordersby = preg_split( '/[,\s]+/', $qv['orderby'] ); + } + + $blog_id = 0; + if ( isset( $qv['blog_id'] ) ) { + $blog_id = absint( $qv['blog_id'] ); + } + + if ( $qv['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { + $switch = $blog_id && get_current_blog_id() !== $blog_id; + if ( $switch ) { + switch_to_blog( $blog_id ); + } + + $last_changed .= wp_cache_get_last_changed( 'posts' ); + + if ( $switch ) { + restore_current_blog(); + } + } + if ( $qv['cache_results'] ) { - $cache_value = wp_cache_get( $cache_key, $cache_group ); + $cache_value = wp_cache_get_query_data( $cache_key, $cache_group, $last_changed ); } if ( false !== $cache_value ) { $this->results = $cache_value['user_data']; @@ -866,7 +896,7 @@ public function query() { 'user_data' => $this->results, 'total_users' => $this->total_users, ); - wp_cache_add( $cache_key, $cache_value, $cache_group ); + wp_cache_set_query_data( $cache_key, $cache_value, $cache_group, $last_changed ); } } } @@ -1056,38 +1086,9 @@ protected function generate_cache_key( array $args, $sql ) { // Replace wpdb placeholder in the SQL statement used by the cache key. $sql = $wpdb->remove_placeholder_escape( $sql ); - $key = md5( $sql ); - $last_changed = wp_cache_get_last_changed( 'users' ); - - if ( empty( $args['orderby'] ) ) { - // Default order is by 'user_login'. - $ordersby = array( 'user_login' => '' ); - } elseif ( is_array( $args['orderby'] ) ) { - $ordersby = $args['orderby']; - } else { - // 'orderby' values may be a comma- or space-separated list. - $ordersby = preg_split( '/[,\s]+/', $args['orderby'] ); - } - - $blog_id = 0; - if ( isset( $args['blog_id'] ) ) { - $blog_id = absint( $args['blog_id'] ); - } - - if ( $args['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { - $switch = $blog_id && get_current_blog_id() !== $blog_id; - if ( $switch ) { - switch_to_blog( $blog_id ); - } - - $last_changed .= wp_cache_get_last_changed( 'posts' ); - - if ( $switch ) { - restore_current_blog(); - } - } + $key = md5( $sql ); - return "get_users:$key:$last_changed"; + return "get_users:$key"; } /** diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 33b775e718215..8756c0fb63cfb 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8061,6 +8061,89 @@ function wp_unique_id_from_values( array $data, string $prefix = '' ): string { return $prefix . $hash; } +/** + * Retrieves cached query data if valid and unchanged. + * + * @param string $cache_key The cache key used for storage and retrieval. + * @param string $group The cache group used for organizing data. + * @param string $last_changed The timestamp of the last modification to the cache group. + * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. + */ +function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { + $cache = wp_cache_get( $cache_key, $group ); + + if ( false === $cache ) { + return false; + } + + if ( $last_changed !== $cache['last_changed'] ) { + return false; + } + + return $cache['data']; +} + +/** + * Stores query-related data in the cache. + * + * @param string $cache_key The cache key under which to store the data. + * @param mixed $data The data to be cached. + * @param string $group The cache group to which the data belongs. + * @param string $last_changed The timestamp or identifier indicating the last change to the cached data. + */ +function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed ) { + wp_cache_set( + $cache_key, + array( + 'data' => $data, + 'last_chagned' => $last_changed, + ), + $group + ); +} + +/** + * Retrieves multiple items from the cache and validates their freshness. + * + * @param array $cache_keys Array of cache keys to retrieve. + * @param string $group The group of the cache to check. + * @param string $last_changed The timestamp of the last cache modification for validation. + * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. + */ +function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) { + $cache = wp_cache_get_multiple( $cache_keys, $group ); + + foreach ( $cache as $key => $value ) { + if ( false === $value ) { + continue; + } + if ( $last_changed !== $value['last_changed'] ) { + $cache[ $key ] = false; + } + } + + return $cache; +} + +/** + * Stores multiple pieces of query data in the cache. + * + * @param array $cache_keys Array of cache keys to define the items to be stored. + * @param mixed $data Data to be stored in the cache for all keys. + * @param string $group Group to which the cached data belongs. + * @param string $last_changed Timestamp indicating the last modification time for the data. + */ +function wp_cache_set_multiple_query_data( $cache_keys, $data, $group, $last_changed ) { + $new_cache = array(); + foreach ( $cache_keys as $key ) { + $new_cache[ $key ] = array( + 'data' => $data, + 'last_changed' => $last_changed, + ); + } + wp_cache_set_multiple( $new_cache, $group ); +} + /** * Gets last changed date for the specified cache group. * diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 7bb584af7c289..2eb145ab48214 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -2069,11 +2069,11 @@ function wp_get_archives( $args = '' ) { if ( 'monthly' === $parsed_args['type'] ) { $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date $order $limit"; $key = md5( $query ); - $key = "wp_get_archives:$key:$last_changed"; - $results = wp_cache_get( $key, 'post-queries' ); + $key = "wp_get_archives:$key"; + $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { $after = $parsed_args['after']; @@ -2094,11 +2094,11 @@ function wp_get_archives( $args = '' ) { } elseif ( 'yearly' === $parsed_args['type'] ) { $query = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date) ORDER BY post_date $order $limit"; $key = md5( $query ); - $key = "wp_get_archives:$key:$last_changed"; - $results = wp_cache_get( $key, 'post-queries' ); + $key = "wp_get_archives:$key"; + $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { $after = $parsed_args['after']; @@ -2118,11 +2118,11 @@ function wp_get_archives( $args = '' ) { } elseif ( 'daily' === $parsed_args['type'] ) { $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS `dayofmonth`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER BY post_date $order $limit"; $key = md5( $query ); - $key = "wp_get_archives:$key:$last_changed"; - $results = wp_cache_get( $key, 'post-queries' ); + $key = "wp_get_archives:$key"; + $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { $after = $parsed_args['after']; @@ -2144,11 +2144,11 @@ function wp_get_archives( $args = '' ) { $week = _wp_mysql_week( '`post_date`' ); $query = "SELECT DISTINCT $week AS `week`, YEAR( `post_date` ) AS `yr`, DATE_FORMAT( `post_date`, '%Y-%m-%d' ) AS `yyyymmdd`, count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit"; $key = md5( $query ); - $key = "wp_get_archives:$key:$last_changed"; - $results = wp_cache_get( $key, 'post-queries' ); + $key = "wp_get_archives:$key"; + $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); } $arc_w_last = ''; if ( $results ) { @@ -2183,11 +2183,11 @@ function wp_get_archives( $args = '' ) { $orderby = ( 'alpha' === $parsed_args['type'] ) ? 'post_title ASC ' : 'post_date DESC, ID DESC '; $query = "SELECT * FROM $wpdb->posts $join $where ORDER BY $orderby $limit"; $key = md5( $query ); - $key = "wp_get_archives:$key:$last_changed"; - $results = wp_cache_get( $key, 'post-queries' ); + $key = "wp_get_archives:$key"; + $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { foreach ( (array) $results as $result ) { diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php index 3e02c17853971..7ca5e49cc9251 100644 --- a/src/wp-includes/link-template.php +++ b/src/wp-includes/link-template.php @@ -2009,9 +2009,9 @@ function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previo if ( $in_same_term || ! empty( $excluded_terms ) ) { $last_changed .= wp_cache_get_last_changed( 'terms' ); } - $cache_key = "adjacent_post:$key:$last_changed"; + $cache_key = "adjacent_post:$key"; - $result = wp_cache_get( $cache_key, 'post-queries' ); + $result = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); if ( false !== $result ) { if ( $result ) { $result = get_post( $result ); @@ -2024,7 +2024,7 @@ function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previo $result = ''; } - wp_cache_set( $cache_key, $result, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $result, 'post-queries', $last_changed ); if ( $result ) { $result = get_post( $result ); diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 19fada6383cfe..57d9c9e8ec0e4 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6089,8 +6089,8 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { $last_changed = wp_cache_get_last_changed( 'posts' ); $hash = md5( $page_path . serialize( $post_type ) ); - $cache_key = "get_page_by_path:$hash:$last_changed"; - $cached = wp_cache_get( $cache_key, 'post-queries' ); + $cache_key = "get_page_by_path:$hash"; + $cached = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); if ( false !== $cached ) { // Special case: '0' is a bad `$page_path`. if ( '0' === $cached || 0 === $cached ) { @@ -6160,7 +6160,7 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { } // We cache misses as well as hits. - wp_cache_set( $cache_key, $found_id, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $found_id, 'post-queries', $last_changed ); if ( $found_id ) { return get_post( $found_id, $output ); diff --git a/src/wp-includes/query.php b/src/wp-includes/query.php index 94c6453b6637b..60da569bfaeee 100644 --- a/src/wp-includes/query.php +++ b/src/wp-includes/query.php @@ -1155,13 +1155,13 @@ function _find_post_by_old_slug( $post_type ) { $key = md5( $query ); $last_changed = wp_cache_get_last_changed( 'posts' ); - $cache_key = "find_post_by_old_slug:$key:$last_changed"; - $cache = wp_cache_get( $cache_key, 'post-queries' ); + $cache_key = "find_post_by_old_slug:$key"; + $cache = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); if ( false !== $cache ) { $id = $cache; } else { $id = (int) $wpdb->get_var( $query ); - wp_cache_set( $cache_key, $id, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $id, 'post-queries', $last_changed ); } return $id; @@ -1198,8 +1198,8 @@ function _find_post_by_old_date( $post_type ) { $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta AS pm_date, $wpdb->posts WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_date' AND post_name = %s" . $date_query, $post_type, get_query_var( 'name' ) ); $key = md5( $query ); $last_changed = wp_cache_get_last_changed( 'posts' ); - $cache_key = "find_post_by_old_date:$key:$last_changed"; - $cache = wp_cache_get( $cache_key, 'post-queries' ); + $cache_key = "find_post_by_old_date:$key"; + $cache = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); if ( false !== $cache ) { $id = $cache; } else { @@ -1208,7 +1208,7 @@ function _find_post_by_old_date( $post_type ) { // Check to see if an old slug matches the old date. $id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts, $wpdb->postmeta AS pm_slug, $wpdb->postmeta AS pm_date WHERE ID = pm_slug.post_id AND ID = pm_date.post_id AND post_type = %s AND pm_slug.meta_key = '_wp_old_slug' AND pm_slug.meta_value = %s AND pm_date.meta_key = '_wp_old_date'" . $date_query, $post_type, get_query_var( 'name' ) ) ); } - wp_cache_set( $cache_key, $id, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $id, 'post-queries', $last_changed ); } } diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 3e235b780fabb..e1cbfa6320538 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -896,11 +896,11 @@ function get_objects_in_term( $term_ids, $taxonomies, $args = array() ) { $sql = "SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tt.term_id IN ($term_ids) ORDER BY tr.object_id $order"; $last_changed = wp_cache_get_last_changed( 'terms' ); - $cache_key = 'get_objects_in_term:' . md5( $sql ) . ":$last_changed"; - $cache = wp_cache_get( $cache_key, 'term-queries' ); + $cache_key = 'get_objects_in_term:' . md5( $sql ); + $cache = wp_cache_get_query_data( $cache_key, 'term-queries', $last_changed ); if ( false === $cache ) { $object_ids = $wpdb->get_col( $sql ); - wp_cache_set( $cache_key, $object_ids, 'term-queries' ); + wp_cache_set_query_data( $cache_key, $object_ids, 'term-queries', $last_changed ); } else { $object_ids = (array) $cache; } diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 6d537263270bc..beffdcde98f31 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -623,11 +623,11 @@ function count_user_posts( $userid, $post_type = 'post', $public_only = false ) $query = "SELECT COUNT(*) FROM $wpdb->posts $where"; $last_changed = wp_cache_get_last_changed( 'posts' ); - $cache_key = 'count_user_posts:' . md5( $query ) . ':' . $last_changed; - $count = wp_cache_get( $cache_key, 'post-queries' ); + $cache_key = 'count_user_posts:' . md5( $query ); + $count = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); if ( false === $count ) { $count = $wpdb->get_var( $query ); - wp_cache_set( $cache_key, $count, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $count, 'post-queries', $last_changed ); } /** From 43f7a9e870b8cb9f82d410e47765bbe258c1ff28 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 28 May 2025 09:52:09 +0100 Subject: [PATCH 02/28] Check key exists --- src/wp-includes/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 8756c0fb63cfb..5cc60279f524b 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8076,7 +8076,7 @@ function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { return false; } - if ( $last_changed !== $cache['last_changed'] ) { + if ( ! isset( $cache['last_changed'] ) || $last_changed !== $cache['last_changed'] ) { return false; } @@ -8117,7 +8117,7 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) if ( false === $value ) { continue; } - if ( $last_changed !== $value['last_changed'] ) { + if ( ! isset( $value['last_changed'] ) || $last_changed !== $value['last_changed'] ) { $cache[ $key ] = false; } } From d13fe9019911f938e94e6ec0ef48fa50f1fb13a2 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 28 May 2025 09:59:09 +0100 Subject: [PATCH 03/28] Check values. --- src/wp-includes/functions.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 5cc60279f524b..a506d27ee8803 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8072,11 +8072,11 @@ function wp_unique_id_from_values( array $data, string $prefix = '' ): string { function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { $cache = wp_cache_get( $cache_key, $group ); - if ( false === $cache ) { + if ( ! is_array( $cache ) ) { return false; } - if ( ! isset( $cache['last_changed'] ) || $last_changed !== $cache['last_changed'] ) { + if ( ! isset( $cache['last_changed'], $cache['data'] ) || $last_changed !== $cache['last_changed'] ) { return false; } @@ -8114,12 +8114,15 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) $cache = wp_cache_get_multiple( $cache_keys, $group ); foreach ( $cache as $key => $value ) { - if ( false === $value ) { + if ( ! is_array( $value ) ) { + $cache[ $key ] = false; continue; } - if ( ! isset( $value['last_changed'] ) || $last_changed !== $value['last_changed'] ) { + if ( ! isset( $value['last_changed'], $value['data'] ) || $last_changed !== $value['last_changed'] ) { $cache[ $key ] = false; + continue; } + $cache[ $key ] = $value['data']; } return $cache; From 015e4f6cb2dda1981121e0679d6f78a4c3262811 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 28 May 2025 10:56:44 +0100 Subject: [PATCH 04/28] Change wp_cache_set_multiple_query_data --- src/wp-includes/functions.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index a506d27ee8803..e137a85123266 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8131,16 +8131,15 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) /** * Stores multiple pieces of query data in the cache. * - * @param array $cache_keys Array of cache keys to define the items to be stored. * @param mixed $data Data to be stored in the cache for all keys. * @param string $group Group to which the cached data belongs. * @param string $last_changed Timestamp indicating the last modification time for the data. */ -function wp_cache_set_multiple_query_data( $cache_keys, $data, $group, $last_changed ) { +function wp_cache_set_multiple_query_data( $data, $group, $last_changed ) { $new_cache = array(); - foreach ( $cache_keys as $key ) { + foreach ( $data as $key => $value ) { $new_cache[ $key ] = array( - 'data' => $data, + 'data' => $value, 'last_changed' => $last_changed, ); } From 17348ec05faf3792c65e46f5e413733af01df3bf Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 28 May 2025 11:25:48 +0100 Subject: [PATCH 05/28] Learn to spell, Jonny --- src/wp-includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index e137a85123266..6b5d5ad2fb795 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8096,7 +8096,7 @@ function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed ) { $cache_key, array( 'data' => $data, - 'last_chagned' => $last_changed, + 'last_changed' => $last_changed, ), $group ); From e6440f1baa9fccfe3d34e4e96f35b8688ca24cbd Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 28 May 2025 12:32:41 +0100 Subject: [PATCH 06/28] Replace `wp_cache_set` with `wp_cache_set_query_data` in `class-wp-query` for enhanced query cache handling. --- src/wp-includes/class-wp-query.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 70c67ecb265f6..1afaef27670f8 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -3356,7 +3356,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set( $cache_key, $cache_value, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); } return $post_parents; @@ -3454,7 +3454,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set( $cache_key, $cache_value, 'post-queries' ); + wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); } if ( ! $q['suppress_filters'] ) { From f90fdbb666f59097959ffe9bf385679ffaed2dea Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 28 May 2025 12:36:24 +0100 Subject: [PATCH 07/28] Refactor cache key handling in `class-wp-query` for improved clarity and consistency. --- src/wp-includes/class-wp-query.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 1afaef27670f8..63775bdebea0c 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -2881,14 +2881,14 @@ public function get_posts() { $comments_request = "SELECT $distinct {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits"; - $key = md5( $comments_request ); - $last_changed = wp_cache_get_last_changed( 'comment' ) . ':' . wp_cache_get_last_changed( 'posts' ); + $key = md5( $comments_request ); + $post_comment_last_changed = wp_cache_get_last_changed( 'comment' ) . ':' . wp_cache_get_last_changed( 'posts' ); $cache_key = "comment_feed:$key"; - $comment_ids = wp_cache_get_query_data( $cache_key, 'comment-queries', $last_changed ); + $comment_ids = wp_cache_get_query_data( $cache_key, 'comment-queries', $post_comment_last_changed ); if ( false === $comment_ids ) { $comment_ids = $wpdb->get_col( $comments_request ); - wp_cache_set_query_data( $cache_key, $comment_ids, 'comment-queries', $last_changed ); + wp_cache_set_query_data( $cache_key, $comment_ids, 'comment-queries', $post_comment_last_changed ); } _prime_comment_caches( $comment_ids ); @@ -3356,7 +3356,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); + wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); } return $post_parents; @@ -3454,7 +3454,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); + wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); } if ( ! $q['suppress_filters'] ) { From d6eba79cec19a9aadb116761b10cc6a3bc230a12 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 4 Jun 2025 23:09:22 -0400 Subject: [PATCH 08/28] Add expiration support to `wp_cache_set_query_data` and `wp_cache_set_multiple_query_data`, and introduce unit tests for query cache functions. --- src/wp-includes/functions.php | 21 ++++- .../functions/wpCacheGetMultipleQueryData.php | 84 +++++++++++++++++++ .../tests/functions/wpCacheGetQueryData.php | 63 ++++++++++++++ .../functions/wpCacheSetMultipleQueryData.php | 32 +++++++ .../tests/functions/wpCacheSetQueryData.php | 30 +++++++ 5 files changed, 226 insertions(+), 4 deletions(-) create mode 100644 tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php create mode 100644 tests/phpunit/tests/functions/wpCacheGetQueryData.php create mode 100644 tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php create mode 100644 tests/phpunit/tests/functions/wpCacheSetQueryData.php diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 6b5d5ad2fb795..a8b9358aad2b1 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8064,6 +8064,8 @@ function wp_unique_id_from_values( array $data, string $prefix = '' ): string { /** * Retrieves cached query data if valid and unchanged. * + * @since 6.9.0 + * * @param string $cache_key The cache key used for storage and retrieval. * @param string $group The cache group used for organizing data. * @param string $last_changed The timestamp of the last modification to the cache group. @@ -8086,25 +8088,32 @@ function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { /** * Stores query-related data in the cache. * + * @since 6.9.0 + * * @param string $cache_key The cache key under which to store the data. * @param mixed $data The data to be cached. * @param string $group The cache group to which the data belongs. * @param string $last_changed The timestamp or identifier indicating the last change to the cached data. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). */ -function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed ) { +function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $expire = 0 ) { wp_cache_set( $cache_key, array( 'data' => $data, 'last_changed' => $last_changed, ), - $group + $group, + $expire ); } /** * Retrieves multiple items from the cache and validates their freshness. * + * @since 6.9.0 + * * @param array $cache_keys Array of cache keys to retrieve. * @param string $group The group of the cache to check. * @param string $last_changed The timestamp of the last cache modification for validation. @@ -8131,11 +8140,15 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) /** * Stores multiple pieces of query data in the cache. * + * @since 6.9.0 + * * @param mixed $data Data to be stored in the cache for all keys. * @param string $group Group to which the cached data belongs. * @param string $last_changed Timestamp indicating the last modification time for the data. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). */ -function wp_cache_set_multiple_query_data( $data, $group, $last_changed ) { +function wp_cache_set_multiple_query_data( $data, $group, $last_changed, $expire = 0 ) { $new_cache = array(); foreach ( $data as $key => $value ) { $new_cache[ $key ] = array( @@ -8143,7 +8156,7 @@ function wp_cache_set_multiple_query_data( $data, $group, $last_changed ) { 'last_changed' => $last_changed, ); } - wp_cache_set_multiple( $new_cache, $group ); + wp_cache_set_multiple( $new_cache, $group, $expire ); } /** diff --git a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php new file mode 100644 index 0000000000000..bcfa648288cd6 --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php @@ -0,0 +1,84 @@ + $last_changed, + 'data' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), + ); + wp_cache_set( 'cache_key', $cache_value, 'query_data' ); + + $result = wp_cache_get_multiple_query_data( array( 'cache_key' ), 'query_data', $last_changed ); + + $this->assertSameSets( $cache_value['data'], $result['cache_key'] ); + } + + /** + * Test that wp_cache_get_multiple_query_data returns an empty array when no data is cached. + * + * @ticket 59592 + */ + public function test_wp_cache_get_multiple_query_data_return_false() { + wp_cache_set( 'cache_key', false, 'query_data' ); + wp_cache_set( 'another_key', null, 'query_data' ); + + $last_changed = wp_cache_get_last_changed( 'query_data' ); + + $result = wp_cache_get_multiple_query_data( array( 'cache_key', 'another_key' ), 'query_data', $last_changed ); + + $this->assertSameSets( + array( + 'cache_key' => false, + 'another_key' => false, + ), + $result + ); + } + + public function test_wp_cache_get_multiple_query_data_with_some_false() { + $last_changed = wp_cache_get_last_changed( 'query_data' ); + wp_cache_set( + 'cache_key', + array( + 'last_changed' => $last_changed, + 'data' => array( 123 ), + ), + 'query_data' + ); + wp_cache_set( + 'another_key', + array( + 'last_changed' => '123', + 'data' => array(), + ), + 'query_data' + ); + + $last_changed = wp_cache_get_last_changed( 'query_data' ); + + $result = wp_cache_get_multiple_query_data( array( 'cache_key', 'another_key' ), 'query_data', $last_changed ); + + $this->assertSameSets( + array( + 'cache_key' => array( 123 ), + 'another_key' => false, + ), + $result + ); + } +} diff --git a/tests/phpunit/tests/functions/wpCacheGetQueryData.php b/tests/phpunit/tests/functions/wpCacheGetQueryData.php new file mode 100644 index 0000000000000..bc41e9868b7ee --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheGetQueryData.php @@ -0,0 +1,63 @@ + $last_changed, + 'data' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), + ); + wp_cache_set( 'cache_key', $cache_value, 'query_data' ); + + $result = wp_cache_get_query_data( 'cache_key', 'query_data', $last_changed ); + + $this->assertSameSets( $cache_value['data'], $result ); + } + + /** + * Test that wp_cache_get_query_data returns false when no data is cached. + * + * @dataProvider data_provider_for_wp_cache_get_query_data_return_false + * + * @ticket 59592 + */ + public function test_wp_cache_get_query_data_return_false( $cache_value ) { + wp_cache_set( 'cache_key', $cache_value, 'query_data' ); + $last_changed = wp_cache_get_last_changed( 'query_data' ); + $this->assertFalse( wp_cache_get_query_data( 'cache_key', 'query_data', $last_changed ) ); + } + + public function data_provider_for_wp_cache_get_query_data_return_false() { + return array( + array( false ), + array( null ), + array( '' ), + array( 0 ), + array( array() ), + array( new StdClass() ), + array( array( 'last_changed' => '123' ) ), + array( + array( + 'last_changed' => '123', + 'data' => array(), + ), + ), + array( array( 'data' => array() ) ), + ); + } +} diff --git a/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php new file mode 100644 index 0000000000000..bf1c1bfea0fcc --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php @@ -0,0 +1,32 @@ + 'value1', + 'key2' => 'value2', + ); + + wp_cache_set_multiple_query_data( $data, $cache_group, $last_changed ); + $cache_values = wp_cache_get_multiple( array( 'key1', 'key2' ), $cache_group ); + $expected_cache_values = array( + 'key1' => array( + + 'data' => 'value1', + 'last_changed' => $last_changed, + ), + 'key2' => array( + + 'data' => 'value2', + 'last_changed' => $last_changed, + ), + ); + $this->assertSameSets( $expected_cache_values, $cache_values ); + } +} diff --git a/tests/phpunit/tests/functions/wpCacheSetQueryData.php b/tests/phpunit/tests/functions/wpCacheSetQueryData.php new file mode 100644 index 0000000000000..3857a64bd7225 --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheSetQueryData.php @@ -0,0 +1,30 @@ + 'value1', + 'key2' => 'value2', + ); + + wp_cache_set_query_data( $cache_key, $data, $cache_group, $last_changed ); + + $cached_data = wp_cache_get( $cache_key, 'query_data' ); + + $this->assertSame( $data, $cached_data['data'] ); + } +} From 71578aa1b89201697aa3824b472904273b9c9e12 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 18 Jun 2025 23:10:26 +0100 Subject: [PATCH 09/28] Move query cache functions to `cache-compat.php` for better compatibility handling. --- src/wp-includes/cache-compat.php | 107 +++++++++++++++++++++++++++++++ src/wp-includes/functions.php | 98 ---------------------------- 2 files changed, 107 insertions(+), 98 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index eafe37cf0077e..1ac879023e228 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -199,3 +199,110 @@ function wp_cache_supports( $feature ) { return false; } endif; + +if ( ! function_exists( 'wp_cache_get_query_data' ) ) : + + /** + * Retrieves cached query data if valid and unchanged. + * + * @since 6.9.0 + * + * @param string $cache_key The cache key used for storage and retrieval. + * @param string $group The cache group used for organizing data. + * @param string $last_changed The timestamp of the last modification to the cache group. + * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. + */ + function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { + $cache = wp_cache_get( $cache_key, $group ); + + if ( ! is_array( $cache ) ) { + return false; + } + + if ( ! isset( $cache['last_changed'], $cache['data'] ) || $last_changed !== $cache['last_changed'] ) { + return false; + } + + return $cache['data']; + } +endif; + +if ( ! function_exists( 'wp_cache_set_query_data' ) ) : + /** + * Stores query-related data in the cache. + * + * @since 6.9.0 + * + * @param string $cache_key The cache key under which to store the data. + * @param mixed $data The data to be cached. + * @param string $group The cache group to which the data belongs. + * @param string $last_changed The timestamp or identifier indicating the last change to the cached data. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + */ + function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $expire = 0 ) { + wp_cache_set( + $cache_key, + array( + 'data' => $data, + 'last_changed' => $last_changed, + ), + $group, + $expire + ); + } +endif; + +if ( ! function_exists( 'wp_cache_get_multiple_query_data' ) ) : + /** + * Retrieves multiple items from the cache and validates their freshness. + * + * @since 6.9.0 + * + * @param array $cache_keys Array of cache keys to retrieve. + * @param string $group The group of the cache to check. + * @param string $last_changed The timestamp of the last cache modification for validation. + * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. + */ + function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) { + $cache = wp_cache_get_multiple( $cache_keys, $group ); + + foreach ( $cache as $key => $value ) { + if ( ! is_array( $value ) ) { + $cache[ $key ] = false; + continue; + } + if ( ! isset( $value['last_changed'], $value['data'] ) || $last_changed !== $value['last_changed'] ) { + $cache[ $key ] = false; + continue; + } + $cache[ $key ] = $value['data']; + } + + return $cache; + } +endif; + +if ( ! function_exists( 'wp_cache_set_multiple_query_data' ) ) : + /** + * Stores multiple pieces of query data in the cache. + * + * @since 6.9.0 + * + * @param mixed $data Data to be stored in the cache for all keys. + * @param string $group Group to which the cached data belongs. + * @param string $last_changed Timestamp indicating the last modification time for the data. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). + */ + function wp_cache_set_multiple_query_data( $data, $group, $last_changed, $expire = 0 ) { + $new_cache = array(); + foreach ( $data as $key => $value ) { + $new_cache[ $key ] = array( + 'data' => $value, + 'last_changed' => $last_changed, + ); + } + wp_cache_set_multiple( $new_cache, $group, $expire ); + } +endif; diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index a8b9358aad2b1..33b775e718215 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -8061,104 +8061,6 @@ function wp_unique_id_from_values( array $data, string $prefix = '' ): string { return $prefix . $hash; } -/** - * Retrieves cached query data if valid and unchanged. - * - * @since 6.9.0 - * - * @param string $cache_key The cache key used for storage and retrieval. - * @param string $group The cache group used for organizing data. - * @param string $last_changed The timestamp of the last modification to the cache group. - * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. - */ -function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { - $cache = wp_cache_get( $cache_key, $group ); - - if ( ! is_array( $cache ) ) { - return false; - } - - if ( ! isset( $cache['last_changed'], $cache['data'] ) || $last_changed !== $cache['last_changed'] ) { - return false; - } - - return $cache['data']; -} - -/** - * Stores query-related data in the cache. - * - * @since 6.9.0 - * - * @param string $cache_key The cache key under which to store the data. - * @param mixed $data The data to be cached. - * @param string $group The cache group to which the data belongs. - * @param string $last_changed The timestamp or identifier indicating the last change to the cached data. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - */ -function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $expire = 0 ) { - wp_cache_set( - $cache_key, - array( - 'data' => $data, - 'last_changed' => $last_changed, - ), - $group, - $expire - ); -} - -/** - * Retrieves multiple items from the cache and validates their freshness. - * - * @since 6.9.0 - * - * @param array $cache_keys Array of cache keys to retrieve. - * @param string $group The group of the cache to check. - * @param string $last_changed The timestamp of the last cache modification for validation. - * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. - */ -function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) { - $cache = wp_cache_get_multiple( $cache_keys, $group ); - - foreach ( $cache as $key => $value ) { - if ( ! is_array( $value ) ) { - $cache[ $key ] = false; - continue; - } - if ( ! isset( $value['last_changed'], $value['data'] ) || $last_changed !== $value['last_changed'] ) { - $cache[ $key ] = false; - continue; - } - $cache[ $key ] = $value['data']; - } - - return $cache; -} - -/** - * Stores multiple pieces of query data in the cache. - * - * @since 6.9.0 - * - * @param mixed $data Data to be stored in the cache for all keys. - * @param string $group Group to which the cached data belongs. - * @param string $last_changed Timestamp indicating the last modification time for the data. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). - */ -function wp_cache_set_multiple_query_data( $data, $group, $last_changed, $expire = 0 ) { - $new_cache = array(); - foreach ( $data as $key => $value ) { - $new_cache[ $key ] = array( - 'data' => $value, - 'last_changed' => $last_changed, - ); - } - wp_cache_set_multiple( $new_cache, $group, $expire ); -} - /** * Gets last changed date for the specified cache group. * From 39e6880734591c4cf587139f00ff118f849b3b72 Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Wed, 18 Jun 2025 23:14:23 +0100 Subject: [PATCH 10/28] Update tests/phpunit/tests/functions/wpCacheSetQueryData.php Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- tests/phpunit/tests/functions/wpCacheSetQueryData.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/functions/wpCacheSetQueryData.php b/tests/phpunit/tests/functions/wpCacheSetQueryData.php index 3857a64bd7225..35e2e7146fe3e 100644 --- a/tests/phpunit/tests/functions/wpCacheSetQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheSetQueryData.php @@ -25,6 +25,7 @@ public function test_wp_cache_set_query_data_sets_data() { $cached_data = wp_cache_get( $cache_key, 'query_data' ); - $this->assertSame( $data, $cached_data['data'] ); + $this->assertSame( $data, $cached_data['data'], 'The data key should contain the cached data.' ); + $this->assertSame( $last_changed, $cached_data['last_changed'], 'The last changed key should contain the last change time stamp' ); } } From f017cc0053441af95c62da84e50fbd4e4b905319 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 18 Jun 2025 23:31:58 +0100 Subject: [PATCH 11/28] Fix comments on tests --- .../tests/functions/wpCacheGetMultipleQueryData.php | 8 ++++++++ tests/phpunit/tests/functions/wpCacheGetQueryData.php | 4 +++- tests/phpunit/tests/functions/wpCacheSetLastChanged.php | 2 ++ .../tests/functions/wpCacheSetMultipleQueryData.php | 9 +++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php index bcfa648288cd6..4b56e63ffc37d 100644 --- a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php @@ -1,7 +1,10 @@ Date: Tue, 8 Jul 2025 20:40:53 +0100 Subject: [PATCH 12/28] Apply suggestions from code review Co-authored-by: Mukesh Panchal --- src/wp-includes/cache-compat.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index 1ac879023e228..3d44db3d6f958 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -207,8 +207,8 @@ function wp_cache_supports( $feature ) { * * @since 6.9.0 * - * @param string $cache_key The cache key used for storage and retrieval. - * @param string $group The cache group used for organizing data. + * @param string $cache_key The cache key used for storage and retrieval. + * @param string $group The cache group used for organizing data. * @param string $last_changed The timestamp of the last modification to the cache group. * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. */ @@ -233,12 +233,12 @@ function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { * * @since 6.9.0 * - * @param string $cache_key The cache key under which to store the data. - * @param mixed $data The data to be cached. - * @param string $group The cache group to which the data belongs. + * @param string $cache_key The cache key under which to store the data. + * @param mixed $data The data to be cached. + * @param string $group The cache group to which the data belongs. * @param string $last_changed The timestamp or identifier indicating the last change to the cached data. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). */ function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $expire = 0 ) { wp_cache_set( @@ -259,8 +259,8 @@ function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $exp * * @since 6.9.0 * - * @param array $cache_keys Array of cache keys to retrieve. - * @param string $group The group of the cache to check. + * @param array $cache_keys Array of cache keys to retrieve. + * @param string $group The group of the cache to check. * @param string $last_changed The timestamp of the last cache modification for validation. * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. */ @@ -289,11 +289,11 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) * * @since 6.9.0 * - * @param mixed $data Data to be stored in the cache for all keys. - * @param string $group Group to which the cached data belongs. + * @param mixed $data Data to be stored in the cache for all keys. + * @param string $group Group to which the cached data belongs. * @param string $last_changed Timestamp indicating the last modification time for the data. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). */ function wp_cache_set_multiple_query_data( $data, $group, $last_changed, $expire = 0 ) { $new_cache = array(); From 49cc290796ac86ad3dacd8eeab0bc092a0dd1340 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 9 Jul 2025 21:14:53 +0100 Subject: [PATCH 13/28] Refactor `class-wp-user-query` to move cache timestamp handling to a new `get_cache_last_changed` method for improved clarity and reuse. --- src/wp-includes/class-wp-user-query.php | 74 +++++++++++++++---------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 340b4e253fef7..e560c423df7a2 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -830,35 +830,7 @@ public function query() { $cache_value = false; $cache_key = $this->generate_cache_key( $qv, $this->request ); $cache_group = 'user-queries'; - $last_changed = wp_cache_get_last_changed( 'users' ); - - if ( empty( $qv['orderby'] ) ) { - // Default order is by 'user_login'. - $ordersby = array( 'user_login' => '' ); - } elseif ( is_array( $qv['orderby'] ) ) { - $ordersby = $qv['orderby']; - } else { - // 'orderby' values may be a comma- or space-separated list. - $ordersby = preg_split( '/[,\s]+/', $qv['orderby'] ); - } - - $blog_id = 0; - if ( isset( $qv['blog_id'] ) ) { - $blog_id = absint( $qv['blog_id'] ); - } - - if ( $qv['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { - $switch = $blog_id && get_current_blog_id() !== $blog_id; - if ( $switch ) { - switch_to_blog( $blog_id ); - } - - $last_changed .= wp_cache_get_last_changed( 'posts' ); - - if ( $switch ) { - restore_current_blog(); - } - } + $last_changed = $this->get_cache_last_changed( $qv ); if ( $qv['cache_results'] ) { $cache_value = wp_cache_get_query_data( $cache_key, $cache_group, $last_changed ); @@ -1073,10 +1045,11 @@ protected function parse_orderby( $orderby ) { * Generate cache key. * * @since 6.3.0 + * @since 6.9.0 $args is no longer used. * * @global wpdb $wpdb WordPress database abstraction object. * - * @param array $args Query arguments. + * @param array $args Unused. Query arguments. * @param string $sql SQL statement. * @return string Cache key. */ @@ -1091,6 +1064,47 @@ protected function generate_cache_key( array $args, $sql ) { return "get_users:$key"; } + /** + * Retrieves the last changed cache timestamp for users and optionally posts. + * + * @since 6.9.0 + * + * @param array $args Query arguments. + * @return string The last changed timestamp string for the relevant cache groups. + */ + protected function get_cache_last_changed( array $args ) { + $last_changed = wp_cache_get_last_changed( 'users' ); + + if ( empty( $args['orderby'] ) ) { + // Default order is by 'user_login'. + $ordersby = array( 'user_login' => '' ); + } elseif ( is_array( $args['orderby'] ) ) { + $ordersby = $args['orderby']; + } else { + // 'orderby' values may be a comma- or space-separated list. + $ordersby = preg_split( '/[,\s]+/', $args['orderby'] ); + } + + if ( $args['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { + $blog_id = 0; + if ( isset( $args['blog_id'] ) ) { + $blog_id = absint( $args['blog_id'] ); + } + $switch = $blog_id && get_current_blog_id() !== $blog_id; + if ( $switch ) { + switch_to_blog( $blog_id ); + } + + $last_changed .= wp_cache_get_last_changed( 'posts' ); + + if ( $switch ) { + restore_current_blog(); + } + } + + return $last_changed; + } + /** * Parses an 'order' query variable and casts it to ASC or DESC as necessary. * From 11605cf19cfca6cd810e52a065438bbde90013f0 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 9 Jul 2025 21:17:59 +0100 Subject: [PATCH 14/28] Refactor `class-wp-user-query` to simplify `$blog_id` initialization and adjust conditionals for improved clarity. --- src/wp-includes/class-wp-user-query.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index e560c423df7a2..89694162a2931 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -1066,8 +1066,8 @@ protected function generate_cache_key( array $args, $sql ) { /** * Retrieves the last changed cache timestamp for users and optionally posts. - * - * @since 6.9.0 + * + * @since 6.9.0 * * @param array $args Query arguments. * @return string The last changed timestamp string for the relevant cache groups. @@ -1085,11 +1085,13 @@ protected function get_cache_last_changed( array $args ) { $ordersby = preg_split( '/[,\s]+/', $args['orderby'] ); } + $blog_id = 0; + if ( isset( $args['blog_id'] ) ) { + $blog_id = absint( $args['blog_id'] ); + } + if ( $args['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { - $blog_id = 0; - if ( isset( $args['blog_id'] ) ) { - $blog_id = absint( $args['blog_id'] ); - } + $switch = $blog_id && get_current_blog_id() !== $blog_id; if ( $switch ) { switch_to_blog( $blog_id ); From 199703abbe3b6e749e4f19eb354ce70c5d3cce6b Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 9 Jul 2025 21:18:32 +0100 Subject: [PATCH 15/28] Refactor `class-wp-user-query` to simplify `$blog_id` initialization and adjust conditionals for improved clarity. --- src/wp-includes/class-wp-user-query.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 89694162a2931..2a3f2c8cb9afb 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -1091,7 +1091,6 @@ protected function get_cache_last_changed( array $args ) { } if ( $args['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { - $switch = $blog_id && get_current_blog_id() !== $blog_id; if ( $switch ) { switch_to_blog( $blog_id ); From c7b5e4d678d4334acbdc459fa0ce24228e98c82b Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 9 Jul 2025 21:31:55 +0100 Subject: [PATCH 16/28] Clarify documentation for `last_changed` parameter in query cache functions. --- src/wp-includes/cache-compat.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index 3d44db3d6f958..5764b1d1e8410 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -209,7 +209,7 @@ function wp_cache_supports( $feature ) { * * @param string $cache_key The cache key used for storage and retrieval. * @param string $group The cache group used for organizing data. - * @param string $last_changed The timestamp of the last modification to the cache group. + * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. */ function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { @@ -236,7 +236,7 @@ function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { * @param string $cache_key The cache key under which to store the data. * @param mixed $data The data to be cached. * @param string $group The cache group to which the data belongs. - * @param string $last_changed The timestamp or identifier indicating the last change to the cached data. + * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). */ @@ -261,7 +261,7 @@ function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $exp * * @param array $cache_keys Array of cache keys to retrieve. * @param string $group The group of the cache to check. - * @param string $last_changed The timestamp of the last cache modification for validation. + * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. */ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) { @@ -291,7 +291,7 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) * * @param mixed $data Data to be stored in the cache for all keys. * @param string $group Group to which the cached data belongs. - * @param string $last_changed Timestamp indicating the last modification time for the data. + * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). */ From 0675270763c28069aff6aa162c2d746733e1d8a8 Mon Sep 17 00:00:00 2001 From: Jonny Harris <148061917+jonnynews@users.noreply.github.com> Date: Fri, 11 Jul 2025 09:44:26 +0100 Subject: [PATCH 17/28] Update src/wp-includes/cache-compat.php Co-authored-by: Mukesh Panchal --- src/wp-includes/cache-compat.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index 5764b1d1e8410..05826280c901a 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -201,7 +201,6 @@ function wp_cache_supports( $feature ) { endif; if ( ! function_exists( 'wp_cache_get_query_data' ) ) : - /** * Retrieves cached query data if valid and unchanged. * From 20ea1fafb7f4088103d217b384e2ddc4c07f6c44 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Tue, 22 Jul 2025 21:29:01 +0100 Subject: [PATCH 18/28] Rename query cache functions to use `salted` naming convention, update parameter handling for timestamps, and adjust all references across the codebase. --- src/wp-includes/cache-compat.php | 40 ++++++++++--------- src/wp-includes/class-wp-comment-query.php | 8 ++-- src/wp-includes/class-wp-network-query.php | 4 +- src/wp-includes/class-wp-query.php | 20 +++++----- src/wp-includes/class-wp-site-query.php | 4 +- src/wp-includes/class-wp-term-query.php | 8 ++-- src/wp-includes/class-wp-user-query.php | 10 ++--- src/wp-includes/general-template.php | 20 +++++----- src/wp-includes/link-template.php | 8 ++-- src/wp-includes/post.php | 4 +- src/wp-includes/query.php | 8 ++-- src/wp-includes/taxonomy.php | 4 +- src/wp-includes/user.php | 4 +- .../functions/wpCacheGetMultipleQueryData.php | 8 ++-- .../tests/functions/wpCacheGetQueryData.php | 6 +-- .../functions/wpCacheSetMultipleQueryData.php | 4 +- .../tests/functions/wpCacheSetQueryData.php | 4 +- 17 files changed, 84 insertions(+), 80 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index 05826280c901a..c5b35357e182c 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -200,7 +200,7 @@ function wp_cache_supports( $feature ) { } endif; -if ( ! function_exists( 'wp_cache_get_query_data' ) ) : +if ( ! function_exists( 'wp_cache_get_salted' ) ) : /** * Retrieves cached query data if valid and unchanged. * @@ -208,17 +208,18 @@ function wp_cache_supports( $feature ) { * * @param string $cache_key The cache key used for storage and retrieval. * @param string $group The cache group used for organizing data. - * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. */ - function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { + function wp_cache_get_salted( $cache_key, $group, $salt ) { + $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; $cache = wp_cache_get( $cache_key, $group ); if ( ! is_array( $cache ) ) { return false; } - if ( ! isset( $cache['last_changed'], $cache['data'] ) || $last_changed !== $cache['last_changed'] ) { + if ( ! isset( $cache['salt'], $cache['data'] ) || $salt !== $cache['salt'] ) { return false; } @@ -226,7 +227,7 @@ function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { } endif; -if ( ! function_exists( 'wp_cache_set_query_data' ) ) : +if ( ! function_exists( 'wp_cache_set_salted' ) ) : /** * Stores query-related data in the cache. * @@ -235,16 +236,17 @@ function wp_cache_get_query_data( $cache_key, $group, $last_changed ) { * @param string $cache_key The cache key under which to store the data. * @param mixed $data The data to be cached. * @param string $group The cache group to which the data belongs. - * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. + * @param string $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). */ - function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $expire = 0 ) { + function wp_cache_set_salted( $cache_key, $data, $group, $salt, $expire = 0 ) { + $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; wp_cache_set( $cache_key, array( - 'data' => $data, - 'last_changed' => $last_changed, + 'data' => $data, + 'salt' => $salt, ), $group, $expire @@ -252,7 +254,7 @@ function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $exp } endif; -if ( ! function_exists( 'wp_cache_get_multiple_query_data' ) ) : +if ( ! function_exists( 'wp_cache_get_multiple_salted' ) ) : /** * Retrieves multiple items from the cache and validates their freshness. * @@ -260,10 +262,11 @@ function wp_cache_set_query_data( $cache_key, $data, $group, $last_changed, $exp * * @param array $cache_keys Array of cache keys to retrieve. * @param string $group The group of the cache to check. - * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. */ - function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) { + function wp_cache_get_multiple_salted( $cache_keys, $group, $salt ) { + $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; $cache = wp_cache_get_multiple( $cache_keys, $group ); foreach ( $cache as $key => $value ) { @@ -271,7 +274,7 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) $cache[ $key ] = false; continue; } - if ( ! isset( $value['last_changed'], $value['data'] ) || $last_changed !== $value['last_changed'] ) { + if ( ! isset( $value['salt'], $value['data'] ) || $salt !== $value['salt'] ) { $cache[ $key ] = false; continue; } @@ -282,7 +285,7 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) } endif; -if ( ! function_exists( 'wp_cache_set_multiple_query_data' ) ) : +if ( ! function_exists( 'wp_cache_set_multiple_salted' ) ) : /** * Stores multiple pieces of query data in the cache. * @@ -290,16 +293,17 @@ function wp_cache_get_multiple_query_data( $cache_keys, $group, $last_changed ) * * @param mixed $data Data to be stored in the cache for all keys. * @param string $group Group to which the cached data belongs. - * @param string $last_changed The timestamp (or multiple timestamps separated by a delimiter) indicating when the cache group(s) were last updated. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). */ - function wp_cache_set_multiple_query_data( $data, $group, $last_changed, $expire = 0 ) { + function wp_cache_set_multiple_salted( $data, $group, $salt, $expire = 0 ) { + $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; $new_cache = array(); foreach ( $data as $key => $value ) { $new_cache[ $key ] = array( - 'data' => $value, - 'last_changed' => $last_changed, + 'data' => $value, + 'salt' => $salt, ); } wp_cache_set_multiple( $new_cache, $group, $expire ); diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index dbe8c9f1d8bda..6c813c7e1a7db 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -452,7 +452,7 @@ public function get_comments() { $last_changed = wp_cache_get_last_changed( 'comment' ); $cache_key = "get_comments:$key"; - $cache_value = wp_cache_get_query_data( $cache_key, 'comment-queries', $last_changed ); + $cache_value = wp_cache_get_salted( $cache_key, 'comment-queries', $last_changed ); if ( false === $cache_value ) { $comment_ids = $this->get_comment_ids(); if ( $comment_ids ) { @@ -463,7 +463,7 @@ public function get_comments() { 'comment_ids' => $comment_ids, 'found_comments' => $this->found_comments, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'comment-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $cache_value, 'comment-queries', $last_changed ); } else { $comment_ids = $cache_value['comment_ids']; $this->found_comments = $cache_value['found_comments']; @@ -1046,7 +1046,7 @@ protected function fill_descendants( $comments ) { foreach ( $_parent_ids as $parent_id ) { $cache_keys[ $parent_id ] = "get_comment_child_ids:$parent_id:$key"; } - $cache_data = wp_cache_get_multiple_query_data( array_values( $cache_keys ), 'comment-queries', $last_changed ); + $cache_data = wp_cache_get_multiple_salted( array_values( $cache_keys ), 'comment-queries', $last_changed ); foreach ( $_parent_ids as $parent_id ) { $parent_child_ids = $cache_data[ $cache_keys[ $parent_id ] ]; if ( false !== $parent_child_ids ) { @@ -1083,7 +1083,7 @@ protected function fill_descendants( $comments ) { $cache_key = "get_comment_child_ids:$parent_id:$key"; $data[ $cache_key ] = $children; } - wp_cache_set_multiple_query_data( $data, 'comment-queries', $last_changed ); + wp_cache_set_multiple_salted( $data, 'comment-queries', $last_changed ); } ++$level; diff --git a/src/wp-includes/class-wp-network-query.php b/src/wp-includes/class-wp-network-query.php index dddcdef6fe725..2ad05435a84cb 100644 --- a/src/wp-includes/class-wp-network-query.php +++ b/src/wp-includes/class-wp-network-query.php @@ -250,7 +250,7 @@ public function get_networks() { $last_changed = wp_cache_get_last_changed( 'networks' ); $cache_key = "get_network_ids:$key"; - $cache_value = wp_cache_get_query_data( $cache_key, 'network-queries', $last_changed ); + $cache_value = wp_cache_get_salted( $cache_key, 'network-queries', $last_changed ); if ( false === $cache_value ) { $network_ids = $this->get_network_ids(); @@ -262,7 +262,7 @@ public function get_networks() { 'network_ids' => $network_ids, 'found_networks' => $this->found_networks, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'network-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $cache_value, 'network-queries', $last_changed ); } else { $network_ids = $cache_value['network_ids']; $this->found_networks = $cache_value['found_networks']; diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index b655d0b584e3e..8676dbc9d8c66 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -2885,10 +2885,10 @@ public function get_posts() { $post_comment_last_changed = wp_cache_get_last_changed( 'comment' ) . ':' . wp_cache_get_last_changed( 'posts' ); $cache_key = "comment_feed:$key"; - $comment_ids = wp_cache_get_query_data( $cache_key, 'comment-queries', $post_comment_last_changed ); + $comment_ids = wp_cache_get_salted( $cache_key, 'comment-queries', $post_comment_last_changed ); if ( false === $comment_ids ) { $comment_ids = $wpdb->get_col( $comments_request ); - wp_cache_set_query_data( $cache_key, $comment_ids, 'comment-queries', $post_comment_last_changed ); + wp_cache_set_salted( $cache_key, $comment_ids, 'comment-queries', $post_comment_last_changed ); } _prime_comment_caches( $comment_ids ); @@ -3246,9 +3246,9 @@ public function get_posts() { $id_query_is_cacheable = false; } - $last_changed = wp_cache_get_last_changed( 'posts' ); + $last_changed = (array) wp_cache_get_last_changed( 'posts' ); if ( ! empty( $this->tax_query->queries ) ) { - $last_changed .= wp_cache_get_last_changed( 'terms' ); + $last_changed[] = wp_cache_get_last_changed( 'terms' ); } if ( $q['cache_results'] && $id_query_is_cacheable ) { @@ -3257,7 +3257,7 @@ public function get_posts() { $cache_found = false; if ( null === $this->posts ) { - $cached_results = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); + $cached_results = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( $cached_results ) { $cache_found = true; @@ -3318,7 +3318,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed ); } return $this->posts; @@ -3356,7 +3356,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed ); } return $post_parents; @@ -3454,7 +3454,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed ); } if ( ! $q['suppress_filters'] ) { @@ -3493,10 +3493,10 @@ public function get_posts() { $comment_last_changed = wp_cache_get_last_changed( 'comment' ); $comment_cache_key = "comment_feed:$comment_key"; - $comment_ids = wp_cache_get_query_data( $comment_cache_key, 'comment-queries', $comment_last_changed ); + $comment_ids = wp_cache_get_salted( $comment_cache_key, 'comment-queries', $comment_last_changed ); if ( false === $comment_ids ) { $comment_ids = $wpdb->get_col( $comments_request ); - wp_cache_set_query_data( $comment_cache_key, $comment_ids, 'comment-queries', $comment_last_changed ); + wp_cache_set_salted( $comment_cache_key, $comment_ids, 'comment-queries', $comment_last_changed ); } _prime_comment_caches( $comment_ids ); diff --git a/src/wp-includes/class-wp-site-query.php b/src/wp-includes/class-wp-site-query.php index 46faa3bb0ba56..d89be175045b4 100644 --- a/src/wp-includes/class-wp-site-query.php +++ b/src/wp-includes/class-wp-site-query.php @@ -358,7 +358,7 @@ public function get_sites() { $last_changed = wp_cache_get_last_changed( 'sites' ); $cache_key = "get_sites:$key"; - $cache_value = wp_cache_get_query_data( $cache_key, 'site-queries', $last_changed ); + $cache_value = wp_cache_get_salted( $cache_key, 'site-queries', $last_changed ); if ( false === $cache_value ) { $site_ids = $this->get_site_ids(); @@ -370,7 +370,7 @@ public function get_sites() { 'site_ids' => $site_ids, 'found_sites' => $this->found_sites, ); - wp_cache_set_query_data( $cache_key, $cache_value, 'site-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $cache_value, 'site-queries', $last_changed ); } else { $site_ids = $cache_value['site_ids']; $this->found_sites = $cache_value['found_sites']; diff --git a/src/wp-includes/class-wp-term-query.php b/src/wp-includes/class-wp-term-query.php index f15a9d4ee6c7b..5408f7dde0f34 100644 --- a/src/wp-includes/class-wp-term-query.php +++ b/src/wp-includes/class-wp-term-query.php @@ -779,7 +779,7 @@ public function get_terms() { if ( $args['cache_results'] ) { $cache_key = $this->generate_cache_key( $args, $this->request ); $last_changed = wp_cache_get_last_changed( 'terms' ); - $cache = wp_cache_get_query_data( $cache_key, 'term-queries', $last_changed ); + $cache = wp_cache_get_salted( $cache_key, 'term-queries', $last_changed ); if ( false !== $cache ) { if ( 'ids' === $_fields ) { @@ -807,7 +807,7 @@ public function get_terms() { if ( 'count' === $_fields ) { $count = $wpdb->get_var( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared if ( $args['cache_results'] ) { - wp_cache_set_query_data( $cache_key, $count, 'term-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $count, 'term-queries', $last_changed ); } return $count; } @@ -816,7 +816,7 @@ public function get_terms() { if ( empty( $terms ) ) { if ( $args['cache_results'] ) { - wp_cache_set_query_data( $cache_key, array(), 'term-queries', $last_changed ); + wp_cache_set_salted( $cache_key, array(), 'term-queries', $last_changed ); } return array(); } @@ -900,7 +900,7 @@ public function get_terms() { } if ( $args['cache_results'] ) { - wp_cache_set_query_data( $cache_key, $term_cache, 'term-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $term_cache, 'term-queries', $last_changed ); } $this->terms = $this->format_terms( $term_objects, $_fields ); diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 2a3f2c8cb9afb..8a697230e8526 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -833,7 +833,7 @@ public function query() { $last_changed = $this->get_cache_last_changed( $qv ); if ( $qv['cache_results'] ) { - $cache_value = wp_cache_get_query_data( $cache_key, $cache_group, $last_changed ); + $cache_value = wp_cache_get_salted( $cache_key, $cache_group, $last_changed ); } if ( false !== $cache_value ) { $this->results = $cache_value['user_data']; @@ -868,7 +868,7 @@ public function query() { 'user_data' => $this->results, 'total_users' => $this->total_users, ); - wp_cache_set_query_data( $cache_key, $cache_value, $cache_group, $last_changed ); + wp_cache_set_salted( $cache_key, $cache_value, $cache_group, $last_changed ); } } } @@ -1070,10 +1070,10 @@ protected function generate_cache_key( array $args, $sql ) { * @since 6.9.0 * * @param array $args Query arguments. - * @return string The last changed timestamp string for the relevant cache groups. + * @return string[] The last changed timestamp string for the relevant cache groups. */ protected function get_cache_last_changed( array $args ) { - $last_changed = wp_cache_get_last_changed( 'users' ); + $last_changed = (array) wp_cache_get_last_changed( 'users' ); if ( empty( $args['orderby'] ) ) { // Default order is by 'user_login'. @@ -1096,7 +1096,7 @@ protected function get_cache_last_changed( array $args ) { switch_to_blog( $blog_id ); } - $last_changed .= wp_cache_get_last_changed( 'posts' ); + $last_changed[] = wp_cache_get_last_changed( 'posts' ); if ( $switch ) { restore_current_blog(); diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 097b6e57267f7..1d39476393eb6 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -2070,10 +2070,10 @@ function wp_get_archives( $args = '' ) { $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date $order $limit"; $key = md5( $query ); $key = "wp_get_archives:$key"; - $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); + $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); + wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { $after = $parsed_args['after']; @@ -2095,10 +2095,10 @@ function wp_get_archives( $args = '' ) { $query = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date) ORDER BY post_date $order $limit"; $key = md5( $query ); $key = "wp_get_archives:$key"; - $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); + $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); + wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { $after = $parsed_args['after']; @@ -2119,10 +2119,10 @@ function wp_get_archives( $args = '' ) { $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS `dayofmonth`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER BY post_date $order $limit"; $key = md5( $query ); $key = "wp_get_archives:$key"; - $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); + $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); + wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { $after = $parsed_args['after']; @@ -2145,10 +2145,10 @@ function wp_get_archives( $args = '' ) { $query = "SELECT DISTINCT $week AS `week`, YEAR( `post_date` ) AS `yr`, DATE_FORMAT( `post_date`, '%Y-%m-%d' ) AS `yyyymmdd`, count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit"; $key = md5( $query ); $key = "wp_get_archives:$key"; - $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); + $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); + wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); } $arc_w_last = ''; if ( $results ) { @@ -2184,10 +2184,10 @@ function wp_get_archives( $args = '' ) { $query = "SELECT * FROM $wpdb->posts $join $where ORDER BY $orderby $limit"; $key = md5( $query ); $key = "wp_get_archives:$key"; - $results = wp_cache_get_query_data( $key, 'post-queries', $last_changed ); + $results = wp_cache_get_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set_query_data( $key, $results, 'post-queries', $last_changed ); + wp_cache_set_salted( $key, $results, 'post-queries', $last_changed ); } if ( $results ) { foreach ( (array) $results as $result ) { diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php index 6d776e3e060a3..674e0a6fbf3f6 100644 --- a/src/wp-includes/link-template.php +++ b/src/wp-includes/link-template.php @@ -2005,13 +2005,13 @@ function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previo $query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort"; $key = md5( $query ); - $last_changed = wp_cache_get_last_changed( 'posts' ); + $last_changed = (array) wp_cache_get_last_changed( 'posts' ); if ( $in_same_term || ! empty( $excluded_terms ) ) { - $last_changed .= wp_cache_get_last_changed( 'terms' ); + $last_changed[] = wp_cache_get_last_changed( 'terms' ); } $cache_key = "adjacent_post:$key"; - $result = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); + $result = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false !== $result ) { if ( $result ) { $result = get_post( $result ); @@ -2024,7 +2024,7 @@ function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previo $result = ''; } - wp_cache_set_query_data( $cache_key, $result, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $result, 'post-queries', $last_changed ); if ( $result ) { $result = get_post( $result ); diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 79d59e7b38cbc..2eda435cb6977 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6105,7 +6105,7 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { $hash = md5( $page_path . serialize( $post_type ) ); $cache_key = "get_page_by_path:$hash"; - $cached = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); + $cached = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false !== $cached ) { // Special case: '0' is a bad `$page_path`. if ( '0' === $cached || 0 === $cached ) { @@ -6175,7 +6175,7 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { } // We cache misses as well as hits. - wp_cache_set_query_data( $cache_key, $found_id, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $found_id, 'post-queries', $last_changed ); if ( $found_id ) { return get_post( $found_id, $output ); diff --git a/src/wp-includes/query.php b/src/wp-includes/query.php index 60da569bfaeee..592e70e0290a3 100644 --- a/src/wp-includes/query.php +++ b/src/wp-includes/query.php @@ -1156,12 +1156,12 @@ function _find_post_by_old_slug( $post_type ) { $key = md5( $query ); $last_changed = wp_cache_get_last_changed( 'posts' ); $cache_key = "find_post_by_old_slug:$key"; - $cache = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); + $cache = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false !== $cache ) { $id = $cache; } else { $id = (int) $wpdb->get_var( $query ); - wp_cache_set_query_data( $cache_key, $id, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $id, 'post-queries', $last_changed ); } return $id; @@ -1199,7 +1199,7 @@ function _find_post_by_old_date( $post_type ) { $key = md5( $query ); $last_changed = wp_cache_get_last_changed( 'posts' ); $cache_key = "find_post_by_old_date:$key"; - $cache = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); + $cache = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false !== $cache ) { $id = $cache; } else { @@ -1208,7 +1208,7 @@ function _find_post_by_old_date( $post_type ) { // Check to see if an old slug matches the old date. $id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts, $wpdb->postmeta AS pm_slug, $wpdb->postmeta AS pm_date WHERE ID = pm_slug.post_id AND ID = pm_date.post_id AND post_type = %s AND pm_slug.meta_key = '_wp_old_slug' AND pm_slug.meta_value = %s AND pm_date.meta_key = '_wp_old_date'" . $date_query, $post_type, get_query_var( 'name' ) ) ); } - wp_cache_set_query_data( $cache_key, $id, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $id, 'post-queries', $last_changed ); } } diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 4c8229e43ef4f..89de39e557624 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -897,10 +897,10 @@ function get_objects_in_term( $term_ids, $taxonomies, $args = array() ) { $last_changed = wp_cache_get_last_changed( 'terms' ); $cache_key = 'get_objects_in_term:' . md5( $sql ); - $cache = wp_cache_get_query_data( $cache_key, 'term-queries', $last_changed ); + $cache = wp_cache_get_salted( $cache_key, 'term-queries', $last_changed ); if ( false === $cache ) { $object_ids = $wpdb->get_col( $sql ); - wp_cache_set_query_data( $cache_key, $object_ids, 'term-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $object_ids, 'term-queries', $last_changed ); } else { $object_ids = (array) $cache; } diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 96c88ac6f9b83..e3f784b288c31 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -624,10 +624,10 @@ function count_user_posts( $userid, $post_type = 'post', $public_only = false ) $last_changed = wp_cache_get_last_changed( 'posts' ); $cache_key = 'count_user_posts:' . md5( $query ); - $count = wp_cache_get_query_data( $cache_key, 'post-queries', $last_changed ); + $count = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false === $count ) { $count = $wpdb->get_var( $query ); - wp_cache_set_query_data( $cache_key, $count, 'post-queries', $last_changed ); + wp_cache_set_salted( $cache_key, $count, 'post-queries', $last_changed ); } /** diff --git a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php index 4b56e63ffc37d..fc2bb4cc8e409 100644 --- a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php @@ -6,7 +6,7 @@ * @group functions * @group cache * - * @covers ::wp_cache_get_multiple_query_data + * @covers ::wp_cache_get_multiple_salted */ class Tests_Functions_wpCacheGetMultipleQueryData extends WP_UnitTestCase { @@ -26,7 +26,7 @@ public function test_wp_cache_get_multiple_query_data_return_data() { ); wp_cache_set( 'cache_key', $cache_value, 'query_data' ); - $result = wp_cache_get_multiple_query_data( array( 'cache_key' ), 'query_data', $last_changed ); + $result = wp_cache_get_multiple_salted( array( 'cache_key' ), 'query_data', $last_changed ); $this->assertSameSets( $cache_value['data'], $result['cache_key'] ); } @@ -42,7 +42,7 @@ public function test_wp_cache_get_multiple_query_data_return_false() { $last_changed = wp_cache_get_last_changed( 'query_data' ); - $result = wp_cache_get_multiple_query_data( array( 'cache_key', 'another_key' ), 'query_data', $last_changed ); + $result = wp_cache_get_multiple_salted( array( 'cache_key', 'another_key' ), 'query_data', $last_changed ); $this->assertSameSets( array( @@ -79,7 +79,7 @@ public function test_wp_cache_get_multiple_query_data_with_some_false() { $last_changed = wp_cache_get_last_changed( 'query_data' ); - $result = wp_cache_get_multiple_query_data( array( 'cache_key', 'another_key' ), 'query_data', $last_changed ); + $result = wp_cache_get_multiple_salted( array( 'cache_key', 'another_key' ), 'query_data', $last_changed ); $this->assertSameSets( array( diff --git a/tests/phpunit/tests/functions/wpCacheGetQueryData.php b/tests/phpunit/tests/functions/wpCacheGetQueryData.php index 7e683aac2b2a3..ab25bc4dc7616 100644 --- a/tests/phpunit/tests/functions/wpCacheGetQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheGetQueryData.php @@ -6,7 +6,7 @@ * @group functions * @group cache * - * @covers ::wp_cache_get_query_data + * @covers ::wp_cache_get_salted */ class Tests_Functions_wpCacheGetQueryData extends WP_UnitTestCase { @@ -26,7 +26,7 @@ public function test_wp_cache_get_query_data_return_data() { ); wp_cache_set( 'cache_key', $cache_value, 'query_data' ); - $result = wp_cache_get_query_data( 'cache_key', 'query_data', $last_changed ); + $result = wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ); $this->assertSameSets( $cache_value['data'], $result ); } @@ -41,7 +41,7 @@ public function test_wp_cache_get_query_data_return_data() { public function test_wp_cache_get_query_data_return_false( $cache_value ) { wp_cache_set( 'cache_key', $cache_value, 'query_data' ); $last_changed = wp_cache_get_last_changed( 'query_data' ); - $this->assertFalse( wp_cache_get_query_data( 'cache_key', 'query_data', $last_changed ) ); + $this->assertFalse( wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ) ); } public function data_provider_for_wp_cache_get_query_data_return_false() { diff --git a/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php index 93b4eceac3d2d..0d03a67553af6 100644 --- a/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php @@ -6,7 +6,7 @@ * @group functions * @group cache * - * @covers ::wp_cache_set_query_data + * @covers ::wp_cache_set_salted */ class Tests_Functions_wpCacheSetMultipleQueryData extends WP_UnitTestCase { /** @@ -22,7 +22,7 @@ public function test_wp_cache_set_multiple_query_data() { 'key2' => 'value2', ); - wp_cache_set_multiple_query_data( $data, $cache_group, $last_changed ); + wp_cache_set_multiple_salted( $data, $cache_group, $last_changed ); $cache_values = wp_cache_get_multiple( array( 'key1', 'key2' ), $cache_group ); $expected_cache_values = array( 'key1' => array( diff --git a/tests/phpunit/tests/functions/wpCacheSetQueryData.php b/tests/phpunit/tests/functions/wpCacheSetQueryData.php index 35e2e7146fe3e..3226c90909b15 100644 --- a/tests/phpunit/tests/functions/wpCacheSetQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheSetQueryData.php @@ -3,7 +3,7 @@ /** * @group functions * - * @covers ::wp_cache_set_query_data + * @covers ::wp_cache_set_salted */ class Tests_Functions_wpCacheSetQueryData extends WP_UnitTestCase { @@ -21,7 +21,7 @@ public function test_wp_cache_set_query_data_sets_data() { 'key2' => 'value2', ); - wp_cache_set_query_data( $cache_key, $data, $cache_group, $last_changed ); + wp_cache_set_salted( $cache_key, $data, $cache_group, $last_changed ); $cached_data = wp_cache_get( $cache_key, 'query_data' ); From 680709097123fafd8f18fa5c343ceec15bef4f32 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Tue, 22 Jul 2025 21:33:41 +0100 Subject: [PATCH 19/28] Refactor `last_changed` handling in `WP_Query` to use array structure for improved clarity and consistency. --- src/wp-includes/class-wp-query.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 8676dbc9d8c66..6f12e48ee41f2 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -2881,14 +2881,17 @@ public function get_posts() { $comments_request = "SELECT $distinct {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits"; - $key = md5( $comments_request ); - $post_comment_last_changed = wp_cache_get_last_changed( 'comment' ) . ':' . wp_cache_get_last_changed( 'posts' ); + $key = md5( $comments_request ); + $last_changed = array( + wp_cache_get_last_changed( 'comment' ), + wp_cache_get_last_changed( 'posts' ), + ); $cache_key = "comment_feed:$key"; - $comment_ids = wp_cache_get_salted( $cache_key, 'comment-queries', $post_comment_last_changed ); + $comment_ids = wp_cache_get_salted( $cache_key, 'comment-queries', $last_changed ); if ( false === $comment_ids ) { $comment_ids = $wpdb->get_col( $comments_request ); - wp_cache_set_salted( $cache_key, $comment_ids, 'comment-queries', $post_comment_last_changed ); + wp_cache_set_salted( $cache_key, $comment_ids, 'comment-queries', $last_changed ); } _prime_comment_caches( $comment_ids ); From 5d61bf7ba595d6d03a748b95584421aff2b7984d Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Tue, 22 Jul 2025 21:42:23 +0100 Subject: [PATCH 20/28] Fix conditional logic for `isset` checks in query cache validation for improved readability. --- src/wp-includes/cache-compat.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index c5b35357e182c..6d0669ae5d3c5 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -219,7 +219,7 @@ function wp_cache_get_salted( $cache_key, $group, $salt ) { return false; } - if ( ! isset( $cache['salt'], $cache['data'] ) || $salt !== $cache['salt'] ) { + if ( ! isset( $cache['salt'] ) || ! isset( $cache['data'] ) || $salt !== $cache['salt'] ) { return false; } From e3b2eb4e861630dab9c672e8615901c09a4184bc Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Tue, 22 Jul 2025 21:58:18 +0100 Subject: [PATCH 21/28] Refactor query cache tests to replace `last_changed` with `salt` for consistency and clarity. --- .../tests/functions/wpCacheGetMultipleQueryData.php | 12 ++++++------ .../phpunit/tests/functions/wpCacheGetQueryData.php | 10 +++++----- .../tests/functions/wpCacheSetMultipleQueryData.php | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php index fc2bb4cc8e409..01a83160afa2c 100644 --- a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php @@ -18,8 +18,8 @@ class Tests_Functions_wpCacheGetMultipleQueryData extends WP_UnitTestCase { public function test_wp_cache_get_multiple_query_data_return_data() { $last_changed = wp_cache_get_last_changed( 'query_data' ); $cache_value = array( - 'last_changed' => $last_changed, - 'data' => array( + 'salt' => $last_changed, + 'data' => array( 'key1' => 'value1', 'key2' => 'value2', ), @@ -63,16 +63,16 @@ public function test_wp_cache_get_multiple_query_data_with_some_false() { wp_cache_set( 'cache_key', array( - 'last_changed' => $last_changed, - 'data' => array( 123 ), + 'salt' => $last_changed, + 'data' => array( 123 ), ), 'query_data' ); wp_cache_set( 'another_key', array( - 'last_changed' => '123', - 'data' => array(), + 'salt' => '123', + 'data' => array(), ), 'query_data' ); diff --git a/tests/phpunit/tests/functions/wpCacheGetQueryData.php b/tests/phpunit/tests/functions/wpCacheGetQueryData.php index ab25bc4dc7616..ce051c6cbd2b4 100644 --- a/tests/phpunit/tests/functions/wpCacheGetQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheGetQueryData.php @@ -18,8 +18,8 @@ class Tests_Functions_wpCacheGetQueryData extends WP_UnitTestCase { public function test_wp_cache_get_query_data_return_data() { $last_changed = wp_cache_get_last_changed( 'query_data' ); $cache_value = array( - 'last_changed' => $last_changed, - 'data' => array( + 'salt' => $last_changed, + 'data' => array( 'key1' => 'value1', 'key2' => 'value2', ), @@ -52,11 +52,11 @@ public function data_provider_for_wp_cache_get_query_data_return_false() { array( 0 ), array( array() ), array( new StdClass() ), - array( array( 'last_changed' => '123' ) ), + array( array( 'salt' => '123' ) ), array( array( - 'last_changed' => '123', - 'data' => array(), + 'salt' => '123', + 'data' => array(), ), ), array( array( 'data' => array() ) ), diff --git a/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php index 0d03a67553af6..0add031365ae5 100644 --- a/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php @@ -27,13 +27,13 @@ public function test_wp_cache_set_multiple_query_data() { $expected_cache_values = array( 'key1' => array( - 'data' => 'value1', - 'last_changed' => $last_changed, + 'data' => 'value1', + 'salt' => $last_changed, ), 'key2' => array( - 'data' => 'value2', - 'last_changed' => $last_changed, + 'data' => 'value2', + 'salt' => $last_changed, ), ); $this->assertSameSets( $expected_cache_values, $cache_values ); From d1248c6b7a33eb4e016cca116e4edf69b9bcad22 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Tue, 22 Jul 2025 22:01:33 +0100 Subject: [PATCH 22/28] Update PHPUnit test to expect `salt` instead of `last_changed` for query cache validation --- tests/phpunit/tests/functions/wpCacheSetQueryData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/functions/wpCacheSetQueryData.php b/tests/phpunit/tests/functions/wpCacheSetQueryData.php index 3226c90909b15..6437aa888d416 100644 --- a/tests/phpunit/tests/functions/wpCacheSetQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheSetQueryData.php @@ -26,6 +26,6 @@ public function test_wp_cache_set_query_data_sets_data() { $cached_data = wp_cache_get( $cache_key, 'query_data' ); $this->assertSame( $data, $cached_data['data'], 'The data key should contain the cached data.' ); - $this->assertSame( $last_changed, $cached_data['last_changed'], 'The last changed key should contain the last change time stamp' ); + $this->assertSame( $last_changed, $cached_data['salt'], 'The last changed key should contain the last change time stamp' ); } } From 42b3dee23b2818604252f451475f6a263cf8af6b Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Tue, 22 Jul 2025 22:17:20 +0100 Subject: [PATCH 23/28] Refactor PHPUnit tests for query cache to use `salted` naming convention and update test cases for consistency. --- ...yData.php => wpCacheGetMultipleSalted.php} | 41 +++++++-- .../tests/functions/wpCacheGetQueryData.php | 65 -------------- .../tests/functions/wpCacheGetSalted.php | 90 +++++++++++++++++++ .../functions/wpCacheSetMultipleQueryData.php | 41 --------- .../functions/wpCacheSetMultipleSalted.php | 75 ++++++++++++++++ .../tests/functions/wpCacheSetQueryData.php | 31 ------- .../tests/functions/wpCacheSetSalted.php | 57 ++++++++++++ 7 files changed, 255 insertions(+), 145 deletions(-) rename tests/phpunit/tests/functions/{wpCacheGetMultipleQueryData.php => wpCacheGetMultipleSalted.php} (54%) delete mode 100644 tests/phpunit/tests/functions/wpCacheGetQueryData.php create mode 100644 tests/phpunit/tests/functions/wpCacheGetSalted.php delete mode 100644 tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php create mode 100644 tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php delete mode 100644 tests/phpunit/tests/functions/wpCacheSetQueryData.php create mode 100644 tests/phpunit/tests/functions/wpCacheSetSalted.php diff --git a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php similarity index 54% rename from tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php rename to tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php index 01a83160afa2c..0ecdb85d76a49 100644 --- a/tests/phpunit/tests/functions/wpCacheGetMultipleQueryData.php +++ b/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php @@ -1,21 +1,21 @@ $last_changed, @@ -32,11 +32,36 @@ public function test_wp_cache_get_multiple_query_data_return_data() { } /** - * Test that wp_cache_get_multiple_query_data returns an empty array when no data is cached. + * Test that wp_cache_get_multiple_salted returns the cached data with a salt. * * @ticket 59592 */ - public function test_wp_cache_get_multiple_query_data_return_false() { + public function test_wp_cache_get_multiple_salted_return_data_array_salt() { + $last_changed = array( + wp_cache_get_last_changed( 'query_data_1' ), + wp_cache_get_last_changed( 'query_data_2' ), + ); + $last_changed_string = implode( ':', $last_changed ); + $cache_value = array( + 'salt' => $last_changed_string, + 'data' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), + ); + wp_cache_set( 'cache_key', $cache_value, 'query_data' ); + + $result = wp_cache_get_multiple_salted( array( 'cache_key' ), 'query_data', $last_changed ); + + $this->assertSameSets( $cache_value['data'], $result['cache_key'] ); + } + + /** + * Test that wp_cache_get_multiple_salted returns an empty array when no data is cached. + * + * @ticket 59592 + */ + public function test_wp_cache_get_multiple_salted_return_false() { wp_cache_set( 'cache_key', false, 'query_data' ); wp_cache_set( 'another_key', null, 'query_data' ); @@ -54,11 +79,11 @@ public function test_wp_cache_get_multiple_query_data_return_false() { } /** - * Test that wp_cache_get_multiple_query_data returns the cached data for multiple keys. + * Test that wp_cache_get_multiple_salted returns the cached data for multiple keys. * * @ticket 59592 */ - public function test_wp_cache_get_multiple_query_data_with_some_false() { + public function test_wp_cache_get_multiple_salted_with_some_false() { $last_changed = wp_cache_get_last_changed( 'query_data' ); wp_cache_set( 'cache_key', diff --git a/tests/phpunit/tests/functions/wpCacheGetQueryData.php b/tests/phpunit/tests/functions/wpCacheGetQueryData.php deleted file mode 100644 index ce051c6cbd2b4..0000000000000 --- a/tests/phpunit/tests/functions/wpCacheGetQueryData.php +++ /dev/null @@ -1,65 +0,0 @@ - $last_changed, - 'data' => array( - 'key1' => 'value1', - 'key2' => 'value2', - ), - ); - wp_cache_set( 'cache_key', $cache_value, 'query_data' ); - - $result = wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ); - - $this->assertSameSets( $cache_value['data'], $result ); - } - - /** - * Test that wp_cache_get_query_data returns false when no data is cached. - * - * @dataProvider data_provider_for_wp_cache_get_query_data_return_false - * - * @ticket 59592 - */ - public function test_wp_cache_get_query_data_return_false( $cache_value ) { - wp_cache_set( 'cache_key', $cache_value, 'query_data' ); - $last_changed = wp_cache_get_last_changed( 'query_data' ); - $this->assertFalse( wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ) ); - } - - public function data_provider_for_wp_cache_get_query_data_return_false() { - return array( - array( false ), - array( null ), - array( '' ), - array( 0 ), - array( array() ), - array( new StdClass() ), - array( array( 'salt' => '123' ) ), - array( - array( - 'salt' => '123', - 'data' => array(), - ), - ), - array( array( 'data' => array() ) ), - ); - } -} diff --git a/tests/phpunit/tests/functions/wpCacheGetSalted.php b/tests/phpunit/tests/functions/wpCacheGetSalted.php new file mode 100644 index 0000000000000..2a831482e83c4 --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheGetSalted.php @@ -0,0 +1,90 @@ + $last_changed, + 'data' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), + ); + wp_cache_set( 'cache_key', $cache_value, 'query_data' ); + + $result = wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ); + + $this->assertSameSets( $cache_value['data'], $result ); + } + + /** + * Test that wp_cache_get_salted returns the cached data with a salt. + * + * @ticket 59592 + */ + public function test_wp_cache_get_salted_return_data_array_salt() { + $last_changed = array( + wp_cache_get_last_changed( 'query_data_1' ), + wp_cache_get_last_changed( 'query_data_2' ), + ); + $last_changed_string = implode( ':', $last_changed ); + $cache_value = array( + 'salt' => $last_changed_string, + 'data' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), + ); + wp_cache_set( 'cache_key', $cache_value, 'query_data' ); + + $result = wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ); + + $this->assertSameSets( $cache_value['data'], $result ); + } + + /** + * Test that wp_cache_get_salted returns false when no data is cached. + * + * @dataProvider data_provider_for_wp_cache_get_salted_return_false + * + * @ticket 59592 + */ + public function test_wp_cache_get_salted_return_false( $cache_value ) { + wp_cache_set( 'cache_key', $cache_value, 'query_data' ); + $last_changed = wp_cache_get_last_changed( 'query_data' ); + $this->assertFalse( wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ) ); + } + + public function data_provider_for_wp_cache_get_salted_return_false() { + return array( + array( false ), + array( null ), + array( '' ), + array( 0 ), + array( array() ), + array( new StdClass() ), + array( array( 'salt' => '123' ) ), + array( + array( + 'salt' => '123', + 'data' => array(), + ), + ), + array( array( 'data' => array() ) ), + ); + } +} diff --git a/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php b/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php deleted file mode 100644 index 0add031365ae5..0000000000000 --- a/tests/phpunit/tests/functions/wpCacheSetMultipleQueryData.php +++ /dev/null @@ -1,41 +0,0 @@ - 'value1', - 'key2' => 'value2', - ); - - wp_cache_set_multiple_salted( $data, $cache_group, $last_changed ); - $cache_values = wp_cache_get_multiple( array( 'key1', 'key2' ), $cache_group ); - $expected_cache_values = array( - 'key1' => array( - - 'data' => 'value1', - 'salt' => $last_changed, - ), - 'key2' => array( - - 'data' => 'value2', - 'salt' => $last_changed, - ), - ); - $this->assertSameSets( $expected_cache_values, $cache_values ); - } -} diff --git a/tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php b/tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php new file mode 100644 index 0000000000000..c8241e2f5b243 --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php @@ -0,0 +1,75 @@ + 'value1', + 'key2' => 'value2', + ); + + wp_cache_set_multiple_salted( $data, $cache_group, $last_changed ); + $cache_values = wp_cache_get_multiple( array( 'key1', 'key2' ), $cache_group ); + $expected_cache_values = array( + 'key1' => array( + + 'data' => 'value1', + 'salt' => $last_changed, + ), + 'key2' => array( + + 'data' => 'value2', + 'salt' => $last_changed, + ), + ); + $this->assertSameSets( $expected_cache_values, $cache_values ); + } + + /** + * Test that wp_cache_set_multiple_salted sets multiple query data with a salt. + * + * @ticket 59592 + */ + public function test_wp_cache_set_multiple_salted_array() { + $cache_group = 'query_data'; + $last_changed = array( + wp_cache_get_last_changed( 'query_data_1' ), + wp_cache_get_last_changed( 'query_data_2' ), + ); + $data = array( + 'key1' => 'value1', + 'key2' => 'value2', + ); + + wp_cache_set_multiple_salted( $data, $cache_group, $last_changed ); + $cache_values = wp_cache_get_multiple( array( 'key1', 'key2' ), $cache_group ); + $last_changed_string = implode( ':', $last_changed ); + $expected_cache_values = array( + 'key1' => array( + + 'data' => 'value1', + 'salt' => $last_changed_string, + ), + 'key2' => array( + + 'data' => 'value2', + 'salt' => $last_changed_string, + ), + ); + $this->assertSameSets( $expected_cache_values, $cache_values ); + } +} diff --git a/tests/phpunit/tests/functions/wpCacheSetQueryData.php b/tests/phpunit/tests/functions/wpCacheSetQueryData.php deleted file mode 100644 index 6437aa888d416..0000000000000 --- a/tests/phpunit/tests/functions/wpCacheSetQueryData.php +++ /dev/null @@ -1,31 +0,0 @@ - 'value1', - 'key2' => 'value2', - ); - - wp_cache_set_salted( $cache_key, $data, $cache_group, $last_changed ); - - $cached_data = wp_cache_get( $cache_key, 'query_data' ); - - $this->assertSame( $data, $cached_data['data'], 'The data key should contain the cached data.' ); - $this->assertSame( $last_changed, $cached_data['salt'], 'The last changed key should contain the last change time stamp' ); - } -} diff --git a/tests/phpunit/tests/functions/wpCacheSetSalted.php b/tests/phpunit/tests/functions/wpCacheSetSalted.php new file mode 100644 index 0000000000000..2bcc17abb6c3d --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheSetSalted.php @@ -0,0 +1,57 @@ + 'value1', + 'key2' => 'value2', + ); + + wp_cache_set_salted( $cache_key, $data, $cache_group, $last_changed ); + + $cached_data = wp_cache_get( $cache_key, 'query_data' ); + + $this->assertSame( $data, $cached_data['data'], 'The data key should contain the cached data.' ); + $this->assertSame( $last_changed, $cached_data['salt'], 'The last changed key should contain the last change time stamp' ); + } + + /** + * Test that wp_cache_set_salted sets the data with a salt. + * + * @ticket 59592 + */ + public function test_wp_cache_set_salted_array_salt() { + $cache_key = 'cache_key'; + $cache_group = 'query_data'; + $last_changed = array( + wp_cache_get_last_changed( 'query_data_1' ), + wp_cache_get_last_changed( 'query_data_2' ), + ); + $data = array( + 'key1' => 'value1', + 'key2' => 'value2', + ); + + wp_cache_set_salted( $cache_key, $data, $cache_group, $last_changed ); + + $cached_data = wp_cache_get( $cache_key, 'query_data' ); + + $last_changed_string = implode( ':', $last_changed ); + $this->assertSame( $data, $cached_data['data'], 'The data key should contain the cached data.' ); + $this->assertSame( $last_changed_string, $cached_data['salt'], 'The last changed key should contain the last change time stamp' ); + } +} From 38a28a244cba45fec22a93414e492a7e6ea6f808 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Wed, 23 Jul 2025 11:47:24 +0100 Subject: [PATCH 24/28] Feedback on @peterwilsoncc. --- src/wp-includes/cache-compat.php | 2 +- src/wp-includes/class-wp-user-query.php | 8 ++++---- .../tests/functions/wpCacheGetMultipleSalted.php | 2 +- tests/phpunit/tests/functions/wpCacheGetSalted.php | 11 ++++++++--- .../tests/functions/wpCacheSetMultipleSalted.php | 2 +- tests/phpunit/tests/functions/wpCacheSetSalted.php | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index 6d0669ae5d3c5..c5cc17024d33a 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -236,7 +236,7 @@ function wp_cache_get_salted( $cache_key, $group, $salt ) { * @param string $cache_key The cache key under which to store the data. * @param mixed $data The data to be cached. * @param string $group The cache group to which the data belongs. - * @param string $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). */ diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 8a697230e8526..2378044f01360 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -1045,15 +1045,15 @@ protected function parse_orderby( $orderby ) { * Generate cache key. * * @since 6.3.0 - * @since 6.9.0 $args is no longer used. - * + * @since 6.9.0 The `$args` parameter was deprecated and renamed to `$deprecated`. + * * @global wpdb $wpdb WordPress database abstraction object. * - * @param array $args Unused. Query arguments. + * @param array $deprecated Unused. * @param string $sql SQL statement. * @return string Cache key. */ - protected function generate_cache_key( array $args, $sql ) { + protected function generate_cache_key( array $deprecated, $sql ) { global $wpdb; // Replace wpdb placeholder in the SQL statement used by the cache key. diff --git a/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php b/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php index 0ecdb85d76a49..196488b616a8b 100644 --- a/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php +++ b/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php @@ -57,7 +57,7 @@ public function test_wp_cache_get_multiple_salted_return_data_array_salt() { } /** - * Test that wp_cache_get_multiple_salted returns an empty array when no data is cached. + * Test that wp_cache_get_multiple_salted returns an array of false values when no data is cached. * * @ticket 59592 */ diff --git a/tests/phpunit/tests/functions/wpCacheGetSalted.php b/tests/phpunit/tests/functions/wpCacheGetSalted.php index 2a831482e83c4..d54a68274eee6 100644 --- a/tests/phpunit/tests/functions/wpCacheGetSalted.php +++ b/tests/phpunit/tests/functions/wpCacheGetSalted.php @@ -1,7 +1,7 @@ assertFalse( wp_cache_get_salted( 'cache_key', 'query_data', $last_changed ) ); } - public function data_provider_for_wp_cache_get_salted_return_false() { + /** + * Data provider for test_wp_cache_get_salted_return_false. + * + * @return array[] Data provider. + */ + public function data_wp_cache_get_salted_return_false() { return array( array( false ), array( null ), diff --git a/tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php b/tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php index c8241e2f5b243..be48428a70096 100644 --- a/tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php +++ b/tests/phpunit/tests/functions/wpCacheSetMultipleSalted.php @@ -1,7 +1,7 @@ Date: Thu, 24 Jul 2025 08:29:34 +1000 Subject: [PATCH 25/28] Docs standards fixes. --- src/wp-includes/cache-compat.php | 26 ++++++++++++------------- src/wp-includes/class-wp-user-query.php | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index c5cc17024d33a..ea92628963f7a 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -206,8 +206,8 @@ function wp_cache_supports( $feature ) { * * @since 6.9.0 * - * @param string $cache_key The cache key used for storage and retrieval. - * @param string $group The cache group used for organizing data. + * @param string $cache_key The cache key used for storage and retrieval. + * @param string $group The cache group used for organizing data. * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. */ @@ -233,12 +233,12 @@ function wp_cache_get_salted( $cache_key, $group, $salt ) { * * @since 6.9.0 * - * @param string $cache_key The cache key under which to store the data. - * @param mixed $data The data to be cached. - * @param string $group The cache group to which the data belongs. + * @param string $cache_key The cache key under which to store the data. + * @param mixed $data The data to be cached. + * @param string $group The cache group to which the data belongs. * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). */ function wp_cache_set_salted( $cache_key, $data, $group, $salt, $expire = 0 ) { $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; @@ -260,8 +260,8 @@ function wp_cache_set_salted( $cache_key, $data, $group, $salt, $expire = 0 ) { * * @since 6.9.0 * - * @param array $cache_keys Array of cache keys to retrieve. - * @param string $group The group of the cache to check. + * @param array $cache_keys Array of cache keys to retrieve. + * @param string $group The group of the cache to check. * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. */ @@ -291,11 +291,11 @@ function wp_cache_get_multiple_salted( $cache_keys, $group, $salt ) { * * @since 6.9.0 * - * @param mixed $data Data to be stored in the cache for all keys. - * @param string $group Group to which the cached data belongs. + * @param mixed $data Data to be stored in the cache for all keys. + * @param string $group Group to which the cached data belongs. * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). */ function wp_cache_set_multiple_salted( $data, $group, $salt, $expire = 0 ) { $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 2378044f01360..014cfb2ac56d9 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -1046,11 +1046,11 @@ protected function parse_orderby( $orderby ) { * * @since 6.3.0 * @since 6.9.0 The `$args` parameter was deprecated and renamed to `$deprecated`. - * + * * @global wpdb $wpdb WordPress database abstraction object. * - * @param array $deprecated Unused. - * @param string $sql SQL statement. + * @param array $deprecated Unused. + * @param string $sql SQL statement. * @return string Cache key. */ protected function generate_cache_key( array $deprecated, $sql ) { From 12a45c716072e6750f0db5417635a742e9059356 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 24 Jul 2025 08:34:50 +1000 Subject: [PATCH 26/28] API consistency: return success/failure results of setting cache. --- src/wp-includes/cache-compat.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index ea92628963f7a..f042ee3287589 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -239,10 +239,11 @@ function wp_cache_get_salted( $cache_key, $group, $salt ) { * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). + * @return bool True on success, false on failure. */ function wp_cache_set_salted( $cache_key, $data, $group, $salt, $expire = 0 ) { $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; - wp_cache_set( + return wp_cache_set( $cache_key, array( 'data' => $data, @@ -296,6 +297,8 @@ function wp_cache_get_multiple_salted( $cache_keys, $group, $salt ) { * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). + * @return bool[] Array of return values, grouped by key. Each value is either + * true on success, or false on failure. */ function wp_cache_set_multiple_salted( $data, $group, $salt, $expire = 0 ) { $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; @@ -306,6 +309,6 @@ function wp_cache_set_multiple_salted( $data, $group, $salt, $expire = 0 ) { 'salt' => $salt, ); } - wp_cache_set_multiple( $new_cache, $group, $expire ); + return wp_cache_set_multiple( $new_cache, $group, $expire ); } endif; From 238c41e6813876c76bd3fc97d6c0c74aeab9134d Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:16:03 +1000 Subject: [PATCH 27/28] Apply suggestions from code review Co-authored-by: Felix Arntz Co-authored-by: Mukesh Panchal --- src/wp-includes/cache-compat.php | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index f042ee3287589..ca59acb45a5ed 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -202,13 +202,13 @@ function wp_cache_supports( $feature ) { if ( ! function_exists( 'wp_cache_get_salted' ) ) : /** - * Retrieves cached query data if valid and unchanged. + * Retrieves cached data if valid and unchanged. * * @since 6.9.0 * - * @param string $cache_key The cache key used for storage and retrieval. - * @param string $group The cache group used for organizing data. - * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. + * @param string $cache_key The cache key used for storage and retrieval. + * @param string $group The cache group used for organizing data. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @return mixed|false The cached data if valid, or false if the cache does not exist or is outdated. */ function wp_cache_get_salted( $cache_key, $group, $salt ) { @@ -229,16 +229,16 @@ function wp_cache_get_salted( $cache_key, $group, $salt ) { if ( ! function_exists( 'wp_cache_set_salted' ) ) : /** - * Stores query-related data in the cache. + * Stores salted data in the cache. * * @since 6.9.0 * - * @param string $cache_key The cache key under which to store the data. - * @param mixed $data The data to be cached. - * @param string $group The cache group to which the data belongs. + * @param string $cache_key The cache key under which to store the data. + * @param mixed $data The data to be cached. + * @param string $group The cache group to which the data belongs. * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). * @return bool True on success, false on failure. */ function wp_cache_set_salted( $cache_key, $data, $group, $salt, $expire = 0 ) { @@ -257,13 +257,13 @@ function wp_cache_set_salted( $cache_key, $data, $group, $salt, $expire = 0 ) { if ( ! function_exists( 'wp_cache_get_multiple_salted' ) ) : /** - * Retrieves multiple items from the cache and validates their freshness. + * Retrieves multiple items from the cache, only considering valid and unchanged items. * * @since 6.9.0 * - * @param array $cache_keys Array of cache keys to retrieve. - * @param string $group The group of the cache to check. - * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. + * @param array $cache_keys Array of cache keys to retrieve. + * @param string $group The group of the cache to check. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @return array An associative array containing cache values. Values are `false` if they are not found or outdated. */ function wp_cache_get_multiple_salted( $cache_keys, $group, $salt ) { @@ -288,15 +288,15 @@ function wp_cache_get_multiple_salted( $cache_keys, $group, $salt ) { if ( ! function_exists( 'wp_cache_set_multiple_salted' ) ) : /** - * Stores multiple pieces of query data in the cache. + * Stores multiple pieces of salted data in the cache. * * @since 6.9.0 * - * @param mixed $data Data to be stored in the cache for all keys. - * @param string $group Group to which the cached data belongs. - * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. - * @param int $expire Optional. When to expire the cache contents, in seconds. - * Default 0 (no expiration). + * @param mixed $data Data to be stored in the cache for all keys. + * @param string $group Group to which the cached data belongs. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. + * @param int $expire Optional. When to expire the cache contents, in seconds. + * Default 0 (no expiration). * @return bool[] Array of return values, grouped by key. Each value is either * true on success, or false on failure. */ From 45909c6ad321684164b18e450c7e85f236464dba Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Thu, 21 Aug 2025 09:07:27 +1000 Subject: [PATCH 28/28] Update src/wp-includes/cache-compat.php Co-authored-by: Mukesh Panchal --- src/wp-includes/cache-compat.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index ca59acb45a5ed..02d887f1b13df 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -236,7 +236,7 @@ function wp_cache_get_salted( $cache_key, $group, $salt ) { * @param string $cache_key The cache key under which to store the data. * @param mixed $data The data to be cached. * @param string $group The cache group to which the data belongs. - * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. + * @param string|string[] $salt The timestamp (or multiple timestamps if an array) indicating when the cache group(s) were last updated. * @param int $expire Optional. When to expire the cache contents, in seconds. * Default 0 (no expiration). * @return bool True on success, false on failure.