: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* 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
* the approximate search engines list, and is translatable.
* @param string[] $terms Array of terms to check.
* @return string[] Terms that are not stopwords.
protected function parse_search_terms( $terms ) {
$strtolower = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
$stopwords = $this->get_search_stopwords();
foreach ( $terms as $term ) {
// Keep before/after spaces when term is for exact match.
if ( preg_match( '/^".+"$/', $term ) ) {
$term = trim( $term, "\"'" );
$term = trim( $term, "\"' " );
// Avoid single A-Z and single dashes.
if ( ! $term || ( 1 === strlen( $term ) && preg_match( '/^[a-z\-]$/i', $term ) ) ) {
if ( in_array( call_user_func( $strtolower, $term ), $stopwords, true ) ) {
* Retrieves stopwords used when parsing search terms.
* @return string[] Stopwords.
protected function get_search_stopwords() {
if ( isset( $this->stopwords ) ) {
* translators: This is a comma-separated list of very common words that should be excluded from a search,
* like a, an, and the. These are usually called "stopwords". You should not simply translate these individual
* words into your language. Instead, look for and provide commonly accepted stopwords in your language.
'about,an,are,as,at,be,by,com,for,from,how,in,is,it,of,on,or,that,the,this,to,was,what,when,where,who,will,with,www',
'Comma-separated list of search stopwords in your language'
foreach ( $words as $word ) {
$word = trim( $word, "\r\n\t " );
* Filters stopwords used when parsing search terms.
* @param string[] $stopwords Array of stopwords.
$this->stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
* Generates SQL for the ORDER BY condition based on passed search terms.
* @global wpdb $wpdb WordPress database abstraction object.
* @param array $q Query variables.
* @return string ORDER BY clause.
protected function parse_search_order( &$q ) {
if ( $q['search_terms_count'] > 1 ) {
$num_terms = count( $q['search_orderby_title'] );
// If the search terms contain negative queries, don't bother ordering by sentence matches.
if ( ! preg_match( '/(?:\s|^)\-/', $q['s'] ) ) {
$like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
// Sentence match in 'post_title'.
$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).
$search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 2 ';
// Any word in title, not needed when $num_terms == 1.
$search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 3 ';
// Sentence match in 'post_content' and 'post_excerpt'.
$search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_excerpt LIKE %s THEN 4 ", $like );
$search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_content LIKE %s THEN 5 ", $like );
$search_orderby = '(CASE ' . $search_orderby . 'ELSE 6 END)';
// Single word or sentence search.
$search_orderby = reset( $q['search_orderby_title'] ) . ' DESC';
* Converts the given orderby alias (if allowed) to a properly-prefixed value.
* @global wpdb $wpdb WordPress database abstraction object.
* @param string $orderby Alias for the field to order by.
* @return string|false Table-prefixed value to used in the ORDER clause. False otherwise.
protected function parse_orderby( $orderby ) {
// Used to filter values.
$primary_meta_query = false;
$meta_clauses = $this->meta_query->get_clauses();
if ( ! empty( $meta_clauses ) ) {
$primary_meta_query = reset( $meta_clauses );
if ( ! empty( $primary_meta_query['key'] ) ) {
$primary_meta_key = $primary_meta_query['key'];
$allowed_keys[] = $primary_meta_key;
$allowed_keys[] = 'meta_value';
$allowed_keys[] = 'meta_value_num';
$allowed_keys = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
// If RAND() contains a seed value, sanitize and add to allowed keys.
if ( preg_match( '/RAND\(([0-9]+)\)/i', $orderby, $matches ) ) {
$orderby = sprintf( 'RAND(%s)', (int) $matches[1] );
$allowed_keys[] = $orderby;
if ( ! in_array( $orderby, $allowed_keys, true ) ) {
$orderby_clause = "{$wpdb->posts}.{$orderby}";
$orderby_clause = 'RAND()';
if ( ! empty( $primary_meta_query['type'] ) ) {
$orderby_clause = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
$orderby_clause = "{$primary_meta_query['alias']}.meta_value";
$orderby_clause = "{$primary_meta_query['alias']}.meta_value+0";
if ( ! empty( $this->query_vars['post__in'] ) ) {
$orderby_clause = "FIELD({$wpdb->posts}.ID," . implode( ',', array_map( 'absint', $this->query_vars['post__in'] ) ) . ')';
if ( ! empty( $this->query_vars['post_parent__in'] ) ) {
$orderby_clause = "FIELD( {$wpdb->posts}.post_parent," . implode( ', ', array_map( 'absint', $this->query_vars['post_parent__in'] ) ) . ' )';
if ( ! empty( $this->query_vars['post_name__in'] ) ) {
$post_name__in = array_map( 'sanitize_title_for_query', $this->query_vars['post_name__in'] );
$post_name__in_string = "'" . implode( "','", $post_name__in ) . "'";
$orderby_clause = "FIELD( {$wpdb->posts}.post_name," . $post_name__in_string . ' )';
if ( array_key_exists( $orderby, $meta_clauses ) ) {
// $orderby corresponds to a meta_query clause.
$meta_clause = $meta_clauses[ $orderby ];
$orderby_clause = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
} elseif ( $rand_with_seed ) {
$orderby_clause = $orderby;
// Default: order by post field.
$orderby_clause = "{$wpdb->posts}.post_" . sanitize_key( $orderby );
* Parse an 'order' query variable and cast it to ASC or DESC as necessary.
* @param string $order The 'order' query variable.
* @return string The sanitized 'order' query variable.
protected function parse_order( $order ) {
if ( ! is_string( $order ) || empty( $order ) ) {
if ( 'ASC' === strtoupper( $order ) ) {
* Sets the 404 property and saves whether query is feed.
public function set_404() {
$is_feed = $this->is_feed;
$this->init_query_flags();
$this->is_feed = $is_feed;
* Fires after a 404 is triggered.
* @param WP_Query $query The WP_Query instance (passed by reference).
do_action_ref_array( 'set_404', array( $this ) );
* Retrieves the value of a query variable.
* @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.
* @return mixed Contents of the query variable.
public function get( $query_var, $default_value = '' ) {
if ( isset( $this->query_vars[ $query_var ] ) ) {
return $this->query_vars[ $query_var ];
* Sets the value of a query variable.
* @param string $query_var Query variable key.
* @param mixed $value Query variable value.
public function set( $query_var, $value ) {
$this->query_vars[ $query_var ] = $value;
* Retrieves an array of posts based on query variables.
* There are a few filters and actions that can be used to modify the post
* @global wpdb $wpdb WordPress database abstraction object.
* @return WP_Post[]|int[] Array of post objects or post IDs.
public function get_posts() {
* Fires after the query variable object is created, but before the actual query is run.
* Note: If using conditional tags, use the method versions within the passed instance
* (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
* like is_main_query() test against the global $wp_query instance, not the passed one.
* @param WP_Query $query The WP_Query instance (passed by reference).
do_action_ref_array( 'pre_get_posts', array( &$this ) );
// 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.
* @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' );
$this->meta_query = new WP_Meta_Query();
$this->meta_query->parse_query_vars( $q );
// Set a flag if a 'pre_get_posts' hook changed the query vars.
$hash = md5( serialize( $this->query_vars ) );
if ( $hash != $this->query_vars_hash ) {
$this->query_vars_changed = true;
$this->query_vars_hash = $hash;
// First let's clear some variables.
$post_status_join = false;
if ( isset( $q['caller_get_posts'] ) ) {
/* translators: 1: caller_get_posts, 2: ignore_sticky_posts */
__( '%1$s is deprecated. Use %2$s instead.' ),
'<code>caller_get_posts</code>',
'<code>ignore_sticky_posts</code>'
if ( ! isset( $q['ignore_sticky_posts'] ) ) {
$q['ignore_sticky_posts'] = $q['caller_get_posts'];
if ( ! isset( $q['ignore_sticky_posts'] ) ) {
$q['ignore_sticky_posts'] = false;
if ( ! isset( $q['suppress_filters'] ) ) {
$q['suppress_filters'] = false;
if ( ! isset( $q['cache_results'] ) ) {
$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'] ) ) {
$q['update_post_meta_cache'] = true;
if ( ! isset( $q['post_type'] ) ) {
if ( $this->is_search ) {
$post_type = $q['post_type'];
if ( empty( $q['posts_per_page'] ) ) {
$q['posts_per_page'] = get_option( 'posts_per_page' );
if ( isset( $q['showposts'] ) && $q['showposts'] ) {
$q['showposts'] = (int) $q['showposts'];
$q['posts_per_page'] = $q['showposts'];
if ( ( isset( $q['posts_per_archive_page'] ) && 0 != $q['posts_per_archive_page'] ) && ( $this->is_archive || $this->is_search ) ) {
$q['posts_per_page'] = $q['posts_per_archive_page'];
if ( ! isset( $q['nopaging'] ) ) {
if ( -1 == $q['posts_per_page'] ) {
// This overrides 'posts_per_page'.
if ( ! empty( $q['posts_per_rss'] ) ) {
$q['posts_per_page'] = $q['posts_per_rss'];