diff --git a/src/wp-includes/cache-compat.php b/src/wp-includes/cache-compat.php index eafe37cf0077e..02d887f1b13df 100644 --- a/src/wp-includes/cache-compat.php +++ b/src/wp-includes/cache-compat.php @@ -199,3 +199,116 @@ function wp_cache_supports( $feature ) { return false; } endif; + +if ( ! function_exists( 'wp_cache_get_salted' ) ) : + /** + * 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. + * @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 ) { + $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; + $cache = wp_cache_get( $cache_key, $group ); + + if ( ! is_array( $cache ) ) { + return false; + } + + if ( ! isset( $cache['salt'] ) || ! isset( $cache['data'] ) || $salt !== $cache['salt'] ) { + return false; + } + + return $cache['data']; + } +endif; + +if ( ! function_exists( 'wp_cache_set_salted' ) ) : + /** + * 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|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; + return wp_cache_set( + $cache_key, + array( + 'data' => $data, + 'salt' => $salt, + ), + $group, + $expire + ); + } +endif; + +if ( ! function_exists( 'wp_cache_get_multiple_salted' ) ) : + /** + * 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. + * @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 ) { + $salt = is_array( $salt ) ? implode( ':', $salt ) : $salt; + $cache = wp_cache_get_multiple( $cache_keys, $group ); + + foreach ( $cache as $key => $value ) { + if ( ! is_array( $value ) ) { + $cache[ $key ] = false; + continue; + } + if ( ! isset( $value['salt'], $value['data'] ) || $salt !== $value['salt'] ) { + $cache[ $key ] = false; + continue; + } + $cache[ $key ] = $value['data']; + } + + return $cache; + } +endif; + +if ( ! function_exists( 'wp_cache_set_multiple_salted' ) ) : + /** + * 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). + * @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; + $new_cache = array(); + foreach ( $data as $key => $value ) { + $new_cache[ $key ] = array( + 'data' => $value, + 'salt' => $salt, + ); + } + return wp_cache_set_multiple( $new_cache, $group, $expire ); + } +endif; diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index 03d3479b6c8de..6c813c7e1a7db 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_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_add( $cache_key, $cache_value, 'comment-queries' ); + 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']; @@ -1044,9 +1044,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_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 ) { @@ -1080,10 +1080,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_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 d69f1a98ed81d..2ad05435a84cb 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_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_add( $cache_key, $cache_value, 'network-queries' ); + 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 52121e2308f9d..6f12e48ee41f2 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -2882,13 +2882,16 @@ 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' ); + $last_changed = array( + 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_salted( $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_salted( $cache_key, $comment_ids, 'comment-queries', $last_changed ); } _prime_comment_caches( $comment_ids ); @@ -3246,15 +3249,21 @@ public function get_posts() { $id_query_is_cacheable = false; } + $last_changed = (array) 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_salted( $cache_key, 'post-queries', $last_changed ); if ( $cached_results ) { + $cache_found = true; /** @var int[] */ $post_ids = array_map( 'intval', $cached_results['posts'] ); @@ -3312,7 +3321,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set( $cache_key, $cache_value, 'post-queries' ); + wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed ); } return $this->posts; @@ -3350,7 +3359,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set( $cache_key, $cache_value, 'post-queries' ); + wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed ); } return $post_parents; @@ -3448,7 +3457,7 @@ public function get_posts() { 'max_num_pages' => $this->max_num_pages, ); - wp_cache_set( $cache_key, $cache_value, 'post-queries' ); + wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed ); } if ( ! $q['suppress_filters'] ) { @@ -3486,11 +3495,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_salted( $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_salted( $comment_cache_key, $comment_ids, 'comment-queries', $comment_last_changed ); } _prime_comment_caches( $comment_ids ); @@ -5062,12 +5071,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..d89be175045b4 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_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_add( $cache_key, $cache_value, 'site-queries' ); + 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 6a2644b1015c7..754278cfbaf52 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_salted( $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_salted( $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_salted( $cache_key, array(), 'term-queries', $last_changed ); } return array(); } @@ -900,7 +901,7 @@ public function get_terms() { } if ( $args['cache_results'] ) { - wp_cache_add( $cache_key, $term_cache, 'term-queries' ); + wp_cache_set_salted( $cache_key, $term_cache, 'term-queries', $last_changed ); } $this->terms = $this->format_terms( $term_objects, $_fields ); @@ -1172,8 +1173,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..014cfb2ac56d9 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -830,8 +830,10 @@ public function query() { $cache_value = false; $cache_key = $this->generate_cache_key( $qv, $this->request ); $cache_group = 'user-queries'; + $last_changed = $this->get_cache_last_changed( $qv ); + if ( $qv['cache_results'] ) { - $cache_value = wp_cache_get( $cache_key, $cache_group ); + $cache_value = wp_cache_get_salted( $cache_key, $cache_group, $last_changed ); } if ( false !== $cache_value ) { $this->results = $cache_value['user_data']; @@ -866,7 +868,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_salted( $cache_key, $cache_value, $cache_group, $last_changed ); } } } @@ -1043,21 +1045,35 @@ protected function parse_orderby( $orderby ) { * Generate cache key. * * @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 $args Query arguments. - * @param string $sql SQL statement. + * @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. $sql = $wpdb->remove_placeholder_escape( $sql ); - $key = md5( $sql ); - $last_changed = wp_cache_get_last_changed( 'users' ); + $key = md5( $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 = (array) wp_cache_get_last_changed( 'users' ); if ( empty( $args['orderby'] ) ) { // Default order is by 'user_login'. @@ -1080,14 +1096,14 @@ protected function generate_cache_key( array $args, $sql ) { 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(); } } - return "get_users:$key:$last_changed"; + return $last_changed; } /** diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index adef3138fe065..72944b436002d 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_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_salted( $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_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_salted( $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_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_salted( $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_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + wp_cache_set_salted( $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_salted( $key, 'post-queries', $last_changed ); if ( ! $results ) { $results = $wpdb->get_results( $query ); - wp_cache_set( $key, $results, 'post-queries' ); + 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 cf41630c4a8c5..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:$last_changed"; + $cache_key = "adjacent_post:$key"; - $result = wp_cache_get( $cache_key, 'post-queries' ); + $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( $cache_key, $result, 'post-queries' ); + 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 c30abdc37970e..3d5b2f5c1a6ac 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6104,8 +6104,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_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( $cache_key, $found_id, 'post-queries' ); + 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 94c6453b6637b..592e70e0290a3 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_salted( $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_salted( $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_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( $cache_key, $id, 'post-queries' ); + 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 dd27fb9aefcb8..acdf5c1e20acb 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_salted( $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_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 6c6ac12fc2143..ab1e38a38b9e9 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_salted( $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_salted( $cache_key, $count, 'post-queries', $last_changed ); } /** diff --git a/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php b/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php new file mode 100644 index 0000000000000..196488b616a8b --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheGetMultipleSalted.php @@ -0,0 +1,117 @@ + $last_changed, + '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 the cached data with a salt. + * + * @ticket 59592 + */ + 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 array of false values 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' ); + + $last_changed = wp_cache_get_last_changed( 'query_data' ); + + $result = wp_cache_get_multiple_salted( array( 'cache_key', 'another_key' ), 'query_data', $last_changed ); + + $this->assertSameSets( + array( + 'cache_key' => false, + 'another_key' => false, + ), + $result + ); + } + + /** + * Test that wp_cache_get_multiple_salted returns the cached data for multiple keys. + * + * @ticket 59592 + */ + 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', + array( + 'salt' => $last_changed, + 'data' => array( 123 ), + ), + 'query_data' + ); + wp_cache_set( + 'another_key', + array( + 'salt' => '123', + 'data' => array(), + ), + 'query_data' + ); + + $last_changed = wp_cache_get_last_changed( 'query_data' ); + + $result = wp_cache_get_multiple_salted( 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/wpCacheGetSalted.php b/tests/phpunit/tests/functions/wpCacheGetSalted.php new file mode 100644 index 0000000000000..d54a68274eee6 --- /dev/null +++ b/tests/phpunit/tests/functions/wpCacheGetSalted.php @@ -0,0 +1,95 @@ + $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_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 ) ); + } + + /** + * 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 ), + 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/wpCacheSetLastChanged.php b/tests/phpunit/tests/functions/wpCacheSetLastChanged.php index 0ffe54b37ea51..f40980bcc3010 100644 --- a/tests/phpunit/tests/functions/wpCacheSetLastChanged.php +++ b/tests/phpunit/tests/functions/wpCacheSetLastChanged.php @@ -1,8 +1,10 @@ '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/wpCacheSetSalted.php b/tests/phpunit/tests/functions/wpCacheSetSalted.php new file mode 100644 index 0000000000000..33cac4aadcb94 --- /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' ); + } +}