| 1906 | * Generate SQL for the WHERE clause based on passed search terms. |
| 1907 | * |
| 1908 | * @since 3.7.0 |
| 1909 | * |
| 1910 | * @global type $wpdb |
| 1911 | * @param array $q |
| 1912 | * @param string $search |
| 1913 | */ |
| 1914 | function parse_search( &$q, $search ) { |
| 1915 | global $wpdb; |
| 1916 | |
| 1917 | // added slashes screw with quote grouping when done early, so done later |
| 1918 | $q['s'] = stripslashes( $q['s'] ); |
| 1919 | if ( empty( $_GET['s'] ) && $this->is_main_query() ) |
| 1920 | $q['s'] = urldecode( $q['s'] ); |
| 1921 | // there are no line breaks in <input /> fields |
| 1922 | $q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] ); |
| 1923 | $q['num_all_terms'] = 1; |
| 1924 | if ( ! empty( $q['sentence'] ) ) { |
| 1925 | $q['search_terms'] = array( $q['s'] ); |
| 1926 | } else { |
| 1927 | if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) { |
| 1928 | $q['num_all_terms'] = count( $matches[0] ); |
| 1929 | $q['search_terms'] = $this->parse_search_terms( $matches[0] ); |
| 1930 | // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence |
| 1931 | if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 ) |
| 1932 | $q['search_terms'] = array( $q['s'] ); |
| 1933 | } else { |
| 1934 | $q['search_terms'] = array( $q['s'] ); |
| 1935 | } |
| 1936 | } |
| 1937 | |
| 1938 | $n = ! empty( $q['exact'] ) ? '' : '%'; |
| 1939 | $searchand = ''; |
| 1940 | $q['search_orderby_title'] = array(); |
| 1941 | foreach ( $q['search_terms'] as $term ) { |
| 1942 | $term = esc_sql( like_escape( $term ) ); |
| 1943 | if ( $n ) |
| 1944 | $q['search_orderby_title'][] = "$wpdb->posts.post_title LIKE '%$term%'"; |
| 1945 | |
| 1946 | $search .= "{$searchand}(($wpdb->posts.post_title LIKE '{$n}{$term}{$n}') OR ($wpdb->posts.post_content LIKE '{$n}{$term}{$n}'))"; |
| 1947 | $searchand = ' AND '; |
| 1948 | } |
| 1949 | |
| 1950 | if ( ! empty( $search ) ) { |
| 1951 | $search = " AND ({$search}) "; |
| 1952 | if ( ! is_user_logged_in() ) |
| 1953 | $search .= " AND ($wpdb->posts.post_password = '') "; |
| 1954 | } |
| 1955 | |
| 1956 | // Allow plugins to contextually add/remove/modify the search section of the database query |
| 1957 | return apply_filters( 'posts_search', $search, $this ); |
| 1958 | } |
| 1959 | |
| 1960 | /** |
| 1961 | * Check if the terms are suitable for searching. |
| 1962 | * |
| 1963 | * Includes array of stopwords (terms) that are excluded from the separate term matching when searching for posts. |
| 1964 | * The list of English stopwords is the approximate search engines list. MySQL has a much longer default list of full-text stopwords. |
| 1965 | * |
| 1966 | * @since 3.7.0 |
| 1967 | * |
| 1968 | * @param array Terms to check |
| 1969 | * @return array |
| 1970 | */ |
| 1971 | protected function parse_search_terms( $terms ) { |
| 1972 | $strtolower_func = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower'; |
| 1973 | $checked = $stopwords = array(); |
| 1974 | |
| 1975 | if ( ! isset( $this->stopwords ) ) { |
| 1976 | // @todo translator comment |
| 1977 | $this->stopwords = 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,the,www', |
| 1978 | 'Comma separated list of common words to exclude when searching (stopwords).' ) ); |
| 1979 | |
| 1980 | $this->stopwords = array_filter( array_map( 'trim', $this->stopwords ) ); |
| 1981 | $this->stopwords = apply_filters( 'wp_search_stopwords', $this->stopwords ); |
| 1982 | } |
| 1983 | |
| 1984 | foreach ( $terms as $term ) { |
| 1985 | // keep before/after spaces when term is for exact match |
| 1986 | if ( preg_match( '/^".+"$/', $term ) ) |
| 1987 | $term = trim( $term, "\"'" ); |
| 1988 | else |
| 1989 | $term = trim( $term, "\"' " ); |
| 1990 | |
| 1991 | // \p{L} matches a single letter that is not a Chinese, Japanese, etc. char |
| 1992 | if ( ! $term || preg_match( '/^\p{L}$/u', $term ) ) |
| 1993 | continue; |
| 1994 | |
| 1995 | if ( in_array( $strtolower_func( $term ), $this->stopwords, true ) ) |
| 1996 | continue; |
| 1997 | |
| 1998 | $checked[] = $term; |
| 1999 | } |
| 2000 | |
| 2001 | return $checked; |
| 2002 | } |
| 2003 | |
| 2004 | /** |
| 2005 | * Generate SQL for the ORDER BY condition based on passed search terms. |
| 2006 | * |
| 2007 | * @global wpdb $wpdb |
| 2008 | * @param array $q |
| 2009 | * @return string |
| 2010 | */ |
| 2011 | function parse_search_order( &$q ) { |
| 2012 | global $wpdb; |
| 2013 | |
| 2014 | $search_orderby = ''; |
| 2015 | // Allow plugins to override sorting on 'post_title' and/or 'post_content'. |
| 2016 | // Passing an empty array would disable sorting. |
| 2017 | $search_orderby_on = apply_filters( 'posts_search_orderby_on', array( 'post_title', 'post_content' ), $this ); |
| 2018 | |
| 2019 | if ( ! empty( $search_orderby_on ) ) { |
| 2020 | if ( $q['num_all_terms'] > 1 ) { |
| 2021 | $num_terms = count( $q['search_orderby_title'] ); |
| 2022 | $search_orderby_s = esc_sql( like_escape( $q['s'] ) ); |
| 2023 | |
| 2024 | if ( in_array( 'post_title', $search_orderby_on, true ) ) { |
| 2025 | $search_orderby = '(CASE '; |
| 2026 | // sentence match in 'post_title' |
| 2027 | $search_orderby .= "WHEN $wpdb->posts.post_title LIKE '%{$search_orderby_s}%' THEN 1 "; |
| 2028 | |
| 2029 | // sanity limit, sort as sentence when more than 6 terms |
| 2030 | // (few searches are longer than 6 terms and most titles are not) |
| 2031 | if ( $num_terms < 7 ) { |
| 2032 | // all words in title |
| 2033 | $search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 2 '; |
| 2034 | // any word in title, not needed when $num_terms == 1 |
| 2035 | if ( $num_terms > 1 ) |
| 2036 | $search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 3 '; |
| 2037 | } |
| 2038 | |
| 2039 | // sentence match in 'post_content' |
| 2040 | if ( in_array( 'post_content', $search_orderby_on, true ) ) |
| 2041 | $search_orderby .= "WHEN $wpdb->posts.post_content LIKE '%{$search_orderby_s}%' THEN 4 "; |
| 2042 | |
| 2043 | $search_orderby .= 'ELSE 5 END)'; |
| 2044 | } elseif ( in_array( 'post_content', $search_orderby_on, true ) ) { |
| 2045 | // Not sorting on 'post_title', order by sentence matches in 'post_content' |
| 2046 | $search_orderby = "$wpdb->posts.post_content LIKE '%{$search_orderby_s}%' DESC"; |
| 2047 | } |
| 2048 | } elseif ( in_array( 'post_title', $search_orderby_on, true ) ) { |
| 2049 | // single word or sentence search |
| 2050 | $search_orderby = reset( $q['search_orderby_title'] ) . ' DESC'; |
| 2051 | } |
| 2052 | } |
| 2053 | // Allow plugins to add/remove/modify the 'order by' for the search section of the database query |
| 2054 | return apply_filters( 'posts_search_orderby', $search_orderby, $this ); |
| 2055 | } |
| 2056 | |
| 2057 | /** |