wp/wp-includes/class-wp-query.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
--- a/wp/wp-includes/class-wp-query.php	Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/class-wp-query.php	Fri Sep 05 18:40:08 2025 +0200
@@ -15,6 +15,7 @@
  * @since 1.5.0
  * @since 4.5.0 Removed the `$comments_popup` property.
  */
+#[AllowDynamicProperties]
 class WP_Query {
 
 	/**
@@ -37,7 +38,7 @@
 	 * Taxonomy query, as passed to get_tax_sql().
 	 *
 	 * @since 3.1.0
-	 * @var WP_Tax_Query A taxonomy query instance.
+	 * @var WP_Tax_Query|null A taxonomy query instance.
 	 */
 	public $tax_query;
 
@@ -108,6 +109,14 @@
 	public $current_post = -1;
 
 	/**
+	 * Whether the caller is before the loop.
+	 *
+	 * @since 6.3.0
+	 * @var bool
+	 */
+	public $before_loop = true;
+
+	/**
 	 * Whether the loop has started and the caller is in the loop.
 	 *
 	 * @since 2.0.0
@@ -445,6 +454,14 @@
 	public $thumbnails_cached = false;
 
 	/**
+	 * Controls whether an attachment query should include filenames or not.
+	 *
+	 * @since 6.0.3
+	 * @var bool
+	 */
+	protected $allow_query_attachment_by_filename = false;
+
+	/**
 	 * Cached list of search stopwords.
 	 *
 	 * @since 3.7.0
@@ -508,6 +525,7 @@
 		$this->post_count   = 0;
 		$this->current_post = -1;
 		$this->in_the_loop  = false;
+		$this->before_loop  = true;
 		unset( $this->request );
 		unset( $this->post );
 		unset( $this->comments );
@@ -522,7 +540,7 @@
 	}
 
 	/**
-	 * Reparse the query vars.
+	 * Reparses the query vars.
 	 *
 	 * @since 1.5.0
 	 */
@@ -601,6 +619,7 @@
 			'post_parent__not_in',
 			'author__in',
 			'author__not_in',
+			'search_columns',
 		);
 
 		foreach ( $array_keys as $key ) {
@@ -613,7 +632,7 @@
 	}
 
 	/**
-	 * Parse a query string and set query type booleans.
+	 * Parses a query string and sets query type booleans.
 	 *
 	 * @since 1.5.0
 	 * @since 4.2.0 Introduced the ability to order by specific clauses of a `$meta_query`, by passing the clause's
@@ -627,141 +646,146 @@
 	 * @since 4.9.0 Introduced the `$comment_count` parameter.
 	 * @since 5.1.0 Introduced the `$meta_compare_key` parameter.
 	 * @since 5.3.0 Introduced the `$meta_type_key` parameter.
+	 * @since 6.1.0 Introduced the `$update_menu_item_cache` parameter.
+	 * @since 6.2.0 Introduced the `$search_columns` parameter.
 	 *
 	 * @param string|array $query {
 	 *     Optional. Array or string of Query parameters.
 	 *
-	 *     @type int             $attachment_id           Attachment post ID. Used for 'attachment' post_type.
-	 *     @type int|string      $author                  Author ID, or comma-separated list of IDs.
-	 *     @type string          $author_name             User 'user_nicename'.
-	 *     @type int[]           $author__in              An array of author IDs to query from.
-	 *     @type int[]           $author__not_in          An array of author IDs not to query from.
-	 *     @type bool            $cache_results           Whether to cache post information. Default true.
-	 *     @type int|string      $cat                     Category ID or comma-separated list of IDs (this or any children).
-	 *     @type int[]           $category__and           An array of category IDs (AND in).
-	 *     @type int[]           $category__in            An array of category IDs (OR in, no children).
-	 *     @type int[]           $category__not_in        An array of category IDs (NOT in).
-	 *     @type string          $category_name           Use category slug (not name, this or any children).
-	 *     @type array|int       $comment_count           Filter results by comment count. Provide an integer to match
-	 *                                                    comment count exactly. Provide an array with integer 'value'
-	 *                                                    and 'compare' operator ('=', '!=', '>', '>=', '<', '<=' ) to
-	 *                                                    compare against comment_count in a specific way.
-	 *     @type string          $comment_status          Comment status.
-	 *     @type int             $comments_per_page       The number of comments to return per page.
-	 *                                                    Default 'comments_per_page' option.
-	 *     @type array           $date_query              An associative array of WP_Date_Query arguments.
-	 *                                                    See WP_Date_Query::__construct().
-	 *     @type int             $day                     Day of the month. Default empty. Accepts numbers 1-31.
-	 *     @type bool            $exact                   Whether to search by exact keyword. Default false.
-	 *     @type string          $fields                  Post fields to query for. Accepts:
-	 *                                                    - '' Returns an array of complete post objects (`WP_Post[]`).
-	 *                                                    - 'ids' Returns an array of post IDs (`int[]`).
-	 *                                                    - 'id=>parent' Returns an associative array of parent post IDs,
-	 *                                                      keyed by post ID (`int[]`).
-	 *                                                    Default ''.
-	 *     @type int             $hour                    Hour of the day. Default empty. Accepts numbers 0-23.
-	 *     @type int|bool        $ignore_sticky_posts     Whether to ignore sticky posts or not. Setting this to false
-	 *                                                    excludes stickies from 'post__in'. Accepts 1|true, 0|false.
-	 *                                                    Default false.
-	 *     @type int             $m                       Combination YearMonth. Accepts any four-digit year and month
-	 *                                                    numbers 1-12. Default empty.
-	 *     @type string|string[] $meta_key                Meta key or keys to filter by.
-	 *     @type string|string[] $meta_value              Meta value or values to filter by.
-	 *     @type string          $meta_compare            MySQL operator used for comparing the meta value.
-	 *                                                    See WP_Meta_Query::__construct for accepted values and default value.
-	 *     @type string          $meta_compare_key        MySQL operator used for comparing the meta key.
-	 *                                                    See WP_Meta_Query::__construct for accepted values and default value.
-	 *     @type string          $meta_type               MySQL data type that the meta_value column will be CAST to for comparisons.
-	 *                                                    See WP_Meta_Query::__construct for accepted values and default value.
-	 *     @type string          $meta_type_key           MySQL data type that the meta_key column will be CAST to for comparisons.
-	 *                                                    See WP_Meta_Query::__construct for accepted values and default value.
-	 *     @type array           $meta_query              An associative array of WP_Meta_Query arguments.
-	 *                                                    See WP_Meta_Query::__construct for accepted values.
-	 *     @type int             $menu_order              The menu order of the posts.
-	 *     @type int             $minute                  Minute of the hour. Default empty. Accepts numbers 0-59.
-	 *     @type int             $monthnum                The two-digit month. Default empty. Accepts numbers 1-12.
-	 *     @type string          $name                    Post slug.
-	 *     @type bool            $nopaging                Show all posts (true) or paginate (false). Default false.
-	 *     @type bool            $no_found_rows           Whether to skip counting the total rows found. Enabling can improve
-	 *                                                    performance. Default false.
-	 *     @type int             $offset                  The number of posts to offset before retrieval.
-	 *     @type string          $order                   Designates ascending or descending order of posts. Default 'DESC'.
-	 *                                                    Accepts 'ASC', 'DESC'.
-	 *     @type string|array    $orderby                 Sort retrieved posts by parameter. One or more options may be passed.
-	 *                                                    To use 'meta_value', or 'meta_value_num', 'meta_key=keyname' must be
-	 *                                                    also be defined. To sort by a specific `$meta_query` clause, use that
-	 *                                                    clause's array key. Accepts:
-	 *                                                    - 'none'
-	 *                                                    - 'name'
-	 *                                                    - 'author'
-	 *                                                    - 'date'
-	 *                                                    - 'title'
-	 *                                                    - 'modified'
-	 *                                                    - 'menu_order'
-	 *                                                    - 'parent'
-	 *                                                    - 'ID'
-	 *                                                    - 'rand'
-	 *                                                    - 'relevance'
-	 *                                                    - 'RAND(x)' (where 'x' is an integer seed value)
-	 *                                                    - 'comment_count'
-	 *                                                    - 'meta_value'
-	 *                                                    - 'meta_value_num'
-	 *                                                    - 'post__in'
-	 *                                                    - 'post_name__in'
-	 *                                                    - 'post_parent__in'
-	 *                                                    - The array keys of `$meta_query`.
-	 *                                                    Default is 'date', except when a search is being performed, when
-	 *                                                    the default is 'relevance'.
-	 *     @type int             $p                       Post ID.
-	 *     @type int             $page                    Show the number of posts that would show up on page X of a
-	 *                                                    static front page.
-	 *     @type int             $paged                   The number of the current page.
-	 *     @type int             $page_id                 Page ID.
-	 *     @type string          $pagename                Page slug.
-	 *     @type string          $perm                    Show posts if user has the appropriate capability.
-	 *     @type string          $ping_status             Ping status.
-	 *     @type int[]           $post__in                An array of post IDs to retrieve, sticky posts will be included.
-	 *     @type int[]           $post__not_in            An array of post IDs not to retrieve. Note: a string of comma-
-	 *                                                    separated IDs will NOT work.
-	 *     @type string          $post_mime_type          The mime type of the post. Used for 'attachment' post_type.
-	 *     @type string[]        $post_name__in           An array of post slugs that results must match.
-	 *     @type int             $post_parent             Page ID to retrieve child pages for. Use 0 to only retrieve
-	 *                                                    top-level pages.
-	 *     @type int[]           $post_parent__in         An array containing parent page IDs to query child pages from.
-	 *     @type int[]           $post_parent__not_in     An array containing parent page IDs not to query child pages from.
-	 *     @type string|string[] $post_type               A post type slug (string) or array of post type slugs.
-	 *                                                    Default 'any' if using 'tax_query'.
-	 *     @type string|string[] $post_status             A post status (string) or array of post statuses.
-	 *     @type int             $posts_per_page          The number of posts to query for. Use -1 to request all posts.
-	 *     @type int             $posts_per_archive_page  The number of posts to query for by archive page. Overrides
-	 *                                                    'posts_per_page' when is_archive(), or is_search() are true.
-	 *     @type string          $s                       Search keyword(s). Prepending a term with a hyphen will
-	 *                                                    exclude posts matching that term. Eg, 'pillow -sofa' will
-	 *                                                    return posts containing 'pillow' but not 'sofa'. The
-	 *                                                    character used for exclusion can be modified using the
-	 *                                                    the 'wp_query_search_exclusion_prefix' filter.
-	 *     @type int             $second                  Second of the minute. Default empty. Accepts numbers 0-59.
-	 *     @type bool            $sentence                Whether to search by phrase. Default false.
-	 *     @type bool            $suppress_filters        Whether to suppress filters. Default false.
-	 *     @type string          $tag                     Tag slug. Comma-separated (either), Plus-separated (all).
-	 *     @type int[]           $tag__and                An array of tag IDs (AND in).
-	 *     @type int[]           $tag__in                 An array of tag IDs (OR in).
-	 *     @type int[]           $tag__not_in             An array of tag IDs (NOT in).
-	 *     @type int             $tag_id                  Tag id or comma-separated list of IDs.
-	 *     @type string[]        $tag_slug__and           An array of tag slugs (AND in).
-	 *     @type string[]        $tag_slug__in            An array of tag slugs (OR in). unless 'ignore_sticky_posts' is
-	 *                                                    true. Note: a string of comma-separated IDs will NOT work.
-	 *     @type array           $tax_query               An associative array of WP_Tax_Query arguments.
-	 *                                                    See WP_Tax_Query->__construct().
-	 *     @type string          $title                   Post title.
-	 *     @type bool            $update_post_meta_cache  Whether to update the post meta cache. Default true.
-	 *     @type bool            $update_post_term_cache  Whether to update the post term cache. Default true.
-	 *     @type bool            $lazy_load_term_meta     Whether to lazy-load term meta. Setting to false will
-	 *                                                    disable cache priming for term meta, so that each
-	 *                                                    get_term_meta() call will hit the database.
-	 *                                                    Defaults to the value of `$update_post_term_cache`.
-	 *     @type int             $w                       The week number of the year. Default empty. Accepts numbers 0-53.
-	 *     @type int             $year                    The four-digit year. Default empty. Accepts any four-digit year.
+	 *     @type int             $attachment_id          Attachment post ID. Used for 'attachment' post_type.
+	 *     @type int|string      $author                 Author ID, or comma-separated list of IDs.
+	 *     @type string          $author_name            User 'user_nicename'.
+	 *     @type int[]           $author__in             An array of author IDs to query from.
+	 *     @type int[]           $author__not_in         An array of author IDs not to query from.
+	 *     @type bool            $cache_results          Whether to cache post information. Default true.
+	 *     @type int|string      $cat                    Category ID or comma-separated list of IDs (this or any children).
+	 *     @type int[]           $category__and          An array of category IDs (AND in).
+	 *     @type int[]           $category__in           An array of category IDs (OR in, no children).
+	 *     @type int[]           $category__not_in       An array of category IDs (NOT in).
+	 *     @type string          $category_name          Use category slug (not name, this or any children).
+	 *     @type array|int       $comment_count          Filter results by comment count. Provide an integer to match
+	 *                                                   comment count exactly. Provide an array with integer 'value'
+	 *                                                   and 'compare' operator ('=', '!=', '>', '>=', '<', '<=' ) to
+	 *                                                   compare against comment_count in a specific way.
+	 *     @type string          $comment_status         Comment status.
+	 *     @type int             $comments_per_page      The number of comments to return per page.
+	 *                                                   Default 'comments_per_page' option.
+	 *     @type array           $date_query             An associative array of WP_Date_Query arguments.
+	 *                                                   See WP_Date_Query::__construct().
+	 *     @type int             $day                    Day of the month. Default empty. Accepts numbers 1-31.
+	 *     @type bool            $exact                  Whether to search by exact keyword. Default false.
+	 *     @type string          $fields                 Post fields to query for. Accepts:
+	 *                                                   - '' Returns an array of complete post objects (`WP_Post[]`).
+	 *                                                   - 'ids' Returns an array of post IDs (`int[]`).
+	 *                                                   - 'id=>parent' Returns an associative array of parent post IDs,
+	 *                                                     keyed by post ID (`int[]`).
+	 *                                                   Default ''.
+	 *     @type int             $hour                   Hour of the day. Default empty. Accepts numbers 0-23.
+	 *     @type int|bool        $ignore_sticky_posts    Whether to ignore sticky posts or not. Setting this to false
+	 *                                                   excludes stickies from 'post__in'. Accepts 1|true, 0|false.
+	 *                                                   Default false.
+	 *     @type int             $m                      Combination YearMonth. Accepts any four-digit year and month
+	 *                                                   numbers 01-12. Default empty.
+	 *     @type string|string[] $meta_key               Meta key or keys to filter by.
+	 *     @type string|string[] $meta_value             Meta value or values to filter by.
+	 *     @type string          $meta_compare           MySQL operator used for comparing the meta value.
+	 *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
+	 *     @type string          $meta_compare_key       MySQL operator used for comparing the meta key.
+	 *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
+	 *     @type string          $meta_type              MySQL data type that the meta_value column will be CAST to for comparisons.
+	 *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
+	 *     @type string          $meta_type_key          MySQL data type that the meta_key column will be CAST to for comparisons.
+	 *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
+	 *     @type array           $meta_query             An associative array of WP_Meta_Query arguments.
+	 *                                                   See WP_Meta_Query::__construct() for accepted values.
+	 *     @type int             $menu_order             The menu order of the posts.
+	 *     @type int             $minute                 Minute of the hour. Default empty. Accepts numbers 0-59.
+	 *     @type int             $monthnum               The two-digit month. Default empty. Accepts numbers 1-12.
+	 *     @type string          $name                   Post slug.
+	 *     @type bool            $nopaging               Show all posts (true) or paginate (false). Default false.
+	 *     @type bool            $no_found_rows          Whether to skip counting the total rows found. Enabling can improve
+	 *                                                   performance. Default false.
+	 *     @type int             $offset                 The number of posts to offset before retrieval.
+	 *     @type string          $order                  Designates ascending or descending order of posts. Default 'DESC'.
+	 *                                                   Accepts 'ASC', 'DESC'.
+	 *     @type string|array    $orderby                Sort retrieved posts by parameter. One or more options may be passed.
+	 *                                                   To use 'meta_value', or 'meta_value_num', 'meta_key=keyname' must be
+	 *                                                   also be defined. To sort by a specific `$meta_query` clause, use that
+	 *                                                   clause's array key. Accepts:
+	 *                                                   - 'none'
+	 *                                                   - 'name'
+	 *                                                   - 'author'
+	 *                                                   - 'date'
+	 *                                                   - 'title'
+	 *                                                   - 'modified'
+	 *                                                   - 'menu_order'
+	 *                                                   - 'parent'
+	 *                                                   - 'ID'
+	 *                                                   - 'rand'
+	 *                                                   - 'relevance'
+	 *                                                   - 'RAND(x)' (where 'x' is an integer seed value)
+	 *                                                   - 'comment_count'
+	 *                                                   - 'meta_value'
+	 *                                                   - 'meta_value_num'
+	 *                                                   - 'post__in'
+	 *                                                   - 'post_name__in'
+	 *                                                   - 'post_parent__in'
+	 *                                                   - The array keys of `$meta_query`.
+	 *                                                   Default is 'date', except when a search is being performed, when
+	 *                                                   the default is 'relevance'.
+	 *     @type int             $p                      Post ID.
+	 *     @type int             $page                   Show the number of posts that would show up on page X of a
+	 *                                                   static front page.
+	 *     @type int             $paged                  The number of the current page.
+	 *     @type int             $page_id                Page ID.
+	 *     @type string          $pagename               Page slug.
+	 *     @type string          $perm                   Show posts if user has the appropriate capability.
+	 *     @type string          $ping_status            Ping status.
+	 *     @type int[]           $post__in               An array of post IDs to retrieve, sticky posts will be included.
+	 *     @type int[]           $post__not_in           An array of post IDs not to retrieve. Note: a string of comma-
+	 *                                                   separated IDs will NOT work.
+	 *     @type string          $post_mime_type         The mime type of the post. Used for 'attachment' post_type.
+	 *     @type string[]        $post_name__in          An array of post slugs that results must match.
+	 *     @type int             $post_parent            Page ID to retrieve child pages for. Use 0 to only retrieve
+	 *                                                   top-level pages.
+	 *     @type int[]           $post_parent__in        An array containing parent page IDs to query child pages from.
+	 *     @type int[]           $post_parent__not_in    An array containing parent page IDs not to query child pages from.
+	 *     @type string|string[] $post_type              A post type slug (string) or array of post type slugs.
+	 *                                                   Default 'any' if using 'tax_query'.
+	 *     @type string|string[] $post_status            A post status (string) or array of post statuses.
+	 *     @type int             $posts_per_page         The number of posts to query for. Use -1 to request all posts.
+	 *     @type int             $posts_per_archive_page The number of posts to query for by archive page. Overrides
+	 *                                                   'posts_per_page' when is_archive(), or is_search() are true.
+	 *     @type string          $s                      Search keyword(s). Prepending a term with a hyphen will
+	 *                                                   exclude posts matching that term. Eg, 'pillow -sofa' will
+	 *                                                   return posts containing 'pillow' but not 'sofa'. The
+	 *                                                   character used for exclusion can be modified using the
+	 *                                                   the 'wp_query_search_exclusion_prefix' filter.
+	 *     @type string[]        $search_columns         Array of column names to be searched. Accepts 'post_title',
+	 *                                                   'post_excerpt' and 'post_content'. Default empty array.
+	 *     @type int             $second                 Second of the minute. Default empty. Accepts numbers 0-59.
+	 *     @type bool            $sentence               Whether to search by phrase. Default false.
+	 *     @type bool            $suppress_filters       Whether to suppress filters. Default false.
+	 *     @type string          $tag                    Tag slug. Comma-separated (either), Plus-separated (all).
+	 *     @type int[]           $tag__and               An array of tag IDs (AND in).
+	 *     @type int[]           $tag__in                An array of tag IDs (OR in).
+	 *     @type int[]           $tag__not_in            An array of tag IDs (NOT in).
+	 *     @type int             $tag_id                 Tag id or comma-separated list of IDs.
+	 *     @type string[]        $tag_slug__and          An array of tag slugs (AND in).
+	 *     @type string[]        $tag_slug__in           An array of tag slugs (OR in). unless 'ignore_sticky_posts' is
+	 *                                                   true. Note: a string of comma-separated IDs will NOT work.
+	 *     @type array           $tax_query              An associative array of WP_Tax_Query arguments.
+	 *                                                   See WP_Tax_Query::__construct().
+	 *     @type string          $title                  Post title.
+	 *     @type bool            $update_post_meta_cache Whether to update the post meta cache. Default true.
+	 *     @type bool            $update_post_term_cache Whether to update the post term cache. Default true.
+	 *     @type bool            $update_menu_item_cache Whether to update the menu item cache. Default false.
+	 *     @type bool            $lazy_load_term_meta    Whether to lazy-load term meta. Setting to false will
+	 *                                                   disable cache priming for term meta, so that each
+	 *                                                   get_term_meta() call will hit the database.
+	 *                                                   Defaults to the value of `$update_post_term_cache`.
+	 *     @type int             $w                      The week number of the year. Default empty. Accepts numbers 0-53.
+	 *     @type int             $year                   The four-digit year. Default empty. Accepts any four-digit year.
 	 * }
 	 */
 	public function parse_query( $query = '' ) {
@@ -790,29 +814,41 @@
 			$qv['p'] = (int) $qv['p'];
 		}
 
-		$qv['page_id']  = absint( $qv['page_id'] );
-		$qv['year']     = absint( $qv['year'] );
-		$qv['monthnum'] = absint( $qv['monthnum'] );
-		$qv['day']      = absint( $qv['day'] );
-		$qv['w']        = absint( $qv['w'] );
+		$qv['page_id']  = is_scalar( $qv['page_id'] ) ? absint( $qv['page_id'] ) : 0;
+		$qv['year']     = is_scalar( $qv['year'] ) ? absint( $qv['year'] ) : 0;
+		$qv['monthnum'] = is_scalar( $qv['monthnum'] ) ? absint( $qv['monthnum'] ) : 0;
+		$qv['day']      = is_scalar( $qv['day'] ) ? absint( $qv['day'] ) : 0;
+		$qv['w']        = is_scalar( $qv['w'] ) ? absint( $qv['w'] ) : 0;
 		$qv['m']        = is_scalar( $qv['m'] ) ? preg_replace( '|[^0-9]|', '', $qv['m'] ) : '';
-		$qv['paged']    = absint( $qv['paged'] );
-		$qv['cat']      = preg_replace( '|[^0-9,-]|', '', $qv['cat'] );    // Comma-separated list of positive or negative integers.
-		$qv['author']   = preg_replace( '|[^0-9,-]|', '', $qv['author'] ); // Comma-separated list of positive or negative integers.
-		$qv['pagename'] = trim( $qv['pagename'] );
-		$qv['name']     = trim( $qv['name'] );
-		$qv['title']    = trim( $qv['title'] );
-		if ( '' !== $qv['hour'] ) {
+		$qv['paged']    = is_scalar( $qv['paged'] ) ? absint( $qv['paged'] ) : 0;
+		$qv['cat']      = preg_replace( '|[^0-9,-]|', '', $qv['cat'] ); // Array or comma-separated list of positive or negative integers.
+		$qv['author']   = is_scalar( $qv['author'] ) ? preg_replace( '|[^0-9,-]|', '', $qv['author'] ) : ''; // Comma-separated list of positive or negative integers.
+		$qv['pagename'] = is_scalar( $qv['pagename'] ) ? trim( $qv['pagename'] ) : '';
+		$qv['name']     = is_scalar( $qv['name'] ) ? trim( $qv['name'] ) : '';
+		$qv['title']    = is_scalar( $qv['title'] ) ? trim( $qv['title'] ) : '';
+
+		if ( is_scalar( $qv['hour'] ) && '' !== $qv['hour'] ) {
 			$qv['hour'] = absint( $qv['hour'] );
-		}
-		if ( '' !== $qv['minute'] ) {
+		} else {
+			$qv['hour'] = '';
+		}
+
+		if ( is_scalar( $qv['minute'] ) && '' !== $qv['minute'] ) {
 			$qv['minute'] = absint( $qv['minute'] );
-		}
-		if ( '' !== $qv['second'] ) {
+		} else {
+			$qv['minute'] = '';
+		}
+
+		if ( is_scalar( $qv['second'] ) && '' !== $qv['second'] ) {
 			$qv['second'] = absint( $qv['second'] );
-		}
-		if ( '' !== $qv['menu_order'] ) {
+		} else {
+			$qv['second'] = '';
+		}
+
+		if ( is_scalar( $qv['menu_order'] ) && '' !== $qv['menu_order'] ) {
 			$qv['menu_order'] = absint( $qv['menu_order'] );
+		} else {
+			$qv['menu_order'] = '';
 		}
 
 		// Fairly large, potentially too large, upper bound for search string lengths.
@@ -821,14 +857,14 @@
 		}
 
 		// Compat. Map subpost to attachment.
-		if ( '' != $qv['subpost'] ) {
+		if ( is_scalar( $qv['subpost'] ) && '' != $qv['subpost'] ) {
 			$qv['attachment'] = $qv['subpost'];
 		}
-		if ( '' != $qv['subpost_id'] ) {
+		if ( is_scalar( $qv['subpost_id'] ) && '' != $qv['subpost_id'] ) {
 			$qv['attachment_id'] = $qv['subpost_id'];
 		}
 
-		$qv['attachment_id'] = absint( $qv['attachment_id'] );
+		$qv['attachment_id'] = is_scalar( $qv['attachment_id'] ) ? absint( $qv['attachment_id'] ) : 0;
 
 		if ( ( '' !== $qv['attachment'] ) || ! empty( $qv['attachment_id'] ) ) {
 			$this->is_single     = true;
@@ -979,7 +1015,7 @@
 			$this->is_admin = true;
 		}
 
-		if ( false !== strpos( $qv['feed'], 'comments-' ) ) {
+		if ( str_contains( $qv['feed'], 'comments-' ) ) {
 			$qv['feed']         = str_replace( 'comments-', '', $qv['feed'] );
 			$qv['withcomments'] = 1;
 		}
@@ -991,7 +1027,7 @@
 		}
 
 		if ( ! ( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed
-				|| ( defined( 'REST_REQUEST' ) && REST_REQUEST && $this->is_main_query() )
+				|| ( wp_is_serving_rest_request() && $this->is_main_query() )
 				|| $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots || $this->is_favicon ) ) {
 			$this->is_home = true;
 		}
@@ -1149,7 +1185,7 @@
 					$term = implode( ',', $term );
 				}
 
-				if ( strpos( $term, '+' ) !== false ) {
+				if ( str_contains( $term, '+' ) ) {
 					$terms = preg_split( '/[+]+/', $term );
 					foreach ( $terms as $term ) {
 						$tax_query[] = array_merge(
@@ -1262,7 +1298,7 @@
 		// Tag stuff.
 
 		if ( '' !== $q['tag'] && ! $this->is_singular && $this->query_vars_changed ) {
-			if ( strpos( $q['tag'], ',' ) !== false ) {
+			if ( str_contains( $q['tag'], ',' ) ) {
 				$tags = preg_split( '/[,\r\n\t ]+/', $q['tag'] );
 				foreach ( (array) $tags as $tag ) {
 					$tag                 = sanitize_term_field( 'slug', $tag, 0, 'post_tag', 'db' );
@@ -1387,6 +1423,32 @@
 		$searchand                 = '';
 		$q['search_orderby_title'] = array();
 
+		$default_search_columns = array( 'post_title', 'post_excerpt', 'post_content' );
+		$search_columns         = ! empty( $q['search_columns'] ) ? $q['search_columns'] : $default_search_columns;
+		if ( ! is_array( $search_columns ) ) {
+			$search_columns = array( $search_columns );
+		}
+
+		/**
+		 * Filters the columns to search in a WP_Query search.
+		 *
+		 * The supported columns are `post_title`, `post_excerpt` and `post_content`.
+		 * They are all included by default.
+		 *
+		 * @since 6.2.0
+		 *
+		 * @param string[] $search_columns Array of column names to be searched.
+		 * @param string   $search         Text being searched.
+		 * @param WP_Query $query          The current WP_Query instance.
+		 */
+		$search_columns = (array) apply_filters( 'post_search_columns', $search_columns, $q['s'], $this );
+
+		// Use only supported search columns.
+		$search_columns = array_intersect( $search_columns, $default_search_columns );
+		if ( empty( $search_columns ) ) {
+			$search_columns = $default_search_columns;
+		}
+
 		/**
 		 * Filters the prefix that indicates that a search term should be excluded from results.
 		 *
@@ -1399,7 +1461,7 @@
 
 		foreach ( $q['search_terms'] as $term ) {
 			// If there is an $exclusion_prefix, terms prefixed with it should be excluded.
-			$exclude = $exclusion_prefix && ( substr( $term, 0, 1 ) === $exclusion_prefix );
+			$exclude = $exclusion_prefix && str_starts_with( $term, $exclusion_prefix );
 			if ( $exclude ) {
 				$like_op  = 'NOT LIKE';
 				$andor_op = 'AND';
@@ -1414,8 +1476,19 @@
 				$q['search_orderby_title'][] = $wpdb->prepare( "{$wpdb->posts}.post_title LIKE %s", $like );
 			}
 
-			$like      = $n . $wpdb->esc_like( $term ) . $n;
-			$search   .= $wpdb->prepare( "{$searchand}(({$wpdb->posts}.post_title $like_op %s) $andor_op ({$wpdb->posts}.post_excerpt $like_op %s) $andor_op ({$wpdb->posts}.post_content $like_op %s))", $like, $like, $like );
+			$like = $n . $wpdb->esc_like( $term ) . $n;
+
+			$search_columns_parts = array();
+			foreach ( $search_columns as $search_column ) {
+				$search_columns_parts[ $search_column ] = $wpdb->prepare( "({$wpdb->posts}.$search_column $like_op %s)", $like );
+			}
+
+			if ( ! empty( $this->allow_query_attachment_by_filename ) ) {
+				$search_columns_parts['attachment'] = $wpdb->prepare( "(sq1.meta_value $like_op %s)", $like );
+			}
+
+			$search .= "$searchand(" . implode( " $andor_op ", $search_columns_parts ) . ')';
+
 			$searchand = ' AND ';
 		}
 
@@ -1430,7 +1503,7 @@
 	}
 
 	/**
-	 * Check if the terms are suitable for searching.
+	 * Checks if the terms are suitable for searching.
 	 *
 	 * Uses an array of stopwords (terms) that are excluded from the separate
 	 * term matching when searching for posts. The list of English stopwords is
@@ -1471,7 +1544,7 @@
 	}
 
 	/**
-	 * Retrieve stopwords used when parsing search terms.
+	 * Retrieves stopwords used when parsing search terms.
 	 *
 	 * @since 3.7.0
 	 *
@@ -1543,8 +1616,10 @@
 				$search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_title LIKE %s THEN 1 ", $like );
 			}
 
-			// Sanity limit, sort as sentence when more than 6 terms
-			// (few searches are longer than 6 terms and most titles are not).
+			/*
+			 * Sanity limit, sort as sentence when more than 6 terms
+			 * (few searches are longer than 6 terms and most titles are not).
+			 */
 			if ( $num_terms < 7 ) {
 				// All words in title.
 				$search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 2 ';
@@ -1751,7 +1826,8 @@
 	 * @since 3.9.0 The `$default_value` argument was introduced.
 	 *
 	 * @param string $query_var     Query variable key.
-	 * @param mixed  $default_value Optional. Value to return if the query variable is not set. Default empty string.
+	 * @param mixed  $default_value Optional. Value to return if the query variable is not set.
+	 *                              Default empty string.
 	 * @return mixed Contents of the query variable.
 	 */
 	public function get( $query_var, $default_value = '' ) {
@@ -1810,6 +1886,16 @@
 		// Fill again in case 'pre_get_posts' unset some vars.
 		$q = $this->fill_query_vars( $q );
 
+		/**
+		 * Filters whether an attachment query should include filenames or not.
+		 *
+		 * @since 6.0.3
+		 *
+		 * @param bool $allow_query_attachment_by_filename Whether or not to include filenames.
+		 */
+		$this->allow_query_attachment_by_filename = apply_filters( 'wp_allow_query_attachment_by_filename', false );
+		remove_all_filters( 'wp_allow_query_attachment_by_filename' );
+
 		// Parse meta query.
 		$this->meta_query = new WP_Meta_Query();
 		$this->meta_query->parse_query_vars( $q );
@@ -1860,19 +1946,21 @@
 		}
 
 		if ( ! isset( $q['cache_results'] ) ) {
-			if ( wp_using_ext_object_cache() ) {
-				$q['cache_results'] = false;
-			} else {
-				$q['cache_results'] = true;
-			}
+			$q['cache_results'] = true;
 		}
 
 		if ( ! isset( $q['update_post_term_cache'] ) ) {
 			$q['update_post_term_cache'] = true;
 		}
 
+		if ( ! isset( $q['update_menu_item_cache'] ) ) {
+			$q['update_menu_item_cache'] = false;
+		}
+
 		if ( ! isset( $q['lazy_load_term_meta'] ) ) {
 			$q['lazy_load_term_meta'] = $q['update_post_term_cache'];
+		} elseif ( $q['lazy_load_term_meta'] ) { // Lazy loading term meta only works if term caches are primed.
+			$q['update_post_term_cache'] = true;
 		}
 
 		if ( ! isset( $q['update_post_meta_cache'] ) ) {
@@ -1932,8 +2020,7 @@
 		}
 
 		if ( isset( $q['page'] ) ) {
-			$q['page'] = trim( $q['page'], '/' );
-			$q['page'] = absint( $q['page'] );
+			$q['page'] = is_scalar( $q['page'] ) ? absint( trim( $q['page'], '/' ) ) : 0;
 		}
 
 		// If true, forcibly turns off SQL_CALC_FOUND_ROWS even when limits are present.
@@ -2175,8 +2262,11 @@
 				}
 				if ( ! $post_type ) {
 					$post_type = 'any';
-				} elseif ( count( $post_type ) == 1 ) {
+				} elseif ( count( $post_type ) === 1 ) {
 					$post_type = $post_type[0];
+				} else {
+					// Sort post types to ensure same cache key generation.
+					sort( $post_type );
 				}
 
 				$post_status_join = true;
@@ -2241,7 +2331,7 @@
 			}
 		}
 
-		if ( ! empty( $this->tax_query->queries ) || ! empty( $this->meta_query->queries ) ) {
+		if ( ! empty( $this->tax_query->queries ) || ! empty( $this->meta_query->queries ) || ! empty( $this->allow_query_attachment_by_filename ) ) {
 			$groupby = "{$wpdb->posts}.ID";
 		}
 
@@ -2268,7 +2358,7 @@
 		// Author stuff for nice URLs.
 
 		if ( '' !== $q['author_name'] ) {
-			if ( strpos( $q['author_name'], '/' ) !== false ) {
+			if ( str_contains( $q['author_name'], '/' ) ) {
 				$q['author_name'] = explode( '/', $q['author_name'] );
 				if ( $q['author_name'][ count( $q['author_name'] ) - 1 ] ) {
 					$q['author_name'] = $q['author_name'][ count( $q['author_name'] ) - 1 ]; // No trailing slash.
@@ -2318,6 +2408,10 @@
 		}
 		$where .= $search . $whichauthor . $whichmimetype;
 
+		if ( ! empty( $this->allow_query_attachment_by_filename ) ) {
+			$join .= " LEFT JOIN {$wpdb->postmeta} AS sq1 ON ( {$wpdb->posts}.ID = sq1.post_id AND sq1.meta_key = '_wp_attached_file' )";
+		}
+
 		if ( ! empty( $this->meta_query->queries ) ) {
 			$clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
 			$join   .= $clauses['join'];
@@ -2451,6 +2545,8 @@
 				$post_type_where = " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", array_map( 'esc_sql', $in_search_post_types ) ) . "')";
 			}
 		} elseif ( ! empty( $post_type ) && is_array( $post_type ) ) {
+			// Sort post types to ensure same cache key generation.
+			sort( $post_type );
 			$post_type_where = " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", esc_sql( $post_type ) ) . "')";
 		} elseif ( ! empty( $post_type ) ) {
 			$post_type_where  = $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s", $post_type );
@@ -2556,7 +2652,7 @@
 			}
 
 			if ( ! empty( $queried_post_types ) ) {
-
+				sort( $queried_post_types );
 				$status_type_clauses = array();
 
 				foreach ( $queried_post_types as $queried_post_type ) {
@@ -2727,12 +2823,12 @@
 			$last_changed = wp_cache_get_last_changed( 'comment' ) . ':' . wp_cache_get_last_changed( 'posts' );
 
 			$cache_key   = "comment_feed:$key:$last_changed";
-			$comment_ids = wp_cache_get( $cache_key, 'comment' );
+			$comment_ids = wp_cache_get( $cache_key, 'comment-queries' );
 			if ( false === $comment_ids ) {
 				$comment_ids = $wpdb->get_col( $comments_request );
-				wp_cache_add( $cache_key, $comment_ids, 'comment' );
+				wp_cache_add( $cache_key, $comment_ids, 'comment-queries' );
 			}
-			_prime_comment_caches( $comment_ids, false );
+			_prime_comment_caches( $comment_ids );
 
 			// Convert to WP_Comment.
 			/** @var WP_Comment[] */
@@ -3013,14 +3109,24 @@
 			$found_rows = 'SQL_CALC_FOUND_ROWS';
 		}
 
-		$old_request = "
-			SELECT $found_rows $distinct $fields
-			FROM {$wpdb->posts} $join
-			WHERE 1=1 $where
-			$groupby
-			$orderby
-			$limits
-		";
+		/*
+		 * Beginning of the string is on a new line to prevent leading whitespace.
+		 *
+		 * The additional indentation of subsequent lines is to ensure the SQL
+		 * queries are identical to those generated when splitting queries. This
+		 * improves caching of the query by ensuring the same cache key is
+		 * generated for the same database queries functionally.
+		 *
+		 * See https://core.trac.wordpress.org/ticket/56841.
+		 * See https://github.com/WordPress/wordpress-develop/pull/6393#issuecomment-2088217429
+		 */
+		$old_request =
+			"SELECT $found_rows $distinct $fields
+					 FROM {$wpdb->posts} $join
+					 WHERE 1=1 $where
+					 $groupby
+					 $orderby
+					 $limits";
 
 		$this->request = $old_request;
 
@@ -3054,6 +3160,79 @@
 		 */
 		$this->posts = apply_filters_ref_array( 'posts_pre_query', array( null, &$this ) );
 
+		/*
+		 * Ensure the ID database query is able to be cached.
+		 *
+		 * Random queries are expected to have unpredictable results and
+		 * cannot be cached. Note the space before `RAND` in the string
+		 * search, that to ensure against a collision with another
+		 * function.
+		 *
+		 * If `$fields` has been modified by the `posts_fields`,
+		 * `posts_fields_request`, `post_clauses` or `posts_clauses_request`
+		 * filters, then caching is disabled to prevent caching collisions.
+		 */
+		$id_query_is_cacheable = ! str_contains( strtoupper( $orderby ), ' RAND(' );
+
+		$cacheable_field_values = array(
+			"{$wpdb->posts}.*",
+			"{$wpdb->posts}.ID, {$wpdb->posts}.post_parent",
+			"{$wpdb->posts}.ID",
+		);
+
+		if ( ! in_array( $fields, $cacheable_field_values, true ) ) {
+			$id_query_is_cacheable = false;
+		}
+
+		if ( $q['cache_results'] && $id_query_is_cacheable ) {
+			$new_request = str_replace( $fields, "{$wpdb->posts}.*", $this->request );
+			$cache_key   = $this->generate_cache_key( $q, $new_request );
+
+			$cache_found = false;
+			if ( null === $this->posts ) {
+				$cached_results = wp_cache_get( $cache_key, 'post-queries', false, $cache_found );
+
+				if ( $cached_results ) {
+					/** @var int[] */
+					$post_ids = array_map( 'intval', $cached_results['posts'] );
+
+					$this->post_count    = count( $post_ids );
+					$this->found_posts   = $cached_results['found_posts'];
+					$this->max_num_pages = $cached_results['max_num_pages'];
+
+					if ( 'ids' === $q['fields'] ) {
+						$this->posts = $post_ids;
+
+						return $this->posts;
+					} elseif ( 'id=>parent' === $q['fields'] ) {
+						_prime_post_parent_id_caches( $post_ids );
+
+						$post_parent_cache_keys = array();
+						foreach ( $post_ids as $post_id ) {
+							$post_parent_cache_keys[] = 'post_parent:' . (string) $post_id;
+						}
+
+						/** @var int[] */
+						$post_parents = wp_cache_get_multiple( $post_parent_cache_keys, 'posts' );
+
+						foreach ( $post_parents as $cache_key => $post_parent ) {
+							$obj              = new stdClass();
+							$obj->ID          = (int) str_replace( 'post_parent:', '', $cache_key );
+							$obj->post_parent = (int) $post_parent;
+
+							$this->posts[] = $obj;
+						}
+
+						return $post_parents;
+					} else {
+						_prime_post_caches( $post_ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
+						/** @var WP_Post[] */
+						$this->posts = array_map( 'get_post', $post_ids );
+					}
+				}
+			}
+		}
+
 		if ( 'ids' === $q['fields'] ) {
 			if ( null === $this->posts ) {
 				$this->posts = $wpdb->get_col( $this->request );
@@ -3064,6 +3243,16 @@
 			$this->post_count = count( $this->posts );
 			$this->set_found_posts( $q, $limits );
 
+			if ( $q['cache_results'] && $id_query_is_cacheable ) {
+				$cache_value = array(
+					'posts'         => $this->posts,
+					'found_posts'   => $this->found_posts,
+					'max_num_pages' => $this->max_num_pages,
+				);
+
+				wp_cache_set( $cache_key, $cache_value, 'post-queries' );
+			}
+
 			return $this->posts;
 		}
 
@@ -3076,19 +3265,45 @@
 			$this->set_found_posts( $q, $limits );
 
 			/** @var int[] */
-			$r = array();
+			$post_parents       = array();
+			$post_ids           = array();
+			$post_parents_cache = array();
+
 			foreach ( $this->posts as $key => $post ) {
 				$this->posts[ $key ]->ID          = (int) $post->ID;
 				$this->posts[ $key ]->post_parent = (int) $post->post_parent;
 
-				$r[ (int) $post->ID ] = (int) $post->post_parent;
+				$post_parents[ (int) $post->ID ] = (int) $post->post_parent;
+				$post_ids[]                      = (int) $post->ID;
+
+				$post_parents_cache[ 'post_parent:' . (string) $post->ID ] = (int) $post->post_parent;
 			}
-
-			return $r;
-		}
+			// Prime post parent caches, so that on second run, there is not another database query.
+			wp_cache_add_multiple( $post_parents_cache, 'posts' );
+
+			if ( $q['cache_results'] && $id_query_is_cacheable ) {
+				$cache_value = array(
+					'posts'         => $post_ids,
+					'found_posts'   => $this->found_posts,
+					'max_num_pages' => $this->max_num_pages,
+				);
+
+				wp_cache_set( $cache_key, $cache_value, 'post-queries' );
+			}
+
+			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 && ! empty( $limits ) && $q['posts_per_page'] < 500 );
+			$split_the_query = (
+				$is_unfiltered_query
+				&& (
+					wp_using_ext_object_cache()
+					|| ( ! empty( $limits ) && $q['posts_per_page'] < 500 )
+				)
+			);
 
 			/**
 			 * Filters whether to split the query.
@@ -3098,23 +3313,36 @@
 			 * complete row at once. One massive result vs. many small results.
 			 *
 			 * @since 3.4.0
+			 * @since 6.6.0 Added the `$old_request` and `$clauses` parameters.
 			 *
 			 * @param bool     $split_the_query Whether or not to split the query.
 			 * @param WP_Query $query           The WP_Query instance.
+			 * @param string   $old_request     The complete SQL query before filtering.
+			 * @param string[] $clauses {
+			 *     Associative array of the clauses for the query.
+			 *
+			 *     @type string $where    The WHERE clause of the query.
+			 *     @type string $groupby  The GROUP BY clause of the query.
+			 *     @type string $join     The JOIN clause of the query.
+			 *     @type string $orderby  The ORDER BY clause of the query.
+			 *     @type string $distinct The DISTINCT clause of the query.
+			 *     @type string $fields   The SELECT clause of the query.
+			 *     @type string $limits   The LIMIT clause of the query.
+			 * }
 			 */
-			$split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
+			$split_the_query = apply_filters( 'split_the_query', $split_the_query, $this, $old_request, compact( $pieces ) );
 
 			if ( $split_the_query ) {
 				// First get the IDs and then fill in the objects.
 
-				$this->request = "
-					SELECT $found_rows $distinct {$wpdb->posts}.ID
-					FROM {$wpdb->posts} $join
-					WHERE 1=1 $where
-					$groupby
-					$orderby
-					$limits
-				";
+				// Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
+				$this->request =
+					"SELECT $found_rows $distinct {$wpdb->posts}.ID
+					 FROM {$wpdb->posts} $join
+					 WHERE 1=1 $where
+					 $groupby
+					 $orderby
+					 $limits";
 
 				/**
 				 * Filters the Post IDs SQL request before sending.
@@ -3126,12 +3354,12 @@
 				 */
 				$this->request = apply_filters( 'posts_request_ids', $this->request, $this );
 
-				$ids = $wpdb->get_col( $this->request );
-
-				if ( $ids ) {
-					$this->posts = $ids;
+				$post_ids = $wpdb->get_col( $this->request );
+
+				if ( $post_ids ) {
+					$this->posts = $post_ids;
 					$this->set_found_posts( $q, $limits );
-					_prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
+					_prime_post_caches( $post_ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
 				} else {
 					$this->posts = array();
 				}
@@ -3147,6 +3375,20 @@
 			$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' );
+
+			$cache_value = array(
+				'posts'         => $post_ids,
+				'found_posts'   => $this->found_posts,
+				'max_num_pages' => $this->max_num_pages,
+			);
+
+			wp_cache_set( $cache_key, $cache_value, 'post-queries' );
+		}
+
 		if ( ! $q['suppress_filters'] ) {
 			/**
 			 * Filters the raw post results array, prior to status checks.
@@ -3179,16 +3421,16 @@
 
 			$comments_request = "SELECT {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits";
 
-			$key          = md5( $comments_request );
-			$last_changed = wp_cache_get_last_changed( 'comment' );
-
-			$cache_key   = "comment_feed:$key:$last_changed";
-			$comment_ids = wp_cache_get( $cache_key, 'comment' );
+			$comment_key          = md5( $comments_request );
+			$comment_last_changed = wp_cache_get_last_changed( 'comment' );
+
+			$comment_cache_key = "comment_feed:$comment_key:$comment_last_changed";
+			$comment_ids       = wp_cache_get( $comment_cache_key, 'comment-queries' );
 			if ( false === $comment_ids ) {
 				$comment_ids = $wpdb->get_col( $comments_request );
-				wp_cache_add( $cache_key, $comment_ids, 'comment' );
+				wp_cache_add( $comment_cache_key, $comment_ids, 'comment-queries' );
 			}
-			_prime_comment_caches( $comment_ids, false );
+			_prime_comment_caches( $comment_ids );
 
 			// Convert to WP_Comment.
 			/** @var WP_Comment[] */
@@ -3268,7 +3510,7 @@
 					// Move to front, after other stickies.
 					array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
 					// Increment the sticky offset. The next sticky will be placed at this offset.
-					$sticky_offset++;
+					++$sticky_offset;
 					// Remove post from sticky posts array.
 					$offset = array_search( $sticky_post->ID, $sticky_posts, true );
 					unset( $sticky_posts[ $offset ] );
@@ -3298,16 +3540,11 @@
 
 				foreach ( $stickies as $sticky_post ) {
 					array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
-					$sticky_offset++;
+					++$sticky_offset;
 				}
 			}
 		}
 
-		// If comments have been fetched as part of the query, make sure comment meta lazy-loading is set up.
-		if ( ! empty( $this->comments ) ) {
-			wp_queue_comments_for_comment_meta_lazyload( $this->comments );
-		}
-
 		if ( ! $q['suppress_filters'] ) {
 			/**
 			 * Filters the array of retrieved posts after they've been fetched and
@@ -3321,8 +3558,10 @@
 			$this->posts = apply_filters_ref_array( 'the_posts', array( $this->posts, &$this ) );
 		}
 
-		// Ensure that any posts added/modified via one of the filters above are
-		// of the type WP_Post and are filtered.
+		/*
+		 * Ensure that any posts added/modified via one of the filters above are
+		 * of the type WP_Post and are filtered.
+		 */
 		if ( $this->posts ) {
 			$this->post_count = count( $this->posts );
 
@@ -3330,7 +3569,12 @@
 			$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 */
@@ -3340,6 +3584,10 @@
 			$this->posts      = array();
 		}
 
+		if ( ! empty( $this->posts ) && $q['update_menu_item_cache'] ) {
+			update_menu_item_cache( $this->posts );
+		}
+
 		if ( $q['lazy_load_term_meta'] ) {
 			wp_queue_posts_for_term_meta_lazyload( $this->posts );
 		}
@@ -3348,7 +3596,7 @@
 	}
 
 	/**
-	 * Set up the amount of found posts and the number of pages (if limit clause was used)
+	 * Sets up the amount of found posts and the number of pages (if limit clause was used)
 	 * for the current query.
 	 *
 	 * @since 3.5.0
@@ -3361,8 +3609,10 @@
 	private function set_found_posts( $q, $limits ) {
 		global $wpdb;
 
-		// Bail if posts is an empty array. Continue if posts is an empty string,
-		// null, or false to accommodate caching plugins that fill posts later.
+		/*
+		 * Bail if posts is an empty array. Continue if posts is an empty string,
+		 * null, or false to accommodate caching plugins that fill posts later.
+		 */
 		if ( $q['no_found_rows'] || ( is_array( $this->posts ) && ! $this->posts ) ) {
 			return;
 		}
@@ -3402,12 +3652,12 @@
 		$this->found_posts = (int) apply_filters_ref_array( 'found_posts', array( $this->found_posts, &$this ) );
 
 		if ( ! empty( $limits ) ) {
-			$this->max_num_pages = ceil( $this->found_posts / $q['posts_per_page'] );
+			$this->max_num_pages = (int) ceil( $this->found_posts / $q['posts_per_page'] );
 		}
 	}
 
 	/**
-	 * Set up the next post and iterate current post index.
+	 * Sets up the next post and iterate current post index.
 	 *
 	 * @since 1.5.0
 	 *
@@ -3415,7 +3665,7 @@
 	 */
 	public function next_post() {
 
-		$this->current_post++;
+		++$this->current_post;
 
 		/** @var WP_Post */
 		$this->post = $this->posts[ $this->current_post ];
@@ -3434,7 +3684,21 @@
 	 */
 	public function the_post() {
 		global $post;
+
+		if ( ! $this->in_the_loop ) {
+			// Only prime the post cache for queries limited to the ID field.
+			$post_ids = array_filter( $this->posts, 'is_numeric' );
+			// Exclude any falsey values, such as 0.
+			$post_ids = array_filter( $post_ids );
+			if ( $post_ids ) {
+				_prime_post_caches( $post_ids, $this->query_vars['update_post_term_cache'], $this->query_vars['update_post_meta_cache'] );
+			}
+			$post_objects = array_map( 'get_post', $this->posts );
+			update_post_author_caches( $post_objects );
+		}
+
 		$this->in_the_loop = true;
+		$this->before_loop = false;
 
 		if ( -1 == $this->current_post ) { // Loop has just started.
 			/**
@@ -3475,6 +3739,8 @@
 			// Do some cleaning up after the loop.
 			$this->rewind_posts();
 		} elseif ( 0 === $this->post_count ) {
+			$this->before_loop = false;
+
 			/**
 			 * Fires if no results are found in a post query.
 			 *
@@ -3490,7 +3756,7 @@
 	}
 
 	/**
-	 * Rewind the posts and reset post index.
+	 * Rewinds the posts and resets post index.
 	 *
 	 * @since 1.5.0
 	 */
@@ -3502,14 +3768,14 @@
 	}
 
 	/**
-	 * Iterate current comment index and return WP_Comment object.
+	 * Iterates current comment index and returns WP_Comment object.
 	 *
 	 * @since 2.2.0
 	 *
 	 * @return WP_Comment Comment object.
 	 */
 	public function next_comment() {
-		$this->current_comment++;
+		++$this->current_comment;
 
 		/** @var WP_Comment */
 		$this->comment = $this->comments[ $this->current_comment ];
@@ -3539,7 +3805,7 @@
 	}
 
 	/**
-	 * Whether there are more comments available.
+	 * Determines whether there are more comments available.
 	 *
 	 * Automatically rewinds comments when finished.
 	 *
@@ -3558,7 +3824,7 @@
 	}
 
 	/**
-	 * Rewind the comments, resets the comment index and comment to first.
+	 * Rewinds the comments, resets the comment index and comment to first.
 	 *
 	 * @since 2.2.0
 	 */
@@ -3720,7 +3986,7 @@
 	}
 
 	/**
-	 * Make private properties readable for backward compatibility.
+	 * Makes private properties readable for backward compatibility.
 	 *
 	 * @since 4.0.0
 	 *
@@ -3734,7 +4000,7 @@
 	}
 
 	/**
-	 * Make private properties checkable for backward compatibility.
+	 * Makes private properties checkable for backward compatibility.
 	 *
 	 * @since 4.0.0
 	 *
@@ -3748,7 +4014,7 @@
 	}
 
 	/**
-	 * Make private/protected methods readable for backward compatibility.
+	 * Makes private/protected methods readable for backward compatibility.
 	 *
 	 * @since 4.0.0
 	 *
@@ -3764,7 +4030,7 @@
 	}
 
 	/**
-	 * Is the query for an existing archive page?
+	 * Determines whether the query is for an existing archive page.
 	 *
 	 * Archive pages include category, tag, author, date, custom post type,
 	 * and custom taxonomy based archives.
@@ -3785,7 +4051,7 @@
 	}
 
 	/**
-	 * Is the query for an existing post type archive page?
+	 * Determines whether the query is for an existing post type archive page.
 	 *
 	 * @since 3.1.0
 	 *
@@ -3804,11 +4070,15 @@
 		}
 		$post_type_object = get_post_type_object( $post_type );
 
+		if ( ! $post_type_object ) {
+			return false;
+		}
+
 		return in_array( $post_type_object->name, (array) $post_types, true );
 	}
 
 	/**
-	 * Is the query for an existing attachment page?
+	 * Determines whether the query is for an existing attachment page.
 	 *
 	 * @since 3.1.0
 	 *
@@ -3828,6 +4098,9 @@
 		$attachment = array_map( 'strval', (array) $attachment );
 
 		$post_obj = $this->get_queried_object();
+		if ( ! $post_obj ) {
+			return false;
+		}
 
 		if ( in_array( (string) $post_obj->ID, $attachment, true ) ) {
 			return true;
@@ -3840,7 +4113,7 @@
 	}
 
 	/**
-	 * Is the query for an existing author archive page?
+	 * Determines whether the query is for an existing author archive page.
 	 *
 	 * If the $author parameter is specified, this function will additionally
 	 * check if the query is for one of the authors specified.
@@ -3861,6 +4134,9 @@
 		}
 
 		$author_obj = $this->get_queried_object();
+		if ( ! $author_obj ) {
+			return false;
+		}
 
 		$author = array_map( 'strval', (array) $author );
 
@@ -3876,7 +4152,7 @@
 	}
 
 	/**
-	 * Is the query for an existing category archive page?
+	 * Determines whether the query is for an existing category archive page.
 	 *
 	 * If the $category parameter is specified, this function will additionally
 	 * check if the query is for one of the categories specified.
@@ -3897,6 +4173,9 @@
 		}
 
 		$cat_obj = $this->get_queried_object();
+		if ( ! $cat_obj ) {
+			return false;
+		}
 
 		$category = array_map( 'strval', (array) $category );
 
@@ -3912,7 +4191,7 @@
 	}
 
 	/**
-	 * Is the query for an existing tag archive page?
+	 * Determines whether the query is for an existing tag archive page.
 	 *
 	 * If the $tag parameter is specified, this function will additionally
 	 * check if the query is for one of the tags specified.
@@ -3933,6 +4212,9 @@
 		}
 
 		$tag_obj = $this->get_queried_object();
+		if ( ! $tag_obj ) {
+			return false;
+		}
 
 		$tag = array_map( 'strval', (array) $tag );
 
@@ -3948,7 +4230,7 @@
 	}
 
 	/**
-	 * Is the query for an existing custom taxonomy archive page?
+	 * Determines whether the query is for an existing custom taxonomy archive page.
 	 *
 	 * If the $taxonomy parameter is specified, this function will additionally
 	 * check if the query is for that specific $taxonomy.
@@ -4004,7 +4286,7 @@
 	}
 
 	/**
-	 * Whether the current URL is within the comments popup window.
+	 * Determines whether the current URL is within the comments popup window.
 	 *
 	 * @since 3.1.0
 	 * @deprecated 4.5.0
@@ -4018,7 +4300,7 @@
 	}
 
 	/**
-	 * Is the query for an existing date archive?
+	 * Determines whether the query is for an existing date archive.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4029,7 +4311,7 @@
 	}
 
 	/**
-	 * Is the query for an existing day archive?
+	 * Determines whether the query is for an existing day archive.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4040,7 +4322,7 @@
 	}
 
 	/**
-	 * Is the query for a feed?
+	 * Determines whether the query is for a feed.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4062,7 +4344,7 @@
 	}
 
 	/**
-	 * Is the query for a comments feed?
+	 * Determines whether the query is for a comments feed.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4073,7 +4355,7 @@
 	}
 
 	/**
-	 * Is the query for the front page of the site?
+	 * Determines whether the query is for the front page of the site.
 	 *
 	 * This is for what is displayed at your site's main URL.
 	 *
@@ -4082,7 +4364,7 @@
 	 * If you set a static page for the front page of your site, this function will return
 	 * true when viewing that page.
 	 *
-	 * Otherwise the same as @see WP_Query::is_home()
+	 * Otherwise the same as {@see WP_Query::is_home()}.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4102,7 +4384,7 @@
 	}
 
 	/**
-	 * Is the query for the blog homepage?
+	 * Determines whether the query is for the blog homepage.
 	 *
 	 * This is the page which shows the time based blog content of your site.
 	 *
@@ -4122,7 +4404,7 @@
 	}
 
 	/**
-	 * Is the query for the Privacy Policy page?
+	 * Determines whether the query is for the Privacy Policy page.
 	 *
 	 * This is the page which shows the Privacy Policy content of your site.
 	 *
@@ -4145,7 +4427,7 @@
 	}
 
 	/**
-	 * Is the query for an existing month archive?
+	 * Determines whether the query is for an existing month archive.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4156,7 +4438,7 @@
 	}
 
 	/**
-	 * Is the query for an existing single page?
+	 * Determines whether the query is for an existing single page.
 	 *
 	 * If the $page parameter is specified, this function will additionally
 	 * check if the query is for one of the pages specified.
@@ -4180,6 +4462,9 @@
 		}
 
 		$page_obj = $this->get_queried_object();
+		if ( ! $page_obj ) {
+			return false;
+		}
 
 		$page = array_map( 'strval', (array) $page );
 
@@ -4206,7 +4491,7 @@
 	}
 
 	/**
-	 * Is the query for a paged result and not for the first page?
+	 * Determines whether the query is for a paged result and not for the first page.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4217,7 +4502,7 @@
 	}
 
 	/**
-	 * Is the query for a post or page preview?
+	 * Determines whether the query is for a post or page preview.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4228,7 +4513,7 @@
 	}
 
 	/**
-	 * Is the query for the robots.txt file?
+	 * Determines whether the query is for the robots.txt file.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4239,7 +4524,7 @@
 	}
 
 	/**
-	 * Is the query for the favicon.ico file?
+	 * Determines whether the query is for the favicon.ico file.
 	 *
 	 * @since 5.4.0
 	 *
@@ -4250,7 +4535,7 @@
 	}
 
 	/**
-	 * Is the query for a search?
+	 * Determines whether the query is for a search.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4261,7 +4546,7 @@
 	}
 
 	/**
-	 * Is the query for an existing single post?
+	 * Determines whether the query is for an existing single post.
 	 *
 	 * Works for any post type excluding pages.
 	 *
@@ -4287,6 +4572,9 @@
 		}
 
 		$post_obj = $this->get_queried_object();
+		if ( ! $post_obj ) {
+			return false;
+		}
 
 		$post = array_map( 'strval', (array) $post );
 
@@ -4312,8 +4600,8 @@
 	}
 
 	/**
-	 * Is the query for an existing single post of any post type (post, attachment, page,
-	 * custom post types)?
+	 * Determines whether the query is for an existing single post of any post type
+	 * (post, attachment, page, custom post types).
 	 *
 	 * If the $post_types parameter is specified, this function will additionally
 	 * check if the query is for one of the Posts Types specified.
@@ -4334,12 +4622,15 @@
 		}
 
 		$post_obj = $this->get_queried_object();
+		if ( ! $post_obj ) {
+			return false;
+		}
 
 		return in_array( $post_obj->post_type, (array) $post_types, true );
 	}
 
 	/**
-	 * Is the query for a specific time?
+	 * Determines whether the query is for a specific time.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4350,7 +4641,7 @@
 	}
 
 	/**
-	 * Is the query for a trackback endpoint call?
+	 * Determines whether the query is for a trackback endpoint call.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4361,7 +4652,7 @@
 	}
 
 	/**
-	 * Is the query for an existing year archive?
+	 * Determines whether the query is for an existing year archive.
 	 *
 	 * @since 3.1.0
 	 *
@@ -4372,7 +4663,7 @@
 	}
 
 	/**
-	 * Is the query a 404 (returns no results)?
+	 * Determines whether the query is a 404 (returns no results).
 	 *
 	 * @since 3.1.0
 	 *
@@ -4383,7 +4674,7 @@
 	}
 
 	/**
-	 * Is the query for an embedded post?
+	 * Determines whether the query is for an embedded post.
 	 *
 	 * @since 4.4.0
 	 *
@@ -4394,11 +4685,11 @@
 	}
 
 	/**
-	 * Is the query the main query?
+	 * Determines whether the query is the main query.
 	 *
 	 * @since 3.3.0
 	 *
-	 * @global WP_Query $wp_query WordPress Query object.
+	 * @global WP_Query $wp_the_query WordPress Query object.
 	 *
 	 * @return bool Whether the query is the main query.
 	 */
@@ -4408,7 +4699,7 @@
 	}
 
 	/**
-	 * Set up global post data.
+	 * Sets up global post data.
 	 *
 	 * @since 4.1.0
 	 * @since 4.4.0 Added the ability to pass a post ID to `$post`.
@@ -4467,7 +4758,7 @@
 	}
 
 	/**
-	 * Generate post data.
+	 * Generates post data.
 	 *
 	 * @since 5.2.0
 	 *
@@ -4488,11 +4779,22 @@
 
 		$authordata = get_userdata( $post->post_author );
 
-		$currentday   = mysql2date( 'd.m.y', $post->post_date, false );
-		$currentmonth = mysql2date( 'm', $post->post_date, false );
-		$numpages     = 1;
-		$multipage    = 0;
-		$page         = $this->get( 'page' );
+		$currentday   = false;
+		$currentmonth = false;
+
+		$post_date = $post->post_date;
+		if ( ! empty( $post_date ) && '0000-00-00 00:00:00' !== $post_date ) {
+			// Avoid using mysql2date for performance reasons.
+			$currentmonth = substr( $post_date, 5, 2 );
+			$day          = substr( $post_date, 8, 2 );
+			$year         = substr( $post_date, 2, 2 );
+
+			$currentday = sprintf( '%s.%s.%s', $day, $currentmonth, $year );
+		}
+
+		$numpages  = 1;
+		$multipage = 0;
+		$page      = $this->get( 'page' );
 		if ( ! $page ) {
 			$page = 1;
 		}
@@ -4510,7 +4812,7 @@
 		}
 
 		$content = $post->post_content;
-		if ( false !== strpos( $content, '<!--nextpage-->' ) ) {
+		if ( str_contains( $content, '<!--nextpage-->' ) ) {
 			$content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
 			$content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
 			$content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
@@ -4520,7 +4822,7 @@
 			$content = str_replace( '<!-- /wp:nextpage -->', '', $content );
 
 			// Ignore nextpage at the beginning of the content.
-			if ( 0 === strpos( $content, '<!--nextpage-->' ) ) {
+			if ( str_starts_with( $content, '<!--nextpage-->' ) ) {
 				$content = substr( $content, 15 );
 			}
 
@@ -4557,6 +4859,89 @@
 
 		return $elements;
 	}
+
+	/**
+	 * Generates cache key.
+	 *
+	 * @since 6.1.0
+	 *
+	 * @global wpdb $wpdb WordPress database abstraction object.
+	 *
+	 * @param array  $args Query arguments.
+	 * @param string $sql  SQL statement.
+	 * @return string Cache key.
+	 */
+	protected function generate_cache_key( array $args, $sql ) {
+		global $wpdb;
+
+		unset(
+			$args['cache_results'],
+			$args['fields'],
+			$args['lazy_load_term_meta'],
+			$args['update_post_meta_cache'],
+			$args['update_post_term_cache'],
+			$args['update_menu_item_cache'],
+			$args['suppress_filters']
+		);
+
+		if ( empty( $args['post_type'] ) ) {
+			if ( $this->is_attachment ) {
+				$args['post_type'] = 'attachment';
+			} elseif ( $this->is_page ) {
+				$args['post_type'] = 'page';
+			} else {
+				$args['post_type'] = 'post';
+			}
+		} elseif ( 'any' === $args['post_type'] ) {
+			$args['post_type'] = array_values( get_post_types( array( 'exclude_from_search' => false ) ) );
+		}
+		$args['post_type'] = (array) $args['post_type'];
+		// Sort post types to ensure same cache key generation.
+		sort( $args['post_type'] );
+
+		if ( isset( $args['post_status'] ) ) {
+			$args['post_status'] = (array) $args['post_status'];
+			// Sort post status to ensure same cache key generation.
+			sort( $args['post_status'] );
+		}
+
+		// Add a default orderby value of date to ensure same cache key generation.
+		if ( ! isset( $q['orderby'] ) ) {
+			$args['orderby'] = 'date';
+		}
+
+		$placeholder = $wpdb->placeholder_escape();
+		array_walk_recursive(
+			$args,
+			/*
+			 * Replace wpdb placeholders with the string used in the database
+			 * query to avoid unreachable cache keys. This is necessary because
+			 * the placeholder is randomly generated in each request.
+			 *
+			 * $value is passed by reference to allow it to be modified.
+			 * array_walk_recursive() does not return an array.
+			 */
+			static function ( &$value ) use ( $wpdb, $placeholder ) {
+				if ( is_string( $value ) && str_contains( $value, $placeholder ) ) {
+					$value = $wpdb->remove_placeholder_escape( $value );
+				}
+			}
+		);
+
+		ksort( $args );
+
+		// Replace wpdb placeholder in the SQL statement used by the cache key.
+		$sql = $wpdb->remove_placeholder_escape( $sql );
+		$key = md5( serialize( $args ) . $sql );
+
+		$last_changed = wp_cache_get_last_changed( 'posts' );
+		if ( ! empty( $this->tax_query->queries ) ) {
+			$last_changed .= wp_cache_get_last_changed( 'terms' );
+		}
+
+		return "wp_query:$key:$last_changed";
+	}
+
 	/**
 	 * After looping through a nested query, this function
 	 * restores the $post global to the current post in this query.
@@ -4573,7 +4958,7 @@
 	}
 
 	/**
-	 * Lazyload term meta for posts in the loop.
+	 * Lazyloads term meta for posts in the loop.
 	 *
 	 * @since 4.4.0
 	 * @deprecated 4.5.0 See wp_queue_posts_for_term_meta_lazyload().
@@ -4588,10 +4973,10 @@
 	}
 
 	/**
-	 * Lazyload comment meta for comments in the loop.
+	 * Lazyloads comment meta for comments in the loop.
 	 *
 	 * @since 4.4.0
-	 * @deprecated 4.5.0 See wp_queue_comments_for_comment_meta_lazyload().
+	 * @deprecated 4.5.0 See wp_lazyload_comment_meta().
 	 *
 	 * @param mixed $check
 	 * @param int   $comment_id