Make WordPress Core

Changeset 59766


Ignore:
Timestamp:
02/06/2025 05:02:17 AM (less than one hour ago)
Author:
peterwilsoncc
Message:

Query: Increase WP_Query cache hits for equivalent arguments.

Introduces normalization a number of arguments passed to WP_Query to increase cache hits for equivalent requests. For example author__in => [ 1, 2 ] and author__in => [ 2, 1 ] will now hit the same cache.

Prior to generating the SQL request and cache key, the following are sorted, made unique and type cast as appropriate.

  • post_type when passed as an array
  • post_status when passed as an array
  • term_querys containing terms
  • cat
  • category__in
  • category__not_in
  • category__and
  • tag_slug__in
  • tag__in
  • tag__not_in
  • tag__and
  • tag_slug__in
  • tag_slug__and
  • post_parent__not_in
  • author
  • author__not_in
  • author__in

The following are sorted for the purposes of generating the cache key and SQL WHERE clause but unmodified for use in the ORDER BY SQL clause:

  • post_name__in
  • post__in
  • post_parent__in

This commit includes changes to unrelated tests, assertions in Tests_Query_ParseQuery::test_parse_query_cat_array_mixed() and WP_Test_REST_Posts_Controller::test_get_items_not_sticky_with_exclude() have been modified to account for the sorting of the items above.

Props thekt12, peterwilsoncc, spacedmonkey, joemcgill, flixos90, mukesh27, pbearne, swissspidy.
Fixes #59516.

Location:
trunk
Files:
4 edited

Legend:

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

    r59495 r59766  
    474474
    475475    private $compat_methods = array( 'init_query_flags', 'parse_tax_query' );
     476
     477    /**
     478     * The cache key generated by the query.
     479     *
     480     * The cache key is generated by the method ::generate_cache_key() after the
     481     * query has been normalized.
     482     *
     483     * @var string
     484     */
     485    private $query_cache_key = '';
    476486
    477487    /**
     
    11021112        if ( ! empty( $qv['post_type'] ) ) {
    11031113            if ( is_array( $qv['post_type'] ) ) {
    1104                 $qv['post_type'] = array_map( 'sanitize_key', $qv['post_type'] );
     1114                $qv['post_type'] = array_map( 'sanitize_key', array_unique( $qv['post_type'] ) );
     1115                sort( $qv['post_type'] );
    11051116            } else {
    11061117                $qv['post_type'] = sanitize_key( $qv['post_type'] );
     
    11101121        if ( ! empty( $qv['post_status'] ) ) {
    11111122            if ( is_array( $qv['post_status'] ) ) {
    1112                 $qv['post_status'] = array_map( 'sanitize_key', $qv['post_status'] );
     1123                $qv['post_status'] = array_map( 'sanitize_key', array_unique( $qv['post_status'] ) );
     1124                sort( $qv['post_status'] );
    11131125            } else {
    11141126                $qv['post_status'] = preg_replace( '|[^a-z0-9_,-]|', '', $qv['post_status'] );
     
    11831195                $term = $q[ $t->query_var ];
    11841196
    1185                 if ( is_array( $term ) ) {
    1186                     $term = implode( ',', $term );
    1187                 }
     1197                if ( ! is_array( $term ) ) {
     1198                    $term = explode( ',', $term );
     1199                    $term = array_map( 'trim', $term );
     1200                }
     1201                sort( $term );
     1202                $term = implode( ',', $term );
    11881203
    11891204                if ( str_contains( $term, '+' ) ) {
     
    12211236            $cat_array = preg_split( '/[,\s]+/', urldecode( $q['cat'] ) );
    12221237            $cat_array = array_map( 'intval', $cat_array );
    1223             $q['cat']  = implode( ',', $cat_array );
     1238            sort( $cat_array );
     1239            $q['cat'] = implode( ',', $cat_array );
    12241240
    12251241            foreach ( $cat_array as $cat ) {
     
    12631279        if ( ! empty( $q['category__in'] ) ) {
    12641280            $q['category__in'] = array_map( 'absint', array_unique( (array) $q['category__in'] ) );
    1265             $tax_query[]       = array(
     1281            sort( $q['category__in'] );
     1282            $tax_query[] = array(
    12661283                'taxonomy'         => 'category',
    12671284                'terms'            => $q['category__in'],
     
    12731290        if ( ! empty( $q['category__not_in'] ) ) {
    12741291            $q['category__not_in'] = array_map( 'absint', array_unique( (array) $q['category__not_in'] ) );
     1292            sort( $q['category__not_in'] );
    12751293            $tax_query[]           = array(
    12761294                'taxonomy'         => 'category',
     
    12831301        if ( ! empty( $q['category__and'] ) ) {
    12841302            $q['category__and'] = array_map( 'absint', array_unique( (array) $q['category__and'] ) );
    1285             $tax_query[]        = array(
     1303            sort( $q['category__and'] );
     1304            $tax_query[] = array(
    12861305                'taxonomy'         => 'category',
    12871306                'terms'            => $q['category__and'],
     
    13011320        if ( '' !== $q['tag'] && ! $this->is_singular && $this->query_vars_changed ) {
    13021321            if ( str_contains( $q['tag'], ',' ) ) {
     1322                // @todo Handle normalizing `tag` query string.
    13031323                $tags = preg_split( '/[,\r\n\t ]+/', $q['tag'] );
    13041324                foreach ( (array) $tags as $tag ) {
    13051325                    $tag                 = sanitize_term_field( 'slug', $tag, 0, 'post_tag', 'db' );
    13061326                    $q['tag_slug__in'][] = $tag;
     1327                    sort( $q['tag_slug__in'] );
    13071328                }
    13081329            } elseif ( preg_match( '/[+\r\n\t ]+/', $q['tag'] ) || ! empty( $q['cat'] ) ) {
     
    13151336                $q['tag']            = sanitize_term_field( 'slug', $q['tag'], 0, 'post_tag', 'db' );
    13161337                $q['tag_slug__in'][] = $q['tag'];
     1338                sort( $q['tag_slug__in'] );
    13171339            }
    13181340        }
     
    13281350        if ( ! empty( $q['tag__in'] ) ) {
    13291351            $q['tag__in'] = array_map( 'absint', array_unique( (array) $q['tag__in'] ) );
    1330             $tax_query[]  = array(
     1352            sort( $q['tag__in'] );
     1353            $tax_query[] = array(
    13311354                'taxonomy' => 'post_tag',
    13321355                'terms'    => $q['tag__in'],
     
    13361359        if ( ! empty( $q['tag__not_in'] ) ) {
    13371360            $q['tag__not_in'] = array_map( 'absint', array_unique( (array) $q['tag__not_in'] ) );
     1361            sort( $q['tag__not_in'] );
    13381362            $tax_query[]      = array(
    13391363                'taxonomy' => 'post_tag',
     
    13451369        if ( ! empty( $q['tag__and'] ) ) {
    13461370            $q['tag__and'] = array_map( 'absint', array_unique( (array) $q['tag__and'] ) );
    1347             $tax_query[]   = array(
     1371            sort( $q['tag__and'] );
     1372            $tax_query[] = array(
    13481373                'taxonomy' => 'post_tag',
    13491374                'terms'    => $q['tag__and'],
     
    13541379        if ( ! empty( $q['tag_slug__in'] ) ) {
    13551380            $q['tag_slug__in'] = array_map( 'sanitize_title_for_query', array_unique( (array) $q['tag_slug__in'] ) );
    1356             $tax_query[]       = array(
     1381            sort( $q['tag_slug__in'] );
     1382            $tax_query[] = array(
    13571383                'taxonomy' => 'post_tag',
    13581384                'terms'    => $q['tag_slug__in'],
     
    13631389        if ( ! empty( $q['tag_slug__and'] ) ) {
    13641390            $q['tag_slug__and'] = array_map( 'sanitize_title_for_query', array_unique( (array) $q['tag_slug__and'] ) );
    1365             $tax_query[]        = array(
     1391            sort( $q['tag_slug__and'] );
     1392            $tax_query[] = array(
    13661393                'taxonomy' => 'post_tag',
    13671394                'terms'    => $q['tag_slug__and'],
     
    21872214        } elseif ( is_array( $q['post_name__in'] ) && ! empty( $q['post_name__in'] ) ) {
    21882215            $q['post_name__in'] = array_map( 'sanitize_title_for_query', $q['post_name__in'] );
    2189             $post_name__in      = "'" . implode( "','", $q['post_name__in'] ) . "'";
    2190             $where             .= " AND {$wpdb->posts}.post_name IN ($post_name__in)";
     2216            // Duplicate array before sorting to allow for the orderby clause.
     2217            $post_name__in_for_where = array_unique( $q['post_name__in'] );
     2218            sort( $post_name__in_for_where );
     2219            $post_name__in = "'" . implode( "','", $post_name__in_for_where ) . "'";
     2220            $where        .= " AND {$wpdb->posts}.post_name IN ($post_name__in)";
    21912221        }
    21922222
     
    22002230            $where .= " AND {$wpdb->posts}.ID = " . $q['p'];
    22012231        } elseif ( $q['post__in'] ) {
    2202             $post__in = implode( ',', array_map( 'absint', $q['post__in'] ) );
     2232            // Duplicate array before sorting to allow for the orderby clause.
     2233            $post__in_for_where = $q['post__in'];
     2234            $post__in_for_where = array_unique( array_map( 'absint', $post__in_for_where ) );
     2235            sort( $post__in_for_where );
     2236            $post__in = implode( ',', array_map( 'absint', $post__in_for_where ) );
    22032237            $where   .= " AND {$wpdb->posts}.ID IN ($post__in)";
    22042238        } elseif ( $q['post__not_in'] ) {
     2239            sort( $q['post__not_in'] );
    22052240            $post__not_in = implode( ',', array_map( 'absint', $q['post__not_in'] ) );
    22062241            $where       .= " AND {$wpdb->posts}.ID NOT IN ($post__not_in)";
     
    22102245            $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_parent = %d ", $q['post_parent'] );
    22112246        } elseif ( $q['post_parent__in'] ) {
    2212             $post_parent__in = implode( ',', array_map( 'absint', $q['post_parent__in'] ) );
     2247            // Duplicate array before sorting to allow for the orderby clause.
     2248            $post_parent__in_for_where = $q['post_parent__in'];
     2249            $post_parent__in_for_where = array_unique( array_map( 'absint', $post_parent__in_for_where ) );
     2250            sort( $post_parent__in_for_where );
     2251            $post_parent__in = implode( ',', array_map( 'absint', $post_parent__in_for_where ) );
    22132252            $where          .= " AND {$wpdb->posts}.post_parent IN ($post_parent__in)";
    22142253        } elseif ( $q['post_parent__not_in'] ) {
     2254            sort( $q['post_parent__not_in'] );
    22152255            $post_parent__not_in = implode( ',', array_map( 'absint', $q['post_parent__not_in'] ) );
    22162256            $where              .= " AND {$wpdb->posts}.post_parent NOT IN ($post_parent__not_in)";
     
    23422382            $q['author'] = addslashes_gpc( '' . urldecode( $q['author'] ) );
    23432383            $authors     = array_unique( array_map( 'intval', preg_split( '/[,\s]+/', $q['author'] ) ) );
     2384            sort( $authors );
    23442385            foreach ( $authors as $author ) {
    23452386                $key         = $author > 0 ? 'author__in' : 'author__not_in';
     
    23502391
    23512392        if ( ! empty( $q['author__not_in'] ) ) {
    2352             $author__not_in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__not_in'] ) ) );
     2393            if ( is_array( $q['author__not_in'] ) ) {
     2394                $q['author__not_in'] = array_unique( array_map( 'absint', $q['author__not_in'] ) );
     2395                sort( $q['author__not_in'] );
     2396            }
     2397            $author__not_in = implode( ',', (array) $q['author__not_in'] );
    23532398            $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
    23542399        } elseif ( ! empty( $q['author__in'] ) ) {
     2400            if ( is_array( $q['author__in'] ) ) {
     2401                $q['author__in'] = array_unique( array_map( 'absint', $q['author__in'] ) );
     2402                sort( $q['author__in'] );
     2403            }
    23552404            $author__in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__in'] ) ) );
    23562405            $where     .= " AND {$wpdb->posts}.post_author IN ($author__in) ";
     
    25892638                $q_status = explode( ',', $q_status );
    25902639            }
     2640            sort( $q_status );
    25912641            $r_status = array();
    25922642            $p_status = array();
     
    49034953        sort( $args['post_type'] );
    49044954
     4955        /*
     4956         * Sort arrays that can be used for ordering prior to cache key generation.
     4957         *
     4958         * These arrays are sorted in the query generator for the purposes of the
     4959         * WHERE clause but the arguments are not modified as they can be used for
     4960         * the orderby clase.
     4961         *
     4962         * Their use in the orderby clause will generate a different SQL query so
     4963         * they can be sorted for the cache key generation.
     4964         */
     4965        $sortable_arrays_with_int_values = array(
     4966            'post__in',
     4967            'post_parent__in',
     4968        );
     4969        foreach ( $sortable_arrays_with_int_values as $key ) {
     4970            if ( isset( $args[ $key ] ) && is_array( $args[ $key ] ) ) {
     4971                $args[ $key ] = array_unique( array_map( 'absint', $args[ $key ] ) );
     4972                sort( $args[ $key ] );
     4973            }
     4974        }
     4975
     4976        // Sort and unique the 'post_name__in' for cache key generation.
     4977        if ( isset( $args['post_name__in'] ) && is_array( $args['post_name__in'] ) ) {
     4978            $args['post_name__in'] = array_unique( $args['post_name__in'] );
     4979            sort( $args['post_name__in'] );
     4980        }
     4981
    49054982        if ( isset( $args['post_status'] ) ) {
    49064983            $args['post_status'] = (array) $args['post_status'];
     
    49435020        }
    49445021
    4945         return "wp_query:$key:$last_changed";
     5022        $this->query_cache_key = "wp_query:$key:$last_changed";
     5023        return $this->query_cache_key;
    49465024    }
    49475025
  • trunk/tests/phpunit/tests/query/cacheResults.php

    r58122 r59766  
    202202
    203203    /**
    204      * @ticket 59442
     204     * @ticket 59516
    205205     *
    206206     * @covers WP_Query::generate_cache_key
    207      *
    208      * @dataProvider data_query_cache_duplicate
    209      */
    210     public function test_generate_cache_key_normalize( $query_vars1, $query_vars2 ) {
     207     */
     208    public function test_post_in_order_by_clauses_are_not_normalized() {
    211209        global $wpdb;
     210
     211        $post_ids = self::$posts;
     212
     213        $query_vars1 = array(
     214            'post__in' => $post_ids,
     215            'orderby'  => 'post__in',
     216        );
     217        $query_vars2 = array(
     218            'post__in' => array_reverse( $post_ids ),
     219            'orderby'  => 'post__in',
     220        );
    212221
    213222        $fields   = "{$wpdb->posts}.ID";
     
    218227        $request2 = str_replace( $fields, "{$wpdb->posts}.*", $query2->request );
    219228
    220         $reflection = new ReflectionMethod( $query1, 'generate_cache_key' );
    221         $reflection->setAccessible( true );
     229        $reflection_q1 = new ReflectionProperty( $query1, 'query_cache_key' );
     230        $reflection_q1->setAccessible( true );
     231
     232        $reflection_q2 = new ReflectionProperty( $query2, 'query_cache_key' );
     233        $reflection_q2->setAccessible( true );
     234
     235        $this->assertNotSame( $request1, $request2, 'Queries should not match' );
     236
     237        $cache_key_1 = $reflection_q1->getValue( $query1 );
     238        $cache_key_2 = $reflection_q2->getValue( $query2 );
     239
     240        $this->assertNotSame( $cache_key_1, $cache_key_2, 'Cache key should differ.' );
     241        $this->assertNotEmpty( $cache_key_1, 'Cache key for query one should not be empty.' );
     242        $this->assertNotEmpty( $cache_key_2, 'Cache key for query two should not be empty.' );
     243
     244        // Test the posts are returned different orders.
     245        $this->assertNotSame( wp_list_pluck( $query1->posts, 'ID' ), wp_list_pluck( $query2->posts, 'ID' ), 'Query one posts should not match the order of query two posts.' );
     246        // Test the posts are the same sets.
     247        $this->assertSameSets( wp_list_pluck( $query1->posts, 'ID' ), wp_list_pluck( $query2->posts, 'ID' ), 'Query one posts should match the set of query two posts.' );
     248    }
     249
     250    /**
     251     * @ticket 59516
     252     *
     253     * @covers WP_Query::generate_cache_key
     254     */
     255    public function test_post_parent_in_order_by_clauses_are_not_normalized() {
     256        global $wpdb;
     257
     258        $parent_pages = self::$pages;
     259        $post_names   = array( 'doctor-dillamond', 'elphaba', 'fiyero', 'glinda', 'the-wizard-of-oz' );
     260        $child_pages  = array();
     261        foreach ( $parent_pages as $key => $parent_page ) {
     262            $child_pages[] = self::factory()->post->create(
     263                array(
     264                    'post_parent' => $parent_page,
     265                    'post_type'   => 'page',
     266                    'post_name'   => $post_names[ $key ],
     267                )
     268            );
     269        }
     270
     271        $query_vars1 = array(
     272            'post_parent__in' => $parent_pages,
     273            'post_type'       => 'page',
     274            'orderby'         => 'post_parent__in',
     275        );
     276
     277        $query_vars2 = array(
     278            'post_parent__in' => array_reverse( $parent_pages ),
     279            'post_type'       => 'page',
     280            'orderby'         => 'post_parent__in',
     281        );
     282
     283        $fields   = "{$wpdb->posts}.ID";
     284        $query1   = new WP_Query( $query_vars1 );
     285        $request1 = str_replace( $fields, "{$wpdb->posts}.*", $query1->request );
     286
     287        $query2   = new WP_Query( $query_vars2 );
     288        $request2 = str_replace( $fields, "{$wpdb->posts}.*", $query2->request );
     289
     290        $reflection_q1 = new ReflectionProperty( $query1, 'query_cache_key' );
     291        $reflection_q1->setAccessible( true );
     292
     293        $reflection_q2 = new ReflectionProperty( $query2, 'query_cache_key' );
     294        $reflection_q2->setAccessible( true );
     295
     296        $this->assertNotSame( $request1, $request2, 'Queries should not match' );
     297
     298        $cache_key_1 = $reflection_q1->getValue( $query1 );
     299        $cache_key_2 = $reflection_q2->getValue( $query2 );
     300
     301        $this->assertNotSame( $cache_key_1, $cache_key_2, 'Cache key should differ.' );
     302        $this->assertNotEmpty( $cache_key_1, 'Cache key for query one should not be empty.' );
     303        $this->assertNotEmpty( $cache_key_2, 'Cache key for query two should not be empty.' );
     304
     305        // Test the posts are returned in the correct order.
     306        $this->assertSame( array( 'doctor-dillamond', 'elphaba', 'fiyero', 'glinda', 'the-wizard-of-oz' ), wp_list_pluck( $query1->posts, 'post_name' ), 'Query one posts should be in alphabetical order' );
     307        $this->assertSame( array( 'the-wizard-of-oz', 'glinda', 'fiyero', 'elphaba', 'doctor-dillamond' ), wp_list_pluck( $query2->posts, 'post_name' ), 'Query two posts should be in reverse alphabetical order.' );
     308        // Test the posts are the same sets.
     309        $this->assertSameSets( wp_list_pluck( $query1->posts, 'ID' ), wp_list_pluck( $query2->posts, 'ID' ), 'Query one posts should match the set of query two posts.' );
     310    }
     311
     312    /**
     313     * @ticket 59516
     314     *
     315     * @covers WP_Query::generate_cache_key
     316     */
     317    public function test_post_name_in_order_by_clauses_are_not_normalized() {
     318        global $wpdb;
     319        $post_names = array( 'doctor-dillamond', 'elphaba', 'glinda', 'the-wizard-of-oz' );
     320        $posts      = array();
     321
     322        foreach ( $post_names as $post_name ) {
     323            $posts[] = self::factory()->post->create(
     324                array(
     325                    'post_name' => $post_name,
     326                )
     327            );
     328        }
     329
     330        $query_vars1 = array(
     331            'post_name__in' => $post_names,
     332            'orderby'       => 'post_name__in',
     333        );
     334
     335        $query_vars2 = array(
     336            'post_name__in' => array_reverse( $post_names ),
     337            'orderby'       => 'post_name__in',
     338        );
     339
     340        $fields   = "{$wpdb->posts}.ID";
     341        $query1   = new WP_Query( $query_vars1 );
     342        $request1 = str_replace( $fields, "{$wpdb->posts}.*", $query1->request );
     343
     344        $query2   = new WP_Query( $query_vars2 );
     345        $request2 = str_replace( $fields, "{$wpdb->posts}.*", $query2->request );
     346
     347        $reflection_q1 = new ReflectionProperty( $query1, 'query_cache_key' );
     348        $reflection_q1->setAccessible( true );
     349
     350        $reflection_q2 = new ReflectionProperty( $query2, 'query_cache_key' );
     351        $reflection_q2->setAccessible( true );
     352
     353        $this->assertNotSame( $request1, $request2, 'Queries should not match' );
     354
     355        $cache_key_1 = $reflection_q1->getValue( $query1 );
     356        $cache_key_2 = $reflection_q2->getValue( $query2 );
     357
     358        $this->assertNotSame( $cache_key_1, $cache_key_2, 'Cache key should differ.' );
     359        $this->assertNotEmpty( $cache_key_1, 'Cache key for query one should not be empty.' );
     360        $this->assertNotEmpty( $cache_key_2, 'Cache key for query two should not be empty.' );
     361
     362        // Test the posts are returned in the correct order.
     363        $this->assertSame( array( 'doctor-dillamond', 'elphaba', 'glinda', 'the-wizard-of-oz' ), wp_list_pluck( $query1->posts, 'post_name' ), 'Query one posts should be in alphabetical order' );
     364        $this->assertSame( array( 'the-wizard-of-oz', 'glinda', 'elphaba', 'doctor-dillamond' ), wp_list_pluck( $query2->posts, 'post_name' ), 'Query two posts should be in reverse alphabetical order.' );
     365        // Test the posts are the same sets.
     366        $this->assertSameSets( wp_list_pluck( $query1->posts, 'ID' ), wp_list_pluck( $query2->posts, 'ID' ), 'Query one posts should match the set of query two posts.' );
     367    }
     368
     369    /**
     370     * @ticket 59442
     371     * @ticket 59516
     372     *
     373     * @covers WP_Query::generate_cache_key
     374     *
     375     * @dataProvider data_query_cache_duplicate
     376     */
     377    public function test_generate_cache_key_normalize( $query_vars1, $query_vars2 ) {
     378        global $wpdb;
     379
     380        $fields   = "{$wpdb->posts}.ID";
     381        $query1   = new WP_Query( $query_vars1 );
     382        $request1 = str_replace( $fields, "{$wpdb->posts}.*", $query1->request );
     383
     384        $query2   = new WP_Query( $query_vars2 );
     385        $request2 = str_replace( $fields, "{$wpdb->posts}.*", $query2->request );
     386
     387        $reflection_q1 = new ReflectionProperty( $query1, 'query_cache_key' );
     388        $reflection_q1->setAccessible( true );
     389
     390        $reflection_q2 = new ReflectionProperty( $query2, 'query_cache_key' );
     391        $reflection_q2->setAccessible( true );
    222392
    223393        $this->assertSame( $request1, $request2, 'Queries should match' );
    224394
    225         $cache_key_1 = $reflection->invoke( $query1, $query_vars1, $request1 );
    226         $cache_key_2 = $reflection->invoke( $query1, $query_vars2, $request2 );
    227 
    228         $this->assertSame( $cache_key_1, $cache_key_2, 'Cache key differs the same paramters.' );
     395        $cache_key_1 = $reflection_q1->getValue( $query1 );
     396        $cache_key_2 = $reflection_q2->getValue( $query2 );
     397
     398        $this->assertSame( $cache_key_1, $cache_key_2, 'Cache key differs the same effective parameters.' );
     399        $this->assertNotEmpty( $cache_key_1, 'Cache key for query one should not be empty.' );
     400        $this->assertNotEmpty( $cache_key_2, 'Cache key for query two should not be empty.' );
    229401    }
    230402
     
    281453    public function data_query_cache_duplicate() {
    282454        return array(
    283             'post type empty'           => array(
     455            'post type empty'                              => array(
    284456                'query_vars1' => array( 'post_type' => '' ),
    285457                'query_vars2' => array( 'post_type' => 'post' ),
    286458            ),
    287             'post type array'           => array(
     459            'post type array'                              => array(
    288460                'query_vars1' => array( 'post_type' => array( 'page' ) ),
    289461                'query_vars2' => array( 'post_type' => 'page' ),
    290462            ),
    291             'orderby empty'             => array(
     463            'orderby empty'                                => array(
    292464                'query_vars1' => array( 'orderby' => null ),
    293465                'query_vars2' => array( 'orderby' => 'date' ),
    294466            ),
    295             'different order parameter' => array(
     467            'different order parameter'                    => array(
    296468                'query_vars1' => array(
    297469                    'post_type'      => 'post',
     
    303475                ),
    304476            ),
    305             'same args'                 => array(
     477            'same args'                                    => array(
    306478                'query_vars1' => array( 'post_type' => 'post' ),
    307479                'query_vars2' => array( 'post_type' => 'post' ),
    308480            ),
    309             'same args any'             => array(
     481            'same args any'                                => array(
    310482                'query_vars1' => array( 'post_type' => 'any' ),
    311483                'query_vars2' => array( 'post_type' => 'any' ),
    312484            ),
    313             'any and post types'        => array(
     485            'any and post types'                           => array(
    314486                'query_vars1' => array( 'post_type' => 'any' ),
    315487                'query_vars2' => array( 'post_type' => array( 'post', 'page', 'attachment' ) ),
    316488            ),
    317             'different order post type' => array(
     489            'different order post type'                    => array(
    318490                'query_vars1' => array( 'post_type' => array( 'post', 'page' ) ),
    319491                'query_vars2' => array( 'post_type' => array( 'page', 'post' ) ),
    320492            ),
    321             'post status array'         => array(
     493            'non-unique post type'                         => array(
     494                'query_vars1' => array( 'post_type' => array( 'post', 'page' ) ),
     495                'query_vars2' => array( 'post_type' => array( 'page', 'post', 'page' ) ),
     496            ),
     497            'post status array'                            => array(
    322498                'query_vars1' => array( 'post_status' => 'publish' ),
    323499                'query_vars2' => array( 'post_status' => array( 'publish' ) ),
    324500            ),
    325             'post status order'         => array(
     501            'post status order'                            => array(
    326502                'query_vars1' => array( 'post_status' => array( 'draft', 'publish' ) ),
    327503                'query_vars2' => array( 'post_status' => array( 'publish', 'draft' ) ),
    328504            ),
    329             'cache parameters'          => array(
     505            'non-unique post status'                       => array(
     506                'query_vars1' => array( 'post_status' => array( 'draft', 'publish' ) ),
     507                'query_vars2' => array( 'post_status' => array( 'draft', 'publish', 'draft' ) ),
     508            ),
     509            'post id int vs string'                        => array(
     510                'query_vars1' => array( 'p' => '1' ),
     511                'query_vars2' => array( 'p' => 1 ),
     512            ),
     513            'page id int vs string'                        => array(
     514                'query_vars1' => array( 'page_id' => '2' ),
     515                'query_vars2' => array( 'page_id' => 2 ),
     516            ),
     517            'attachment id int vs string'                  => array(
     518                'query_vars1' => array( 'attachment_id' => '3' ),
     519                'query_vars2' => array( 'attachment_id' => 3 ),
     520            ),
     521            'date and time values int vs string'           => array(
     522                'query_vars1' => array(
     523                    'year'     => '2013',
     524                    'monthnum' => '12',
     525                    'day'      => '12',
     526                    'hour'     => '12',
     527                    'minute'   => '12',
     528                    'second'   => '12',
     529                ),
     530                'query_vars2' => array(
     531                    'year'     => 2013,
     532                    'monthnum' => 12,
     533                    'day'      => 12,
     534                    'hour'     => 12,
     535                    'minute'   => 12,
     536                    'second'   => 12,
     537                ),
     538            ),
     539            'offset value int vs string'                   => array(
     540                'query_vars1' => array( 'offset' => '5' ),
     541                'query_vars2' => array( 'offset' => 5 ),
     542            ),
     543            'posts per page value int vs string'           => array(
     544                'query_vars1' => array( 'posts_per_page' => '5' ),
     545                'query_vars2' => array( 'posts_per_page' => 5 ),
     546            ),
     547            'paged value int vs string'                    => array(
     548                'query_vars1' => array( 'paged' => '2' ),
     549                'query_vars2' => array( 'paged' => 2 ),
     550            ),
     551            'menu_order value int vs string'               => array(
     552                'query_vars1' => array( 'menu_order' => '2' ),
     553                'query_vars2' => array( 'menu_order' => 2 ),
     554            ),
     555            'post__in different order'                     => array(
     556                'query_vars1' => array( 'post__in' => array( 1, 2, 3, 4, 5 ) ),
     557                'query_vars2' => array( 'post__in' => array( 5, 4, 3, 2, 1 ) ),
     558            ),
     559            'post__in non-unique'                          => array(
     560                'query_vars1' => array( 'post__in' => array( 1, 2, 3, 4, 5 ) ),
     561                'query_vars2' => array( 'post__in' => array( 1, 2, 3, 4, 5, 1, 2, 3 ) ),
     562            ),
     563            'post_parent__in different order'              => array(
     564                'query_vars1' => array( 'post_parent__in' => array( 1, 2, 3, 4, 5 ) ),
     565                'query_vars2' => array( 'post_parent__in' => array( 5, 4, 3, 2, 1 ) ),
     566            ),
     567            'post_parent__in non-unique'                   => array(
     568                'query_vars1' => array( 'post_parent__in' => array( 1, 2, 3, 4, 5 ) ),
     569                'query_vars2' => array( 'post_parent__in' => array( 1, 2, 3, 4, 5, 1, 2, 3 ) ),
     570            ),
     571            'post_name__in different order'                => array(
     572                'query_vars1' => array( 'post_name__in' => array( 'elphaba', 'glinda', 'the-wizard-of-oz', 'doctor-dillamond' ) ),
     573                'query_vars2' => array( 'post_name__in' => array( 'doctor-dillamond', 'elphaba', 'the-wizard-of-oz', 'glinda' ) ),
     574            ),
     575            'post_name__in non-unique'                     => array(
     576                'query_vars1' => array( 'post_name__in' => array( 'elphaba', 'glinda', 'the-wizard-of-oz', 'doctor-dillamond' ) ),
     577                'query_vars2' => array( 'post_name__in' => array( 'elphaba', 'glinda', 'elphaba', 'glinda', 'the-wizard-of-oz', 'doctor-dillamond' ) ),
     578            ),
     579            'cat different order (array)'                  => array(
     580                'query_vars_1' => array( 'cat' => array( '1', '2' ) ),
     581                'query_vars_2' => array( 'cat' => array( '2', '1' ) ),
     582            ),
     583            'cat different order (string)'                 => array(
     584                'query_vars_1' => array( 'cat' => '2,1' ),
     585                'query_vars_2' => array( 'cat' => '1,2' ),
     586            ),
     587            'cat queries int vs string'                    => array(
     588                'query_vars_1' => array( 'cat' => '2' ),
     589                'query_vars_2' => array( 'cat' => 2 ),
     590            ),
     591            'category__in queries different order (array)' => array(
     592                'query_vars_1' => array( 'category__in' => array( '1', '2' ) ),
     593                'query_vars_2' => array( 'category__in' => array( '2', '1' ) ),
     594            ),
     595            'category__in queries with non-unique array'   => array(
     596                'query_vars_1' => array( 'category__in' => array( '1', '1' ) ),
     597                'query_vars_2' => array( 'category__in' => array( '1' ) ),
     598            ),
     599            'category__in queries string vs array (array)' => array(
     600                'query_vars_1' => array( 'category__in' => array( '1' ) ),
     601                'query_vars_2' => array( 'category__in' => array( 1 ) ),
     602            ),
     603            'category__not_in different order (array)'     => array(
     604                'query_vars_1' => array( 'category__not_in' => array( '1', '2' ) ),
     605                'query_vars_2' => array( 'category__not_in' => array( '2', '1' ) ),
     606            ),
     607            'category__not_in with non-unique array'       => array(
     608                'query_vars_1' => array( 'category__not_in' => array( '1', '1' ) ),
     609                'query_vars_2' => array( 'category__not_in' => array( '1' ) ),
     610            ),
     611            'category__not_in queries string vs array (array)' => array(
     612                'query_vars_1' => array( 'category__not_in' => array( '1' ) ),
     613                'query_vars_2' => array( 'category__not_in' => array( 1 ) ),
     614            ),
     615            'category__and queries width different order (array)' => array(
     616                'query_vars_1' => array( 'category__and' => array( '1', '2' ) ),
     617                'query_vars_2' => array( 'category__and' => array( '2', '1' ) ),
     618            ),
     619            'category__and with non-unique array'          => array(
     620                'query_vars_1' => array( 'category__and' => array( '1', '1', '2' ) ),
     621                'query_vars_2' => array( 'category__and' => array( '1', '2' ) ),
     622            ),
     623            'category__and queries string vs array (array)' => array(
     624                'query_vars_1' => array( 'category__and' => array( '1', '2' ) ),
     625                'query_vars_2' => array( 'category__and' => array( 1, 2 ) ),
     626            ),
     627            'author queries different order (string)'      => array(
     628                'query_vars_1' => array( 'author' => '1,2' ),
     629                'query_vars_2' => array( 'author' => '2,1' ),
     630            ),
     631            'author with non-unique string'                => array(
     632                'query_vars_1' => array( 'author' => '1,1' ),
     633                'query_vars_2' => array( 'author' => '1' ),
     634            ),
     635            'author queries int vs string (string)'        => array(
     636                'query_vars_1' => array( 'author' => 1 ),
     637                'query_vars_2' => array( 'author' => '1' ),
     638            ),
     639            'author queries int vs string (array)'         => array(
     640                'query_vars_1' => array( 'author' => array( 1 ) ),
     641                'query_vars_2' => array( 'author' => array( '1' ) ),
     642            ),
     643            'author__in different order'                   => array(
     644                'query_vars_1' => array( 'author__in' => array( 1, 2 ) ),
     645                'query_vars_2' => array( 'author__in' => array( 2, 1 ) ),
     646            ),
     647            'author__in with non-unique array'             => array(
     648                'query_vars_1' => array( 'author__in' => array( 1, 1, 2 ) ),
     649                'query_vars_2' => array( 'author__in' => array( 1, 2 ) ),
     650            ),
     651            'author__in queries int vs string (array)'     => array(
     652                'query_vars_1' => array( 'author__in' => array( 1 ) ),
     653                'query_vars_2' => array( 'author__in' => array( '1' ) ),
     654            ),
     655            'author__not_in different order (array)'       => array(
     656                'query_vars_1' => array( 'author__not_in' => array( 1, 2 ) ),
     657                'query_vars_2' => array( 'author__not_in' => array( 2, 1 ) ),
     658            ),
     659            'author__not_in queries int vs string (array)' => array(
     660                'query_vars_1' => array( 'author__not_in' => array( 1 ) ),
     661                'query_vars_2' => array( 'author__not_in' => array( '1' ) ),
     662            ),
     663            'tag_slug__in order'                           => array(
     664                'query_vars_1' => array( 'tag_slug__in' => array( 'foo', 'bar' ) ),
     665                'query_vars_2' => array( 'tag_slug__in' => array( 'bar', 'foo' ) ),
     666            ),
     667            'tag_slug__in non-unique vs unique'            => array(
     668                'query_vars_1' => array( 'tag_slug__in' => array( 'foo', 'bar', 'bar' ) ),
     669                'query_vars_2' => array( 'tag_slug__in' => array( 'foo', 'bar' ) ),
     670            ),
     671            'tag_slug__and order'                          => array(
     672                'query_vars_1' => array( 'tag_slug__and' => array( 'foo', 'bar' ) ),
     673                'query_vars_2' => array( 'tag_slug__and' => array( 'bar', 'foo' ) ),
     674            ),
     675            'tag_slug__and non-unique'                     => array(
     676                'query_vars_1' => array( 'tag_slug__and' => array( 'foo', 'bar', 'foo' ) ),
     677                'query_vars_2' => array( 'tag_slug__and' => array( 'bar', 'foo' ) ),
     678            ),
     679            'tag__in queries different order (array)'      => array(
     680                'query_vars_1' => array( 'tag__in' => array( 1, 2 ) ),
     681                'query_vars_2' => array( 'tag__in' => array( 2, 1 ) ),
     682            ),
     683            'tag__in queries non-unique array'             => array(
     684                'query_vars_1' => array( 'tag__in' => array( 1, 2, 1 ) ),
     685                'query_vars_2' => array( 'tag__in' => array( 2, 1 ) ),
     686            ),
     687            'tag__in queries int vs string'                => array(
     688                'query_vars_1' => array( 'tag__in' => array( 2, 1 ) ),
     689                'query_vars_2' => array( 'tag__in' => array( '2', '1' ) ),
     690            ),
     691            'tag__and queries different order (array)'     => array(
     692                'query_vars_1' => array( 'tag__and' => array( 1, 2 ) ),
     693                'query_vars_2' => array( 'tag__and' => array( 2, 1 ) ),
     694            ),
     695            'tag__and queries non-unique array'            => array(
     696                'query_vars_1' => array( 'tag__and' => array( 1, 2, 2 ) ),
     697                'query_vars_2' => array( 'tag__and' => array( 2, 1 ) ),
     698            ),
     699            'tag__not_in queries different order (array)'  => array(
     700                'query_vars_1' => array( 'tag__not_in' => array( 1, 2 ) ),
     701                'query_vars_2' => array( 'tag__not_in' => array( 2, 1 ) ),
     702            ),
     703            'tag__not_in queries non-unique array'         => array(
     704                'query_vars_1' => array( 'tag__not_in' => array( 1, 2, 2 ) ),
     705                'query_vars_2' => array( 'tag__not_in' => array( 1, 2 ) ),
     706            ),
     707            'tag__not_in queries int vs string (array)'    => array(
     708                'query_vars_1' => array( 'tag__not_in' => array( '1' ) ),
     709                'query_vars_2' => array( 'tag__not_in' => array( 1 ) ),
     710            ),
     711            'cache parameters'                             => array(
    330712                'query_vars1' => array(
    331713                    'update_post_meta_cache' => true,
  • trunk/tests/phpunit/tests/query/parseQuery.php

    r53891 r59766  
    152152        );
    153153
    154         $this->assertSame( '1,-1', $q->query_vars['cat'] );
     154        $this->assertSame( '-1,1', $q->query_vars['cat'] );
    155155    }
    156156
  • trunk/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r59630 r59766  
    15751575        $this->assertNotContains( $id3, $ids );
    15761576
    1577         $this->assertPostsWhere( " AND {posts}.ID NOT IN ($id3,$id2) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
     1577        $this->assertPostsWhere( " AND {posts}.ID NOT IN ($id2,$id3) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" );
    15781578    }
    15791579
Note: See TracChangeset for help on using the changeset viewer.