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