Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
3.70% covered (danger)
3.70%
1 / 27
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SearchDatabase
3.85% covered (danger)
3.85%
1 / 26
16.67% covered (danger)
16.67%
1 / 6
118.57
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 doSearchText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doSearchTextInDB
n/a
0 / 0
n/a
0 / 0
0
 doSearchTitle
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 doSearchTitleInDB
n/a
0 / 0
n/a
0 / 0
0
 filter
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 extractNamespacePrefix
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 regexTerm
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Database search engine
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Search
8 */
9
10namespace MediaWiki\Search;
11
12use MediaWiki\MediaWikiServices;
13use MediaWiki\Status\Status;
14use Wikimedia\Rdbms\DBQueryError;
15use Wikimedia\Rdbms\IConnectionProvider;
16
17/**
18 * Base search engine base class for database-backed searches
19 * @stable to extend
20 * @ingroup Search
21 * @since 1.23
22 */
23abstract class SearchDatabase extends SearchEngine {
24    /**
25     * @var string[] search terms
26     */
27    protected $searchTerms = [];
28    protected IConnectionProvider $dbProvider;
29
30    public function __construct( IConnectionProvider $dbProvider ) {
31        $this->dbProvider = $dbProvider;
32    }
33
34    /**
35     * @param string $term
36     * @return ISearchResultSet|Status|null
37     */
38    final public function doSearchText( $term ) {
39        return $this->doSearchTextInDB( $this->extractNamespacePrefix( $term ) );
40    }
41
42    /**
43     * Perform a full text search query and return a result set.
44     *
45     * @param string $term Raw search term
46     * @return SqlSearchResultSet|null
47     */
48    abstract protected function doSearchTextInDB( $term );
49
50    /**
51     * @param string $term
52     * @return ISearchResultSet|null
53     */
54    final public function doSearchTitle( $term ) {
55        try {
56            return $this->doSearchTitleInDB( $this->extractNamespacePrefix( $term ) );
57        } catch ( DBQueryError $dqe ) {
58            if ( $dqe->errno == 1064 ) {
59                throw new DBQueryError(
60                    $dqe->db,
61                    "Query incompatible with database engine. For more information: " .
62                    "https://bugs.mysql.com/bug.php?id=78485 https://jira.mariadb.org/browse/MDEV-21750 / " .
63                    "https://phabricator.wikimedia.org/T355096",
64                    1064, $dqe->sql, __METHOD__
65                    );
66            } else {
67                throw $dqe;
68            }
69        }
70    }
71
72    /**
73     * Perform a title-only search query and return a result set.
74     *
75     * @param string $term Raw search term
76     * @return SqlSearchResultSet|null
77     */
78    abstract protected function doSearchTitleInDB( $term );
79
80    /**
81     * Return a 'cleaned up' search string
82     *
83     * @param string $text
84     * @return string
85     */
86    protected function filter( $text ) {
87        // List of chars allowed in the search query.
88        // This must include chars used in the search syntax.
89        // Usually " (phrase) or * (wildcards) if supported by the engine
90        $lc = $this->legalSearchChars( self::CHARS_ALL );
91        return trim( preg_replace( "/[^{$lc}]/", " ", $text ) );
92    }
93
94    /**
95     * Extract the optional namespace prefix and set self::namespaces
96     * accordingly and return the query string
97     * @param string $term
98     * @return string the query string without any namespace prefix
99     */
100    final protected function extractNamespacePrefix( $term ) {
101        $queryAndNs = self::parseNamespacePrefixes( $term );
102        if ( $queryAndNs === false ) {
103            return $term;
104        }
105        $this->namespaces = $queryAndNs[1];
106        return $queryAndNs[0];
107    }
108
109    protected function regexTerm( string $string, ?string $wildcard = null ): string {
110        $regex = preg_quote( $string, '/' );
111        if ( MediaWikiServices::getInstance()->getContentLanguage()->hasWordBreaks() ) {
112            if ( $wildcard ) {
113                // Don't cut off the final bit!
114                $regex = "\b$regex";
115            } else {
116                $regex = "\b$regex\b";
117            }
118        } else {
119            // For Chinese, words may legitimately abut other words in the text literal.
120            // Don't add \b boundary checks... note this could cause false positives
121            // for Latin chars.
122        }
123        return $regex;
124    }
125}
126
127/** @deprecated class alias since 1.46 */
128class_alias( SearchDatabase::class, 'SearchDatabase' );