MediaWiki  master
PageHistoryCountHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Rest\Handler;
4 
14 use RequestContext;
15 use Title;
16 use User;
17 use WANObjectCache;
23 
29  private const COUNT_LIMITS = [
30  'anonymous' => 10000,
31  'bot' => 10000,
32  'editors' => 25000,
33  'edits' => 30000,
34  'minor' => 1000,
35  'reverted' => 30000
36  ];
37 
38  private const DEPRECATED_COUNT_TYPES = [
39  'anonedits' => 'anonymous',
40  'botedits' => 'bot',
41  'revertededits' => 'reverted'
42  ];
43 
44  private const REVERTED_TAG_NAMES = [ 'mw-undo', 'mw-rollback' ];
45 
47  private $revisionStore;
48 
51 
54 
56  private $loadBalancer;
57 
59  private $cache;
60 
62  private $user;
63 
71  public function __construct(
73  NameTableStoreFactory $nameTableStoreFactory,
77  ) {
78  $this->revisionStore = $revisionStore;
79  $this->changeTagDefStore = $nameTableStoreFactory->getChangeTagDef();
80  $this->permissionManager = $permissionManager;
81  $this->loadBalancer = $loadBalancer;
82  $this->cache = $cache;
83 
84  // @todo Inject this, when there is a good way to do that
85  $this->user = RequestContext::getMain()->getUser();
86  }
87 
88  private function normalizeType( $type ) {
89  return self::DEPRECATED_COUNT_TYPES[$type] ?? $type;
90  }
91 
98  private function validateParameterCombination( $type ) {
99  $params = $this->getValidatedParams();
100  if ( !$params ) {
101  return;
102  }
103 
104  if ( $params['from'] || $params['to'] ) {
105  if ( $type === 'edits' || $type === 'editors' ) {
106  if ( !$params['from'] || !$params['to'] ) {
107  throw new LocalizedHttpException(
108  new MessageValue( 'rest-pagehistorycount-parameters-invalid' ),
109  400
110  );
111  }
112  } else {
113  throw new LocalizedHttpException(
114  new MessageValue( 'rest-pagehistorycount-parameters-invalid' ),
115  400
116  );
117  }
118  }
119  }
120 
127  public function run( $title, $type ) {
128  $normalizedType = $this->normalizeType( $type );
129  $this->validateParameterCombination( $normalizedType );
130  $titleObj = Title::newFromText( $title );
131  if ( !$titleObj || !$titleObj->getArticleID() ) {
132  throw new LocalizedHttpException(
133  new MessageValue( 'rest-nonexistent-title',
134  [ new ScalarParam( ParamType::PLAINTEXT, $title ) ]
135  ),
136  404
137  );
138  }
139 
140  if ( !$this->permissionManager->userCan( 'read', $this->user, $titleObj ) ) {
141  throw new LocalizedHttpException(
142  new MessageValue( 'rest-permission-denied-title',
143  [ new ScalarParam( ParamType::PLAINTEXT, $title ) ]
144  ),
145  403
146  );
147  }
148 
149  $count = $this->getCount( $titleObj, $normalizedType );
150  $countLimit = self::COUNT_LIMITS[$normalizedType];
151  $response = $this->getResponseFactory()->createJson( [
152  'count' => $count > $countLimit ? $countLimit : $count,
153  'limit' => $count > $countLimit
154  ] );
155 
156  // Inform clients who use a deprecated "type" value, so they can adjust
157  if ( isset( self::DEPRECATED_COUNT_TYPES[$type] ) ) {
158  $docs = '<https://www.mediawiki.org/wiki/API:REST/History_API' .
159  '#Get_page_history_counts>; rel="deprecation"';
160  $response->setHeader( 'Deprecation', 'version="v1"' );
161  $response->setHeader( 'Link', $docs );
162  }
163 
164  return $response;
165  }
166 
173  private function getCount( $title, $type ) {
174  switch ( $type ) {
175  case 'anonymous':
176  return $this->getCachedCount( $title, $type,
177  function ( RevisionRecord $fromRev = null ) use ( $title ) {
178  return $this->getAnonCount( $title->getArticleID(), $fromRev );
179  }
180  );
181 
182  case 'bot':
183  return $this->getCachedCount( $title, $type,
184  function ( RevisionRecord $fromRev = null ) use ( $title ) {
185  return $this->getBotCount( $title->getArticleID(), $fromRev );
186  }
187  );
188 
189  case 'editors':
190  $from = $this->getValidatedParams()['from'] ?? null;
191  $to = $this->getValidatedParams()['to'] ?? null;
192  if ( $from || $to ) {
193  return $this->getEditorsCount(
194  $title->getArticleID(),
195  $from ? $this->getRevisionOrThrow( $from ) : null,
196  $to ? $this->getRevisionOrThrow( $to ) : null
197  );
198  } else {
199  return $this->getCachedCount( $title, $type,
200  function ( RevisionRecord $fromRev = null ) use ( $title ) {
201  return $this->getEditorsCount( $title->getArticleID(), $fromRev );
202  }, false
203  );
204  }
205 
206  case 'edits':
207  $from = $this->getValidatedParams()['from'] ?? null;
208  $to = $this->getValidatedParams()['to'] ?? null;
209  if ( $from || $to ) {
210  return $this->getEditsCount(
211  $title->getArticleID(),
212  $from ? $this->getRevisionOrThrow( $from ) : null,
213  $to ? $this->getRevisionOrThrow( $to ) : null
214  );
215  } else {
216  return $this->getCachedCount( $title, $type,
217  function ( RevisionRecord $fromRev = null ) use ( $title ) {
218  return $this->getEditsCount( $title->getArticleID(), $fromRev );
219  }
220  );
221  }
222 
223  case 'reverted':
224  return $this->getCachedCount( $title, $type,
225  function ( RevisionRecord $fromRev = null ) use ( $title ) {
226  return $this->getRevertedCount( $title->getArticleID(), $fromRev );
227  }
228  );
229 
230  case 'minor':
231  // The query for minor counts is inefficient for the database for pages with many revisions.
232  // If the specified title contains more revisions than allowed, we will return an error.
233  $editsCount = $this->getCachedCount( $title, 'edits',
234  function ( RevisionRecord $fromRev = null ) use ( $title ) {
235  return $this->getEditsCount( $title->getArticleID(), $fromRev );
236  }
237  );
238  if ( $editsCount > self::COUNT_LIMITS[$type] * 2 ) {
239  throw new LocalizedHttpException(
240  new MessageValue( 'rest-pagehistorycount-too-many-revisions' ),
241  500
242  );
243  }
244  return $this->getCachedCount( $title, $type,
245  function ( RevisionRecord $fromRev = null ) use ( $title ) {
246  return $this->getMinorCount( $title->getArticleID(), $fromRev );
247  }
248  );
249 
250  // Sanity check
251  default:
252  throw new LocalizedHttpException(
253  new MessageValue( 'rest-pagehistorycount-type-unrecognized',
254  [ new ScalarParam( ParamType::PLAINTEXT, $type ) ]
255  ),
256  500
257  );
258  }
259  }
260 
268  private function getCachedCount( $title, $type,
269  callable $fetchCount, $incrementalUpdate = true
270  ) {
271  $pageId = $title->getArticleID();
272  return $this->cache->getWithSetCallback(
273  $this->cache->makeKey( 'rest', 'pagehistorycount', $pageId, $type ),
275  function ( $oldValue ) use ( $title, $fetchCount, $incrementalUpdate ) {
276  $currentRev = $this->revisionStore->getKnownCurrentRevision( $title );
277  if ( $oldValue && $incrementalUpdate ) {
278  if ( $oldValue['revision'] === $currentRev->getId() ) {
279  // Should never happen, but just in case
280  return $oldValue;
281  }
282  $rev = $this->revisionStore->getRevisionById( $oldValue['revision'] );
283  if ( $rev ) {
284  $additionalCount = $fetchCount( $rev );
285  return [
286  'revision' => $currentRev->getId(),
287  'count' => $oldValue['count'] + $additionalCount
288  ];
289  }
290  }
291  // Nothing was previously stored, or incremental update was done for too long,
292  // recalculate from scratch.
293  return [
294  'revision' => $currentRev->getId(),
295  'count' => $fetchCount()
296  ];
297  },
298  [
299  'touchedCallback' => function () use ( $title ) {
300  return wfTimestampOrNull(
301  TS_UNIX,
302  $this->revisionStore->getKnownCurrentRevision( $title )->getTimestamp()
303  );
304  },
305  'checkKeys' => [
306  "RevDelRevisionList:page:$pageId",
307  "DerivedPageDataUpdater:restore:page:$pageId"
308  ],
309  'version' => 1,
310  'lockTSE' => WANObjectCache::TTL_MINUTE * 5
311  ]
312  )['count'];
313  }
314 
320  protected function getAnonCount( $pageId, RevisionRecord $fromRev = null ) {
321  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
322 
323  $cond = [
324  'rev_page' => $pageId,
325  'actor_user IS NULL',
326  $dbr->bitAnd( 'rev_deleted',
327  RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_USER ) . " = 0"
328  ];
329 
330  if ( $fromRev ) {
331  $oldTs = $dbr->addQuotes( $dbr->timestamp( $fromRev->getTimestamp() ) );
332  $cond[] = "(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
333  "OR rev_timestamp > {$oldTs}";
334  }
335 
336  $edits = $dbr->selectRowCount(
337  [
338  'revision_actor_temp',
339  'revision',
340  'actor'
341  ],
342  '1',
343  $cond,
344  __METHOD__,
345  [ 'LIMIT' => self::COUNT_LIMITS['anonymous'] + 1 ], // extra to detect truncation
346  [
347  'revision' => [
348  'JOIN',
349  'revactor_rev = rev_id AND revactor_page = rev_page'
350  ],
351  'actor' => [
352  'JOIN',
353  'revactor_actor = actor_id'
354  ]
355  ]
356  );
357  return $edits;
358  }
359 
365  protected function getBotCount( $pageId, RevisionRecord $fromRev = null ) {
366  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
367 
368  $cond = [
369  'rev_page=' . intval( $pageId ),
370  $dbr->bitAnd( 'rev_deleted',
371  RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_USER ) . " = 0",
372  'EXISTS(' .
373  $dbr->selectSQLText(
374  'user_groups',
375  '1',
376  [
377  'actor.actor_user = ug_user',
378  'ug_group' => $this->permissionManager->getGroupsWithPermission( 'bot' ),
379  'ug_expiry IS NULL OR ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() )
380  ],
381  __METHOD__
382  ) .
383  ')'
384  ];
385  if ( $fromRev ) {
386  $oldTs = $dbr->addQuotes( $dbr->timestamp( $fromRev->getTimestamp() ) );
387  $cond[] = "(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
388  "OR rev_timestamp > {$oldTs}";
389  }
390 
391  $edits = $dbr->selectRowCount(
392  [
393  'revision_actor_temp',
394  'revision',
395  'actor',
396  ],
397  '1',
398  $cond,
399  __METHOD__,
400  [ 'LIMIT' => self::COUNT_LIMITS['bot'] + 1 ], // extra to detect truncation
401  [
402  'revision' => [
403  'JOIN',
404  'revactor_rev = rev_id AND revactor_page = rev_page'
405  ],
406  'actor' => [
407  'JOIN',
408  'revactor_actor = actor_id'
409  ],
410  ]
411  );
412  return $edits;
413  }
414 
421  protected function getEditorsCount( $pageId,
422  RevisionRecord $fromRev = null,
423  RevisionRecord $toRev = null
424  ) {
425  list( $fromRev, $toRev ) = $this->orderRevisions( $fromRev, $toRev );
426  return $this->revisionStore->countAuthorsBetween( $pageId, $fromRev,
427  $toRev, $this->user, self::COUNT_LIMITS['editors'] );
428  }
429 
435  protected function getRevertedCount( $pageId, RevisionRecord $fromRev = null ) {
436  $tagIds = [];
437 
438  foreach ( self::REVERTED_TAG_NAMES as $tagName ) {
439  try {
440  $tagIds[] = $this->changeTagDefStore->getId( $tagName );
441  } catch ( NameTableAccessException $e ) {
442  // If no revisions are tagged with a name, no tag id will be present
443  }
444  }
445  if ( !$tagIds ) {
446  return 0;
447  }
448 
449  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
450 
451  $cond = [
452  'rev_page' => $pageId,
453  $dbr->bitAnd( 'rev_deleted', RevisionRecord::DELETED_TEXT ) . " = 0"
454  ];
455  if ( $fromRev ) {
456  $oldTs = $dbr->addQuotes( $dbr->timestamp( $fromRev->getTimestamp() ) );
457  $cond[] = "(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
458  "OR rev_timestamp > {$oldTs}";
459  }
460  $edits = $dbr->selectRowCount(
461  [
462  'revision',
463  'change_tag'
464  ],
465  '1',
466  [ 'rev_page' => $pageId ],
467  __METHOD__,
468  [
469  'LIMIT' => self::COUNT_LIMITS['reverted'] + 1, // extra to detect truncation
470  'GROUP BY' => 'rev_id'
471  ],
472  [
473  'change_tag' => [
474  'JOIN',
475  [
476  'ct_rev_id = rev_id',
477  'ct_tag_id' => $tagIds,
478  ]
479  ],
480  ]
481  );
482  return $edits;
483  }
484 
490  protected function getMinorCount( $pageId, RevisionRecord $fromRev = null ) {
491  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
492  $cond = [
493  'rev_page' => $pageId,
494  'rev_minor_edit != 0',
495  $dbr->bitAnd( 'rev_deleted', RevisionRecord::DELETED_TEXT ) . " = 0"
496  ];
497  if ( $fromRev ) {
498  $oldTs = $dbr->addQuotes( $dbr->timestamp( $fromRev->getTimestamp() ) );
499  $cond[] = "(rev_timestamp = {$oldTs} AND rev_id > {$fromRev->getId()}) " .
500  "OR rev_timestamp > {$oldTs}";
501  }
502  $edits = $dbr->selectRowCount( 'revision', '1',
503  $cond,
504  __METHOD__,
505  [ 'LIMIT' => self::COUNT_LIMITS['minor'] + 1 ] // extra to detect truncation
506  );
507 
508  return $edits;
509  }
510 
517  protected function getEditsCount(
518  $pageId,
519  RevisionRecord $fromRev = null,
520  RevisionRecord $toRev = null
521  ) {
522  list( $fromRev, $toRev ) = $this->orderRevisions( $fromRev, $toRev );
523  return $this->revisionStore->countRevisionsBetween(
524  $pageId,
525  $fromRev,
526  $toRev,
527  self::COUNT_LIMITS['edits'] // Will be increased by 1 to detect truncation
528  );
529  }
530 
536  private function getRevisionOrThrow( $revId ) {
537  $rev = $this->revisionStore->getRevisionById( $revId );
538  if ( !$rev ) {
539  throw new LocalizedHttpException(
540  new MessageValue( 'rest-nonexistent-revision', [ $revId ] ),
541  404
542  );
543  }
544  return $rev;
545  }
546 
554  private function orderRevisions(
555  RevisionRecord $fromRev = null,
556  RevisionRecord $toRev = null
557  ) {
558  if ( $fromRev && $toRev && ( $fromRev->getTimestamp() > $toRev->getTimestamp() ||
559  ( $fromRev->getTimestamp() === $toRev->getTimestamp()
560  && $fromRev->getId() > $toRev->getId() ) )
561  ) {
562  return [ $toRev, $fromRev ];
563  }
564  return [ $fromRev, $toRev ];
565  }
566 
567  public function needsWriteAccess() {
568  return false;
569  }
570 
571  public function getParamSettings() {
572  return [
573  'title' => [
574  self::PARAM_SOURCE => 'path',
575  ParamValidator::PARAM_TYPE => 'string',
576  ParamValidator::PARAM_REQUIRED => true,
577  ],
578  'type' => [
579  self::PARAM_SOURCE => 'path',
580  ParamValidator::PARAM_TYPE => array_merge(
581  array_keys( self::COUNT_LIMITS ),
582  array_keys( self::DEPRECATED_COUNT_TYPES )
583  ),
584  ParamValidator::PARAM_REQUIRED => true,
585  ],
586  'from' => [
587  self::PARAM_SOURCE => 'query',
588  ParamValidator::PARAM_TYPE => 'integer',
589  ParamValidator::PARAM_REQUIRED => false
590  ],
591  'to' => [
592  self::PARAM_SOURCE => 'query',
593  ParamValidator::PARAM_TYPE => 'integer',
594  ParamValidator::PARAM_REQUIRED => false
595  ]
596  ];
597  }
598 }
MediaWiki\Rest\Handler\PageHistoryCountHandler\$revisionStore
RevisionStore $revisionStore
Definition: PageHistoryCountHandler.php:47
MediaWiki\Rest\Handler\PageHistoryCountHandler\run
run( $title, $type)
Definition: PageHistoryCountHandler.php:127
MediaWiki\Rest\Handler
Definition: CompareHandler.php:3
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:317
MediaWiki\Rest\Handler\PageHistoryCountHandler\$cache
WANObjectCache $cache
Definition: PageHistoryCountHandler.php:59
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
MediaWiki\Rest\Handler\PageHistoryCountHandler\getBotCount
getBotCount( $pageId, RevisionRecord $fromRev=null)
Definition: PageHistoryCountHandler.php:365
MediaWiki\Rest\Handler\getResponseFactory
getResponseFactory()
Get the ResponseFactory which can be used to generate Response objects.
Definition: Handler.php:92
$response
$response
Definition: opensearch_desc.php:38
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:79
MediaWiki\Rest\Handler\PageHistoryCountHandler\REVERTED_TAG_NAMES
const REVERTED_TAG_NAMES
Definition: PageHistoryCountHandler.php:44
MediaWiki\Rest\Handler\PageHistoryCountHandler\getEditsCount
getEditsCount( $pageId, RevisionRecord $fromRev=null, RevisionRecord $toRev=null)
Definition: PageHistoryCountHandler.php:517
MediaWiki\Rest\Handler\PageHistoryCountHandler\getAnonCount
getAnonCount( $pageId, RevisionRecord $fromRev=null)
Definition: PageHistoryCountHandler.php:320
MediaWiki\Rest\Handler\PageHistoryCountHandler\getParamSettings
getParamSettings()
Fetch ParamValidator settings for parameters.
Definition: PageHistoryCountHandler.php:571
MediaWiki\Rest\Handler\PageHistoryCountHandler\__construct
__construct(RevisionStore $revisionStore, NameTableStoreFactory $nameTableStoreFactory, PermissionManager $permissionManager, ILoadBalancer $loadBalancer, WANObjectCache $cache)
Definition: PageHistoryCountHandler.php:71
MediaWiki\Rest\Handler\PageHistoryCountHandler\COUNT_LIMITS
const COUNT_LIMITS
The maximum number of counts to return per type of revision.
Definition: PageHistoryCountHandler.php:29
Wikimedia\Message\ScalarParam
Value object representing a message parameter holding a single value.
Definition: ScalarParam.php:10
IExpiringStore\TTL_MINUTE
const TTL_MINUTE
Definition: IExpiringStore.php:33
MediaWiki\Rest\Handler\PageHistoryCountHandler\$changeTagDefStore
NameTableStore $changeTagDefStore
Definition: PageHistoryCountHandler.php:50
Wikimedia\Message\MessageValue
Value object representing a message for i18n.
Definition: MessageValue.php:14
$dbr
$dbr
Definition: testCompression.php:52
MediaWiki\Rest\Handler\PageHistoryCountHandler\validateParameterCombination
validateParameterCombination( $type)
Validates that the provided parameter combination is supported.
Definition: PageHistoryCountHandler.php:98
MediaWiki\Rest\Handler\PageHistoryCountHandler\DEPRECATED_COUNT_TYPES
const DEPRECATED_COUNT_TYPES
Definition: PageHistoryCountHandler.php:38
MediaWiki\Rest\Handler\PageHistoryCountHandler\getRevisionOrThrow
getRevisionOrThrow( $revId)
Definition: PageHistoryCountHandler.php:536
MediaWiki\Rest\Handler\PageHistoryCountHandler\$permissionManager
PermissionManager $permissionManager
Definition: PageHistoryCountHandler.php:53
MediaWiki\Rest\Handler\PageHistoryCountHandler\getRevertedCount
getRevertedCount( $pageId, RevisionRecord $fromRev=null)
Definition: PageHistoryCountHandler.php:435
MediaWiki\Rest\Handler\PageHistoryCountHandler
Handler class for Core REST API endpoints that perform operations on revisions.
Definition: PageHistoryCountHandler.php:27
MediaWiki\Rest\Handler\PageHistoryCountHandler\orderRevisions
orderRevisions(RevisionRecord $fromRev=null, RevisionRecord $toRev=null)
Reorders revisions if they are present.
Definition: PageHistoryCountHandler.php:554
MediaWiki\Rest\Handler\PageHistoryCountHandler\getEditorsCount
getEditorsCount( $pageId, RevisionRecord $fromRev=null, RevisionRecord $toRev=null)
Definition: PageHistoryCountHandler.php:421
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:1887
MediaWiki\Rest\Handler\PageHistoryCountHandler\getMinorCount
getMinorCount( $pageId, RevisionRecord $fromRev=null)
Definition: PageHistoryCountHandler.php:490
MediaWiki\Rest\Response
Definition: Response.php:8
$title
$title
Definition: testCompression.php:36
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
RequestContext
Group all the pieces relevant to the context of a request into one instance.
Definition: RequestContext.php:34
MediaWiki\Rest\Handler\getValidatedParams
getValidatedParams()
Fetch the validated parameters.
Definition: Handler.php:188
MediaWiki\Rest\Handler\PageHistoryCountHandler\$user
User $user
Definition: PageHistoryCountHandler.php:62
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:48
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:116
MediaWiki\Storage\NameTableStore
Definition: NameTableStore.php:36
MediaWiki\Rest\Handler\PageHistoryCountHandler\getCount
getCount( $title, $type)
Definition: PageHistoryCountHandler.php:173
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:447
MediaWiki\Rest\Handler\PageHistoryCountHandler\$loadBalancer
ILoadBalancer $loadBalancer
Definition: PageHistoryCountHandler.php:56
IExpiringStore\TTL_WEEK
const TTL_WEEK
Definition: IExpiringStore.php:36
MediaWiki\Storage\NameTableStoreFactory\getChangeTagDef
getChangeTagDef( $wiki=false)
Get a NameTableStore for the change_tag_def table.
Definition: NameTableStoreFactory.php:127
Title
Represents a title within MediaWiki.
Definition: Title.php:42
MediaWiki\Storage\NameTableAccessException
Exception representing a failure to look up a row from a name table.
Definition: NameTableAccessException.php:32
MediaWiki\Storage\NameTableStoreFactory
Definition: NameTableStoreFactory.php:26
MediaWiki\Rest\Handler\PageHistoryCountHandler\needsWriteAccess
needsWriteAccess()
Indicates whether this route requires write access.
Definition: PageHistoryCountHandler.php:567
MediaWiki\Rest\Handler\PageHistoryCountHandler\getCachedCount
getCachedCount( $title, $type, callable $fetchCount, $incrementalUpdate=true)
Definition: PageHistoryCountHandler.php:268
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:52
MediaWiki\Rest\Handler\PageHistoryCountHandler\normalizeType
normalizeType( $type)
Definition: PageHistoryCountHandler.php:88
Wikimedia\ParamValidator\ParamValidator
Service for formatting and validating API parameters.
Definition: ParamValidator.php:42
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
MediaWiki\Rest\LocalizedHttpException
Definition: LocalizedHttpException.php:7
Wikimedia\Message\ParamType
The constants used to specify parameter types.
Definition: ParamType.php:11
MediaWiki\Rest\SimpleHandler
Definition: SimpleHandler.php:14
$type
$type
Definition: testCompression.php:50