Merge "maintenance: Script to rename titles for Unicode uppercasing changes"
[lhc/web/wiklou.git] / includes / search / SearchResult.php
1 <?php
2 /**
3 * Search engine result
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 use MediaWiki\MediaWikiServices;
25
26 /**
27 * @todo FIXME: This class is horribly factored. It would probably be better to
28 * have a useful base class to which you pass some standard information, then
29 * let the fancy self-highlighters extend that.
30 * @ingroup Search
31 */
32 class SearchResult {
33
34 /**
35 * @var Revision
36 */
37 protected $mRevision = null;
38
39 /**
40 * @var File
41 */
42 protected $mImage = null;
43
44 /**
45 * @var Title
46 */
47 protected $mTitle;
48
49 /**
50 * @var string
51 */
52 protected $mText;
53
54 /**
55 * @var SearchEngine
56 */
57 protected $searchEngine;
58
59 /**
60 * A function returning a set of extension data.
61 * @var Closure|null
62 */
63 protected $extensionData;
64
65 /**
66 * Return a new SearchResult and initializes it with a title.
67 *
68 * @param Title $title
69 * @param SearchResultSet|null $parentSet
70 * @return SearchResult
71 */
72 public static function newFromTitle( $title, SearchResultSet $parentSet = null ) {
73 $result = new static();
74 $result->initFromTitle( $title );
75 if ( $parentSet ) {
76 $parentSet->augmentResult( $result );
77 }
78 return $result;
79 }
80
81 /**
82 * Initialize from a Title and if possible initializes a corresponding
83 * Revision and File.
84 *
85 * @param Title $title
86 */
87 protected function initFromTitle( $title ) {
88 $this->mTitle = $title;
89 $services = MediaWikiServices::getInstance();
90 if ( !is_null( $this->mTitle ) ) {
91 $id = false;
92 Hooks::run( 'SearchResultInitFromTitle', [ $title, &$id ] );
93 $this->mRevision = Revision::newFromTitle(
94 $this->mTitle, $id, Revision::READ_NORMAL );
95 if ( $this->mTitle->getNamespace() === NS_FILE ) {
96 $this->mImage = $services->getRepoGroup()->findFile( $this->mTitle );
97 }
98 }
99 $this->searchEngine = $services->newSearchEngine();
100 }
101
102 /**
103 * Check if this is result points to an invalid title
104 *
105 * @return bool
106 */
107 function isBrokenTitle() {
108 return is_null( $this->mTitle );
109 }
110
111 /**
112 * Check if target page is missing, happens when index is out of date
113 *
114 * @return bool
115 */
116 function isMissingRevision() {
117 return !$this->mRevision && !$this->mImage;
118 }
119
120 /**
121 * @return Title
122 */
123 function getTitle() {
124 return $this->mTitle;
125 }
126
127 /**
128 * Get the file for this page, if one exists
129 * @return File|null
130 */
131 function getFile() {
132 return $this->mImage;
133 }
134
135 /**
136 * Lazy initialization of article text from DB
137 */
138 protected function initText() {
139 if ( !isset( $this->mText ) ) {
140 if ( $this->mRevision != null ) {
141 $this->mText = $this->searchEngine->getTextFromContent(
142 $this->mTitle, $this->mRevision->getContent() );
143 } else { // TODO: can we fetch raw wikitext for commons images?
144 $this->mText = '';
145 }
146 }
147 }
148
149 /**
150 * @param string[] $terms Terms to highlight (this parameter is deprecated and ignored)
151 * @return string Highlighted text snippet, null (and not '') if not supported
152 */
153 function getTextSnippet( $terms = [] ) {
154 return '';
155 }
156
157 /**
158 * @return string Highlighted title, '' if not supported
159 */
160 function getTitleSnippet() {
161 return '';
162 }
163
164 /**
165 * @return string Highlighted redirect name (redirect to this page), '' if none or not supported
166 */
167 function getRedirectSnippet() {
168 return '';
169 }
170
171 /**
172 * @return Title|null Title object for the redirect to this page, null if none or not supported
173 */
174 function getRedirectTitle() {
175 return null;
176 }
177
178 /**
179 * @return string Highlighted relevant section name, null if none or not supported
180 */
181 function getSectionSnippet() {
182 return '';
183 }
184
185 /**
186 * @return Title|null Title object (pagename+fragment) for the section,
187 * null if none or not supported
188 */
189 function getSectionTitle() {
190 return null;
191 }
192
193 /**
194 * @return string Highlighted relevant category name or '' if none or not supported
195 */
196 public function getCategorySnippet() {
197 return '';
198 }
199
200 /**
201 * @return string Timestamp
202 */
203 function getTimestamp() {
204 if ( $this->mRevision ) {
205 return $this->mRevision->getTimestamp();
206 } elseif ( $this->mImage ) {
207 return $this->mImage->getTimestamp();
208 }
209 return '';
210 }
211
212 /**
213 * @return int Number of words
214 */
215 function getWordCount() {
216 $this->initText();
217 return str_word_count( $this->mText );
218 }
219
220 /**
221 * @return int Size in bytes
222 */
223 function getByteSize() {
224 $this->initText();
225 return strlen( $this->mText );
226 }
227
228 /**
229 * @return string Interwiki prefix of the title (return iw even if title is broken)
230 */
231 function getInterwikiPrefix() {
232 return '';
233 }
234
235 /**
236 * @return string Interwiki namespace of the title (since we likely can't resolve it locally)
237 */
238 function getInterwikiNamespaceText() {
239 return '';
240 }
241
242 /**
243 * Did this match file contents (eg: PDF/DJVU)?
244 * @return bool
245 */
246 function isFileMatch() {
247 return false;
248 }
249
250 /**
251 * Get the extension data as:
252 * augmentor name => data
253 * @return array[]
254 */
255 public function getExtensionData() {
256 if ( $this->extensionData ) {
257 return call_user_func( $this->extensionData );
258 } else {
259 return [];
260 }
261 }
262
263 /**
264 * Set extension data for this result.
265 * The data is:
266 * augmentor name => data
267 * @param Closure|array $extensionData Takes no arguments, returns
268 * either array of extension data or null.
269 */
270 public function setExtensionData( $extensionData ) {
271 if ( $extensionData instanceof Closure ) {
272 $this->extensionData = $extensionData;
273 } elseif ( is_array( $extensionData ) ) {
274 wfDeprecated( __METHOD__ . ' with array argument', '1.32' );
275 $this->extensionData = function () use ( $extensionData ) {
276 return $extensionData;
277 };
278 } else {
279 $type = is_object( $extensionData )
280 ? get_class( $extensionData )
281 : gettype( $extensionData );
282 throw new \InvalidArgumentException(
283 __METHOD__ . " must be called with Closure|array, but received $type" );
284 }
285 }
286 }