This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Prefix search of page names. |
||
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 | */ |
||
22 | |||
23 | /** |
||
24 | * Handles searching prefixes of titles and finding any page |
||
25 | * names that match. Used largely by the OpenSearch implementation. |
||
26 | * @deprecated Since 1.27, Use SearchEngine::prefixSearchSubpages or SearchEngine::completionSearch |
||
27 | * |
||
28 | * @ingroup Search |
||
29 | */ |
||
30 | abstract class PrefixSearch { |
||
31 | /** |
||
32 | * Do a prefix search of titles and return a list of matching page names. |
||
33 | * @deprecated Since 1.23, use TitlePrefixSearch or StringPrefixSearch classes |
||
34 | * |
||
35 | * @param string $search |
||
36 | * @param int $limit |
||
37 | * @param array $namespaces Used if query is not explicitly prefixed |
||
38 | * @param int $offset How many results to offset from the beginning |
||
39 | * @return array Array of strings |
||
40 | */ |
||
41 | public static function titleSearch( $search, $limit, $namespaces = [], $offset = 0 ) { |
||
42 | $prefixSearch = new StringPrefixSearch; |
||
43 | return $prefixSearch->search( $search, $limit, $namespaces, $offset ); |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * Do a prefix search of titles and return a list of matching page names. |
||
48 | * |
||
49 | * @param string $search |
||
50 | * @param int $limit |
||
51 | * @param array $namespaces Used if query is not explicitly prefixed |
||
52 | * @param int $offset How many results to offset from the beginning |
||
53 | * @return array Array of strings or Title objects |
||
54 | */ |
||
55 | public function search( $search, $limit, $namespaces = [], $offset = 0 ) { |
||
56 | $search = trim( $search ); |
||
57 | if ( $search == '' ) { |
||
58 | return []; // Return empty result |
||
59 | } |
||
60 | |||
61 | $hasNamespace = $this->extractNamespace( $search ); |
||
62 | if ( $hasNamespace ) { |
||
63 | list( $namespace, $search ) = $hasNamespace; |
||
64 | $namespaces = [ $namespace ]; |
||
65 | } else { |
||
66 | $namespaces = $this->validateNamespaces( $namespaces ); |
||
67 | Hooks::run( 'PrefixSearchExtractNamespace', [ &$namespaces, &$search ] ); |
||
68 | } |
||
69 | |||
70 | return $this->searchBackend( $namespaces, $search, $limit, $offset ); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Figure out if given input contains an explicit namespace. |
||
75 | * |
||
76 | * @param string $input |
||
77 | * @return false|array Array of namespace and remaining text, or false if no namespace given. |
||
78 | */ |
||
79 | protected function extractNamespace( $input ) { |
||
80 | if ( strpos( $input, ':' ) === false ) { |
||
81 | return false; |
||
82 | } |
||
83 | |||
84 | // Namespace prefix only |
||
85 | $title = Title::newFromText( $input . 'Dummy' ); |
||
86 | View Code Duplication | if ( |
|
87 | $title && |
||
88 | $title->getText() === 'Dummy' && |
||
89 | !$title->inNamespace( NS_MAIN ) && |
||
90 | !$title->isExternal() |
||
91 | ) { |
||
92 | return [ $title->getNamespace(), '' ]; |
||
93 | } |
||
94 | |||
95 | // Namespace prefix with additional input |
||
96 | $title = Title::newFromText( $input ); |
||
97 | View Code Duplication | if ( |
|
98 | $title && |
||
99 | !$title->inNamespace( NS_MAIN ) && |
||
100 | !$title->isExternal() |
||
101 | ) { |
||
102 | // getText provides correct capitalization |
||
103 | return [ $title->getNamespace(), $title->getText() ]; |
||
104 | } |
||
105 | |||
106 | return false; |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Do a prefix search for all possible variants of the prefix |
||
111 | * @param string $search |
||
112 | * @param int $limit |
||
113 | * @param array $namespaces |
||
114 | * @param int $offset How many results to offset from the beginning |
||
115 | * |
||
116 | * @return array |
||
117 | */ |
||
118 | public function searchWithVariants( $search, $limit, array $namespaces, $offset = 0 ) { |
||
119 | $searches = $this->search( $search, $limit, $namespaces, $offset ); |
||
120 | |||
121 | // if the content language has variants, try to retrieve fallback results |
||
122 | $fallbackLimit = $limit - count( $searches ); |
||
123 | if ( $fallbackLimit > 0 ) { |
||
124 | global $wgContLang; |
||
125 | |||
126 | $fallbackSearches = $wgContLang->autoConvertToAllVariants( $search ); |
||
127 | $fallbackSearches = array_diff( array_unique( $fallbackSearches ), [ $search ] ); |
||
128 | |||
129 | foreach ( $fallbackSearches as $fbs ) { |
||
130 | $fallbackSearchResult = $this->search( $fbs, $fallbackLimit, $namespaces ); |
||
131 | $searches = array_merge( $searches, $fallbackSearchResult ); |
||
132 | $fallbackLimit -= count( $fallbackSearchResult ); |
||
133 | |||
134 | if ( $fallbackLimit == 0 ) { |
||
135 | break; |
||
136 | } |
||
137 | } |
||
138 | } |
||
139 | return $searches; |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * When implemented in a descendant class, receives an array of Title objects and returns |
||
144 | * either an unmodified array or an array of strings corresponding to titles passed to it. |
||
145 | * |
||
146 | * @param array $titles |
||
147 | * @return array |
||
148 | */ |
||
149 | abstract protected function titles( array $titles ); |
||
150 | |||
151 | /** |
||
152 | * When implemented in a descendant class, receives an array of titles as strings and returns |
||
153 | * either an unmodified array or an array of Title objects corresponding to strings received. |
||
154 | * |
||
155 | * @param array $strings |
||
156 | * |
||
157 | * @return array |
||
158 | */ |
||
159 | abstract protected function strings( array $strings ); |
||
160 | |||
161 | /** |
||
162 | * Do a prefix search of titles and return a list of matching page names. |
||
163 | * @param array $namespaces |
||
164 | * @param string $search |
||
165 | * @param int $limit |
||
166 | * @param int $offset How many results to offset from the beginning |
||
167 | * @return array Array of strings |
||
168 | */ |
||
169 | protected function searchBackend( $namespaces, $search, $limit, $offset ) { |
||
170 | if ( count( $namespaces ) == 1 ) { |
||
171 | $ns = $namespaces[0]; |
||
172 | if ( $ns == NS_MEDIA ) { |
||
173 | $namespaces = [ NS_FILE ]; |
||
174 | } elseif ( $ns == NS_SPECIAL ) { |
||
175 | return $this->titles( $this->specialSearch( $search, $limit, $offset ) ); |
||
176 | } |
||
177 | } |
||
178 | $srchres = []; |
||
179 | if ( Hooks::run( |
||
180 | 'PrefixSearchBackend', |
||
181 | [ $namespaces, $search, $limit, &$srchres, $offset ] |
||
182 | ) ) { |
||
183 | return $this->titles( $this->defaultSearchBackend( $namespaces, $search, $limit, $offset ) ); |
||
184 | } |
||
185 | return $this->strings( |
||
186 | $this->handleResultFromHook( $srchres, $namespaces, $search, $limit, $offset ) ); |
||
187 | } |
||
188 | |||
189 | private function handleResultFromHook( $srchres, $namespaces, $search, $limit, $offset ) { |
||
190 | if ( $offset === 0 ) { |
||
191 | // Only perform exact db match if offset === 0 |
||
192 | // This is still far from perfect but at least we avoid returning the |
||
193 | // same title afain and again when the user is scrolling with a query |
||
194 | // that matches a title in the db. |
||
195 | $rescorer = new SearchExactMatchRescorer(); |
||
196 | $srchres = $rescorer->rescore( $search, $namespaces, $srchres, $limit ); |
||
197 | } |
||
198 | return $srchres; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Prefix search special-case for Special: namespace. |
||
203 | * |
||
204 | * @param string $search Term |
||
205 | * @param int $limit Max number of items to return |
||
206 | * @param int $offset Number of items to offset |
||
207 | * @return array |
||
208 | */ |
||
209 | protected function specialSearch( $search, $limit, $offset ) { |
||
210 | global $wgContLang; |
||
211 | |||
212 | $searchParts = explode( '/', $search, 2 ); |
||
213 | $searchKey = $searchParts[0]; |
||
214 | $subpageSearch = isset( $searchParts[1] ) ? $searchParts[1] : null; |
||
215 | |||
216 | // Handle subpage search separately. |
||
217 | if ( $subpageSearch !== null ) { |
||
218 | // Try matching the full search string as a page name |
||
219 | $specialTitle = Title::makeTitleSafe( NS_SPECIAL, $searchKey ); |
||
220 | if ( !$specialTitle ) { |
||
221 | return []; |
||
222 | } |
||
223 | $special = SpecialPageFactory::getPage( $specialTitle->getText() ); |
||
224 | if ( $special ) { |
||
225 | $subpages = $special->prefixSearchSubpages( $subpageSearch, $limit, $offset ); |
||
226 | return array_map( function ( $sub ) use ( $specialTitle ) { |
||
227 | return $specialTitle->getSubpage( $sub ); |
||
228 | }, $subpages ); |
||
229 | } else { |
||
230 | return []; |
||
231 | } |
||
232 | } |
||
233 | |||
234 | # normalize searchKey, so aliases with spaces can be found - bug 25675 |
||
235 | $searchKey = str_replace( ' ', '_', $searchKey ); |
||
236 | $searchKey = $wgContLang->caseFold( $searchKey ); |
||
237 | |||
238 | // Unlike SpecialPage itself, we want the canonical forms of both |
||
239 | // canonical and alias title forms... |
||
240 | $keys = []; |
||
241 | foreach ( SpecialPageFactory::getNames() as $page ) { |
||
242 | $keys[$wgContLang->caseFold( $page )] = $page; |
||
243 | } |
||
244 | |||
245 | foreach ( $wgContLang->getSpecialPageAliases() as $page => $aliases ) { |
||
246 | if ( !in_array( $page, SpecialPageFactory::getNames() ) ) {# bug 20885 |
||
247 | continue; |
||
248 | } |
||
249 | |||
250 | foreach ( $aliases as $alias ) { |
||
251 | $keys[$wgContLang->caseFold( $alias )] = $alias; |
||
252 | } |
||
253 | } |
||
254 | ksort( $keys ); |
||
255 | |||
256 | $srchres = []; |
||
257 | $skipped = 0; |
||
258 | foreach ( $keys as $pageKey => $page ) { |
||
259 | if ( $searchKey === '' || strpos( $pageKey, $searchKey ) === 0 ) { |
||
260 | // bug 27671: Don't use SpecialPage::getTitleFor() here because it |
||
261 | // localizes its input leading to searches for e.g. Special:All |
||
262 | // returning Spezial:MediaWiki-Systemnachrichten and returning |
||
263 | // Spezial:Alle_Seiten twice when $wgLanguageCode == 'de' |
||
264 | if ( $offset > 0 && $skipped < $offset ) { |
||
265 | $skipped++; |
||
266 | continue; |
||
267 | } |
||
268 | $srchres[] = Title::makeTitleSafe( NS_SPECIAL, $page ); |
||
269 | } |
||
270 | |||
271 | if ( count( $srchres ) >= $limit ) { |
||
272 | break; |
||
273 | } |
||
274 | } |
||
275 | |||
276 | return $srchres; |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * Unless overridden by PrefixSearchBackend hook... |
||
281 | * This is case-sensitive (First character may |
||
282 | * be automatically capitalized by Title::secureAndSpit() |
||
283 | * later on depending on $wgCapitalLinks) |
||
284 | * |
||
285 | * @param array|null $namespaces Namespaces to search in |
||
286 | * @param string $search Term |
||
287 | * @param int $limit Max number of items to return |
||
288 | * @param int $offset Number of items to skip |
||
289 | * @return Title[] Array of Title objects |
||
290 | */ |
||
291 | public function defaultSearchBackend( $namespaces, $search, $limit, $offset ) { |
||
292 | // Backwards compatability with old code. Default to NS_MAIN if no namespaces provided. |
||
293 | if ( $namespaces === null ) { |
||
294 | $namespaces = []; |
||
295 | } |
||
296 | if ( !$namespaces ) { |
||
297 | $namespaces[] = NS_MAIN; |
||
298 | } |
||
299 | |||
300 | // Construct suitable prefix for each namespace. They differ in cases where |
||
301 | // some namespaces always capitalize and some don't. |
||
302 | $prefixes = []; |
||
303 | foreach ( $namespaces as $namespace ) { |
||
304 | // For now, if special is included, ignore the other namespaces |
||
305 | if ( $namespace == NS_SPECIAL ) { |
||
306 | return $this->specialSearch( $search, $limit, $offset ); |
||
307 | } |
||
308 | |||
309 | $title = Title::makeTitleSafe( $namespace, $search ); |
||
310 | // Why does the prefix default to empty? |
||
311 | $prefix = $title ? $title->getDBkey() : ''; |
||
312 | $prefixes[$prefix][] = $namespace; |
||
313 | } |
||
314 | |||
315 | $dbr = wfGetDB( DB_REPLICA ); |
||
316 | // Often there is only one prefix that applies to all requested namespaces, |
||
317 | // but sometimes there are two if some namespaces do not always capitalize. |
||
318 | $conds = []; |
||
319 | foreach ( $prefixes as $prefix => $namespaces ) { |
||
320 | $condition = [ |
||
321 | 'page_namespace' => $namespaces, |
||
322 | 'page_title' . $dbr->buildLike( $prefix, $dbr->anyString() ), |
||
323 | ]; |
||
324 | $conds[] = $dbr->makeList( $condition, LIST_AND ); |
||
325 | } |
||
326 | |||
327 | $table = 'page'; |
||
328 | $fields = [ 'page_id', 'page_namespace', 'page_title' ]; |
||
329 | $conds = $dbr->makeList( $conds, LIST_OR ); |
||
330 | $options = [ |
||
331 | 'LIMIT' => $limit, |
||
332 | 'ORDER BY' => [ 'page_title', 'page_namespace' ], |
||
333 | 'OFFSET' => $offset |
||
334 | ]; |
||
335 | |||
336 | $res = $dbr->select( $table, $fields, $conds, __METHOD__, $options ); |
||
337 | |||
338 | return iterator_to_array( TitleArray::newFromResult( $res ) ); |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Validate an array of numerical namespace indexes |
||
343 | * |
||
344 | * @param array $namespaces |
||
345 | * @return array (default: contains only NS_MAIN) |
||
346 | */ |
||
347 | protected function validateNamespaces( $namespaces ) { |
||
348 | global $wgContLang; |
||
349 | |||
350 | // We will look at each given namespace against wgContLang namespaces |
||
351 | $validNamespaces = $wgContLang->getNamespaces(); |
||
352 | if ( is_array( $namespaces ) && count( $namespaces ) > 0 ) { |
||
353 | $valid = []; |
||
354 | foreach ( $namespaces as $ns ) { |
||
355 | if ( is_numeric( $ns ) && array_key_exists( $ns, $validNamespaces ) ) { |
||
356 | $valid[] = $ns; |
||
357 | } |
||
358 | } |
||
359 | if ( count( $valid ) > 0 ) { |
||
360 | return $valid; |
||
361 | } |
||
362 | } |
||
363 | |||
364 | return [ NS_MAIN ]; |
||
365 | } |
||
366 | } |
||
367 | |||
368 | /** |
||
369 | * Performs prefix search, returning Title objects |
||
370 | * @deprecated Since 1.27, Use SearchEngine::prefixSearchSubpages or SearchEngine::completionSearch |
||
371 | * @ingroup Search |
||
372 | */ |
||
373 | class TitlePrefixSearch extends PrefixSearch { |
||
0 ignored issues
–
show
|
|||
374 | |||
375 | protected function titles( array $titles ) { |
||
376 | return $titles; |
||
377 | } |
||
378 | |||
379 | protected function strings( array $strings ) { |
||
380 | $titles = array_map( 'Title::newFromText', $strings ); |
||
381 | $lb = new LinkBatch( $titles ); |
||
382 | $lb->setCaller( __METHOD__ ); |
||
383 | $lb->execute(); |
||
384 | return $titles; |
||
385 | } |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * Performs prefix search, returning strings |
||
390 | * @deprecated Since 1.27, Use SearchEngine::prefixSearchSubpages or SearchEngine::completionSearch |
||
391 | * @ingroup Search |
||
392 | */ |
||
393 | class StringPrefixSearch extends PrefixSearch { |
||
0 ignored issues
–
show
The class
PrefixSearch has been deprecated with message: Since 1.27, Use SearchEngine::prefixSearchSubpages or SearchEngine::completionSearch
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead. ![]() |
|||
394 | |||
395 | protected function titles( array $titles ) { |
||
396 | return array_map( function ( Title $t ) { |
||
397 | return $t->getPrefixedText(); |
||
398 | }, $titles ); |
||
399 | } |
||
400 | |||
401 | protected function strings( array $strings ) { |
||
402 | return $strings; |
||
403 | } |
||
404 | } |
||
405 |
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.