From 48f17267fb018427506b5d59e9e108dfd9131eb1 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 9 Sep 2022 02:04:18 +0000 Subject: [PATCH] Query: Improve `WP_Query`'s cache key generation for taxonomy queries. Modify how `WP_Query` determines whether a database query contains a taxonomy component and accounts for term changes when generating the cache key. This presents a stale cache been used under some circumstances. Props Chouby, costdev, peterwilsoncc. See #22176. git-svn-id: https://develop.svn.wordpress.org/trunk@54111 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 2 +- tests/phpunit/tests/query/cacheResults.php | 91 ++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 214d18fda8..32b5fafaf3 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -3094,7 +3094,7 @@ class WP_Query { $key = md5( serialize( $cache_args ) . $new_request ); $last_changed = wp_cache_get_last_changed( 'posts' ); - if ( ! empty( $this->tax_query->queried_terms ) ) { + if ( ! empty( $this->tax_query->queries ) ) { $last_changed .= wp_cache_get_last_changed( 'terms' ); } diff --git a/tests/phpunit/tests/query/cacheResults.php b/tests/phpunit/tests/query/cacheResults.php index 226e82aeca..d93b5ae9f1 100644 --- a/tests/phpunit/tests/query/cacheResults.php +++ b/tests/phpunit/tests/query/cacheResults.php @@ -2,6 +2,7 @@ /** * @group query + * @covers WP_Query::get_posts */ class Test_Query_CacheResults extends WP_UnitTestCase { /** @@ -893,4 +894,94 @@ class Test_Query_CacheResults extends WP_UnitTestCase { $this->assertEmpty( $posts2 ); $this->assertNotSame( $query1->found_posts, $query2->found_posts ); } + + /** + * @ticket 22176 + */ + public function test_query_cache_should_exclude_post_with_excluded_term() { + $term_id = self::$t1; + // Post 0 has the term applied + $post_id = self::$posts[0]; + + $args = array( + 'fields' => 'ids', + 'tax_query' => array( + array( + 'taxonomy' => 'category', + 'terms' => array( $term_id ), + 'operator' => 'NOT IN', + ), + ), + ); + + $post_ids_q1 = get_posts( $args ); + $this->assertNotContains( $post_id, $post_ids_q1, 'First query includes the post ID.' ); + + $num_queries = get_num_queries(); + $post_ids_q2 = get_posts( $args ); + $this->assertNotContains( $post_id, $post_ids_q2, 'Second query includes the post ID.' ); + + $this->assertSame( $num_queries, get_num_queries(), 'Second query is not cached.' ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_should_exclude_post_when_excluded_term_is_added_after_caching() { + $term_id = self::$t1; + // Post 1 does not have the term applied. + $post_id = self::$posts[1]; + + $args = array( + 'fields' => 'ids', + 'tax_query' => array( + array( + 'taxonomy' => 'category', + 'terms' => array( $term_id ), + 'operator' => 'NOT IN', + ), + ), + ); + + $post_ids_q1 = get_posts( $args ); + $this->assertContains( $post_id, $post_ids_q1, 'First query does not include the post ID.' ); + + wp_set_object_terms( $post_id, array( $term_id ), 'category' ); + + $num_queries = get_num_queries(); + $post_ids_q2 = get_posts( $args ); + $this->assertNotContains( $post_id, $post_ids_q2, 'Second query includes the post ID.' ); + $this->assertNotSame( $num_queries, get_num_queries(), 'Applying term does not invalidate previous cache.' ); + } + + /** + * @ticket 22176 + */ + public function test_query_cache_should_not_exclude_post_when_excluded_term_is_removed_after_caching() { + $term_id = self::$t1; + // Post 0 has the term applied. + $post_id = self::$posts[0]; + + $args = array( + 'fields' => 'ids', + 'tax_query' => array( + array( + 'taxonomy' => 'category', + 'terms' => array( $term_id ), + 'operator' => 'NOT IN', + ), + ), + ); + + $post_ids_q1 = get_posts( $args ); + $this->assertNotContains( $post_id, $post_ids_q1, 'First query includes the post ID.' ); + + // Clear the post of terms. + wp_set_object_terms( $post_id, array(), 'category' ); + + $num_queries = get_num_queries(); + $post_ids_q2 = get_posts( $args ); + $this->assertContains( $post_id, $post_ids_q2, 'Second query does not include the post ID.' ); + $this->assertNotSame( $num_queries, get_num_queries(), 'Removing term does not invalidate previous cache.' ); + } }