MediaWiki  master
ApiQueryBase.php
Go to the documentation of this file.
1 <?php
26 
34 abstract class ApiQueryBase extends ApiBase {
36 
38 
44  public function __construct( ApiQuery $queryModule, $moduleName, $paramPrefix = '' ) {
45  parent::__construct( $queryModule->getMain(), $moduleName, $paramPrefix );
46  $this->mQueryModule = $queryModule;
47  $this->mDb = null;
48  $this->resetQueryParams();
49  }
50 
51  /************************************************************************/
67  public function getCacheMode( $params ) {
68  return 'private';
69  }
70 
80  public function requestExtraData( $pageSet ) {
81  }
82 
85  /************************************************************************/
94  public function getQuery() {
95  return $this->mQueryModule;
96  }
97 
99  public function getParent() {
100  return $this->getQuery();
101  }
102 
107  protected function getDB() {
108  if ( is_null( $this->mDb ) ) {
109  $this->mDb = $this->getQuery()->getDB();
110  }
111 
112  return $this->mDb;
113  }
114 
123  public function selectNamedDB( $name, $db, $groups ) {
124  $this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups );
125  return $this->mDb;
126  }
127 
132  protected function getPageSet() {
133  return $this->getQuery()->getPageSet();
134  }
135 
138  /************************************************************************/
146  protected function resetQueryParams() {
147  $this->tables = [];
148  $this->where = [];
149  $this->fields = [];
150  $this->options = [];
151  $this->join_conds = [];
152  }
153 
161  protected function addTables( $tables, $alias = null ) {
162  if ( is_array( $tables ) ) {
163  if ( $alias !== null ) {
164  ApiBase::dieDebug( __METHOD__, 'Multiple table aliases not supported' );
165  }
166  $this->tables = array_merge( $this->tables, $tables );
167  } elseif ( $alias !== null ) {
168  $this->tables[$alias] = $tables;
169  } else {
170  $this->tables[] = $tables;
171  }
172  }
173 
182  protected function addJoinConds( $join_conds ) {
183  if ( !is_array( $join_conds ) ) {
184  ApiBase::dieDebug( __METHOD__, 'Join conditions have to be arrays' );
185  }
186  $this->join_conds = array_merge( $this->join_conds, $join_conds );
187  }
188 
193  protected function addFields( $value ) {
194  if ( is_array( $value ) ) {
195  $this->fields = array_merge( $this->fields, $value );
196  } else {
197  $this->fields[] = $value;
198  }
199  }
200 
207  protected function addFieldsIf( $value, $condition ) {
208  if ( $condition ) {
209  $this->addFields( $value );
210 
211  return true;
212  }
213 
214  return false;
215  }
216 
228  protected function addWhere( $value ) {
229  if ( is_array( $value ) ) {
230  // Sanity check: don't insert empty arrays,
231  // Database::makeList() chokes on them
232  if ( count( $value ) ) {
233  $this->where = array_merge( $this->where, $value );
234  }
235  } else {
236  $this->where[] = $value;
237  }
238  }
239 
246  protected function addWhereIf( $value, $condition ) {
247  if ( $condition ) {
248  $this->addWhere( $value );
249 
250  return true;
251  }
252 
253  return false;
254  }
255 
261  protected function addWhereFld( $field, $value ) {
262  if ( $value !== null && !( is_array( $value ) && !$value ) ) {
263  $this->where[$field] = $value;
264  }
265  }
266 
275  protected function addWhereIDsFld( $table, $field, $ids ) {
276  // Use count() to its full documented capabilities to simultaneously
277  // test for null, empty array or empty countable object
278  if ( count( $ids ) ) {
279  $ids = $this->filterIDs( [ [ $table, $field ] ], $ids );
280 
281  if ( $ids === [] ) {
282  // Return nothing, no IDs are valid
283  $this->where[] = '0 = 1';
284  } else {
285  $this->where[$field] = $ids;
286  }
287  }
288  return count( $ids );
289  }
290 
303  protected function addWhereRange( $field, $dir, $start, $end, $sort = true ) {
304  $isDirNewer = ( $dir === 'newer' );
305  $after = ( $isDirNewer ? '>=' : '<=' );
306  $before = ( $isDirNewer ? '<=' : '>=' );
307  $db = $this->getDB();
308 
309  if ( !is_null( $start ) ) {
310  $this->addWhere( $field . $after . $db->addQuotes( $start ) );
311  }
312 
313  if ( !is_null( $end ) ) {
314  $this->addWhere( $field . $before . $db->addQuotes( $end ) );
315  }
316 
317  if ( $sort ) {
318  $order = $field . ( $isDirNewer ? '' : ' DESC' );
319  // Append ORDER BY
320  $optionOrderBy = isset( $this->options['ORDER BY'] )
321  ? (array)$this->options['ORDER BY']
322  : [];
323  $optionOrderBy[] = $order;
324  $this->addOption( 'ORDER BY', $optionOrderBy );
325  }
326  }
327 
338  protected function addTimestampWhereRange( $field, $dir, $start, $end, $sort = true ) {
339  $db = $this->getDB();
340  $this->addWhereRange( $field, $dir,
341  $db->timestampOrNull( $start ), $db->timestampOrNull( $end ), $sort );
342  }
343 
350  protected function addOption( $name, $value = null ) {
351  if ( is_null( $value ) ) {
352  $this->options[] = $name;
353  } else {
354  $this->options[$name] = $value;
355  }
356  }
357 
375  protected function select( $method, $extraQuery = [], array &$hookData = null ) {
376  $tables = array_merge(
377  $this->tables,
378  isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : []
379  );
380  $fields = array_merge(
381  $this->fields,
382  isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : []
383  );
384  $where = array_merge(
385  $this->where,
386  isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : []
387  );
388  $options = array_merge(
389  $this->options,
390  isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : []
391  );
392  $join_conds = array_merge(
393  $this->join_conds,
394  isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : []
395  );
396 
397  if ( $hookData !== null ) {
398  Hooks::run( 'ApiQueryBaseBeforeQuery',
399  [ $this, &$tables, &$fields, &$where, &$options, &$join_conds, &$hookData ]
400  );
401  }
402 
403  $res = $this->getDB()->select( $tables, $fields, $where, $method, $options, $join_conds );
404 
405  if ( $hookData !== null ) {
406  Hooks::run( 'ApiQueryBaseAfterQuery', [ $this, $res, &$hookData ] );
407  }
408 
409  return $res;
410  }
411 
425  protected function processRow( $row, array &$data, array &$hookData ) {
426  return Hooks::run( 'ApiQueryBaseProcessRow', [ $this, $row, &$data, &$hookData ] );
427  }
428 
431  /************************************************************************/
443  public static function addTitleInfo( &$arr, $title, $prefix = '' ) {
444  $arr[$prefix . 'ns'] = (int)$title->getNamespace();
445  $arr[$prefix . 'title'] = $title->getPrefixedText();
446  }
447 
454  protected function addPageSubItems( $pageId, $data ) {
455  $result = $this->getResult();
457 
458  return $result->addValue( [ 'query', 'pages', (int)$pageId ],
459  $this->getModuleName(),
460  $data );
461  }
462 
471  protected function addPageSubItem( $pageId, $item, $elemname = null ) {
472  if ( is_null( $elemname ) ) {
473  $elemname = $this->getModulePrefix();
474  }
475  $result = $this->getResult();
476  $fit = $result->addValue( [ 'query', 'pages', $pageId,
477  $this->getModuleName() ], null, $item );
478  if ( !$fit ) {
479  return false;
480  }
481  $result->addIndexedTagName( [ 'query', 'pages', $pageId,
482  $this->getModuleName() ], $elemname );
483 
484  return true;
485  }
486 
492  protected function setContinueEnumParameter( $paramName, $paramValue ) {
493  $this->getContinuationManager()->addContinueParam( $this, $paramName, $paramValue );
494  }
495 
506  public function titlePartToKey( $titlePart, $namespace = NS_MAIN ) {
507  $t = Title::makeTitleSafe( $namespace, $titlePart . 'x' );
508  if ( !$t || $t->hasFragment() ) {
509  // Invalid title (e.g. bad chars) or contained a '#'.
510  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $titlePart ) ] );
511  }
512  if ( $namespace != $t->getNamespace() || $t->isExternal() ) {
513  // This can happen in two cases. First, if you call titlePartToKey with a title part
514  // that looks like a namespace, but with $defaultNamespace = NS_MAIN. It would be very
515  // difficult to handle such a case. Such cases cannot exist and are therefore treated
516  // as invalid user input. The second case is when somebody specifies a title interwiki
517  // prefix.
518  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $titlePart ) ] );
519  }
520 
521  return substr( $t->getDBkey(), 0, -1 );
522  }
523 
532  public function prefixedTitlePartToKey( $titlePart, $defaultNamespace = NS_MAIN ) {
533  $t = Title::newFromText( $titlePart . 'x', $defaultNamespace );
534  if ( !$t || $t->hasFragment() || $t->isExternal() ) {
535  // Invalid title (e.g. bad chars) or contained a '#'.
536  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $titlePart ) ] );
537  }
538 
539  return [ $t->getNamespace(), substr( $t->getDBkey(), 0, -1 ) ];
540  }
541 
546  public function validateSha1Hash( $hash ) {
547  return (bool)preg_match( '/^[a-f0-9]{40}$/', $hash );
548  }
549 
554  public function validateSha1Base36Hash( $hash ) {
555  return (bool)preg_match( '/^[a-z0-9]{31}$/', $hash );
556  }
557 
563  public function userCanSeeRevDel() {
564  return $this->getPermissionManager()->userHasAnyRight(
565  $this->getUser(),
566  'deletedhistory',
567  'deletedtext',
568  'suppressrevision',
569  'viewsuppressed'
570  );
571  }
572 
583  IResultWrapper $res, $fname = __METHOD__, $fieldPrefix = 'page'
584  ) {
585  if ( !$res->numRows() ) {
586  return;
587  }
588 
589  $services = MediaWikiServices::getInstance();
590  $nsInfo = $services->getNamespaceInfo();
591  $namespaceField = $fieldPrefix . '_namespace';
592  $titleField = $fieldPrefix . '_title';
593 
594  $usernames = [];
595  foreach ( $res as $row ) {
596  if ( $nsInfo->hasGenderDistinction( $row->$namespaceField ) ) {
597  $usernames[] = $row->$titleField;
598  }
599  }
600 
601  if ( $usernames === [] ) {
602  return;
603  }
604 
605  $genderCache = $services->getGenderCache();
606  $genderCache->doQuery( $usernames, $fname );
607  }
608 
611  /************************************************************************/
624  public function showHiddenUsersAddBlockInfo( $showBlockInfo ) {
625  wfDeprecated( __METHOD__, '1.34' );
626  return $this->addBlockInfoToQuery( $showBlockInfo );
627  }
628 
630 }
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
getDB()
Get the Query database connection (read-only)
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
getResult()
Get the result object.
Definition: ApiBase.php:640
const NS_MAIN
Definition: Defines.php:60
showHiddenUsersAddBlockInfo( $showBlockInfo)
Filters hidden users (where the user doesn&#39;t have the right to view them) Also adds relevant block in...
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
processRow( $row, array &$data, array &$hookData)
Call the ApiQueryBaseProcessRow hook.
prefixedTitlePartToKey( $titlePart, $defaultNamespace=NS_MAIN)
Convert an input title or title prefix into a namespace constant and dbkey.
validateSha1Base36Hash( $hash)
getMain()
Get the main module.
Definition: ApiBase.php:536
This is a base class for all Query modules.
$sort
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:2005
getPageSet()
Get the PageSet object to work on.
requestExtraData( $pageSet)
Override this method to request extra fields from the pageSet using $pageSet->requestField(&#39;fieldName...
trait ApiQueryBlockInfoTrait
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:616
addPageSubItem( $pageId, $item, $elemname=null)
Same as addPageSubItems(), but one element of $data at a time.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
addWhereIf( $value, $condition)
Same as addWhere(), but add the WHERE clauses only if a condition is met.
executeGenderCacheFromResultWrapper(IResultWrapper $res, $fname=__METHOD__, $fieldPrefix='page')
Preprocess the result set to fill the GenderCache with the necessary information before using self::a...
userCanSeeRevDel()
Check whether the current user has permission to view revision-deleted fields.
getContinuationManager()
Get the continuation manager.
Definition: ApiBase.php:680
addPageSubItems( $pageId, $data)
Add a sub-element under the page element with the given page ID.
Result wrapper for grabbing data queried from an IDatabase object.
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
addFields( $value)
Add a set of fields to select to the internal array.
getCacheMode( $params)
Get the cache mode for the data generated by this module.
This is the main query class.
Definition: ApiQuery.php:37
numRows()
Get the number of rows in a result object.
selectNamedDB( $name, $db, $groups)
Selects the query database connection with the given name.
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:612
getQuery()
Get the main Query module.
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
__construct(ApiQuery $queryModule, $moduleName, $paramPrefix='')
filterIDs( $fields, array $ids)
Filter out-of-range values from a list of positive integer IDs.
Definition: ApiBase.php:1876
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:528
addWhere( $value)
Add a set of WHERE clauses to the internal array.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks...
Definition: ApiBase.php:710
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:2211
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
This abstract class implements many basic API functions, and is the base of all API classes...
Definition: ApiBase.php:42
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
addBlockInfoToQuery( $showBlockInfo)
Filters hidden users (where the user doesn&#39;t have the right to view them) Also adds relevant block in...
validateSha1Hash( $hash)
addWhereIDsFld( $table, $field, $ids)
Like addWhereFld for an integer list of IDs.
resetQueryParams()
Blank the internal arrays with query parameters.
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319
addWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, and an ORDER BY clause to sort in the right direction...
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.