--- 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