Make WordPress Core

Changeset 56656


Ignore:
Timestamp:
09/21/2023 07:32:55 PM (9 months ago)
Author:
spacedmonkey
Message:

Query: Improved handling of filtered queries in WP_Query.

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.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-query.php

    r56549 r56656  
    32713271        }
    32723272
     3273        $is_unfiltered_query = $old_request == $this->request && "{$wpdb->posts}.*" === $fields;
     3274
    32733275        if ( null === $this->posts ) {
    32743276            $split_the_query = (
    3275                 $old_request == $this->request
    3276                 && "{$wpdb->posts}.*" === $fields
     3277                $is_unfiltered_query
    32773278                && (
    32783279                    wp_using_ext_object_cache()
     
    33373338            $this->posts = array_map( 'get_post', $this->posts );
    33383339        }
     3340
     3341        $unfiltered_posts = $this->posts;
    33393342
    33403343        if ( $q['cache_results'] && $id_query_is_cacheable && ! $cache_found ) {
     
    35303533
    35313534            if ( $q['cache_results'] ) {
    3532                 update_post_caches( $this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
     3535                if ( $is_unfiltered_query && $unfiltered_posts === $this->posts ) {
     3536                    update_post_caches( $this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
     3537                } else {
     3538                    $post_ids = wp_list_pluck( $this->posts, 'ID' );
     3539                    _prime_post_caches( $post_ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
     3540                }
    35333541            }
    35343542
  • trunk/tests/phpunit/tests/query/cacheResults.php

    r55349 r56656  
    10811081
    10821082    /**
     1083     * @ticket 58599
     1084     */
     1085    public function test_query_posts_fields_request() {
     1086        global $wpdb;
     1087
     1088        $args = array(
     1089            'update_post_meta_cache' => false,
     1090            'update_post_term_cache' => false,
     1091            'no_found_rows'          => true,
     1092        );
     1093
     1094        add_filter( 'posts_fields_request', array( $this, 'filter_posts_fields_request' ) );
     1095
     1096        $before = get_num_queries();
     1097        $query1 = new WP_Query();
     1098        $posts1 = $query1->query( $args );
     1099        $after  = get_num_queries();
     1100
     1101        foreach ( $posts1 as $_post ) {
     1102            $this->assertNotSame( get_post( $_post->ID )->post_content, $_post->post_content );
     1103        }
     1104
     1105        $this->assertSame( 2, $after - $before, 'There should only be 2 queries run, one for request and one prime post objects.' );
     1106
     1107        $this->assertStringContainsString(
     1108            "SELECT $wpdb->posts.*",
     1109            $wpdb->last_query,
     1110            'Check that _prime_post_caches is called.'
     1111        );
     1112    }
     1113
     1114    public function filter_posts_fields_request( $fields ) {
     1115        global $wpdb;
     1116        return "{$wpdb->posts}.ID";
     1117    }
     1118
     1119    /**
     1120     * @ticket 58599
     1121     * @dataProvider data_query_filter_posts_results
     1122     */
     1123    public function test_query_filter_posts_results( $filter ) {
     1124        global $wpdb;
     1125
     1126        $args = array(
     1127            'update_post_meta_cache' => false,
     1128            'update_post_term_cache' => false,
     1129            'no_found_rows'          => true,
     1130        );
     1131
     1132        add_filter( $filter, array( $this, 'filter_posts_results' ) );
     1133
     1134        $before = get_num_queries();
     1135        $query1 = new WP_Query();
     1136        $posts1 = $query1->query( $args );
     1137        $after  = get_num_queries();
     1138
     1139        $this->assertCount( 1, $posts1 );
     1140
     1141        $this->assertSame( 2, $after - $before, 'There should only be 2 queries run, one for request and one prime post objects.' );
     1142
     1143        $this->assertStringContainsString(
     1144            "SELECT $wpdb->posts.*",
     1145            $wpdb->last_query,
     1146            'Check that _prime_post_caches is called.'
     1147        );
     1148    }
     1149
     1150    public function filter_posts_results() {
     1151        return array( get_post( self::$posts[0] ) );
     1152    }
     1153
     1154    public function data_query_filter_posts_results() {
     1155        return array(
     1156            array( 'posts_results' ),
     1157            array( 'the_posts' ),
     1158        );
     1159    }
     1160
     1161    /**
    10831162     * @ticket 22176
    10841163     */
Note: See TracChangeset for help on using the changeset viewer.