Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
16.28% covered (danger)
16.28%
7 / 43
11.76% covered (danger)
11.76%
2 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
SearchResultSet
16.28% covered (danger)
16.28%
7 / 43
11.76% covered (danger)
11.76%
2 / 17
391.76
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 numRows
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 count
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTotalHits
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasRewrittenQuery
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryAfterRewrite
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryAfterRewriteSnippet
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasSuggestion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSuggestionQuery
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSuggestionSnippet
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInterwikiResults
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasInterwikiResults
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 searchContainedSyntax
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasMoreResults
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 shrink
62.50% covered (warning)
62.50%
5 / 8
0.00% covered (danger)
0.00%
0 / 1
3.47
 extractResults
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 extractTitles
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Search result sets
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Search
8 */
9
10use MediaWiki\Title\Title;
11use Wikimedia\HtmlArmor\HtmlArmor;
12
13/**
14 * @ingroup Search
15 */
16class SearchResultSet extends BaseSearchResultSet {
17
18    use SearchResultSetTrait;
19
20    /** @var bool */
21    protected $containedSyntax = false;
22
23    /**
24     * Cache of titles.
25     * Lists titles of the result set, in the same order as results.
26     * @var Title[]|null
27     */
28    private $titles;
29
30    /**
31     * Cache of results - serialization of the result iterator
32     * as an array.
33     * @var SearchResult[]
34     */
35    protected $results;
36
37    /**
38     * @var bool True when there are more pages of search results available.
39     */
40    private $hasMoreResults;
41
42    /**
43     * @param bool $containedSyntax True when query is not requesting a simple
44     *  term match
45     * @param bool $hasMoreResults True when there are more pages of search
46     *  results available.
47     */
48    public function __construct( $containedSyntax = false, $hasMoreResults = false ) {
49        if ( static::class === self::class ) {
50            // This class will eventually be abstract. SearchEngine implementations
51            // already have to extend this class anyways to provide the actual
52            // search results.
53            wfDeprecated( __METHOD__, '1.32' );
54        }
55        $this->containedSyntax = $containedSyntax;
56        $this->hasMoreResults = $hasMoreResults;
57    }
58
59    /** @inheritDoc */
60    public function numRows() {
61        return $this->count();
62    }
63
64    final public function count(): int {
65        return count( $this->extractResults() );
66    }
67
68    /**
69     * Some search modes return a total hit count for the query
70     * in the entire article database. This may include pages
71     * in namespaces that would not be matched on the given
72     * settings.
73     *
74     * Return null if no total hits number is supported.
75     *
76     * @return int|null
77     */
78    public function getTotalHits() {
79        return null;
80    }
81
82    /**
83     * Some search modes will run an alternative query that it thinks gives
84     * a better result than the provided search. Returns true if this has
85     * occurred.
86     *
87     * @return bool
88     */
89    public function hasRewrittenQuery() {
90        return false;
91    }
92
93    /**
94     * @return string|null The search the query was internally rewritten to,
95     *  or null when the result of the original query was returned.
96     */
97    public function getQueryAfterRewrite() {
98        return null;
99    }
100
101    /**
102     * @return HtmlArmor|string|null Same as self::getQueryAfterRewrite(), but
103     *  with changes highlighted if HtmlArmor is returned. Null when the query
104     *  was not rewritten.
105     */
106    public function getQueryAfterRewriteSnippet() {
107        return null;
108    }
109
110    /**
111     * Some search modes return a suggested alternate term if there are
112     * no exact hits. Returns true if there is one on this set.
113     *
114     * @return bool
115     */
116    public function hasSuggestion() {
117        return false;
118    }
119
120    /**
121     * @return string|null Suggested query, null if none
122     */
123    public function getSuggestionQuery() {
124        return null;
125    }
126
127    /**
128     * @return HtmlArmor|string HTML highlighted suggested query, '' if none
129     */
130    public function getSuggestionSnippet() {
131        return '';
132    }
133
134    /**
135     * Return a result set of hits on other (multiple) wikis associated with this one
136     *
137     * @param int $type
138     * @return ISearchResultSet[]|null
139     */
140    public function getInterwikiResults( $type = self::SECONDARY_RESULTS ) {
141        return null;
142    }
143
144    /**
145     * Check if there are results on other wikis
146     *
147     * @param int $type
148     * @return bool
149     */
150    public function hasInterwikiResults( $type = self::SECONDARY_RESULTS ) {
151        return false;
152    }
153
154    /**
155     * Did the search contain search syntax?  If so, Special:Search won't offer
156     * the user a link to a create a page named by the search string because the
157     * name would contain the search syntax.
158     * @return bool
159     */
160    public function searchContainedSyntax() {
161        return $this->containedSyntax;
162    }
163
164    /**
165     * @return bool True when there are more pages of search results available.
166     */
167    public function hasMoreResults() {
168        return $this->hasMoreResults;
169    }
170
171    /**
172     * @param int $limit Shrink result set to $limit and flag
173     *  if more results are available.
174     */
175    public function shrink( $limit ) {
176        if ( $this->count() > $limit ) {
177            $this->hasMoreResults = true;
178            // shrinking result set for implementations that
179            // have not implemented extractResults and use
180            // the default cache location. Other implementations
181            // must override this as well.
182            if ( is_array( $this->results ) ) {
183                $this->results = array_slice( $this->results, 0, $limit );
184                $this->titles = null;
185            } else {
186                throw new \UnexpectedValueException(
187                    "When overriding result store extending classes must "
188                    . " also override " . __METHOD__ );
189            }
190        }
191    }
192
193    /**
194     * Extract all the results in the result set as array.
195     * @return SearchResult[]
196     */
197    public function extractResults() {
198        if ( $this->results === null ) {
199            $this->results = [];
200            if ( $this->numRows() == 0 ) {
201                // Don't bother if we've got empty result
202                return $this->results;
203            }
204            $this->rewind();
205            foreach ( $this as $result ) {
206                $this->results[] = $result;
207            }
208            $this->rewind();
209        }
210        return $this->results;
211    }
212
213    /**
214     * Extract all the titles in the result set.
215     * @return Title[]
216     */
217    public function extractTitles() {
218        if ( $this->titles === null ) {
219            if ( $this->numRows() == 0 ) {
220                // Don't bother if we've got empty result
221                $this->titles = [];
222            } else {
223                $this->titles = array_map(
224                    static function ( SearchResult $result ) {
225                        return $result->getTitle();
226                    },
227                    $this->extractResults() );
228            }
229        }
230        return $this->titles;
231    }
232}