diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index d25493dc5c4e1..ad660a665ea6d 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -1101,6 +1101,7 @@ 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'] ); + sort( $qv['post_type'] ); } else { $qv['post_type'] = sanitize_key( $qv['post_type'] ); } @@ -1109,6 +1110,7 @@ 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'] ); + sort( $qv['post_status'] ); } else { $qv['post_status'] = preg_replace( '|[^a-z0-9_,-]|', '', $qv['post_status'] ); } @@ -1181,9 +1183,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 ); @@ -1219,7 +1224,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 ) { @@ -1261,7 +1267,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', @@ -1271,6 +1278,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'], @@ -1303,6 +1311,7 @@ public function parse_tax_query( &$q ) { 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'] ); @@ -1313,6 +1322,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'] ); } } @@ -1326,6 +1336,7 @@ public function parse_tax_query( &$q ) { if ( ! empty( $q['tag__in'] ) ) { $q['tag__in'] = array_map( 'absint', array_unique( (array) $q['tag__in'] ) ); + sort( $q['tag__in'] ); $tax_query[] = array( 'taxonomy' => 'post_tag', 'terms' => $q['tag__in'], @@ -1334,6 +1345,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'], @@ -1352,7 +1364,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', @@ -2185,8 +2198,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 = $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. @@ -2198,9 +2214,13 @@ 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'] ) ); + $post__in_for_where = $q['post__in']; + // Duplicate array before sorting to allow for the orderby clause. + 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)"; } @@ -2208,9 +2228,13 @@ 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']; + 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)"; } @@ -2348,9 +2372,15 @@ public function get_posts() { } if ( ! empty( $q['author__not_in'] ) ) { + if ( is_array( $q['author__not_in'] ) ) { + sort( $q['author__not_in'] ); + } $author__not_in = implode( ',', array_map( 'absint', array_unique( (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'] ) ) { + 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) "; } @@ -2587,6 +2617,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(); 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 31ed21d6782b7..fc8105afab726 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() {