MediaWiki  master
WatchedItemQueryService.php
Go to the documentation of this file.
1 <?php
2 
4 use Wikimedia\Assert\Assert;
5 
17 
18  const DIR_OLDER = 'older';
19  const DIR_NEWER = 'newer';
20 
21  const INCLUDE_FLAGS = 'flags';
22  const INCLUDE_USER = 'user';
23  const INCLUDE_USER_ID = 'userid';
24  const INCLUDE_COMMENT = 'comment';
25  const INCLUDE_PATROL_INFO = 'patrol';
26  const INCLUDE_SIZES = 'sizes';
27  const INCLUDE_LOG_INFO = 'loginfo';
28 
29  // FILTER_* constants are part of public API (are used in ApiQueryWatchlist and
30  // ApiQueryWatchlistRaw classes) and should not be changed.
31  // Changing values of those constants will result in a breaking change in the API
32  const FILTER_MINOR = 'minor';
33  const FILTER_NOT_MINOR = '!minor';
34  const FILTER_BOT = 'bot';
35  const FILTER_NOT_BOT = '!bot';
36  const FILTER_ANON = 'anon';
37  const FILTER_NOT_ANON = '!anon';
38  const FILTER_PATROLLED = 'patrolled';
39  const FILTER_NOT_PATROLLED = '!patrolled';
40  const FILTER_UNREAD = 'unread';
41  const FILTER_NOT_UNREAD = '!unread';
42  const FILTER_CHANGED = 'changed';
43  const FILTER_NOT_CHANGED = '!changed';
44 
45  const SORT_ASC = 'ASC';
46  const SORT_DESC = 'DESC';
47 
51  private $loadBalancer;
52 
53  public function __construct( LoadBalancer $loadBalancer ) {
54  $this->loadBalancer = $loadBalancer;
55  }
56 
61  private function getConnection() {
62  return $this->loadBalancer->getConnection( DB_SLAVE, [ 'watchlist' ] );
63  }
64 
69  private function reuseConnection( DatabaseBase $connection ) {
70  $this->loadBalancer->reuseConnection( $connection );
71  }
72 
119  $options += [
120  'includeFields' => [],
121  'namespaceIds' => [],
122  'filters' => [],
123  'allRevisions' => false,
124  'usedInGenerator' => false
125  ];
126 
127  Assert::parameter(
128  !isset( $options['rcTypes'] )
129  || !array_diff( $options['rcTypes'], [ RC_EDIT, RC_NEW, RC_LOG, RC_EXTERNAL, RC_CATEGORIZE ] ),
130  '$options[\'rcTypes\']',
131  'must be an array containing only: RC_EDIT, RC_NEW, RC_LOG, RC_EXTERNAL and/or RC_CATEGORIZE'
132  );
133  Assert::parameter(
134  !isset( $options['dir'] ) || in_array( $options['dir'], [ self::DIR_OLDER, self::DIR_NEWER ] ),
135  '$options[\'dir\']',
136  'must be DIR_OLDER or DIR_NEWER'
137  );
138  Assert::parameter(
139  !isset( $options['start'] ) && !isset( $options['end'] ) && !isset( $options['startFrom'] )
140  || isset( $options['dir'] ),
141  '$options[\'dir\']',
142  'must be provided when providing any of options: start, end, startFrom'
143  );
144  Assert::parameter(
145  !isset( $options['startFrom'] )
146  || ( is_array( $options['startFrom'] ) && count( $options['startFrom'] ) === 2 ),
147  '$options[\'startFrom\']',
148  'must be a two-element array'
149  );
150  if ( array_key_exists( 'watchlistOwner', $options ) ) {
151  Assert::parameterType(
152  User::class,
153  $options['watchlistOwner'],
154  '$options[\'watchlistOwner\']'
155  );
156  Assert::parameter(
157  isset( $options['watchlistOwnerToken'] ),
158  '$options[\'watchlistOwnerToken\']',
159  'must be provided when providing watchlistOwner option'
160  );
161  }
162 
163  $tables = [ 'recentchanges', 'watchlist' ];
164  if ( !$options['allRevisions'] ) {
165  $tables[] = 'page';
166  }
167 
168  $db = $this->getConnection();
169 
171  $conds = $this->getWatchedItemsWithRCInfoQueryConds( $db, $user, $options );
172  $dbOptions = $this->getWatchedItemsWithRCInfoQueryDbOptions( $options );
173  $joinConds = $this->getWatchedItemsWithRCInfoQueryJoinConds( $options );
174 
175  $res = $db->select(
176  $tables,
177  $fields,
178  $conds,
179  __METHOD__,
180  $dbOptions,
181  $joinConds
182  );
183 
184  $this->reuseConnection( $db );
185 
186  $items = [];
187  foreach ( $res as $row ) {
188  $items[] = [
189  new WatchedItem(
190  $user,
191  new TitleValue( (int)$row->rc_namespace, $row->rc_title ),
192  $row->wl_notificationtimestamp
193  ),
194  $this->getRecentChangeFieldsFromRow( $row )
195  ];
196  }
197 
198  return $items;
199  }
200 
220  public function getWatchedItemsForUser( User $user, array $options = [] ) {
221  if ( $user->isAnon() ) {
222  // TODO: should this just return an empty array or rather complain loud at this point
223  // as e.g. ApiBase::getWatchlistUser does?
224  return [];
225  }
226 
227  $options += [ 'namespaceIds' => [] ];
228 
229  Assert::parameter(
230  !isset( $options['sort'] ) || in_array( $options['sort'], [ self::SORT_ASC, self::SORT_DESC ] ),
231  '$options[\'sort\']',
232  'must be SORT_ASC or SORT_DESC'
233  );
234  Assert::parameter(
235  !isset( $options['filter'] ) || in_array(
236  $options['filter'], [ self::FILTER_CHANGED, self::FILTER_NOT_CHANGED ]
237  ),
238  '$options[\'filter\']',
239  'must be FILTER_CHANGED or FILTER_NOT_CHANGED'
240  );
241  Assert::parameter(
242  !isset( $options['from'] ) && !isset( $options['until'] ) && !isset( $options['startFrom'] )
243  || isset( $options['sort'] ),
244  '$options[\'sort\']',
245  'must be provided if any of "from", "until", "startFrom" options is provided'
246  );
247 
248  $db = $this->getConnection();
249 
250  $conds = $this->getWatchedItemsForUserQueryConds( $db, $user, $options );
251  $dbOptions = $this->getWatchedItemsForUserQueryDbOptions( $options );
252 
253  $res = $db->select(
254  'watchlist',
255  [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
256  $conds,
257  __METHOD__,
258  $dbOptions
259  );
260 
261  $this->reuseConnection( $db );
262 
263  $watchedItems = [];
264  foreach ( $res as $row ) {
265  // todo these could all be cached at some point?
266  $watchedItems[] = new WatchedItem(
267  $user,
268  new TitleValue( (int)$row->wl_namespace, $row->wl_title ),
269  $row->wl_notificationtimestamp
270  );
271  }
272 
273  return $watchedItems;
274  }
275 
276  private function getRecentChangeFieldsFromRow( stdClass $row ) {
277  // This can be simplified to single array_filter call filtering by key value,
278  // once we stop supporting PHP 5.5
279  $allFields = get_object_vars( $row );
280  $rcKeys = array_filter(
281  array_keys( $allFields ),
282  function( $key ) {
283  return substr( $key, 0, 3 ) === 'rc_';
284  }
285  );
286  return array_intersect_key( $allFields, array_flip( $rcKeys ) );
287  }
288 
290  $fields = [
291  'rc_id',
292  'rc_namespace',
293  'rc_title',
294  'rc_timestamp',
295  'rc_type',
296  'rc_deleted',
297  'wl_notificationtimestamp'
298  ];
299 
300  $rcIdFields = [
301  'rc_cur_id',
302  'rc_this_oldid',
303  'rc_last_oldid',
304  ];
305  if ( $options['usedInGenerator'] ) {
306  if ( $options['allRevisions'] ) {
307  $rcIdFields = [ 'rc_this_oldid' ];
308  } else {
309  $rcIdFields = [ 'rc_cur_id' ];
310  }
311  }
312  $fields = array_merge( $fields, $rcIdFields );
313 
314  if ( in_array( self::INCLUDE_FLAGS, $options['includeFields'] ) ) {
315  $fields = array_merge( $fields, [ 'rc_type', 'rc_minor', 'rc_bot' ] );
316  }
317  if ( in_array( self::INCLUDE_USER, $options['includeFields'] ) ) {
318  $fields[] = 'rc_user_text';
319  }
320  if ( in_array( self::INCLUDE_USER_ID, $options['includeFields'] ) ) {
321  $fields[] = 'rc_user';
322  }
323  if ( in_array( self::INCLUDE_COMMENT, $options['includeFields'] ) ) {
324  $fields[] = 'rc_comment';
325  }
326  if ( in_array( self::INCLUDE_PATROL_INFO, $options['includeFields'] ) ) {
327  $fields = array_merge( $fields, [ 'rc_patrolled', 'rc_log_type' ] );
328  }
329  if ( in_array( self::INCLUDE_SIZES, $options['includeFields'] ) ) {
330  $fields = array_merge( $fields, [ 'rc_old_len', 'rc_new_len' ] );
331  }
332  if ( in_array( self::INCLUDE_LOG_INFO, $options['includeFields'] ) ) {
333  $fields = array_merge( $fields, [ 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ] );
334  }
335 
336  return $fields;
337  }
338 
340  DatabaseBase $db,
341  User $user,
343  ) {
344  $watchlistOwnerId = $this->getWatchlistOwnerId( $user, $options );
345  $conds = [ 'wl_user' => $watchlistOwnerId ];
346 
347  if ( !$options['allRevisions'] ) {
348  $conds[] = $db->makeList(
349  [ 'rc_this_oldid=page_latest', 'rc_type=' . RC_LOG ],
350  LIST_OR
351  );
352  }
353 
354  if ( $options['namespaceIds'] ) {
355  $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
356  }
357 
358  if ( array_key_exists( 'rcTypes', $options ) ) {
359  $conds['rc_type'] = array_map( 'intval', $options['rcTypes'] );
360  }
361 
362  $conds = array_merge(
363  $conds,
364  $this->getWatchedItemsWithRCInfoQueryFilterConds( $user, $options )
365  );
366 
367  $conds = array_merge( $conds, $this->getStartEndConds( $db, $options ) );
368 
369  if ( !isset( $options['start'] ) && !isset( $options['end'] ) ) {
370  if ( $db->getType() === 'mysql' ) {
371  // This is an index optimization for mysql
372  $conds[] = "rc_timestamp > ''";
373  }
374  }
375 
376  $conds = array_merge( $conds, $this->getUserRelatedConds( $db, $user, $options ) );
377 
378  $deletedPageLogCond = $this->getExtraDeletedPageLogEntryRelatedCond( $db, $user );
379  if ( $deletedPageLogCond ) {
380  $conds[] = $deletedPageLogCond;
381  }
382 
383  if ( array_key_exists( 'startFrom', $options ) ) {
384  $conds[] = $this->getStartFromConds( $db, $options );
385  }
386 
387  return $conds;
388  }
389 
390  private function getWatchlistOwnerId( User $user, array $options ) {
391  if ( array_key_exists( 'watchlistOwner', $options ) ) {
393  $watchlistOwner = $options['watchlistOwner'];
394  $ownersToken = $watchlistOwner->getOption( 'watchlisttoken' );
395  $token = $options['watchlistOwnerToken'];
396  if ( $ownersToken == '' || !hash_equals( $ownersToken, $token ) ) {
397  throw new UsageException(
398  'Incorrect watchlist token provided -- please set a correct token in Special:Preferences',
399  'bad_wltoken'
400  );
401  }
402  return $watchlistOwner->getId();
403  }
404  return $user->getId();
405  }
406 
408  $conds = [];
409 
410  if ( in_array( self::FILTER_MINOR, $options['filters'] ) ) {
411  $conds[] = 'rc_minor != 0';
412  } elseif ( in_array( self::FILTER_NOT_MINOR, $options['filters'] ) ) {
413  $conds[] = 'rc_minor = 0';
414  }
415 
416  if ( in_array( self::FILTER_BOT, $options['filters'] ) ) {
417  $conds[] = 'rc_bot != 0';
418  } elseif ( in_array( self::FILTER_NOT_BOT, $options['filters'] ) ) {
419  $conds[] = 'rc_bot = 0';
420  }
421 
422  if ( in_array( self::FILTER_ANON, $options['filters'] ) ) {
423  $conds[] = 'rc_user = 0';
424  } elseif ( in_array( self::FILTER_NOT_ANON, $options['filters'] ) ) {
425  $conds[] = 'rc_user != 0';
426  }
427 
428  if ( $user->useRCPatrol() || $user->useNPPatrol() ) {
429  // TODO: not sure if this should simply ignore patrolled filters if user does not have the patrol
430  // right, or maybe rather fail loud at this point, same as e.g. ApiQueryWatchlist does?
431  if ( in_array( self::FILTER_PATROLLED, $options['filters'] ) ) {
432  $conds[] = 'rc_patrolled != 0';
433  } elseif ( in_array( self::FILTER_NOT_PATROLLED, $options['filters'] ) ) {
434  $conds[] = 'rc_patrolled = 0';
435  }
436  }
437 
438  if ( in_array( self::FILTER_UNREAD, $options['filters'] ) ) {
439  $conds[] = 'rc_timestamp >= wl_notificationtimestamp';
440  } elseif ( in_array( self::FILTER_NOT_UNREAD, $options['filters'] ) ) {
441  // TODO: should this be changed to use Database::makeList?
442  $conds[] = 'wl_notificationtimestamp IS NULL OR rc_timestamp < wl_notificationtimestamp';
443  }
444 
445  return $conds;
446  }
447 
448  private function getStartEndConds( DatabaseBase $db, array $options ) {
449  if ( !isset( $options['start'] ) && ! isset( $options['end'] ) ) {
450  return [];
451  }
452 
453  $conds = [];
454 
455  if ( isset( $options['start'] ) ) {
456  $after = $options['dir'] === self::DIR_OLDER ? '<=' : '>=';
457  $conds[] = 'rc_timestamp ' . $after . ' ' . $db->addQuotes( $options['start'] );
458  }
459  if ( isset( $options['end'] ) ) {
460  $before = $options['dir'] === self::DIR_OLDER ? '>=' : '<=';
461  $conds[] = 'rc_timestamp ' . $before . ' ' . $db->addQuotes( $options['end'] );
462  }
463 
464  return $conds;
465  }
466 
468  if ( !array_key_exists( 'onlyByUser', $options ) && !array_key_exists( 'notByUser', $options ) ) {
469  return [];
470  }
471 
472  $conds = [];
473 
474  if ( array_key_exists( 'onlyByUser', $options ) ) {
475  $conds['rc_user_text'] = $options['onlyByUser'];
476  } elseif ( array_key_exists( 'notByUser', $options ) ) {
477  $conds[] = 'rc_user_text != ' . $db->addQuotes( $options['notByUser'] );
478  }
479 
480  // Avoid brute force searches (bug 17342)
481  $bitmask = 0;
482  if ( !$user->isAllowed( 'deletedhistory' ) ) {
483  $bitmask = Revision::DELETED_USER;
484  } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
486  }
487  if ( $bitmask ) {
488  $conds[] = $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask";
489  }
490 
491  return $conds;
492  }
493 
495  // LogPage::DELETED_ACTION hides the affected page, too. So hide those
496  // entirely from the watchlist, or someone could guess the title.
497  $bitmask = 0;
498  if ( !$user->isAllowed( 'deletedhistory' ) ) {
499  $bitmask = LogPage::DELETED_ACTION;
500  } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
502  }
503  if ( $bitmask ) {
504  return $db->makeList( [
505  'rc_type != ' . RC_LOG,
506  $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
507  ], LIST_OR );
508  }
509  return '';
510  }
511 
512  private function getStartFromConds( DatabaseBase $db, array $options ) {
513  $op = $options['dir'] === self::DIR_OLDER ? '<' : '>';
514  list( $rcTimestamp, $rcId ) = $options['startFrom'];
515  $rcTimestamp = $db->addQuotes( $db->timestamp( $rcTimestamp ) );
516  $rcId = (int)$rcId;
517  return $db->makeList(
518  [
519  "rc_timestamp $op $rcTimestamp",
520  $db->makeList(
521  [
522  "rc_timestamp = $rcTimestamp",
523  "rc_id $op= $rcId"
524  ],
525  LIST_AND
526  )
527  ],
528  LIST_OR
529  );
530  }
531 
533  $conds = [ 'wl_user' => $user->getId() ];
534  if ( $options['namespaceIds'] ) {
535  $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
536  }
537  if ( isset( $options['filter'] ) ) {
538  $filter = $options['filter'];
539  if ( $filter === self::FILTER_CHANGED ) {
540  $conds[] = 'wl_notificationtimestamp IS NOT NULL';
541  } else {
542  $conds[] = 'wl_notificationtimestamp IS NULL';
543  }
544  }
545 
546  if ( isset( $options['from'] ) ) {
547  $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
548  $conds[] = $this->getFromUntilTargetConds( $db, $options['from'], $op );
549  }
550  if ( isset( $options['until'] ) ) {
551  $op = $options['sort'] === self::SORT_ASC ? '<' : '>';
552  $conds[] = $this->getFromUntilTargetConds( $db, $options['until'], $op );
553  }
554  if ( isset( $options['startFrom'] ) ) {
555  $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
556  $conds[] = $this->getFromUntilTargetConds( $db, $options['startFrom'], $op );
557  }
558 
559  return $conds;
560  }
561 
571  private function getFromUntilTargetConds( DatabaseBase $db, LinkTarget $target, $op ) {
572  return $db->makeList(
573  [
574  "wl_namespace $op " . $target->getNamespace(),
575  $db->makeList(
576  [
577  'wl_namespace = ' . $target->getNamespace(),
578  "wl_title $op= " . $db->addQuotes( $target->getDBkey() )
579  ],
580  LIST_AND
581  )
582  ],
583  LIST_OR
584  );
585  }
586 
588  $dbOptions = [];
589 
590  if ( array_key_exists( 'dir', $options ) ) {
591  $sort = $options['dir'] === self::DIR_OLDER ? ' DESC' : '';
592  $dbOptions['ORDER BY'] = [ 'rc_timestamp' . $sort, 'rc_id' . $sort ];
593  }
594 
595  if ( array_key_exists( 'limit', $options ) ) {
596  $dbOptions['LIMIT'] = (int)$options['limit'];
597  }
598 
599  return $dbOptions;
600  }
601 
603  $dbOptions = [];
604  if ( array_key_exists( 'sort', $options ) ) {
605  $dbOptions['ORDER BY'] = [
606  "wl_namespace {$options['sort']}",
607  "wl_title {$options['sort']}"
608  ];
609  if ( count( $options['namespaceIds'] ) === 1 ) {
610  $dbOptions['ORDER BY'] = "wl_title {$options['sort']}";
611  }
612  }
613  if ( array_key_exists( 'limit', $options ) ) {
614  $dbOptions['LIMIT'] = (int)$options['limit'];
615  }
616  return $dbOptions;
617  }
618 
620  $joinConds = [
621  'watchlist' => [ 'INNER JOIN',
622  [
623  'wl_namespace=rc_namespace',
624  'wl_title=rc_title'
625  ]
626  ]
627  ];
628  if ( !$options['allRevisions'] ) {
629  $joinConds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
630  }
631  return $joinConds;
632  }
633 
634 }
getWatchedItemsWithRCInfoQueryFields(array $options)
const RC_CATEGORIZE
Definition: Defines.php:173
getWatchedItemsWithRecentChangeInfo(User $user, array $options=[])
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
the array() calling protocol came about after MediaWiki 1.4rc1.
getWatchedItemsForUser(User $user, array $options=[])
For simple listing of user's watchlist items, see WatchedItemStore::getWatchedItemsForUser.
getFromUntilTargetConds(DatabaseBase $db, LinkTarget $target, $op)
Creates a query condition part for getting only items before or after the given link target (while or...
getWatchedItemsForUserQueryDbOptions(array $options)
getStartEndConds(DatabaseBase $db, array $options)
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
isAllowedAny()
Check if user is allowed to access a feature / make an action.
Definition: User.php:3546
getWatchedItemsWithRCInfoQueryDbOptions(array $options)
$sort
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:36
getWatchedItemsWithRCInfoQueryJoinConds(array $options)
makeList($a, $mode=LIST_COMMA)
Makes an encoded list of strings from an array.
Definition: Database.php:1518
getStartFromConds(DatabaseBase $db, array $options)
getExtraDeletedPageLogEntryRelatedCond(DatabaseBase $db, User $user)
getUserRelatedConds(DatabaseBase $db, User $user, array $options)
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist & $tables
Definition: hooks.txt:981
getNamespace()
Get the namespace index.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:47
Database load balancing object.
timestamp($ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
Definition: Database.php:2855
bitAnd($fieldLeft, $fieldRight)
Definition: Database.php:1628
const LIST_AND
Definition: Defines.php:194
isAllowed($action= '')
Internal mechanics of testing a permission.
Definition: User.php:3576
isAnon()
Get whether the user is anonymous.
Definition: User.php:3521
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1020
getWatchedItemsWithRCInfoQueryFilterConds(User $user, array $options)
$res
Definition: database.txt:21
getDBkey()
Get the main part with underscores.
const DELETED_RESTRICTED
Definition: Revision.php:79
Representation of a pair of user and title for watchlist entries.
Definition: WatchedItem.php:32
const DB_SLAVE
Definition: Defines.php:46
addQuotes($s)
Adds quotes and backslashes.
Definition: Database.php:1958
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
const DELETED_RESTRICTED
Definition: LogPage.php:36
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition: hooks.txt:242
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition: User.php:3589
const LIST_OR
Definition: Defines.php:197
Database abstraction object.
Definition: Database.php:32
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
getWatchlistOwnerId(User $user, array $options)
const DELETED_USER
Definition: Revision.php:78
getId()
Get the user's ID.
Definition: User.php:2114
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
getWatchedItemsWithRCInfoQueryConds(DatabaseBase $db, User $user, array $options)
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition: User.php:3598
const RC_EXTERNAL
Definition: Defines.php:172
const RC_NEW
Definition: Defines.php:170
const DELETED_ACTION
Definition: LogPage.php:33
reuseConnection(DatabaseBase $connection)
__construct(LoadBalancer $loadBalancer)
const RC_EDIT
Definition: Defines.php:169
This exception will be thrown when dieUsage is called to stop module execution.
Definition: ApiMain.php:1947
getWatchedItemsForUserQueryConds(DatabaseBase $db, User $user, array $options)
const RC_LOG
Definition: Defines.php:171