+ $this->query_vars_hash = md5( serialize( $this->query_vars ) );
+ $this->query_vars_changed = false;
+
+ do_action_ref_array('parse_query', array(&$this));
+ }
+
+ /*
+ * Parses various taxonomy related query vars.
+ *
+ * @access protected
+ * @since 3.1.0
+ *
+ * @param array &$q The query variables
+ */
+ function parse_tax_query( &$q ) {
+ if ( ! empty( $q['tax_query'] ) && is_array( $q['tax_query'] ) ) {
+ $tax_query = $q['tax_query'];
+ } else {
+ $tax_query = array();
+ }
+
+ if ( !empty($q['taxonomy']) && !empty($q['term']) ) {
+ $tax_query[] = array(
+ 'taxonomy' => $q['taxonomy'],
+ 'terms' => array( $q['term'] ),
+ 'field' => 'slug',
+ );
+ }
+
+ foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) {
+ if ( 'post_tag' == $taxonomy )
+ continue; // Handled further down in the $q['tag'] block
+
+ if ( $t->query_var && !empty( $q[$t->query_var] ) ) {
+ $tax_query_defaults = array(
+ 'taxonomy' => $taxonomy,
+ 'field' => 'slug',
+ );
+
+ if ( isset( $t->rewrite['hierarchical'] ) && $t->rewrite['hierarchical'] ) {
+ $q[$t->query_var] = wp_basename( $q[$t->query_var] );
+ }
+
+ $term = $q[$t->query_var];
+
+ if ( strpos($term, '+') !== false ) {
+ $terms = preg_split( '/[+]+/', $term );
+ foreach ( $terms as $term ) {
+ $tax_query[] = array_merge( $tax_query_defaults, array(
+ 'terms' => array( $term )
+ ) );
+ }
+ } else {
+ $tax_query[] = array_merge( $tax_query_defaults, array(
+ 'terms' => preg_split( '/[,]+/', $term )
+ ) );
+ }
+ }
+ }
+
+ // Category stuff
+ if ( !empty($q['cat']) && '0' != $q['cat'] && !$this->is_singular && $this->query_vars_changed ) {
+ $q['cat'] = ''.urldecode($q['cat']).'';
+ $q['cat'] = addslashes_gpc($q['cat']);
+ $cat_array = preg_split('/[,\s]+/', $q['cat']);
+ $q['cat'] = '';
+ $req_cats = array();
+ foreach ( (array) $cat_array as $cat ) {
+ $cat = intval($cat);
+ $req_cats[] = $cat;
+ $in = ($cat > 0);
+ $cat = abs($cat);
+ if ( $in ) {
+ $q['category__in'][] = $cat;
+ $q['category__in'] = array_merge( $q['category__in'], get_term_children($cat, 'category') );
+ } else {
+ $q['category__not_in'][] = $cat;
+ $q['category__not_in'] = array_merge( $q['category__not_in'], get_term_children($cat, 'category') );
+ }
+ }
+ $q['cat'] = implode(',', $req_cats);
+ }
+
+ if ( ! empty( $q['category__and'] ) && 1 === count( (array) $q['category__and'] ) ) {
+ $q['category__and'] = (array) $q['category__and'];
+ if ( ! isset( $q['category__in'] ) )
+ $q['category__in'] = array();
+ $q['category__in'][] = absint( reset( $q['category__and'] ) );
+ unset( $q['category__and'] );
+ }
+
+ if ( ! empty( $q['category__in'] ) ) {
+ $q['category__in'] = array_map( 'absint', array_unique( (array) $q['category__in'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'category',
+ 'terms' => $q['category__in'],
+ 'field' => 'term_id',
+ 'include_children' => false
+ );
+ }
+
+ if ( ! empty($q['category__not_in']) ) {
+ $q['category__not_in'] = array_map( 'absint', array_unique( (array) $q['category__not_in'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'category',
+ 'terms' => $q['category__not_in'],
+ 'operator' => 'NOT IN',
+ 'include_children' => false
+ );
+ }
+
+ if ( ! empty($q['category__and']) ) {
+ $q['category__and'] = array_map( 'absint', array_unique( (array) $q['category__and'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'category',
+ 'terms' => $q['category__and'],
+ 'field' => 'term_id',
+ 'operator' => 'AND',
+ 'include_children' => false
+ );
+ }
+
+ // Tag stuff
+ if ( '' != $q['tag'] && !$this->is_singular && $this->query_vars_changed ) {
+ if ( strpos($q['tag'], ',') !== false ) {
+ $tags = preg_split('/[,\r\n\t ]+/', $q['tag']);
+ foreach ( (array) $tags as $tag ) {
+ $tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
+ $q['tag_slug__in'][] = $tag;
+ }
+ } else if ( preg_match('/[+\r\n\t ]+/', $q['tag']) || !empty($q['cat']) ) {
+ $tags = preg_split('/[+\r\n\t ]+/', $q['tag']);
+ foreach ( (array) $tags as $tag ) {
+ $tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
+ $q['tag_slug__and'][] = $tag;
+ }
+ } else {
+ $q['tag'] = sanitize_term_field('slug', $q['tag'], 0, 'post_tag', 'db');
+ $q['tag_slug__in'][] = $q['tag'];
+ }
+ }
+
+ if ( !empty($q['tag_id']) ) {
+ $q['tag_id'] = absint( $q['tag_id'] );
+ $tax_query[] = array(
+ 'taxonomy' => 'post_tag',
+ 'terms' => $q['tag_id']
+ );
+ }
+
+ if ( !empty($q['tag__in']) ) {
+ $q['tag__in'] = array_map('absint', array_unique( (array) $q['tag__in'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'post_tag',
+ 'terms' => $q['tag__in']
+ );
+ }
+
+ if ( !empty($q['tag__not_in']) ) {
+ $q['tag__not_in'] = array_map('absint', array_unique( (array) $q['tag__not_in'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'post_tag',
+ 'terms' => $q['tag__not_in'],
+ 'operator' => 'NOT IN'
+ );
+ }
+
+ if ( !empty($q['tag__and']) ) {
+ $q['tag__and'] = array_map('absint', array_unique( (array) $q['tag__and'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'post_tag',
+ 'terms' => $q['tag__and'],
+ 'operator' => 'AND'
+ );
+ }
+
+ if ( !empty($q['tag_slug__in']) ) {
+ $q['tag_slug__in'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__in'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'post_tag',
+ 'terms' => $q['tag_slug__in'],
+ 'field' => 'slug'
+ );
+ }
+
+ if ( !empty($q['tag_slug__and']) ) {
+ $q['tag_slug__and'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__and'] ) );
+ $tax_query[] = array(
+ 'taxonomy' => 'post_tag',
+ 'terms' => $q['tag_slug__and'],
+ 'field' => 'slug',
+ 'operator' => 'AND'
+ );
+ }
+
+ $this->tax_query = new WP_Tax_Query( $tax_query );
+
+ do_action( 'parse_tax_query', $this );
+ }
+
+ /**
+ * Generate SQL for the WHERE clause based on passed search terms.
+ *
+ * @since 3.7.0
+ *
+ * @global type $wpdb
+ * @param array $q Query variables.
+ */
+ protected function parse_search( &$q ) {
+ global $wpdb;
+
+ $search = '';
+
+ // added slashes screw with quote grouping when done early, so done later
+ $q['s'] = stripslashes( $q['s'] );
+ if ( empty( $_GET['s'] ) && $this->is_main_query() )
+ $q['s'] = urldecode( $q['s'] );
+ // there are no line breaks in <input /> fields
+ $q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] );
+ $q['search_terms_count'] = 1;
+ if ( ! empty( $q['sentence'] ) ) {
+ $q['search_terms'] = array( $q['s'] );
+ } else {
+ if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) {
+ $q['search_terms_count'] = count( $matches[0] );
+ $q['search_terms'] = $this->parse_search_terms( $matches[0] );
+ // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
+ if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 )
+ $q['search_terms'] = array( $q['s'] );
+ } else {
+ $q['search_terms'] = array( $q['s'] );
+ }
+ }
+
+ $n = ! empty( $q['exact'] ) ? '' : '%';
+ $searchand = '';
+ $q['search_orderby_title'] = array();
+ foreach ( $q['search_terms'] as $term ) {
+ $term = like_escape( esc_sql( $term ) );
+ if ( $n )
+ $q['search_orderby_title'][] = "$wpdb->posts.post_title LIKE '%$term%'";
+
+ $search .= "{$searchand}(($wpdb->posts.post_title LIKE '{$n}{$term}{$n}') OR ($wpdb->posts.post_content LIKE '{$n}{$term}{$n}'))";
+ $searchand = ' AND ';
+ }
+
+ if ( ! empty( $search ) ) {
+ $search = " AND ({$search}) ";
+ if ( ! is_user_logged_in() )
+ $search .= " AND ($wpdb->posts.post_password = '') ";
+ }
+
+ return $search;
+ }
+
+ /**
+ * Check 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.
+ *
+ * @since 3.7.0
+ *
+ * @param array Terms to check.
+ * @return array Terms that are not stopwords.
+ */
+ protected function parse_search_terms( $terms ) {
+ $strtolower = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
+ $checked = array();
+
+ $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, "\"'" );
+ else
+ $term = trim( $term, "\"' " );
+
+ // \p{L} matches a single letter that is not a Chinese, Japanese, etc. char
+ if ( ! $term || preg_match( '/^\p{L}$/u', $term ) )
+ continue;
+
+ if ( in_array( call_user_func( $strtolower, $term ), $stopwords, true ) )
+ continue;
+
+ $checked[] = $term;
+ }
+
+ return $checked;
+ }
+
+ /**
+ * Retrieve stopwords used when parsing search terms.
+ *
+ * @since 3.7.0
+ *
+ * @return array Stopwords.
+ */
+ protected function get_search_stopwords() {
+ if ( isset( $this->stopwords ) )
+ return $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.
+ */
+ $words = explode( ',', _x( '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 " );
+ if ( $word )
+ $stopwords[] = $word;
+ }
+
+ /**
+ * Filter stopwords used when parsing search terms.
+ *
+ * @since 3.7.0
+ *
+ * @param array $stopwords Stopwords.
+ */
+ $this->stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
+ return $this->stopwords;
+ }
+
+ /**
+ * Generate SQL for the ORDER BY condition based on passed search terms.
+ *
+ * @global wpdb $wpdb
+ * @param array $q Query variables.
+ * @return string ORDER BY clause.
+ */
+ protected function parse_search_order( &$q ) {
+ global $wpdb;
+
+ $search_orderby = '';
+
+ if ( $q['search_terms_count'] > 1 ) {
+ $num_terms = count( $q['search_orderby_title'] );
+ $search_orderby_s = like_escape( esc_sql( $q['s'] ) );
+
+ $search_orderby = '(CASE ';
+ // sentence match in 'post_title'
+ $search_orderby .= "WHEN $wpdb->posts.post_title LIKE '%{$search_orderby_s}%' THEN 1 ";
+
+ // 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 ';
+ // any word in title, not needed when $num_terms == 1
+ if ( $num_terms > 1 )
+ $search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 3 ';
+ }
+
+ // sentence match in 'post_content'
+ $search_orderby .= "WHEN $wpdb->posts.post_content LIKE '%{$search_orderby_s}%' THEN 4 ";
+ $search_orderby .= 'ELSE 5 END)';
+ } else {
+ // single word or sentence search
+ $search_orderby = reset( $q['search_orderby_title'] ) . ' DESC';
+ }
+
+ return $search_orderby;