Skip to content

Commit

Permalink
Query: Improved handling of filtered queries in WP_Query.
Browse files Browse the repository at this point in the history
The `WP_Query` class enables developers to customize queries using filters like `posts_fields_request`, `posts_request`, and `the_posts`, which can modify both the queried fields and retrieved post objects. In some cases with these filters, incomplete or invalid post objects lacking essential data may arise. To address this, if any of these filters are active during a query, the `get_posts` method now avoids caching post objects with the usual `update_post_caches` function call, opting for a call to `_prime_post_caches` instead. This may occasionally trigger new database queries to prime the post data cache. While this enhancement may result in rare additional database queries, it ensures that invalid post objects aren't cached, prioritizing data consistency and integrity in filtered query scenarios.

Props saulirajala, spacedmonkey, flixos90, mukesh27, peterwilsoncc.
Fixes #58599.

git-svn-id: https://develop.svn.wordpress.org/trunk@56656 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
spacedmonkey committed Sep 21, 2023
1 parent 58d47f5 commit e486ac0
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 3 deletions.
14 changes: 11 additions & 3 deletions src/wp-includes/class-wp-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -3270,10 +3270,11 @@ public function get_posts() {
return $post_parents;
}

$is_unfiltered_query = $old_request == $this->request && "{$wpdb->posts}.*" === $fields;

if ( null === $this->posts ) {
$split_the_query = (
$old_request == $this->request
&& "{$wpdb->posts}.*" === $fields
$is_unfiltered_query
&& (
wp_using_ext_object_cache()
|| ( ! empty( $limits ) && $q['posts_per_page'] < 500 )
Expand Down Expand Up @@ -3337,6 +3338,8 @@ public function get_posts() {
$this->posts = array_map( 'get_post', $this->posts );
}

$unfiltered_posts = $this->posts;

if ( $q['cache_results'] && $id_query_is_cacheable && ! $cache_found ) {
$post_ids = wp_list_pluck( $this->posts, 'ID' );

Expand Down Expand Up @@ -3529,7 +3532,12 @@ public function get_posts() {
$this->posts = array_map( 'get_post', $this->posts );

if ( $q['cache_results'] ) {
update_post_caches( $this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
if ( $is_unfiltered_query && $unfiltered_posts === $this->posts ) {
update_post_caches( $this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
} else {
$post_ids = wp_list_pluck( $this->posts, 'ID' );
_prime_post_caches( $post_ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
}
}

/** @var WP_Post */
Expand Down
79 changes: 79 additions & 0 deletions tests/phpunit/tests/query/cacheResults.php
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,85 @@ public function test_query_cache_delete_term() {
$this->assertNotSame( $query1->found_posts, $query2->found_posts );
}

/**
* @ticket 58599
*/
public function test_query_posts_fields_request() {
global $wpdb;

$args = array(
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'no_found_rows' => true,
);

add_filter( 'posts_fields_request', array( $this, 'filter_posts_fields_request' ) );

$before = get_num_queries();
$query1 = new WP_Query();
$posts1 = $query1->query( $args );
$after = get_num_queries();

foreach ( $posts1 as $_post ) {
$this->assertNotSame( get_post( $_post->ID )->post_content, $_post->post_content );
}

$this->assertSame( 2, $after - $before, 'There should only be 2 queries run, one for request and one prime post objects.' );

$this->assertStringContainsString(
"SELECT $wpdb->posts.*",
$wpdb->last_query,
'Check that _prime_post_caches is called.'
);
}

public function filter_posts_fields_request( $fields ) {
global $wpdb;
return "{$wpdb->posts}.ID";
}

/**
* @ticket 58599
* @dataProvider data_query_filter_posts_results
*/
public function test_query_filter_posts_results( $filter ) {
global $wpdb;

$args = array(
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'no_found_rows' => true,
);

add_filter( $filter, array( $this, 'filter_posts_results' ) );

$before = get_num_queries();
$query1 = new WP_Query();
$posts1 = $query1->query( $args );
$after = get_num_queries();

$this->assertCount( 1, $posts1 );

$this->assertSame( 2, $after - $before, 'There should only be 2 queries run, one for request and one prime post objects.' );

$this->assertStringContainsString(
"SELECT $wpdb->posts.*",
$wpdb->last_query,
'Check that _prime_post_caches is called.'
);
}

public function filter_posts_results() {
return array( get_post( self::$posts[0] ) );
}

public function data_query_filter_posts_results() {
return array(
array( 'posts_results' ),
array( 'the_posts' ),
);
}

/**
* @ticket 22176
*/
Expand Down

0 comments on commit e486ac0

Please sign in to comment.