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 ( $this->mDb === null ) {
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 
230  protected function addWhere( $value ) {
231  if ( is_array( $value ) ) {
232  // Sanity check: don't insert empty arrays,
233  // Database::makeList() chokes on them
234  if ( count( $value ) ) {
235  $this->where = array_merge( $this->where, $value );
236  }
237  } else {
238  $this->where[] = $value;
239  }
240  }
241 
248  protected function addWhereIf( $value, $condition ) {
249  if ( $condition ) {
250  $this->addWhere( $value );
251 
252  return true;
253  }
254 
255  return false;
256  }
257 
267  protected function addWhereFld( $field, $value ) {
268  if ( $value !== null && !( is_array( $value ) && !$value ) ) {
269  $this->where[$field] = $value;
270  }
271  }
272 
294  protected function addWhereIDsFld( $table, $field, $ids ) {
295  // Use count() to its full documented capabilities to simultaneously
296  // test for null, empty array or empty countable object
297  if ( count( $ids ) ) {
298  $ids = $this->filterIDs( [ [ $table, $field ] ], $ids );
299 
300  if ( $ids === [] ) {
301  // Return nothing, no IDs are valid
302  $this->where[] = '0 = 1';
303  } else {
304  $this->where[$field] = $ids;
305  }
306  }
307  return count( $ids );
308  }
309 
322  protected function addWhereRange( $field, $dir, $start, $end, $sort = true ) {
323  $isDirNewer = ( $dir === 'newer' );
324  $after = ( $isDirNewer ? '>=' : '<=' );
325  $before = ( $isDirNewer ? '<=' : '>=' );
326  $db = $this->getDB();
327 
328  if ( $start !== null ) {
329  $this->addWhere( $field . $after . $db->addQuotes( $start ) );
330  }
331 
332  if ( $end !== null ) {
333  $this->addWhere( $field . $before . $db->addQuotes( $end ) );
334  }
335 
336  if ( $sort ) {
337  $order = $field . ( $isDirNewer ? '' : ' DESC' );
338  // Append ORDER BY
339  $optionOrderBy = isset( $this->options['ORDER BY'] )
340  ? (array)$this->options['ORDER BY']
341  : [];
342  $optionOrderBy[] = $order;
343  $this->addOption( 'ORDER BY', $optionOrderBy );
344  }
345  }
346 
357  protected function addTimestampWhereRange( $field, $dir, $start, $end, $sort = true ) {
358  $db = $this->getDB();
359  $this->addWhereRange( $field, $dir,
360  $db->timestampOrNull( $start ), $db->timestampOrNull( $end ), $sort );
361  }
362 
369  protected function addOption( $name, $value = null ) {
370  if ( $value === null ) {
371  $this->options[] = $name;
372  } else {
373  $this->options[$name] = $value;
374  }
375  }
376 
394  protected function select( $method, $extraQuery = [], array &$hookData = null ) {
395  $tables = array_merge(
396  $this->tables,
397  isset( $extraQuery['tables'] ) ? (array)$extraQuery['tables'] : []
398  );
399  $fields = array_merge(
400  $this->fields,
401  isset( $extraQuery['fields'] ) ? (array)$extraQuery['fields'] : []
402  );
403  $where = array_merge(
404  $this->where,
405  isset( $extraQuery['where'] ) ? (array)$extraQuery['where'] : []
406  );
407  $options = array_merge(
408  $this->options,
409  isset( $extraQuery['options'] ) ? (array)$extraQuery['options'] : []
410  );
411  $join_conds = array_merge(
412  $this->join_conds,
413  isset( $extraQuery['join_conds'] ) ? (array)$extraQuery['join_conds'] : []
414  );
415 
416  if ( $hookData !== null ) {
417  Hooks::run( 'ApiQueryBaseBeforeQuery',
418  [ $this, &$tables, &$fields, &$where, &$options, &$join_conds, &$hookData ]
419  );
420  }
421 
422  $res = $this->getDB()->select( $tables, $fields, $where, $method, $options, $join_conds );
423 
424  if ( $hookData !== null ) {
425  Hooks::run( 'ApiQueryBaseAfterQuery', [ $this, $res, &$hookData ] );
426  }
427 
428  return $res;
429  }
430 
444  protected function processRow( $row, array &$data, array &$hookData ) {
445  return Hooks::run( 'ApiQueryBaseProcessRow', [ $this, $row, &$data, &$hookData ] );
446  }
447 
450  /************************************************************************/
462  public static function addTitleInfo( &$arr, $title, $prefix = '' ) {
463  $arr[$prefix . 'ns'] = (int)$title->getNamespace();
464  $arr[$prefix . 'title'] = $title->getPrefixedText();
465  }
466 
473  protected function addPageSubItems( $pageId, $data ) {
474  $result = $this->getResult();
476 
477  return $result->addValue( [ 'query', 'pages', (int)$pageId ],
478  $this->getModuleName(),
479  $data );
480  }
481 
490  protected function addPageSubItem( $pageId, $item, $elemname = null ) {
491  if ( $elemname === null ) {
492  $elemname = $this->getModulePrefix();
493  }
494  $result = $this->getResult();
495  $fit = $result->addValue( [ 'query', 'pages', $pageId,
496  $this->getModuleName() ], null, $item );
497  if ( !$fit ) {
498  return false;
499  }
500  $result->addIndexedTagName( [ 'query', 'pages', $pageId,
501  $this->getModuleName() ], $elemname );
502 
503  return true;
504  }
505 
511  protected function setContinueEnumParameter( $paramName, $paramValue ) {
512  $this->getContinuationManager()->addContinueParam( $this, $paramName, $paramValue );
513  }
514 
525  public function titlePartToKey( $titlePart, $namespace = NS_MAIN ) {
526  $t = Title::makeTitleSafe( $namespace, $titlePart . 'x' );
527  if ( !$t || $t->hasFragment() ) {
528  // Invalid title (e.g. bad chars) or contained a '#'.
529  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $titlePart ) ] );
530  }
531  if ( $namespace != $t->getNamespace() || $t->isExternal() ) {
532  // This can happen in two cases. First, if you call titlePartToKey with a title part
533  // that looks like a namespace, but with $defaultNamespace = NS_MAIN. It would be very
534  // difficult to handle such a case. Such cases cannot exist and are therefore treated
535  // as invalid user input. The second case is when somebody specifies a title interwiki
536  // prefix.
537  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $titlePart ) ] );
538  }
539 
540  return substr( $t->getDBkey(), 0, -1 );
541  }
542 
551  protected function parsePrefixedTitlePart( $titlePart, $defaultNamespace = NS_MAIN ) {
552  try {
553  $titleParser = MediaWikiServices::getInstance()->getTitleParser();
554  $t = $titleParser->parseTitle( $titlePart . 'X', $defaultNamespace );
555  } catch ( MalformedTitleException $e ) {
556  $t = null;
557  }
558 
559  if ( !$t || $t->hasFragment() || $t->isExternal() || $t->getDBkey() === 'X' ) {
560  // Invalid title (e.g. bad chars) or contained a '#'.
561  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $titlePart ) ] );
562  }
563 
564  return new TitleValue( $t->getNamespace(), substr( $t->getDBkey(), 0, -1 ) );
565  }
566 
576  public function prefixedTitlePartToKey( $titlePart, $defaultNamespace = NS_MAIN ) {
577  wfDeprecated( __METHOD__, '1.35' );
578  $t = $this->parsePrefixedTitlePart( $titlePart, $defaultNamespace );
579  return [ $t->getNamespace(), $t->getDBkey() ];
580  }
581 
586  public function validateSha1Hash( $hash ) {
587  return (bool)preg_match( '/^[a-f0-9]{40}$/', $hash );
588  }
589 
594  public function validateSha1Base36Hash( $hash ) {
595  return (bool)preg_match( '/^[a-z0-9]{31}$/', $hash );
596  }
597 
603  public function userCanSeeRevDel() {
604  return $this->getPermissionManager()->userHasAnyRight(
605  $this->getUser(),
606  'deletedhistory',
607  'deletedtext',
608  'suppressrevision',
609  'viewsuppressed'
610  );
611  }
612 
623  IResultWrapper $res, $fname = __METHOD__, $fieldPrefix = 'page'
624  ) {
625  if ( !$res->numRows() ) {
626  return;
627  }
628 
629  $services = MediaWikiServices::getInstance();
630  if ( !$services->getContentLanguage()->needsGenderDistinction() ) {
631  return;
632  }
633 
634  $nsInfo = $services->getNamespaceInfo();
635  $namespaceField = $fieldPrefix . '_namespace';
636  $titleField = $fieldPrefix . '_title';
637 
638  $usernames = [];
639  foreach ( $res as $row ) {
640  if ( $nsInfo->hasGenderDistinction( $row->$namespaceField ) ) {
641  $usernames[] = $row->$titleField;
642  }
643  }
644 
645  if ( $usernames === [] ) {
646  return;
647  }
648 
649  $genderCache = $services->getGenderCache();
650  $genderCache->doQuery( $usernames, $fname );
651  }
652 
655  /************************************************************************/
667  public function showHiddenUsersAddBlockInfo( $showBlockInfo ) {
668  wfDeprecated( __METHOD__, '1.34' );
669  $this->addBlockInfoToQuery( $showBlockInfo );
670  }
671 
673 }
ApiQueryBase\validateSha1Base36Hash
validateSha1Base36Hash( $hash)
Definition: ApiQueryBase.php:594
ApiQueryBase\addWhereIDsFld
addWhereIDsFld( $table, $field, $ids)
Like addWhereFld for an integer list of IDs.
Definition: ApiQueryBase.php:294
ApiQueryBase\showHiddenUsersAddBlockInfo
showHiddenUsersAddBlockInfo( $showBlockInfo)
Filters hidden users (where the user doesn't have the right to view them) Also adds relevant block in...
Definition: ApiQueryBase.php:667
ApiQueryBase\addPageSubItems
addPageSubItems( $pageId, $data)
Add a sub-element under the page element with the given page ID.
Definition: ApiQueryBase.php:473
ApiQueryBase\processRow
processRow( $row, array &$data, array &$hookData)
Call the ApiQueryBaseProcessRow hook.
Definition: ApiQueryBase.php:444
ApiQueryBase\addFields
addFields( $value)
Add a set of fields to select to the internal array.
Definition: ApiQueryBase.php:193
ApiQuery
This is the main query class.
Definition: ApiQuery.php:37
ApiQueryBase\resetQueryParams
resetQueryParams()
Blank the internal arrays with query parameters.
Definition: ApiQueryBase.php:146
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:137
ApiQueryBase\getParent
getParent()
Get the parent of this module.1.25 ApiBase|null
Definition: ApiQueryBase.php:99
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1379
ApiQueryBase\addTimestampWhereRange
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
Definition: ApiQueryBase.php:357
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:538
ApiQueryBase\getQuery
getQuery()
Get the main Query module.
Definition: ApiQueryBase.php:94
ApiQueryBase\$fields
$fields
Definition: ApiQueryBase.php:37
ApiQueryBase\addOption
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
Definition: ApiQueryBase.php:369
$res
$res
Definition: testCompression.php:57
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
ApiQueryBase\$options
$options
Definition: ApiQueryBase.php:37
ApiQueryBase\addFieldsIf
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
Definition: ApiQueryBase.php:207
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:48
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
NS_MAIN
const NS_MAIN
Definition: Defines.php:69
ApiQueryBlockInfoTrait
trait ApiQueryBlockInfoTrait
Definition: ApiQueryBlockInfoTrait.php:28
ApiQueryBase\executeGenderCacheFromResultWrapper
executeGenderCacheFromResultWrapper(IResultWrapper $res, $fname=__METHOD__, $fieldPrefix='page')
Preprocess the result set to fill the GenderCache with the necessary information before using self::a...
Definition: ApiQueryBase.php:622
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1033
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:24
ApiQueryBase
This is a base class for all Query modules.
Definition: ApiQueryBase.php:34
ApiQueryBase\getDB
getDB()
Get the Query database connection (read-only)
Definition: ApiQueryBase.php:107
ApiQueryBase\addTables
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
Definition: ApiQueryBase.php:161
ApiQueryBase\select
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
Definition: ApiQueryBase.php:394
$title
$title
Definition: testCompression.php:38
ApiQueryBase\$mDb
$mDb
Definition: ApiQueryBase.php:37
ApiQueryBase\$where
$where
Definition: ApiQueryBase.php:37
ApiBase\getModulePrefix
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:426
ApiQueryBase\addWhereRange
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.
Definition: ApiQueryBase.php:322
ApiBase\getContinuationManager
getContinuationManager()
Get the continuation manager.
Definition: ApiBase.php:578
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:621
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
ApiBase\getPermissionManager
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:608
ApiQueryBase\requestExtraData
requestExtraData( $pageSet)
Override this method to request extra fields from the pageSet using $pageSet->requestField('fieldName...
Definition: ApiQueryBase.php:80
ApiQueryBase\addJoinConds
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
Definition: ApiQueryBase.php:182
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1488
ApiQueryBase\addWhereFld
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
Definition: ApiQueryBase.php:267
ApiQueryBase\getCacheMode
getCacheMode( $params)
Get the cache mode for the data generated by this module.
Definition: ApiQueryBase.php:67
ApiQueryBase\prefixedTitlePartToKey
prefixedTitlePartToKey( $titlePart, $defaultNamespace=NS_MAIN)
Convert an input title or title prefix into a namespace constant and dbkey.
Definition: ApiQueryBase.php:576
ApiQueryBase\getPageSet
getPageSet()
Get the PageSet object to work on.
Definition: ApiQueryBase.php:132
ApiBase\filterIDs
filterIDs( $fields, array $ids)
Filter out-of-range values from a list of positive integer IDs.
Definition: ApiBase.php:1250
ApiQueryBase\selectNamedDB
selectNamedDB( $name, $db, $groups)
Selects the query database connection with the given name.
Definition: ApiQueryBase.php:123
ApiQueryBase\$join_conds
$join_conds
Definition: ApiQueryBase.php:37
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:25
ApiQueryBase\$tables
$tables
Definition: ApiQueryBase.php:37
ApiQueryBase\__construct
__construct(ApiQuery $queryModule, $moduleName, $paramPrefix='')
Definition: ApiQueryBase.php:44
ApiQueryBase\parsePrefixedTitlePart
parsePrefixedTitlePart( $titlePart, $defaultNamespace=NS_MAIN)
Convert an input title or title prefix into a TitleValue.
Definition: ApiQueryBase.php:551
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:418
ApiQueryBase\$mQueryModule
$mQueryModule
Definition: ApiQueryBase.php:37
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:434
ApiQueryBase\addWhere
addWhere( $value)
Add a set of WHERE clauses to the internal array.
Definition: ApiQueryBase.php:230
$t
$t
Definition: testCompression.php:74
ApiQueryBase\setContinueEnumParameter
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
Definition: ApiQueryBase.php:511
ApiQueryBase\titlePartToKey
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
Definition: ApiQueryBase.php:525
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
ApiQueryBase\userCanSeeRevDel
userCanSeeRevDel()
Check whether the current user has permission to view revision-deleted fields.
Definition: ApiQueryBase.php:603
ApiQueryBase\addPageSubItem
addPageSubItem( $pageId, $item, $elemname=null)
Same as addPageSubItems(), but one element of $data at a time.
Definition: ApiQueryBase.php:490
ApiBase\dieDebug
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:1571
ApiQueryBase\addWhereIf
addWhereIf( $value, $condition)
Same as addWhere(), but add the WHERE clauses only if a condition is met.
Definition: ApiQueryBase.php:248
ApiQueryBase\addTitleInfo
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
Definition: ApiQueryBase.php:462
addBlockInfoToQuery
addBlockInfoToQuery( $showBlockInfo)
Filters hidden users (where the user doesn't have the right to view them) Also adds relevant block in...
Definition: ApiQueryBlockInfoTrait.php:38
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:37
ApiQueryBase\validateSha1Hash
validateSha1Hash( $hash)
Definition: ApiQueryBase.php:586