From 4edede48ebc80089b8d1699e3402298d07cd042e Mon Sep 17 00:00:00 2001 From: peterwilsoncc Date: Wed, 9 Nov 2022 00:57:02 +0000 Subject: [PATCH] Query: Prevent ID only queries erroring when starting the loop. Ensure only full post objects are passed to `update_post_author_caches()` when called within `WP_Query::the_post()`. This prevents an error when starting the Loop for Queries initiated with a subset of fields or IDs only. Props konyoldeath, dd32, lozula, TimothyBlynJacobs, spacedmonkey, mxbclang, peterwilsoncc. Fixes #56948. git-svn-id: https://develop.svn.wordpress.org/trunk@54771 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 10 +++- src/wp-includes/post.php | 9 ++++ tests/phpunit/tests/query/cacheResults.php | 61 ++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 69efdf5e2a..1e695bd052 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -3586,7 +3586,15 @@ class WP_Query { global $post; if ( ! $this->in_the_loop ) { - update_post_author_caches( $this->posts ); + // Only prime the post cache for queries limited to the ID field. + $post_ids = array_filter( $this->posts, 'is_numeric' ); + // Exclude any falsey values, such as 0. + $post_ids = array_filter( $post_ids ); + if ( $post_ids ) { + _prime_post_caches( $post_ids, $this->query_vars['update_post_term_cache'], $this->query_vars['update_post_meta_cache'] ); + } + $post_objects = array_map( 'get_post', $this->posts ); + update_post_author_caches( $post_objects ); } $this->in_the_loop = true; diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index d7e7bfec99..6a7c637831 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -7461,6 +7461,15 @@ function update_post_caches( &$posts, $post_type = 'post', $update_term_cache = * @param WP_Post[] $posts Array of post objects. */ function update_post_author_caches( $posts ) { + /* + * cache_users() is a pluggable function so is not available prior + * to the `plugins_loaded` hook firing. This is to ensure against + * fatal errors when the function is not available. + */ + if ( ! function_exists( 'cache_users' ) ) { + return; + } + $author_ids = wp_list_pluck( $posts, 'post_author' ); $author_ids = array_map( 'absint', $author_ids ); $author_ids = array_unique( array_filter( $author_ids ) ); diff --git a/tests/phpunit/tests/query/cacheResults.php b/tests/phpunit/tests/query/cacheResults.php index d133dd862c..6e2dde1f0f 100644 --- a/tests/phpunit/tests/query/cacheResults.php +++ b/tests/phpunit/tests/query/cacheResults.php @@ -1210,4 +1210,65 @@ class Test_Query_CacheResults extends WP_UnitTestCase { array( 'id=>parent', 'id=>parent' ), ); } + + /** + * Ensure starting the loop warms the author cache. + * + * @since 6.1.1 + * @ticket 56948 + * + * @covers WP_Query::the_post + * + * @dataProvider data_author_cache_warmed_by_the_loop + * + * @param string $fields Query fields. + */ + public function test_author_cache_warmed_by_the_loop( $fields ) { + // Update post author for the parent post. + self::factory()->post->update_object( self::$pages[0], array( 'post_author' => self::$author_id ) ); + + self::factory()->post->create( + array( + 'post_author' => self::$author_id, + 'post_parent' => self::$pages[0], + 'post_type' => 'page', + ) + ); + + $query_1 = new WP_Query( + array( + 'post_type' => 'page', + 'fields' => $fields, + 'author' => self::$author_id, + ) + ); + + // Start the loop. + $start_loop_queries = get_num_queries(); + $query_1->the_post(); + $num_loop_queries = get_num_queries() - $start_loop_queries; + $this->assertSame( 2, $num_loop_queries, 'Unexpected number of queries while initializing the loop.' ); + + $start_author_queries = get_num_queries(); + get_user_by( 'ID', self::$author_id ); + $num_author_queries = get_num_queries() - $start_author_queries; + $this->assertSame( 0, $num_author_queries, 'Author cache is not warmed by the loop.' ); + } + + /** + * Data provider for test_author_cache_warmed_by_the_loop + * + * @return array[] + */ + public function data_author_cache_warmed_by_the_loop() { + return array( + 'fields: empty' => array( '' ), + 'fields: all' => array( 'all' ), + 'fields: ids' => array( 'ids' ), + /* + * `id=>parent` is untested pending the resolution of an existing bug. + * See https://core.trac.wordpress.org/ticket/56992 + */ + ); + } }