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