: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param string $groupby The GROUP BY clause of the query.
* @param WP_Query $query The WP_Query instance (passed by reference).
$groupby = apply_filters_ref_array( 'posts_groupby_request', array( $groupby, &$this ) );
* Filters the JOIN clause of the query.
* For use by caching plugins.
* @param string $join The JOIN clause of the query.
* @param WP_Query $query The WP_Query instance (passed by reference).
$join = apply_filters_ref_array( 'posts_join_request', array( $join, &$this ) );
* Filters the ORDER BY clause of the query.
* For use by caching plugins.
* @param string $orderby The ORDER BY clause of the query.
* @param WP_Query $query The WP_Query instance (passed by reference).
$orderby = apply_filters_ref_array( 'posts_orderby_request', array( $orderby, &$this ) );
* Filters the DISTINCT clause of the query.
* For use by caching plugins.
* @param string $distinct The DISTINCT clause of the query.
* @param WP_Query $query The WP_Query instance (passed by reference).
$distinct = apply_filters_ref_array( 'posts_distinct_request', array( $distinct, &$this ) );
* Filters the SELECT clause of the query.
* For use by caching plugins.
* @param string $fields The SELECT clause of the query.
* @param WP_Query $query The WP_Query instance (passed by reference).
$fields = apply_filters_ref_array( 'posts_fields_request', array( $fields, &$this ) );
* Filters the LIMIT clause of the query.
* For use by caching plugins.
* @param string $limits The LIMIT clause of the query.
* @param WP_Query $query The WP_Query instance (passed by reference).
$limits = apply_filters_ref_array( 'post_limits_request', array( $limits, &$this ) );
* Filters all query clauses at once, for convenience.
* For use by caching plugins.
* Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
* fields (SELECT), and LIMIT clauses.
* @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.
* @param WP_Query $query The WP_Query instance (passed by reference).
$clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );
$where = isset( $clauses['where'] ) ? $clauses['where'] : '';
$groupby = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
$join = isset( $clauses['join'] ) ? $clauses['join'] : '';
$orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
$distinct = isset( $clauses['distinct'] ) ? $clauses['distinct'] : '';
$fields = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
$limits = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
if ( ! empty( $groupby ) ) {
$groupby = 'GROUP BY ' . $groupby;
if ( ! empty( $orderby ) ) {
$orderby = 'ORDER BY ' . $orderby;
if ( ! $q['no_found_rows'] && ! empty( $limits ) ) {
$found_rows = 'SQL_CALC_FOUND_ROWS';
* 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
"SELECT $found_rows $distinct $fields
FROM {$wpdb->posts} $join
$this->request = $old_request;
if ( ! $q['suppress_filters'] ) {
* Filters the completed SQL query before sending.
* @param string $request The complete SQL query.
* @param WP_Query $query The WP_Query instance (passed by reference).
$this->request = apply_filters_ref_array( 'posts_request', array( $this->request, &$this ) );
* Filters the posts array before the query takes place.
* Return a non-null value to bypass WordPress' default post queries.
* Filtering functions that require pagination information are encouraged to set
* the `found_posts` and `max_num_pages` properties of the WP_Query object,
* passed to the filter by reference. If WP_Query does not perform a database
* query, it will not have enough information to generate these values itself.
* @param WP_Post[]|int[]|null $posts Return an array of post data to short-circuit WP's query,
* or null to allow WP to run its normal queries.
* @param WP_Query $query The WP_Query instance (passed by reference).
$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
* 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}.ID, {$wpdb->posts}.post_parent",
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 );
if ( null === $this->posts ) {
$cached_results = wp_cache_get( $cache_key, 'post-queries', false, $cache_found );
$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;
} 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;
$post_parents = wp_cache_get_multiple( $post_parent_cache_keys, 'posts' );
foreach ( $post_parents as $cache_key => $post_parent ) {
$obj->ID = (int) str_replace( 'post_parent:', '', $cache_key );
$obj->post_parent = (int) $post_parent;
_prime_post_caches( $post_ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
$this->posts = array_map( 'get_post', $post_ids );
if ( 'ids' === $q['fields'] ) {
if ( null === $this->posts ) {
$this->posts = $wpdb->get_col( $this->request );
$this->posts = array_map( 'intval', $this->posts );
$this->post_count = count( $this->posts );
$this->set_found_posts( $q, $limits );
if ( $q['cache_results'] && $id_query_is_cacheable ) {
'found_posts' => $this->found_posts,
'max_num_pages' => $this->max_num_pages,
wp_cache_set( $cache_key, $cache_value, 'post-queries' );
if ( 'id=>parent' === $q['fields'] ) {
if ( null === $this->posts ) {
$this->posts = $wpdb->get_results( $this->request );
$this->post_count = count( $this->posts );
$this->set_found_posts( $q, $limits );
$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;
$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;
// 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 ) {
'found_posts' => $this->found_posts,
'max_num_pages' => $this->max_num_pages,
wp_cache_set( $cache_key, $cache_value, 'post-queries' );
$is_unfiltered_query = $old_request == $this->request && "{$wpdb->posts}.*" === $fields;
if ( null === $this->posts ) {
wp_using_ext_object_cache()
|| ( ! empty( $limits ) && $q['posts_per_page'] < 500 )
* Filters whether to split the query.
* Splitting the query will cause it to fetch just the IDs of the found posts
* (and then individually fetch each post by ID), rather than fetching every
* complete row at once. One massive result vs. many small results.
* @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, $old_request, compact( $pieces ) );
if ( $split_the_query ) {
// First get the IDs and then fill in the objects.
// Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
"SELECT $found_rows $distinct {$wpdb->posts}.ID
FROM {$wpdb->posts} $join
* Filters the Post IDs SQL request before sending.
* @param string $request The post ID request.
* @param WP_Query $query The WP_Query instance.
$this->request = apply_filters( 'posts_request_ids', $this->request, $this );
$post_ids = $wpdb->get_col( $this->request );
$this->posts = $post_ids;
$this->set_found_posts( $q, $limits );
_prime_post_caches( $post_ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
$this->posts = $wpdb->get_results( $this->request );
$this->set_found_posts( $q, $limits );
// Convert to WP_Post objects.
$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' );
'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.
* @param WP_Post[] $posts Array of post objects.
* @param WP_Query $query The WP_Query instance (passed by reference).
$this->posts = apply_filters_ref_array( 'posts_results', array( $this->posts, &$this ) );
if ( ! empty( $this->posts ) && $this->is_comment_feed && $this->is_singular ) {
/** This filter is documented in wp-includes/query.php */
$cjoin = apply_filters_ref_array( 'comment_feed_join', array( '', &$this ) );
/** This filter is documented in wp-includes/query.php */
$cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'", &$this ) );
/** This filter is documented in wp-includes/query.php */
$cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( '', &$this ) );
$cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
/** This filter is documented in wp-includes/query.php */
$corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
$corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
/** This filter is documented in wp-includes/query.php */
$climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option( 'posts_per_rss' ), &$this ) );
$comments_request = "SELECT {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits";
$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( $comment_cache_key, $comment_ids, 'comment-queries' );
_prime_comment_caches( $comment_ids );
// Convert to WP_Comment.
$this->comments = array_map( 'get_comment', $comment_ids );
$this->comment_count = count( $this->comments );
// Check post status to determine if post should be displayed.
if ( ! empty( $this->posts ) && ( $this->is_single || $this->is_page ) ) {
$status = get_post_status( $this->posts[0] );
if ( 'attachment' === $this->posts[0]->post_type && 0 === (int) $this->posts[0]->post_parent ) {
$this->is_attachment = true;
// If the post_status was specifically requested, let it pass through.
if ( ! in_array( $status, $q_status, true ) ) {
$post_status_obj = get_post_status_object( $status );
if ( $post_status_obj && ! $post_status_obj->public ) {
if ( ! is_user_logged_in() ) {
// User must be logged in to view unpublished posts.
if ( $post_status_obj->protected ) {
// User must have edit permissions on the draft to preview.
if ( ! current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
$this->is_preview = true;
if ( 'future' !== $status ) {
$this->posts[0]->post_date = current_time( 'mysql' );
} elseif ( $post_status_obj->private ) {
if ( ! current_user_can( $read_cap, $this->posts[0]->ID ) ) {
} elseif ( ! $post_status_obj ) {
// Post status is not registered, assume it's not public.
if ( ! current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
if ( $this->is_preview && $this->posts && current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
* Filters the single post for preview mode.
* @param WP_Post $post_preview The Post object.
* @param WP_Query $query The WP_Query instance (passed by reference).
$this->posts[0] = get_post( apply_filters_ref_array( 'the_preview', array( $this->posts[0], &$this ) ) );
// Put sticky posts at the top of the posts array.
$sticky_posts = get_option( 'sticky_posts' );