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