diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index f95cb6149fc43..f088bda6da6d0 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -474,6 +474,16 @@ class WP_Query { private $compat_methods = array( 'init_query_flags', 'parse_tax_query' ); + /** + * The cache key generated by the query. + * + * The cache key is generated by the method ::generate_cache_key() after the + * query has been normalized. + * + * @var string + */ + private $query_cache_key = ''; + /** * Resets query flags to false. * @@ -1101,7 +1111,8 @@ public function parse_query( $query = '' ) { if ( ! empty( $qv['post_type'] ) ) { if ( is_array( $qv['post_type'] ) ) { - $qv['post_type'] = array_map( 'sanitize_key', $qv['post_type'] ); + $qv['post_type'] = array_map( 'sanitize_key', array_unique( $qv['post_type'] ) ); + sort( $qv['post_type'] ); } else { $qv['post_type'] = sanitize_key( $qv['post_type'] ); } @@ -1109,7 +1120,8 @@ public function parse_query( $query = '' ) { if ( ! empty( $qv['post_status'] ) ) { if ( is_array( $qv['post_status'] ) ) { - $qv['post_status'] = array_map( 'sanitize_key', $qv['post_status'] ); + $qv['post_status'] = array_map( 'sanitize_key', array_unique( $qv['post_status'] ) ); + sort( $qv['post_status'] ); } else { $qv['post_status'] = preg_replace( '|[^a-z0-9_,-]|', '', $qv['post_status'] ); } @@ -1182,9 +1194,12 @@ public function parse_tax_query( &$q ) { $term = $q[ $t->query_var ]; - if ( is_array( $term ) ) { - $term = implode( ',', $term ); + if ( ! is_array( $term ) ) { + $term = explode( ',', $term ); + $term = array_map( 'trim', $term ); } + sort( $term ); + $term = implode( ',', $term ); if ( str_contains( $term, '+' ) ) { $terms = preg_split( '/[+]+/', $term ); @@ -1220,7 +1235,8 @@ public function parse_tax_query( &$q ) { $cat_array = preg_split( '/[,\s]+/', urldecode( $q['cat'] ) ); $cat_array = array_map( 'intval', $cat_array ); - $q['cat'] = implode( ',', $cat_array ); + sort( $cat_array ); + $q['cat'] = implode( ',', $cat_array ); foreach ( $cat_array as $cat ) { if ( $cat > 0 ) { @@ -1262,7 +1278,8 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['category__in'] ) ) { $q['category__in'] = array_map( 'absint', array_unique( (array) $q['category__in'] ) ); - $tax_query[] = array( + sort( $q['category__in'] ); + $tax_query[] = array( 'taxonomy' => 'category', 'terms' => $q['category__in'], 'field' => 'term_id', @@ -1272,6 +1289,7 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['category__not_in'] ) ) { $q['category__not_in'] = array_map( 'absint', array_unique( (array) $q['category__not_in'] ) ); + sort( $q['category__not_in'] ); $tax_query[] = array( 'taxonomy' => 'category', 'terms' => $q['category__not_in'], @@ -1282,7 +1300,8 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['category__and'] ) ) { $q['category__and'] = array_map( 'absint', array_unique( (array) $q['category__and'] ) ); - $tax_query[] = array( + sort( $q['category__and'] ); + $tax_query[] = array( 'taxonomy' => 'category', 'terms' => $q['category__and'], 'field' => 'term_id', @@ -1300,10 +1319,12 @@ public function parse_tax_query( &$q ) { if ( '' !== $q['tag'] && ! $this->is_singular && $this->query_vars_changed ) { if ( str_contains( $q['tag'], ',' ) ) { + // @todo Handle normalizing `tag` query string. $tags = preg_split( '/[,\r\n\t ]+/', $q['tag'] ); foreach ( (array) $tags as $tag ) { $tag = sanitize_term_field( 'slug', $tag, 0, 'post_tag', 'db' ); $q['tag_slug__in'][] = $tag; + sort( $q['tag_slug__in'] ); } } elseif ( preg_match( '/[+\r\n\t ]+/', $q['tag'] ) || ! empty( $q['cat'] ) ) { $tags = preg_split( '/[+\r\n\t ]+/', $q['tag'] ); @@ -1314,6 +1335,7 @@ public function parse_tax_query( &$q ) { } else { $q['tag'] = sanitize_term_field( 'slug', $q['tag'], 0, 'post_tag', 'db' ); $q['tag_slug__in'][] = $q['tag']; + sort( $q['tag_slug__in'] ); } } @@ -1327,7 +1349,8 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['tag__in'] ) ) { $q['tag__in'] = array_map( 'absint', array_unique( (array) $q['tag__in'] ) ); - $tax_query[] = array( + sort( $q['tag__in'] ); + $tax_query[] = array( 'taxonomy' => 'post_tag', 'terms' => $q['tag__in'], ); @@ -1335,6 +1358,7 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['tag__not_in'] ) ) { $q['tag__not_in'] = array_map( 'absint', array_unique( (array) $q['tag__not_in'] ) ); + sort( $q['tag__not_in'] ); $tax_query[] = array( 'taxonomy' => 'post_tag', 'terms' => $q['tag__not_in'], @@ -1344,7 +1368,8 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['tag__and'] ) ) { $q['tag__and'] = array_map( 'absint', array_unique( (array) $q['tag__and'] ) ); - $tax_query[] = array( + sort( $q['tag__and'] ); + $tax_query[] = array( 'taxonomy' => 'post_tag', 'terms' => $q['tag__and'], 'operator' => 'AND', @@ -1353,7 +1378,8 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['tag_slug__in'] ) ) { $q['tag_slug__in'] = array_map( 'sanitize_title_for_query', array_unique( (array) $q['tag_slug__in'] ) ); - $tax_query[] = array( + sort( $q['tag_slug__in'] ); + $tax_query[] = array( 'taxonomy' => 'post_tag', 'terms' => $q['tag_slug__in'], 'field' => 'slug', @@ -1362,7 +1388,8 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['tag_slug__and'] ) ) { $q['tag_slug__and'] = array_map( 'sanitize_title_for_query', array_unique( (array) $q['tag_slug__and'] ) ); - $tax_query[] = array( + sort( $q['tag_slug__and'] ); + $tax_query[] = array( 'taxonomy' => 'post_tag', 'terms' => $q['tag_slug__and'], 'field' => 'slug', @@ -2186,8 +2213,11 @@ public function get_posts() { $where .= " AND {$wpdb->posts}.post_name = '" . $q['attachment'] . "'"; } elseif ( is_array( $q['post_name__in'] ) && ! empty( $q['post_name__in'] ) ) { $q['post_name__in'] = array_map( 'sanitize_title_for_query', $q['post_name__in'] ); - $post_name__in = "'" . implode( "','", $q['post_name__in'] ) . "'"; - $where .= " AND {$wpdb->posts}.post_name IN ($post_name__in)"; + // Duplicate array before sorting to allow for the orderby clause. + $post_name__in_for_where = array_unique( $q['post_name__in'] ); + sort( $post_name__in_for_where ); + $post_name__in = "'" . implode( "','", $post_name__in_for_where ) . "'"; + $where .= " AND {$wpdb->posts}.post_name IN ($post_name__in)"; } // If an attachment is requested by number, let it supersede any post number. @@ -2199,9 +2229,14 @@ public function get_posts() { if ( $q['p'] ) { $where .= " AND {$wpdb->posts}.ID = " . $q['p']; } elseif ( $q['post__in'] ) { - $post__in = implode( ',', array_map( 'absint', $q['post__in'] ) ); + // Duplicate array before sorting to allow for the orderby clause. + $post__in_for_where = $q['post__in']; + $post__in_for_where = array_unique( array_map( 'absint', $post__in_for_where ) ); + sort( $post__in_for_where ); + $post__in = implode( ',', array_map( 'absint', $post__in_for_where ) ); $where .= " AND {$wpdb->posts}.ID IN ($post__in)"; } elseif ( $q['post__not_in'] ) { + sort( $q['post__not_in'] ); $post__not_in = implode( ',', array_map( 'absint', $q['post__not_in'] ) ); $where .= " AND {$wpdb->posts}.ID NOT IN ($post__not_in)"; } @@ -2209,9 +2244,14 @@ public function get_posts() { if ( is_numeric( $q['post_parent'] ) ) { $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_parent = %d ", $q['post_parent'] ); } elseif ( $q['post_parent__in'] ) { - $post_parent__in = implode( ',', array_map( 'absint', $q['post_parent__in'] ) ); + // Duplicate array before sorting to allow for the orderby clause. + $post_parent__in_for_where = $q['post_parent__in']; + $post_parent__in_for_where = array_unique( array_map( 'absint', $post_parent__in_for_where ) ); + sort( $post_parent__in_for_where ); + $post_parent__in = implode( ',', array_map( 'absint', $post_parent__in_for_where ) ); $where .= " AND {$wpdb->posts}.post_parent IN ($post_parent__in)"; } elseif ( $q['post_parent__not_in'] ) { + sort( $q['post_parent__not_in'] ); $post_parent__not_in = implode( ',', array_map( 'absint', $q['post_parent__not_in'] ) ); $where .= " AND {$wpdb->posts}.post_parent NOT IN ($post_parent__not_in)"; } @@ -2341,6 +2381,7 @@ public function get_posts() { if ( ! empty( $q['author'] ) && '0' != $q['author'] ) { $q['author'] = addslashes_gpc( '' . urldecode( $q['author'] ) ); $authors = array_unique( array_map( 'intval', preg_split( '/[,\s]+/', $q['author'] ) ) ); + sort( $authors ); foreach ( $authors as $author ) { $key = $author > 0 ? 'author__in' : 'author__not_in'; $q[ $key ][] = abs( $author ); @@ -2349,9 +2390,17 @@ public function get_posts() { } if ( ! empty( $q['author__not_in'] ) ) { - $author__not_in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__not_in'] ) ) ); + if ( is_array( $q['author__not_in'] ) ) { + $q['author__not_in'] = array_unique( array_map( 'absint', $q['author__not_in'] ) ); + sort( $q['author__not_in'] ); + } + $author__not_in = implode( ',', (array) $q['author__not_in'] ); $where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) "; } elseif ( ! empty( $q['author__in'] ) ) { + if ( is_array( $q['author__in'] ) ) { + $q['author__in'] = array_unique( array_map( 'absint', $q['author__in'] ) ); + sort( $q['author__in'] ); + } $author__in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__in'] ) ) ); $where .= " AND {$wpdb->posts}.post_author IN ($author__in) "; } @@ -2588,6 +2637,7 @@ public function get_posts() { if ( ! is_array( $q_status ) ) { $q_status = explode( ',', $q_status ); } + sort( $q_status ); $r_status = array(); $p_status = array(); $e_status = array(); @@ -4902,6 +4952,33 @@ protected function generate_cache_key( array $args, $sql ) { // Sort post types to ensure same cache key generation. sort( $args['post_type'] ); + /* + * Sort arrays that can be used for ordering prior to cache key generation. + * + * These arrays are sorted in the query generator for the purposes of the + * WHERE clause but the arguments are not modified as they can be used for + * the orderby clase. + * + * Their use in the orderby clause will generate a different SQL query so + * they can be sorted for the cache key generation. + */ + $sortable_arrays_with_int_values = array( + 'post__in', + 'post_parent__in', + ); + foreach ( $sortable_arrays_with_int_values as $key ) { + if ( isset( $args[ $key ] ) && is_array( $args[ $key ] ) ) { + $args[ $key ] = array_unique( array_map( 'absint', $args[ $key ] ) ); + sort( $args[ $key ] ); + } + } + + // Sort and unique the 'post_name__in' for cache key generation. + if ( isset( $args['post_name__in'] ) && is_array( $args['post_name__in'] ) ) { + $args['post_name__in'] = array_unique( $args['post_name__in'] ); + sort( $args['post_name__in'] ); + } + if ( isset( $args['post_status'] ) ) { $args['post_status'] = (array) $args['post_status']; // Sort post status to ensure same cache key generation. @@ -4942,7 +5019,8 @@ static function ( &$value ) use ( $wpdb, $placeholder ) { $last_changed .= wp_cache_get_last_changed( 'terms' ); } - return "wp_query:$key:$last_changed"; + $this->query_cache_key = "wp_query:$key:$last_changed"; + return $this->query_cache_key; } /** diff --git a/tests/phpunit/tests/query/cacheResults.php b/tests/phpunit/tests/query/cacheResults.php index 1912f69735034..84c17eb8720b9 100644 --- a/tests/phpunit/tests/query/cacheResults.php +++ b/tests/phpunit/tests/query/cacheResults.php @@ -200,8 +200,81 @@ public function test_generate_cache_key_unregister_post_type() { $this->assertNotSame( $cache_key_1, $cache_key_2, 'Cache key should differ after unregistering post type.' ); } + /** + * @ticket 59516 + * + * @covers WP_Query::generate_cache_key + * + * @dataProvider data_orderby_clauses_are_not_normalized + */ + public function test_orderby_clauses_are_not_normalized( $query_vars1, $query_vars2 ) { + global $wpdb; + + $this->assertArrayHasKey( 'orderby', $query_vars1, 'First query vars should have orderby.' ); + $this->assertArrayHasKey( 'orderby', $query_vars2, 'Second query vars should have orderby.' ); + + $fields = "{$wpdb->posts}.ID"; + $query1 = new WP_Query( $query_vars1 ); + $request1 = str_replace( $fields, "{$wpdb->posts}.*", $query1->request ); + + $query2 = new WP_Query( $query_vars2 ); + $request2 = str_replace( $fields, "{$wpdb->posts}.*", $query2->request ); + + $reflection_q1 = new ReflectionProperty( $query1, 'query_cache_key' ); + $reflection_q1->setAccessible( true ); + + $reflection_q2 = new ReflectionProperty( $query2, 'query_cache_key' ); + $reflection_q2->setAccessible( true ); + + $this->assertNotSame( $request1, $request2, 'Queries should not match' ); + + $cache_key_1 = $reflection_q1->getValue( $query1 ); + $cache_key_2 = $reflection_q2->getValue( $query2 ); + + $this->assertNotSame( $cache_key_1, $cache_key_2, 'Cache key should differ.' ); + $this->assertNotEmpty( $cache_key_1, 'Cache key for query one should not be empty.' ); + $this->assertNotEmpty( $cache_key_2, 'Cache key for query two should not be empty.' ); + } + + public function data_orderby_clauses_are_not_normalized() { + return array( + 'orderby post__in' => array( + 'query_vars1' => array( + 'post__in' => array( 1, 2, 3, 4, 5 ), + 'orderby' => 'post__in', + ), + 'query_vars2' => array( + 'post__in' => array( 5, 4, 3, 2, 1 ), + 'orderby' => 'post__in', + ), + ), + 'post parent in order' => array( + 'query_vars1' => array( + 'post_parent__in' => array( 1, 2, 3, 4, 5 ), + 'orderby' => 'post_parent__in', + ), + 'query_vars2' => array( + 'post_parent__in' => array( 5, 4, 3, 2, 1 ), + 'orderby' => 'post_parent__in', + ), + ), + 'orderby post_name__in' => array( + 'query_vars1' => array( + 'post_name__in' => array( 'elphaba', 'glinda', 'the-wizard-of-oz', 'doctor-dillamond' ), + 'orderby' => 'post_name__in', + ), + 'query_vars2' => array( + 'post_name__in' => array( 'doctor-dillamond', 'elphaba', 'the-wizard-of-oz', 'glinda' ), + 'orderby' => 'post_name__in', + ), + ), + ); + } + + /** * @ticket 59442 + * @ticket 59516 * * @covers WP_Query::generate_cache_key * @@ -217,15 +290,20 @@ public function test_generate_cache_key_normalize( $query_vars1, $query_vars2 ) $query2 = new WP_Query( $query_vars2 ); $request2 = str_replace( $fields, "{$wpdb->posts}.*", $query2->request ); - $reflection = new ReflectionMethod( $query1, 'generate_cache_key' ); - $reflection->setAccessible( true ); + $reflection_q1 = new ReflectionProperty( $query1, 'query_cache_key' ); + $reflection_q1->setAccessible( true ); + + $reflection_q2 = new ReflectionProperty( $query2, 'query_cache_key' ); + $reflection_q2->setAccessible( true ); $this->assertSame( $request1, $request2, 'Queries should match' ); - $cache_key_1 = $reflection->invoke( $query1, $query_vars1, $request1 ); - $cache_key_2 = $reflection->invoke( $query1, $query_vars2, $request2 ); + $cache_key_1 = $reflection_q1->getValue( $query1 ); + $cache_key_2 = $reflection_q2->getValue( $query2 ); - $this->assertSame( $cache_key_1, $cache_key_2, 'Cache key differs the same paramters.' ); + $this->assertSame( $cache_key_1, $cache_key_2, 'Cache key differs the same effective parameters.' ); + $this->assertNotEmpty( $cache_key_1, 'Cache key for query one should not be empty.' ); + $this->assertNotEmpty( $cache_key_2, 'Cache key for query two should not be empty.' ); } /** @@ -280,19 +358,19 @@ public function test_query_cache( $args ) { */ public function data_query_cache_duplicate() { return array( - 'post type empty' => array( + 'post type empty' => array( 'query_vars1' => array( 'post_type' => '' ), 'query_vars2' => array( 'post_type' => 'post' ), ), - 'post type array' => array( + 'post type array' => array( 'query_vars1' => array( 'post_type' => array( 'page' ) ), 'query_vars2' => array( 'post_type' => 'page' ), ), - 'orderby empty' => array( + 'orderby empty' => array( 'query_vars1' => array( 'orderby' => null ), 'query_vars2' => array( 'orderby' => 'date' ), ), - 'different order parameter' => array( + 'different order parameter' => array( 'query_vars1' => array( 'post_type' => 'post', 'posts_per_page' => 15, @@ -302,31 +380,241 @@ public function data_query_cache_duplicate() { 'post_type' => 'post', ), ), - 'same args' => array( + 'same args' => array( 'query_vars1' => array( 'post_type' => 'post' ), 'query_vars2' => array( 'post_type' => 'post' ), ), - 'same args any' => array( + 'same args any' => array( 'query_vars1' => array( 'post_type' => 'any' ), 'query_vars2' => array( 'post_type' => 'any' ), ), - 'any and post types' => array( + 'any and post types' => array( 'query_vars1' => array( 'post_type' => 'any' ), 'query_vars2' => array( 'post_type' => array( 'post', 'page', 'attachment' ) ), ), - 'different order post type' => array( + 'different order post type' => array( 'query_vars1' => array( 'post_type' => array( 'post', 'page' ) ), 'query_vars2' => array( 'post_type' => array( 'page', 'post' ) ), ), - 'post status array' => array( + 'non-unique post type' => array( + 'query_vars1' => array( 'post_type' => array( 'post', 'page' ) ), + 'query_vars2' => array( 'post_type' => array( 'page', 'post', 'page' ) ), + ), + 'post status array' => array( 'query_vars1' => array( 'post_status' => 'publish' ), 'query_vars2' => array( 'post_status' => array( 'publish' ) ), ), - 'post status order' => array( + 'post status order' => array( 'query_vars1' => array( 'post_status' => array( 'draft', 'publish' ) ), 'query_vars2' => array( 'post_status' => array( 'publish', 'draft' ) ), ), - 'cache parameters' => array( + 'non-unique post status' => array( + 'query_vars1' => array( 'post_status' => array( 'draft', 'publish' ) ), + 'query_vars2' => array( 'post_status' => array( 'draft', 'publish', 'draft' ) ), + ), + 'post id int vs string' => array( + 'query_vars1' => array( 'p' => '1' ), + 'query_vars2' => array( 'p' => 1 ), + ), + 'page id int vs string' => array( + 'query_vars1' => array( 'page_id' => '2' ), + 'query_vars2' => array( 'page_id' => 2 ), + ), + 'attachment id int vs string' => array( + 'query_vars1' => array( 'attachment_id' => '3' ), + 'query_vars2' => array( 'attachment_id' => 3 ), + ), + 'date and time values int vs string' => array( + 'query_vars1' => array( + 'year' => '2013', + 'monthnum' => '12', + 'day' => '12', + 'hour' => '12', + 'minute' => '12', + 'second' => '12', + ), + 'query_vars2' => array( + 'year' => 2013, + 'monthnum' => 12, + 'day' => 12, + 'hour' => 12, + 'minute' => 12, + 'second' => 12, + ), + ), + 'offset value int vs string' => array( + 'query_vars1' => array( 'offset' => '5' ), + 'query_vars2' => array( 'offset' => 5 ), + ), + 'posts per page value int vs string' => array( + 'query_vars1' => array( 'posts_per_page' => '5' ), + 'query_vars2' => array( 'posts_per_page' => 5 ), + ), + 'paged value int vs string' => array( + 'query_vars1' => array( 'paged' => '2' ), + 'query_vars2' => array( 'paged' => 2 ), + ), + 'menu_order value int vs string' => array( + 'query_vars1' => array( 'menu_order' => '2' ), + 'query_vars2' => array( 'menu_order' => 2 ), + ), + 'post__in different order' => array( + 'query_vars1' => array( 'post__in' => array( 1, 2, 3, 4, 5 ) ), + 'query_vars2' => array( 'post__in' => array( 5, 4, 3, 2, 1 ) ), + ), + 'post__in non-unique' => array( + 'query_vars1' => array( 'post__in' => array( 1, 2, 3, 4, 5 ) ), + 'query_vars2' => array( 'post__in' => array( 1, 2, 3, 4, 5, 1, 2, 3 ) ), + ), + 'post_parent__in different order' => array( + 'query_vars1' => array( 'post_parent__in' => array( 1, 2, 3, 4, 5 ) ), + 'query_vars2' => array( 'post_parent__in' => array( 5, 4, 3, 2, 1 ) ), + ), + 'post_parent__in non-unique' => array( + 'query_vars1' => array( 'post_parent__in' => array( 1, 2, 3, 4, 5 ) ), + 'query_vars2' => array( 'post_parent__in' => array( 1, 2, 3, 4, 5, 1, 2, 3 ) ), + ), + 'post_name__in different order' => array( + 'query_vars1' => array( 'post_name__in' => array( 'elphaba', 'glinda', 'the-wizard-of-oz', 'doctor-dillamond' ) ), + 'query_vars2' => array( 'post_name__in' => array( 'doctor-dillamond', 'elphaba', 'the-wizard-of-oz', 'glinda' ) ), + ), + 'post_name__in non-unique' => array( + 'query_vars1' => array( 'post_name__in' => array( 'elphaba', 'glinda', 'the-wizard-of-oz', 'doctor-dillamond' ) ), + 'query_vars2' => array( 'post_name__in' => array( 'elphaba', 'glinda', 'elphaba', 'glinda', 'the-wizard-of-oz', 'doctor-dillamond' ) ), + ), + 'cat different order (array)' => array( + 'query_vars_1' => array( 'cat' => array( '1', '2' ) ), + 'query_vars_2' => array( 'cat' => array( '2', '1' ) ), + ), + 'cat different order (string)' => array( + 'query_vars_1' => array( 'cat' => '2,1' ), + 'query_vars_2' => array( 'cat' => '1,2' ), + ), + 'cat queries int vs string' => array( + 'query_vars_1' => array( 'cat' => '2' ), + 'query_vars_2' => array( 'cat' => 2 ), + ), + 'category__in queries different order (array)' => array( + 'query_vars_1' => array( 'category__in' => array( '1', '2' ) ), + 'query_vars_2' => array( 'category__in' => array( '2', '1' ) ), + ), + 'category__in queries with non-unique array' => array( + 'query_vars_1' => array( 'category__in' => array( '1', '1' ) ), + 'query_vars_2' => array( 'category__in' => array( '1' ) ), + ), + 'category__in queries string vs array (array)' => array( + 'query_vars_1' => array( 'category__in' => array( '1' ) ), + 'query_vars_2' => array( 'category__in' => array( 1 ) ), + ), + 'category__not_in different order (array)' => array( + 'query_vars_1' => array( 'category__not_in' => array( '1', '2' ) ), + 'query_vars_2' => array( 'category__not_in' => array( '2', '1' ) ), + ), + 'category__not_in with non-unique array' => array( + 'query_vars_1' => array( 'category__not_in' => array( '1', '1' ) ), + 'query_vars_2' => array( 'category__not_in' => array( '1' ) ), + ), + 'category__not_in queries string vs array (array)' => array( + 'query_vars_1' => array( 'category__not_in' => array( '1' ) ), + 'query_vars_2' => array( 'category__not_in' => array( 1 ) ), + ), + 'category__and queries width different order (array)' => array( + 'query_vars_1' => array( 'category__and' => array( '1', '2' ) ), + 'query_vars_2' => array( 'category__and' => array( '2', '1' ) ), + ), + 'category__and with non-unique array' => array( + 'query_vars_1' => array( 'category__and' => array( '1', '1', '2' ) ), + 'query_vars_2' => array( 'category__and' => array( '1', '2' ) ), + ), + 'category__and queries string vs array (array)' => array( + 'query_vars_1' => array( 'category__and' => array( '1', '2' ) ), + 'query_vars_2' => array( 'category__and' => array( 1, 2 ) ), + ), + 'author queries different order (string)' => array( + 'query_vars_1' => array( 'author' => '1,2' ), + 'query_vars_2' => array( 'author' => '2,1' ), + ), + 'author with non-unique string' => array( + 'query_vars_1' => array( 'author' => '1,1' ), + 'query_vars_2' => array( 'author' => '1' ), + ), + 'author queries int vs string (string)' => array( + 'query_vars_1' => array( 'author' => 1 ), + 'query_vars_2' => array( 'author' => '1' ), + ), + 'author queries int vs string (array)' => array( + 'query_vars_1' => array( 'author' => array( 1 ) ), + 'query_vars_2' => array( 'author' => array( '1' ) ), + ), + 'author__in different order' => array( + 'query_vars_1' => array( 'author__in' => array( 1, 2 ) ), + 'query_vars_2' => array( 'author__in' => array( 2, 1 ) ), + ), + 'author__in with non-unique array' => array( + 'query_vars_1' => array( 'author__in' => array( 1, 1, 2 ) ), + 'query_vars_2' => array( 'author__in' => array( 1, 2 ) ), + ), + 'author__in queries int vs string (array)' => array( + 'query_vars_1' => array( 'author__in' => array( 1 ) ), + 'query_vars_2' => array( 'author__in' => array( '1' ) ), + ), + 'author__not_in different order (array)' => array( + 'query_vars_1' => array( 'author__not_in' => array( 1, 2 ) ), + 'query_vars_2' => array( 'author__not_in' => array( 2, 1 ) ), + ), + 'author__not_in queries int vs string (array)' => array( + 'query_vars_1' => array( 'author__not_in' => array( 1 ) ), + 'query_vars_2' => array( 'author__not_in' => array( '1' ) ), + ), + 'tag_slug__in order' => array( + 'query_vars_1' => array( 'tag_slug__in' => array( 'foo', 'bar' ) ), + 'query_vars_2' => array( 'tag_slug__in' => array( 'bar', 'foo' ) ), + ), + 'tag_slug__in non-unique vs unique' => array( + 'query_vars_1' => array( 'tag_slug__in' => array( 'foo', 'bar', 'bar' ) ), + 'query_vars_2' => array( 'tag_slug__in' => array( 'foo', 'bar' ) ), + ), + 'tag_slug__and order' => array( + 'query_vars_1' => array( 'tag_slug__and' => array( 'foo', 'bar' ) ), + 'query_vars_2' => array( 'tag_slug__and' => array( 'bar', 'foo' ) ), + ), + 'tag_slug__and non-unique' => array( + 'query_vars_1' => array( 'tag_slug__and' => array( 'foo', 'bar', 'foo' ) ), + 'query_vars_2' => array( 'tag_slug__and' => array( 'bar', 'foo' ) ), + ), + 'tag__in queries different order (array)' => array( + 'query_vars_1' => array( 'tag__in' => array( 1, 2 ) ), + 'query_vars_2' => array( 'tag__in' => array( 2, 1 ) ), + ), + 'tag__in queries non-unique array' => array( + 'query_vars_1' => array( 'tag__in' => array( 1, 2, 1 ) ), + 'query_vars_2' => array( 'tag__in' => array( 2, 1 ) ), + ), + 'tag__in queries int vs string' => array( + 'query_vars_1' => array( 'tag__in' => array( 2, 1 ) ), + 'query_vars_2' => array( 'tag__in' => array( '2', '1' ) ), + ), + 'tag__and queries different order (array)' => array( + 'query_vars_1' => array( 'tag__and' => array( 1, 2 ) ), + 'query_vars_2' => array( 'tag__and' => array( 2, 1 ) ), + ), + 'tag__and queries non-unique array' => array( + 'query_vars_1' => array( 'tag__and' => array( 1, 2, 2 ) ), + 'query_vars_2' => array( 'tag__and' => array( 2, 1 ) ), + ), + 'tag__not_in queries different order (array)' => array( + 'query_vars_1' => array( 'tag__not_in' => array( 1, 2 ) ), + 'query_vars_2' => array( 'tag__not_in' => array( 2, 1 ) ), + ), + 'tag__not_in queries non-unique array' => array( + 'query_vars_1' => array( 'tag__not_in' => array( 1, 2, 2 ) ), + 'query_vars_2' => array( 'tag__not_in' => array( 1, 2 ) ), + ), + 'tag__not_in queries int vs string (array)' => array( + 'query_vars_1' => array( 'tag__not_in' => array( '1' ) ), + 'query_vars_2' => array( 'tag__not_in' => array( 1 ) ), + ), + 'cache parameters' => array( 'query_vars1' => array( 'update_post_meta_cache' => true, 'update_post_term_cache' => true, diff --git a/tests/phpunit/tests/query/parseQuery.php b/tests/phpunit/tests/query/parseQuery.php index bbf3f1217fb2e..94ced1ecd6e75 100644 --- a/tests/phpunit/tests/query/parseQuery.php +++ b/tests/phpunit/tests/query/parseQuery.php @@ -151,7 +151,7 @@ public function test_parse_query_cat_array_mixed() { ) ); - $this->assertSame( '1,-1', $q->query_vars['cat'] ); + $this->assertSame( '-1,1', $q->query_vars['cat'] ); } /** diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index a1b8de0474359..c42fded13222a 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -1574,7 +1574,7 @@ public function test_get_items_not_sticky_with_exclude() { $this->assertNotContains( $id2, $ids ); $this->assertNotContains( $id3, $ids ); - $this->assertPostsWhere( " AND {posts}.ID NOT IN ($id3,$id2) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" ); + $this->assertPostsWhere( " AND {posts}.ID NOT IN ($id2,$id3) AND {posts}.post_type = 'post' AND (({posts}.post_status = 'publish'))" ); } public function test_get_items_not_sticky_with_exclude_no_sticky_posts() {