wikimedia /
mediawiki
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 | * |
||
| 4 | * |
||
| 5 | * Created on July 30, 2007 |
||
| 6 | * |
||
| 7 | * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com" |
||
| 8 | * |
||
| 9 | * This program is free software; you can redistribute it and/or modify |
||
| 10 | * it under the terms of the GNU General Public License as published by |
||
| 11 | * the Free Software Foundation; either version 2 of the License, or |
||
| 12 | * (at your option) any later version. |
||
| 13 | * |
||
| 14 | * This program is distributed in the hope that it will be useful, |
||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 17 | * GNU General Public License for more details. |
||
| 18 | * |
||
| 19 | * You should have received a copy of the GNU General Public License along |
||
| 20 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
| 21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 22 | * http://www.gnu.org/copyleft/gpl.html |
||
| 23 | * |
||
| 24 | * @file |
||
| 25 | */ |
||
| 26 | |||
| 27 | /** |
||
| 28 | * Query module to perform full text search within wiki titles and content |
||
| 29 | * |
||
| 30 | * @ingroup API |
||
| 31 | */ |
||
| 32 | class ApiQuerySearch extends ApiQueryGeneratorBase { |
||
| 33 | use SearchApi; |
||
| 34 | |||
| 35 | /** @var array list of api allowed params */ |
||
| 36 | private $allowedParams; |
||
| 37 | |||
| 38 | public function __construct( ApiQuery $query, $moduleName ) { |
||
| 39 | parent::__construct( $query, $moduleName, 'sr' ); |
||
| 40 | } |
||
| 41 | |||
| 42 | public function execute() { |
||
| 43 | $this->run(); |
||
| 44 | } |
||
| 45 | |||
| 46 | public function executeGenerator( $resultPageSet ) { |
||
| 47 | $this->run( $resultPageSet ); |
||
| 48 | } |
||
| 49 | |||
| 50 | /** |
||
| 51 | * @param ApiPageSet $resultPageSet |
||
| 52 | * @return void |
||
| 53 | */ |
||
| 54 | private function run( $resultPageSet = null ) { |
||
| 55 | global $wgContLang; |
||
| 56 | $params = $this->extractRequestParams(); |
||
| 57 | |||
| 58 | // Extract parameters |
||
| 59 | $query = $params['search']; |
||
| 60 | $what = $params['what']; |
||
| 61 | $interwiki = $params['interwiki']; |
||
| 62 | $searchInfo = array_flip( $params['info'] ); |
||
| 63 | $prop = array_flip( $params['prop'] ); |
||
| 64 | |||
| 65 | // Deprecated parameters |
||
| 66 | if ( isset( $prop['hasrelated'] ) ) { |
||
| 67 | $this->logFeatureUsage( 'action=search&srprop=hasrelated' ); |
||
| 68 | $this->setWarning( 'srprop=hasrelated has been deprecated' ); |
||
| 69 | } |
||
| 70 | if ( isset( $prop['score'] ) ) { |
||
| 71 | $this->logFeatureUsage( 'action=search&srprop=score' ); |
||
| 72 | $this->setWarning( 'srprop=score has been deprecated' ); |
||
| 73 | } |
||
| 74 | |||
| 75 | // Create search engine instance and set options |
||
| 76 | $search = $this->buildSearchEngine( $params ); |
||
| 77 | $search->setFeatureData( 'rewrite', (bool)$params['enablerewrites'] ); |
||
| 78 | $search->setFeatureData( 'interwiki', (bool)$interwiki ); |
||
| 79 | |||
| 80 | $query = $search->transformSearchTerm( $query ); |
||
| 81 | $query = $search->replacePrefixes( $query ); |
||
| 82 | |||
| 83 | // Perform the actual search |
||
| 84 | if ( $what == 'text' ) { |
||
| 85 | $matches = $search->searchText( $query ); |
||
| 86 | } elseif ( $what == 'title' ) { |
||
| 87 | $matches = $search->searchTitle( $query ); |
||
| 88 | } elseif ( $what == 'nearmatch' ) { |
||
| 89 | // near matches must receive the user input as provided, otherwise |
||
| 90 | // the near matches within namespaces are lost. |
||
| 91 | $matches = $search->getNearMatcher( $this->getConfig() ) |
||
| 92 | ->getNearMatchResultSet( $params['search'] ); |
||
| 93 | } else { |
||
| 94 | // We default to title searches; this is a terrible legacy |
||
| 95 | // of the way we initially set up the MySQL fulltext-based |
||
| 96 | // search engine with separate title and text fields. |
||
| 97 | // In the future, the default should be for a combined index. |
||
| 98 | $what = 'title'; |
||
| 99 | $matches = $search->searchTitle( $query ); |
||
| 100 | |||
| 101 | // Not all search engines support a separate title search, |
||
| 102 | // for instance the Lucene-based engine we use on Wikipedia. |
||
| 103 | // In this case, fall back to full-text search (which will |
||
| 104 | // include titles in it!) |
||
| 105 | if ( is_null( $matches ) ) { |
||
| 106 | $what = 'text'; |
||
| 107 | $matches = $search->searchText( $query ); |
||
| 108 | } |
||
| 109 | } |
||
| 110 | if ( is_null( $matches ) ) { |
||
| 111 | $this->dieUsage( "{$what} search is disabled", "search-{$what}-disabled" ); |
||
| 112 | } elseif ( $matches instanceof Status && !$matches->isGood() ) { |
||
| 113 | $this->dieUsage( $matches->getWikiText( false, false, 'en' ), 'search-error' ); |
||
| 114 | } |
||
| 115 | |||
| 116 | if ( $resultPageSet === null ) { |
||
| 117 | $apiResult = $this->getResult(); |
||
| 118 | // Add search meta data to result |
||
| 119 | if ( isset( $searchInfo['totalhits'] ) ) { |
||
| 120 | $totalhits = $matches->getTotalHits(); |
||
|
0 ignored issues
–
show
|
|||
| 121 | if ( $totalhits !== null ) { |
||
| 122 | $apiResult->addValue( [ 'query', 'searchinfo' ], |
||
| 123 | 'totalhits', $totalhits ); |
||
| 124 | } |
||
| 125 | } |
||
| 126 | if ( isset( $searchInfo['suggestion'] ) && $matches->hasSuggestion() ) { |
||
|
0 ignored issues
–
show
The method
hasSuggestion does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 127 | $apiResult->addValue( [ 'query', 'searchinfo' ], |
||
| 128 | 'suggestion', $matches->getSuggestionQuery() ); |
||
|
0 ignored issues
–
show
The method
getSuggestionQuery does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 129 | $apiResult->addValue( [ 'query', 'searchinfo' ], |
||
| 130 | 'suggestionsnippet', $matches->getSuggestionSnippet() ); |
||
|
0 ignored issues
–
show
The method
getSuggestionSnippet does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 131 | } |
||
| 132 | if ( isset( $searchInfo['rewrittenquery'] ) && $matches->hasRewrittenQuery() ) { |
||
|
0 ignored issues
–
show
The method
hasRewrittenQuery does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 133 | $apiResult->addValue( [ 'query', 'searchinfo' ], |
||
| 134 | 'rewrittenquery', $matches->getQueryAfterRewrite() ); |
||
|
0 ignored issues
–
show
The method
getQueryAfterRewrite does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 135 | $apiResult->addValue( [ 'query', 'searchinfo' ], |
||
| 136 | 'rewrittenquerysnippet', $matches->getQueryAfterRewriteSnippet() ); |
||
|
0 ignored issues
–
show
The method
getQueryAfterRewriteSnippet does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 137 | } |
||
| 138 | } |
||
| 139 | |||
| 140 | // Add the search results to the result |
||
| 141 | $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); |
||
|
0 ignored issues
–
show
The method
termMatches does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 142 | $titles = []; |
||
| 143 | $count = 0; |
||
| 144 | $result = $matches->next(); |
||
|
0 ignored issues
–
show
The method
next does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 145 | $limit = $params['limit']; |
||
| 146 | |||
| 147 | while ( $result ) { |
||
| 148 | View Code Duplication | if ( ++$count > $limit ) { |
|
| 149 | // We've reached the one extra which shows that there are |
||
| 150 | // additional items to be had. Stop here... |
||
| 151 | $this->setContinueEnumParameter( 'offset', $params['offset'] + $params['limit'] ); |
||
| 152 | break; |
||
| 153 | } |
||
| 154 | |||
| 155 | // Silently skip broken and missing titles |
||
| 156 | if ( $result->isBrokenTitle() || $result->isMissingRevision() ) { |
||
| 157 | $result = $matches->next(); |
||
| 158 | continue; |
||
| 159 | } |
||
| 160 | |||
| 161 | $title = $result->getTitle(); |
||
| 162 | if ( $resultPageSet === null ) { |
||
| 163 | $vals = []; |
||
| 164 | ApiQueryBase::addTitleInfo( $vals, $title ); |
||
| 165 | |||
| 166 | if ( isset( $prop['snippet'] ) ) { |
||
| 167 | $vals['snippet'] = $result->getTextSnippet( $terms ); |
||
| 168 | } |
||
| 169 | if ( isset( $prop['size'] ) ) { |
||
| 170 | $vals['size'] = $result->getByteSize(); |
||
| 171 | } |
||
| 172 | if ( isset( $prop['wordcount'] ) ) { |
||
| 173 | $vals['wordcount'] = $result->getWordCount(); |
||
| 174 | } |
||
| 175 | if ( isset( $prop['timestamp'] ) ) { |
||
| 176 | $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() ); |
||
| 177 | } |
||
| 178 | if ( isset( $prop['titlesnippet'] ) ) { |
||
| 179 | $vals['titlesnippet'] = $result->getTitleSnippet(); |
||
| 180 | } |
||
| 181 | if ( isset( $prop['categorysnippet'] ) ) { |
||
| 182 | $vals['categorysnippet'] = $result->getCategorySnippet(); |
||
| 183 | } |
||
| 184 | if ( !is_null( $result->getRedirectTitle() ) ) { |
||
| 185 | if ( isset( $prop['redirecttitle'] ) ) { |
||
| 186 | $vals['redirecttitle'] = $result->getRedirectTitle()->getPrefixedText(); |
||
| 187 | } |
||
| 188 | if ( isset( $prop['redirectsnippet'] ) ) { |
||
| 189 | $vals['redirectsnippet'] = $result->getRedirectSnippet(); |
||
| 190 | } |
||
| 191 | } |
||
| 192 | if ( !is_null( $result->getSectionTitle() ) ) { |
||
| 193 | if ( isset( $prop['sectiontitle'] ) ) { |
||
| 194 | $vals['sectiontitle'] = $result->getSectionTitle()->getFragment(); |
||
| 195 | } |
||
| 196 | if ( isset( $prop['sectionsnippet'] ) ) { |
||
| 197 | $vals['sectionsnippet'] = $result->getSectionSnippet(); |
||
| 198 | } |
||
| 199 | } |
||
| 200 | if ( isset( $prop['isfilematch'] ) ) { |
||
| 201 | $vals['isfilematch'] = $result->isFileMatch(); |
||
| 202 | } |
||
| 203 | |||
| 204 | // Add item to results and see whether it fits |
||
| 205 | $fit = $apiResult->addValue( [ 'query', $this->getModuleName() ], |
||
|
0 ignored issues
–
show
The variable
$apiResult does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
Loading history...
|
|||
| 206 | null, $vals ); |
||
| 207 | if ( !$fit ) { |
||
| 208 | $this->setContinueEnumParameter( 'offset', $params['offset'] + $count - 1 ); |
||
| 209 | break; |
||
| 210 | } |
||
| 211 | } else { |
||
| 212 | $titles[] = $title; |
||
| 213 | } |
||
| 214 | |||
| 215 | $result = $matches->next(); |
||
| 216 | } |
||
| 217 | |||
| 218 | $hasInterwikiResults = false; |
||
| 219 | $totalhits = null; |
||
| 220 | if ( $interwiki && $resultPageSet === null && $matches->hasInterwikiResults() ) { |
||
|
0 ignored issues
–
show
The method
hasInterwikiResults does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 221 | foreach ( $matches->getInterwikiResults() as $interwikiMatches ) { |
||
|
0 ignored issues
–
show
The method
getInterwikiResults does only exist in SearchResultSet, but not in Status.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 222 | $hasInterwikiResults = true; |
||
| 223 | |||
| 224 | // Include number of results if requested |
||
| 225 | if ( $resultPageSet === null && isset( $searchInfo['totalhits'] ) ) { |
||
| 226 | $totalhits += $interwikiMatches->getTotalHits(); |
||
| 227 | } |
||
| 228 | |||
| 229 | $result = $interwikiMatches->next(); |
||
| 230 | while ( $result ) { |
||
| 231 | $title = $result->getTitle(); |
||
| 232 | |||
| 233 | if ( $resultPageSet === null ) { |
||
| 234 | $vals = [ |
||
| 235 | 'namespace' => $result->getInterwikiNamespaceText(), |
||
| 236 | 'title' => $title->getText(), |
||
| 237 | 'url' => $title->getFullURL(), |
||
| 238 | ]; |
||
| 239 | |||
| 240 | // Add item to results and see whether it fits |
||
| 241 | $fit = $apiResult->addValue( |
||
| 242 | [ 'query', 'interwiki' . $this->getModuleName(), $result->getInterwikiPrefix() ], |
||
| 243 | null, |
||
| 244 | $vals |
||
| 245 | ); |
||
| 246 | |||
| 247 | if ( !$fit ) { |
||
| 248 | // We hit the limit. We can't really provide any meaningful |
||
| 249 | // pagination info so just bail out |
||
| 250 | break; |
||
| 251 | } |
||
| 252 | } else { |
||
| 253 | $titles[] = $title; |
||
| 254 | } |
||
| 255 | |||
| 256 | $result = $interwikiMatches->next(); |
||
| 257 | } |
||
| 258 | } |
||
| 259 | if ( $totalhits !== null ) { |
||
| 260 | $apiResult->addValue( [ 'query', 'interwikisearchinfo' ], |
||
| 261 | 'totalhits', $totalhits ); |
||
| 262 | } |
||
| 263 | } |
||
| 264 | |||
| 265 | if ( $resultPageSet === null ) { |
||
| 266 | $apiResult->addIndexedTagName( [ |
||
| 267 | 'query', $this->getModuleName() |
||
| 268 | ], 'p' ); |
||
| 269 | if ( $hasInterwikiResults ) { |
||
| 270 | $apiResult->addIndexedTagName( [ |
||
| 271 | 'query', 'interwiki' . $this->getModuleName() |
||
| 272 | ], 'p' ); |
||
| 273 | } |
||
| 274 | } else { |
||
| 275 | View Code Duplication | $resultPageSet->setRedirectMergePolicy( function ( $current, $new ) { |
|
| 276 | if ( !isset( $current['index'] ) || $new['index'] < $current['index'] ) { |
||
| 277 | $current['index'] = $new['index']; |
||
| 278 | } |
||
| 279 | return $current; |
||
| 280 | } ); |
||
| 281 | $resultPageSet->populateFromTitles( $titles ); |
||
| 282 | $offset = $params['offset'] + 1; |
||
| 283 | foreach ( $titles as $index => $title ) { |
||
| 284 | $resultPageSet->setGeneratorData( $title, [ 'index' => $index + $offset ] ); |
||
| 285 | } |
||
| 286 | } |
||
| 287 | } |
||
| 288 | |||
| 289 | public function getCacheMode( $params ) { |
||
| 290 | return 'public'; |
||
| 291 | } |
||
| 292 | |||
| 293 | public function getAllowedParams() { |
||
| 294 | if ( $this->allowedParams !== null ) { |
||
| 295 | return $this->allowedParams; |
||
| 296 | } |
||
| 297 | |||
| 298 | $this->allowedParams = $this->buildCommonApiParams() + [ |
||
| 299 | 'what' => [ |
||
| 300 | ApiBase::PARAM_TYPE => [ |
||
| 301 | 'title', |
||
| 302 | 'text', |
||
| 303 | 'nearmatch', |
||
| 304 | ] |
||
| 305 | ], |
||
| 306 | 'info' => [ |
||
| 307 | ApiBase::PARAM_DFLT => 'totalhits|suggestion|rewrittenquery', |
||
| 308 | ApiBase::PARAM_TYPE => [ |
||
| 309 | 'totalhits', |
||
| 310 | 'suggestion', |
||
| 311 | 'rewrittenquery', |
||
| 312 | ], |
||
| 313 | ApiBase::PARAM_ISMULTI => true, |
||
| 314 | ], |
||
| 315 | 'prop' => [ |
||
| 316 | ApiBase::PARAM_DFLT => 'size|wordcount|timestamp|snippet', |
||
| 317 | ApiBase::PARAM_TYPE => [ |
||
| 318 | 'size', |
||
| 319 | 'wordcount', |
||
| 320 | 'timestamp', |
||
| 321 | 'snippet', |
||
| 322 | 'titlesnippet', |
||
| 323 | 'redirecttitle', |
||
| 324 | 'redirectsnippet', |
||
| 325 | 'sectiontitle', |
||
| 326 | 'sectionsnippet', |
||
| 327 | 'isfilematch', |
||
| 328 | 'categorysnippet', |
||
| 329 | 'score', // deprecated |
||
| 330 | 'hasrelated', // deprecated |
||
| 331 | ], |
||
| 332 | ApiBase::PARAM_ISMULTI => true, |
||
| 333 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [], |
||
| 334 | ], |
||
| 335 | 'interwiki' => false, |
||
| 336 | 'enablerewrites' => false, |
||
| 337 | ]; |
||
| 338 | |||
| 339 | return $this->allowedParams; |
||
| 340 | } |
||
| 341 | |||
| 342 | public function getSearchProfileParams() { |
||
| 343 | return [ |
||
| 344 | 'qiprofile' => [ |
||
| 345 | 'profile-type' => SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE, |
||
| 346 | 'help-message' => 'apihelp-query+search-param-qiprofile', |
||
| 347 | ], |
||
| 348 | ]; |
||
| 349 | } |
||
| 350 | |||
| 351 | protected function getExamplesMessages() { |
||
| 352 | return [ |
||
| 353 | 'action=query&list=search&srsearch=meaning' |
||
| 354 | => 'apihelp-query+search-example-simple', |
||
| 355 | 'action=query&list=search&srwhat=text&srsearch=meaning' |
||
| 356 | => 'apihelp-query+search-example-text', |
||
| 357 | 'action=query&generator=search&gsrsearch=meaning&prop=info' |
||
| 358 | => 'apihelp-query+search-example-generator', |
||
| 359 | ]; |
||
| 360 | } |
||
| 361 | |||
| 362 | public function getHelpUrls() { |
||
| 363 | return 'https://www.mediawiki.org/wiki/API:Search'; |
||
| 364 | } |
||
| 365 | } |
||
| 366 |
It seems like the method you are trying to call exists only in some of the possible types.
Let’s take a look at an example:
Available Fixes
Add an additional type-check:
Only allow a single type to be passed if the variable comes from a parameter: