Make WordPress Core

Ticket #7394: 7394.5.diff

File 7394.5.diff, 12.2 KB (added by wonderboymusic, 11 years ago)
  • tests/phpunit/tests/query/search.php

     
     1<?php
     2/**
     3 *
     4 * @group query
     5 * @group search
     6 */
     7class Tests_Search_Results extends WP_UnitTestCase {
     8        protected $q;
     9        protected $post_type;
     10
     11        function setUp() {
     12                parent::setUp();
     13
     14                $this->post_type = rand_str( 12 );
     15                register_post_type( $this->post_type );
     16
     17                $this->q = new WP_Query();
     18        }
     19
     20        function tearDown() {
     21                parent::tearDown();
     22
     23                _unregister_post_type( $this->post_type );
     24                unset( $this->q );
     25        }
     26
     27        function get_search_results( $terms ) {
     28                $args = http_build_query( array( 's' => $terms, 'post_type' => $this->post_type ) );
     29                return $this->q->query( $args );
     30        }
     31
     32        function test_search_order_title_relevance() {
     33                foreach ( range( 1, 7 ) as $i )
     34                        $this->factory->post->create( array( 'post_content' => $i . rand_str() . ' about', 'post_type' => $this->post_type ) );
     35                $post_id = $this->factory->post->create( array( 'post_title' => 'About', 'post_type' => $this->post_type ) );
     36
     37                $posts = $this->get_search_results( 'About' );
     38                $this->assertEquals( $post_id, reset( $posts )->ID );
     39        }
     40
     41        function test_search_order_content_relevance() {
     42                $post_id1 = $this->factory->post->create( array( 'post_title' => 'About', 'post_type' => $this->post_type ) );
     43                $post_id2 = $this->factory->post->create( array( 'post_content' => 'About', 'post_type' => $this->post_type ) );
     44
     45                $posts1 = $this->get_search_results( 'About' );
     46                $this->assertEquals( $post_id1, reset( $posts1 )->ID );
     47
     48                add_filter( 'posts_search_orderby_on', array( $this, 'filter_posts_search_orderby_on' ) );
     49
     50                $posts2 = $this->get_search_results( 'About' );
     51                $this->assertEquals( $post_id2, reset( $posts2 )->ID );
     52
     53                remove_filter( 'posts_search_orderby_on', array( $this, 'filter_posts_search_orderby_on' ) );
     54        }
     55
     56        function filter_posts_search_orderby_on() {
     57                return array( 'post_content' );
     58        }
     59
     60        function test_search_terms_query_var() {
     61                $terms = 'This is a search term';
     62                $query = new WP_Query( array( 's' => 'This is a search term' ) );
     63                $this->assertNotEquals( explode( ' ', $terms ), $query->get( 'search_terms' ) );
     64                $this->assertEquals( array( 'search', 'term' ), $query->get( 'search_terms' ) );
     65        }
     66
     67        function test_filter_stopwords() {
     68                $terms = 'This is a search term';
     69                add_filter( 'wp_search_stopwords', array( $this, 'filter_wp_search_stopwords' ) );
     70                $query = new WP_Query( array( 's' => $terms ) );
     71                remove_filter( 'wp_search_stopwords', array( $this, 'filter_wp_search_stopwords' ) );
     72
     73                $this->assertNotEquals( array( 'search', 'term' ), $query->get( 'search_terms' ) );
     74                $this->assertEquals( array( 'This', 'is', 'search', 'term' ), $query->get( 'search_terms' ) );
     75        }
     76
     77        function filter_wp_search_stopwords() {
     78                return array();
     79        }
     80}
     81 No newline at end of file
  • src/wp-includes/functions.php

     
    37033703}
    37043704
    37053705/**
    3706  * Used internally to tidy up the search terms.
    3707  *
    3708  * @access private
    3709  * @since 2.9.0
    3710  *
    3711  * @param string $t
    3712  * @return string
    3713  */
    3714 function _search_terms_tidy($t) {
    3715         return trim($t, "\"'\n\r ");
    3716 }
    3717 
    3718 /**
    37193706 * Returns true.
    37203707 *
    37213708 * Useful for returning true to filters easily.
     
    40424029}
    40434030
    40444031/**
     4032 * Check if the terms are suitable for searching.
     4033 *
     4034 * Includes array of stopwords (terms) that are excluded from the separate term matching when searching for posts.
     4035 * The list of English stopwords is the approximate search engines list. MySQL has a much longer default list of full-text stopwords.
     4036 *
     4037 * @since 3.7.0
     4038 * @access private
     4039 *
     4040 * @param array Terms to check
     4041 * @return array
     4042 */
     4043function _check_search_terms( $terms ) {
     4044        $strtolower_func = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
     4045        $checked = $stopwords = array();
     4046
     4047        $_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,the,www', 'Comma separated list of common words to exclude when searching (stopwords).' ) );
     4048
     4049        foreach( $_words as $word ) {
     4050                $word = trim( $word, "\r\n\t " );
     4051                if ( ! $word )
     4052                        continue;
     4053                $stopwords[] = $word;
     4054        }
     4055
     4056        $stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
     4057
     4058        foreach ( $terms as $term ) {
     4059                // keep before/after spaces when term is for exact match
     4060                if ( preg_match( '/^".+"$/', $term ) )
     4061                        $term = trim( $term, "\"'" );
     4062                else
     4063                        $term = trim( $term, "\"' " );
     4064
     4065                // \p{L} matches a single letter that is not a Chinese, Japanese, etc. char
     4066                if ( ! $term || preg_match( '/^\p{L}$/u', $term ) )
     4067                        continue;
     4068
     4069                if ( in_array( $strtolower_func( $term ), $stopwords, true ) )
     4070                        continue;
     4071
     4072                $checked[] = $term;
     4073        }
     4074
     4075        return $checked;
     4076}
     4077
     4078/**
    40454079 * Load the auth check for monitoring whether the user is still logged in.
    40464080 *
    40474081 * Can be disabled with remove_action( 'admin_enqueue_scripts', 'wp_auth_check_load' );
  • src/wp-includes/query.php

     
    18891889        }
    18901890
    18911891        /**
     1892         * Generate SQL for the WHERE clause based on passed search terms.
     1893         *
     1894         * @since 3.7.0
     1895         *
     1896         * @global type $wpdb
     1897         * @param array $q
     1898         * @param string $search
     1899         */
     1900        function parse_search( &$q, $search ) {
     1901                global $wpdb;
     1902
     1903                // added slashes screw with quote grouping when done early, so done later
     1904                $q['s'] = stripslashes( $q['s'] );
     1905                if ( empty( $_GET['s'] ) && $this->is_main_query() )
     1906                        $q['s'] = urldecode( $q['s'] );
     1907                // there are no line breaks in <input /> fields
     1908                $q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] );
     1909                $q['num_all_terms'] = 1;
     1910                if ( ! empty( $q['sentence'] ) ) {
     1911                        $q['search_terms'] = array( $q['s'] );
     1912                } else {
     1913                        if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) {
     1914                                $q['num_all_terms'] = count( $matches[0] );
     1915                                $q['search_terms'] = _check_search_terms( $matches[0] );
     1916                                // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
     1917                                if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 )
     1918                                        $q['search_terms'] = array( $q['s'] );
     1919                        } else {
     1920                                $q['search_terms'] = array( $q['s'] );
     1921                        }
     1922                }
     1923
     1924                $n = ! empty( $q['exact'] ) ? '' : '%';
     1925                $searchand = '';
     1926                $q['search_orderby_title'] = array();
     1927                foreach ( $q['search_terms'] as $term ) {
     1928                        $term = esc_sql( like_escape( $term ) );
     1929                        if ( $n )
     1930                                $q['search_orderby_title'][] = "$wpdb->posts.post_title LIKE '%$term%'";
     1931
     1932                        $search .= "{$searchand}(($wpdb->posts.post_title LIKE '{$n}{$term}{$n}') OR ($wpdb->posts.post_content LIKE '{$n}{$term}{$n}'))";
     1933                        $searchand = ' AND ';
     1934                }
     1935
     1936                if ( ! empty( $search ) ) {
     1937                        $search = " AND ({$search}) ";
     1938                        if ( ! is_user_logged_in() )
     1939                                $search .= " AND ($wpdb->posts.post_password = '') ";
     1940                }
     1941
     1942                // Allow plugins to contextually add/remove/modify the search section of the database query
     1943                return apply_filters( 'posts_search', $search, $this );
     1944        }
     1945
     1946        /**
     1947         * Generate SQL for the ORDER BY condition based on passed search terms.
     1948         *
     1949         * @global wpdb $wpdb
     1950         * @param array $q
     1951         * @return string
     1952         */
     1953        function parse_search_order( &$q ) {
     1954                global $wpdb;
     1955
     1956                $search_orderby = '';
     1957                // Allow plugins to override sorting on 'post_title' and/or 'post_content'.
     1958                // Passing an empty array would disable sorting.
     1959                $search_orderby_on = apply_filters( 'posts_search_orderby_on', array( 'post_title', 'post_content' ), $this );
     1960
     1961                if ( ! empty( $search_orderby_on ) ) {
     1962                        if ( $q['num_all_terms'] > 1 ) {
     1963                                $num_terms = count( $q['search_orderby_title'] );
     1964                                $search_orderby_s = esc_sql( like_escape( $q['s'] ) );
     1965
     1966                                if ( in_array( 'post_title', $search_orderby_on, true ) ) {
     1967                                        $search_orderby = '(CASE ';
     1968                                        // sentence match in 'post_title'
     1969                                        $search_orderby .= "WHEN $wpdb->posts.post_title LIKE '%{$search_orderby_s}%' THEN 1 ";
     1970
     1971                                        // sanity limit, sort as sentence when more than 6 terms
     1972                                        // (few searches are longer than 6 terms and most titles are not)
     1973                                        if ( $num_terms < 7 ) {
     1974                                                // all words in title
     1975                                                $search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 2 ';
     1976                                                // any word in title, not needed when $num_terms == 1
     1977                                                if ( $num_terms > 1 )
     1978                                                        $search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 3 ';
     1979                                        }
     1980
     1981                                        // sentence match in 'post_content'
     1982                                        if ( in_array( 'post_content', $search_orderby_on, true ) )
     1983                                                $search_orderby .= "WHEN $wpdb->posts.post_content LIKE '%{$search_orderby_s}%' THEN 4 ";
     1984
     1985                                        $search_orderby .= 'ELSE 5 END)';
     1986                                } elseif ( in_array( 'post_content', $search_orderby_on, true ) ) {
     1987                                        // Not sorting on 'post_title', order by sentence matches in 'post_content'
     1988                                        $search_orderby = "$wpdb->posts.post_content LIKE '%{$search_orderby_s}%' DESC";
     1989                                }
     1990                        } elseif ( in_array( 'post_title', $search_orderby_on, true ) ) {
     1991                                // single word or sentence search
     1992                                $search_orderby = reset( $q['search_orderby_title'] ) . ' DESC';
     1993                        }
     1994                }
     1995                // Allow plugins to add/remove/modify the 'order by' for the search section of the database query
     1996                return apply_filters( 'posts_search_orderby', $search_orderby, $this );
     1997        }
     1998
     1999        /**
    18922000         * Sets the 404 property and saves whether query is feed.
    18932001         *
    18942002         * @since 2.0.0
     
    22302338                }
    22312339
    22322340                // If a search pattern is specified, load the posts that match
    2233                 if ( !empty($q['s']) ) {
    2234                         // added slashes screw with quote grouping when done early, so done later
    2235                         $q['s'] = stripslashes($q['s']);
    2236                         if ( empty( $_GET['s'] ) && $this->is_main_query() )
    2237                                 $q['s'] = urldecode($q['s']);
    2238                         if ( !empty($q['sentence']) ) {
    2239                                 $q['search_terms'] = array($q['s']);
    2240                         } else {
    2241                                 preg_match_all('/".*?("|$)|((?<=[\r\n\t ",+])|^)[^\r\n\t ",+]+/', $q['s'], $matches);
    2242                                 $q['search_terms'] = array_map('_search_terms_tidy', $matches[0]);
    2243                         }
    2244                         $n = !empty($q['exact']) ? '' : '%';
    2245                         $searchand = '';
    2246                         foreach( (array) $q['search_terms'] as $term ) {
    2247                                 $term = esc_sql( like_escape( $term ) );
    2248                                 $search .= "{$searchand}(($wpdb->posts.post_title LIKE '{$n}{$term}{$n}') OR ($wpdb->posts.post_content LIKE '{$n}{$term}{$n}'))";
    2249                                 $searchand = ' AND ';
    2250                         }
     2341                // Sanity check: search string shouldn't be more than 1600 characters.
     2342                // See ticket #21688 for more info.
     2343                if ( ! empty( $q['s'] ) && strlen( $q['s'] ) < 1600 )
     2344                        $search = $this->parse_search( $q, $search );
    22512345
    2252                         if ( !empty($search) ) {
    2253                                 $search = " AND ({$search}) ";
    2254                                 if ( !is_user_logged_in() )
    2255                                         $search .= " AND ($wpdb->posts.post_password = '') ";
    2256                         }
    2257                 }
    2258 
    2259                 // Allow plugins to contextually add/remove/modify the search section of the database query
    2260                 $search = apply_filters_ref_array('posts_search', array( $search, &$this ) );
    2261 
    22622346                // Taxonomies
    22632347                if ( !$this->is_singular ) {
    22642348                        $this->parse_tax_query( $q );
     
    24552539                                $orderby .= " {$q['order']}";
    24562540                }
    24572541
     2542                // Order search results by relevance when another "orderby" is not specified in the query
     2543                if ( ! empty( $q['search_orderby_title'] ) && empty( $q['orderby'] ) ) {
     2544                        $search_orderby = $this->parse_search_order( $q );
     2545                        if ( $search_orderby )
     2546                                $orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby;
     2547                }
     2548
    24582549                if ( is_array( $post_type ) && count( $post_type ) > 1 ) {
    24592550                        $post_type_cap = 'multiple_post_type';
    24602551                } else {
  • src/wp-includes/deprecated.php

     
    33173317function _save_post_hook() {}
    33183318
    33193319/**
     3320 * Formerly used internally to tidy up the search terms.
     3321 *
     3322 * @access private
     3323 * @since 2.9.0
     3324 * @deprecated 3.7.0
     3325 */
     3326function _search_terms_tidy( $t ) {
     3327        _deprecated_function( __FUNCTION__, '3.5', '' );
     3328        return trim( $t, "\"'\n\r " );
     3329}
     3330
     3331/**
    33203332 * Check if the installed version of GD supports particular image type
    33213333 *
    33223334 * @since 2.9.0