MediaWiki  master
UserOptionsManager.php
Go to the documentation of this file.
1 <?php
21 namespace MediaWiki\User;
22 
24 use HTMLCheckMatrix;
25 use HTMLFormField;
27 use IContextSource;
28 use InvalidArgumentException;
29 use LanguageCode;
35 use Psr\Log\LoggerInterface;
36 use User;
39 
45 
49  public const CONSTRUCTOR_OPTIONS = [
50  'HiddenPrefs'
51  ];
52 
54  private $serviceOptions;
55 
58 
61 
63  private $loadBalancer;
64 
66  private $logger;
67 
69  private $optionsCache = [];
70 
72  private $originalOptionsCache = [];
73 
75  private $hookRunner;
76 
79 
88  public function __construct(
89  ServiceOptions $options,
93  LoggerInterface $logger,
94  HookContainer $hookContainer
95  ) {
96  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
97  $this->serviceOptions = $options;
98  $this->defaultOptionsLookup = $defaultOptionsLookup;
99  $this->languageConverterFactory = $languageConverterFactory;
100  $this->loadBalancer = $loadBalancer;
101  $this->logger = $logger;
102  $this->hookRunner = new HookRunner( $hookContainer );
103  }
104 
108  public function getDefaultOptions(): array {
109  return $this->defaultOptionsLookup->getDefaultOptions();
110  }
111 
115  public function getDefaultOption( string $opt ) {
116  return $this->defaultOptionsLookup->getDefaultOption( $opt );
117  }
118 
122  public function getOption(
123  UserIdentity $user,
124  string $oname,
125  $defaultOverride = null,
126  bool $ignoreHidden = false,
127  int $queryFlags = self::READ_NORMAL
128  ) {
129  # We want 'disabled' preferences to always behave as the default value for
130  # users, even if they have set the option explicitly in their settings (ie they
131  # set it, and then it was disabled removing their ability to change it). But
132  # we don't want to erase the preferences in the database in case the preference
133  # is re-enabled again. So don't touch $mOptions, just override the returned value
134  if ( !$ignoreHidden && in_array( $oname, $this->serviceOptions->get( 'HiddenPrefs' ) ) ) {
135  return $this->defaultOptionsLookup->getDefaultOption( $oname );
136  }
137 
138  $options = $this->loadUserOptions( $user, $queryFlags );
139  if ( array_key_exists( $oname, $options ) ) {
140  return $options[$oname];
141  }
142  return $defaultOverride;
143  }
144 
148  public function getOptions(
149  UserIdentity $user,
150  int $flags = 0,
151  int $queryFlags = self::READ_NORMAL
152  ): array {
153  $options = $this->loadUserOptions( $user, $queryFlags );
154 
155  # We want 'disabled' preferences to always behave as the default value for
156  # users, even if they have set the option explicitly in their settings (ie they
157  # set it, and then it was disabled removing their ability to change it). But
158  # we don't want to erase the preferences in the database in case the preference
159  # is re-enabled again. So don't touch $mOptions, just override the returned value
160  foreach ( $this->serviceOptions->get( 'HiddenPrefs' ) as $pref ) {
161  $default = $this->defaultOptionsLookup->getDefaultOption( $pref );
162  if ( $default !== null ) {
163  $options[$pref] = $default;
164  }
165  }
166 
167  if ( $flags & self::EXCLUDE_DEFAULTS ) {
168  $options = array_diff_assoc( $options, $this->defaultOptionsLookup->getDefaultOptions() );
169  }
170 
171  return $options;
172  }
173 
183  public function setOption( UserIdentity $user, string $oname, $val ) {
184  // In case the options are modified, we need to refetch
185  // latest options in case they were not fetched from master
186  // so that we don't get a race condition trying to save modified options.
187  $this->loadUserOptions( $user, self::READ_LATEST );
188 
189  // Explicitly NULL values should refer to defaults
190  if ( $val === null ) {
191  $val = $this->defaultOptionsLookup->getDefaultOption( $oname );
192  }
193 
194  $userKey = $this->getCacheKey( $user );
195  $this->optionsCache[$userKey][$oname] = $val;
196  }
197 
210  public function resetOptions(
211  UserIdentity $user,
213  $resetKinds = [ 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ]
214  ) {
215  $oldOptions = $this->loadUserOptions( $user, self::READ_LATEST );
216  $defaultOptions = $this->defaultOptionsLookup->getDefaultOptions();
217 
218  if ( !is_array( $resetKinds ) ) {
219  $resetKinds = [ $resetKinds ];
220  }
221 
222  if ( in_array( 'all', $resetKinds ) ) {
223  $newOptions = $defaultOptions;
224  } else {
225  $optionKinds = $this->getOptionKinds( $user, $context );
226  $resetKinds = array_intersect( $resetKinds, $this->listOptionKinds() );
227  $newOptions = [];
228 
229  // Use default values for the options that should be deleted, and
230  // copy old values for the ones that shouldn't.
231  foreach ( $oldOptions as $key => $value ) {
232  if ( in_array( $optionKinds[$key], $resetKinds ) ) {
233  if ( array_key_exists( $key, $defaultOptions ) ) {
234  $newOptions[$key] = $defaultOptions[$key];
235  }
236  } else {
237  $newOptions[$key] = $value;
238  }
239  }
240  }
241 
242  // TODO: Deprecate passing full user to the hook
243  $this->hookRunner->onUserResetAllOptions(
244  User::newFromIdentity( $user ), $newOptions, $oldOptions, $resetKinds
245  );
246 
247  $this->optionsCache[$this->getCacheKey( $user )] = $newOptions;
248  }
249 
273  public function listOptionKinds(): array {
274  return [
275  'registered',
276  'registered-multiselect',
277  'registered-checkmatrix',
278  'userjs',
279  'special',
280  'unused'
281  ];
282  }
283 
297  public function getOptionKinds(
298  UserIdentity $userIdentity,
300  $options = null
301  ): array {
302  if ( $options === null ) {
303  $options = $this->loadUserOptions( $userIdentity );
304  }
305 
306  // TODO: injecting the preferences factory creates a cyclic dependency between
307  // PreferencesFactory and UserOptionsManager. See T250822
308  $preferencesFactory = MediaWikiServices::getInstance()->getPreferencesFactory();
309  $user = User::newFromIdentity( $userIdentity );
310  $prefs = $preferencesFactory->getFormDescriptor( $user, $context );
311  $mapping = [];
312 
313  // Pull out the "special" options, so they don't get converted as
314  // multiselect or checkmatrix.
315  $specialOptions = array_fill_keys( $preferencesFactory->getSaveBlacklist(), true );
316  foreach ( $specialOptions as $name => $value ) {
317  unset( $prefs[$name] );
318  }
319 
320  // Multiselect and checkmatrix options are stored in the database with
321  // one key per option, each having a boolean value. Extract those keys.
322  $multiselectOptions = [];
323  foreach ( $prefs as $name => $info ) {
324  if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
325  ( isset( $info['class'] ) && $info['class'] == HTMLMultiSelectField::class )
326  ) {
327  $opts = HTMLFormField::flattenOptions( $info['options'] );
328  $prefix = $info['prefix'] ?? $name;
329 
330  foreach ( $opts as $value ) {
331  $multiselectOptions["$prefix$value"] = true;
332  }
333 
334  unset( $prefs[$name] );
335  }
336  }
337  $checkmatrixOptions = [];
338  foreach ( $prefs as $name => $info ) {
339  if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
340  ( isset( $info['class'] ) && $info['class'] == HTMLCheckMatrix::class )
341  ) {
342  $columns = HTMLFormField::flattenOptions( $info['columns'] );
343  $rows = HTMLFormField::flattenOptions( $info['rows'] );
344  $prefix = $info['prefix'] ?? $name;
345 
346  foreach ( $columns as $column ) {
347  foreach ( $rows as $row ) {
348  $checkmatrixOptions["$prefix$column-$row"] = true;
349  }
350  }
351 
352  unset( $prefs[$name] );
353  }
354  }
355 
356  // $value is ignored
357  foreach ( $options as $key => $value ) {
358  if ( isset( $prefs[$key] ) ) {
359  $mapping[$key] = 'registered';
360  } elseif ( isset( $multiselectOptions[$key] ) ) {
361  $mapping[$key] = 'registered-multiselect';
362  } elseif ( isset( $checkmatrixOptions[$key] ) ) {
363  $mapping[$key] = 'registered-checkmatrix';
364  } elseif ( isset( $specialOptions[$key] ) ) {
365  $mapping[$key] = 'special';
366  } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
367  $mapping[$key] = 'userjs';
368  } else {
369  $mapping[$key] = 'unused';
370  }
371  }
372 
373  return $mapping;
374  }
375 
383  public function saveOptions( UserIdentity $user ) {
384  if ( !$user->isRegistered() ) {
385  throw new InvalidArgumentException( __METHOD__ . ' was called on anon user' );
386  }
387 
388  $userKey = $this->getCacheKey( $user );
389  // Not using getOptions(), to keep hidden preferences in database
390  $saveOptions = $this->loadUserOptions( $user, self::READ_LATEST );
391  $originalOptions = $this->originalOptionsCache[$userKey] ?? [];
392 
393  // Allow hooks to abort, for instance to save to a global profile.
394  // Reset options to default state before saving.
395  // TODO: Deprecate passing User to the hook.
396  if ( !$this->hookRunner->onUserSaveOptions(
397  User::newFromIdentity( $user ), $saveOptions, $originalOptions )
398  ) {
399  return;
400  }
401  // In case options were modified by the hook
402  $this->optionsCache[$userKey] = $saveOptions;
403 
404  $userId = $user->getId();
405  $insert_rows = []; // all the new preference rows
406  foreach ( $saveOptions as $key => $value ) {
407  // Don't bother storing default values
408  $defaultOption = $this->defaultOptionsLookup->getDefaultOption( $key );
409  if ( ( $defaultOption === null && $value !== false && $value !== null )
410  || $value != $defaultOption
411  ) {
412  $insert_rows[] = [
413  'up_user' => $userId,
414  'up_property' => $key,
415  'up_value' => $value,
416  ];
417  }
418  }
419 
420  $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
421 
422  $res = $dbw->select(
423  'user_properties',
424  [ 'up_property', 'up_value' ],
425  [ 'up_user' => $userId ],
426  __METHOD__
427  );
428 
429  // Find prior rows that need to be removed or updated. These rows will
430  // all be deleted (the latter so that INSERT IGNORE applies the new values).
431  $keysDelete = [];
432  foreach ( $res as $row ) {
433  if ( !isset( $saveOptions[$row->up_property] ) ||
434  $saveOptions[$row->up_property] !== $row->up_value
435  ) {
436  $keysDelete[] = $row->up_property;
437  }
438  }
439 
440  if ( !count( $keysDelete ) && !count( $insert_rows ) ) {
441  return;
442  }
443  $this->originalOptionsCache[$userKey] = null;
444 
445  if ( count( $keysDelete ) ) {
446  // Do the DELETE by PRIMARY KEY for prior rows.
447  // In the past a very large portion of calls to this function are for setting
448  // 'rememberpassword' for new accounts (a preference that has since been removed).
449  // Doing a blanket per-user DELETE for new accounts with no rows in the table
450  // caused gap locks on [max user ID,+infinity) which caused high contention since
451  // updates would pile up on each other as they are for higher (newer) user IDs.
452  // It might not be necessary these days, but it shouldn't hurt either.
453  $dbw->delete(
454  'user_properties',
455  [
456  'up_user' => $userId,
457  'up_property' => $keysDelete
458  ],
459  __METHOD__
460  );
461  }
462  // Insert the new preference rows
463  $dbw->insert(
464  'user_properties',
465  $insert_rows,
466  __METHOD__,
467  [ 'IGNORE' ]
468  );
469  }
470 
485  public function loadUserOptions(
486  UserIdentity $user,
487  int $queryFlags = self::READ_NORMAL,
488  array $data = null
489  ): array {
490  $userKey = $this->getCacheKey( $user );
491  if ( $this->canUseCachedValues( $user, $queryFlags )
492  && isset( $this->optionsCache[$userKey] )
493  ) {
494  return $this->optionsCache[$userKey];
495  }
496 
497  $options = $this->defaultOptionsLookup->getDefaultOptions();
498 
499  if ( !$user->isRegistered() ) {
500  // For unlogged-in users, load language/variant options from request.
501  // There's no need to do it for logged-in users: they can set preferences,
502  // and handling of page content is done by $pageLang->getPreferredVariant() and such,
503  // so don't override user's choice (especially when the user chooses site default).
504  $variant = $this->languageConverterFactory->getLanguageConverter()->getDefaultVariant();
505  $options['variant'] = $variant;
506  $options['language'] = $variant;
507  $this->optionsCache[$userKey] = $options;
508  return $options;
509  }
510 
511  // In case options were already loaded from the database before and no options
512  // changes were saved to the database, we can use the cached original options.
513  if ( $this->canUseCachedValues( $user, $queryFlags )
514  && isset( $this->originalOptionsCache[$userKey] )
515  ) {
516  $this->logger->debug( 'Loading options from override cache', [
517  'user_id' => $user->getId()
518  ] );
519  return $this->originalOptionsCache[$userKey];
520  } else {
521  if ( !is_array( $data ) ) {
522  $this->logger->debug( 'Loading options from database', [
523  'user_id' => $user->getId()
524  ] );
525 
526  $dbr = $this->getDBForQueryFlags( $queryFlags );
527  $res = $dbr->select(
528  'user_properties',
529  [ 'up_property', 'up_value' ],
530  [ 'up_user' => $user->getId() ],
531  __METHOD__
532  );
533 
534  $data = [];
535  foreach ( $res as $row ) {
536  // Convert '0' to 0. PHP's boolean conversion considers them both
537  // false, but e.g. JavaScript considers the former as true.
538  // @todo: T54542 Somehow determine the desired type (string/int/bool)
539  // and convert all values here.
540  if ( $row->up_value === '0' ) {
541  $row->up_value = 0;
542  }
543  $data[$row->up_property] = $row->up_value;
544  }
545  }
546 
547  foreach ( $data as $property => $value ) {
548  $options[$property] = $value;
549  }
550  }
551 
552  // Replace deprecated language codes
553  $options['language'] = LanguageCode::replaceDeprecatedCodes( $options['language'] );
554  // Need to store what we have so far before the hook to prevent
555  // infinite recursion if the hook attempts to reload options
556  $this->originalOptionsCache[$userKey] = $options;
557  $this->queryFlagsUsedForCaching[$userKey] = $queryFlags;
558  // TODO: Deprecate passing full User object into the hook.
559  $this->hookRunner->onUserLoadOptions(
560  User::newFromIdentity( $user ), $options
561  );
562 
563  $this->originalOptionsCache[$userKey] = $options;
564  $this->optionsCache[$userKey] = $options;
565 
566  return $this->optionsCache[$userKey];
567  }
568 
574  public function clearUserOptionsCache( UserIdentity $user ) {
575  $cacheKey = $this->getCacheKey( $user );
576  $this->optionsCache[$cacheKey] = null;
577  $this->originalOptionsCache[$cacheKey] = null;
578  $this->queryFlagsUsedForCaching[$cacheKey] = null;
579  }
580 
586  private function getCacheKey( UserIdentity $user ): string {
587  return $user->isRegistered() ? "u:{$user->getId()}" : "anon:{$user->getName()}";
588  }
589 
594  private function getDBForQueryFlags( $queryFlags ): IDatabase {
595  list( $mode, ) = DBAccessObjectUtils::getDBOptions( $queryFlags );
596  return $this->loadBalancer->getConnectionRef( $mode, [] );
597  }
598 
605  private function canUseCachedValues( UserIdentity $user, int $queryFlags ) : bool {
606  if ( !$user->isRegistered() ) {
607  // Anon users don't have options stored in the database,
608  // so $queryFlags are ignored.
609  return true;
610  }
611  if ( $queryFlags >= self::READ_LOCKING ) {
612  return false;
613  }
614  $userKey = $this->getCacheKey( $user );
615  $queryFlagsUsed = $this->queryFlagsUsedForCaching[$userKey] ?? self::READ_NONE;
616  return $queryFlagsUsed >= $queryFlags;
617  }
618 }
MediaWiki\User\UserOptionsManager\setOption
setOption(UserIdentity $user, string $oname, $val)
Set the given option for a user.
Definition: UserOptionsManager.php:183
LanguageCode\replaceDeprecatedCodes
static replaceDeprecatedCodes( $code)
Replace deprecated language codes that were used in previous versions of MediaWiki to up-to-date,...
Definition: LanguageCode.php:161
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:163
MediaWiki\User\UserOptionsManager\saveOptions
saveOptions(UserIdentity $user)
Saves the non-default options for this user, as previously set e.g.
Definition: UserOptionsManager.php:383
MediaWiki\User\UserOptionsManager\getDefaultOptions
getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: UserOptionsManager.php:108
MediaWiki\User\UserOptionsManager\$hookRunner
HookRunner $hookRunner
Definition: UserOptionsManager.php:75
MediaWiki\User\UserOptionsManager\getOptions
getOptions(UserIdentity $user, int $flags=0, int $queryFlags=self::READ_NORMAL)
Get all user's options.The user to get the option for Bitwise combination of: UserOptionsManager::EXC...
Definition: UserOptionsManager.php:148
User\newFromIdentity
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
Definition: User.php:616
DBAccessObjectUtils\getDBOptions
static getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
Definition: DBAccessObjectUtils.php:52
$res
$res
Definition: testCompression.php:57
MediaWiki\Languages\LanguageConverterFactory
An interface for creating language converters.
Definition: LanguageConverterFactory.php:44
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:32
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
MediaWiki\User\UserOptionsManager\listOptionKinds
listOptionKinds()
Return a list of the types of user options currently returned by UserOptionsManager::getOptionKinds()...
Definition: UserOptionsManager.php:273
MediaWiki\User\UserOptionsManager\getOption
getOption(UserIdentity $user, string $oname, $defaultOverride=null, bool $ignoreHidden=false, int $queryFlags=self::READ_NORMAL)
Get the user's current setting for a given option.The user to get the option for The option to check ...
Definition: UserOptionsManager.php:122
$dbr
$dbr
Definition: testCompression.php:54
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:195
MediaWiki\User\DefaultOptionsLookup
A service class to control default user options.
Definition: DefaultOptionsLookup.php:35
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:27
MediaWiki\User\UserOptionsManager\$loadBalancer
ILoadBalancer $loadBalancer
Definition: UserOptionsManager.php:63
MediaWiki\User\UserIdentity\isRegistered
isRegistered()
MediaWiki\User\UserOptionsManager\getDBForQueryFlags
getDBForQueryFlags( $queryFlags)
Definition: UserOptionsManager.php:594
MediaWiki\User\UserOptionsManager\getCacheKey
getCacheKey(UserIdentity $user)
Gets a unique key for various caches.
Definition: UserOptionsManager.php:586
MediaWiki\User\UserOptionsManager\$originalOptionsCache
array $originalOptionsCache
Cached original user options fetched from database.
Definition: UserOptionsManager.php:72
HTMLFormField
The parent class to generate form fields.
Definition: HTMLFormField.php:9
MediaWiki\User\UserOptionsManager\clearUserOptionsCache
clearUserOptionsCache(UserIdentity $user)
Clears cached user options.
Definition: UserOptionsManager.php:574
MediaWiki\User\UserOptionsManager\$defaultOptionsLookup
DefaultOptionsLookup $defaultOptionsLookup
Definition: UserOptionsManager.php:57
MediaWiki\User\UserOptionsManager\getOptionKinds
getOptionKinds(UserIdentity $userIdentity, IContextSource $context, $options=null)
Return an associative array mapping preferences keys to the kind of a preference they're used for.
Definition: UserOptionsManager.php:297
DB_MASTER
const DB_MASTER
Definition: defines.php:26
IDBAccessObject\READ_NONE
const READ_NONE
Constants for object loading bitfield flags (higher => higher QoS)
Definition: IDBAccessObject.php:75
MediaWiki\User\UserOptionsManager\getDefaultOption
getDefaultOption(string $opt)
Get a given default option value.Name of option to retrieve string|null Default option value
Definition: UserOptionsManager.php:115
DBAccessObjectUtils
Helper class for DAO classes.
Definition: DBAccessObjectUtils.php:29
MediaWiki\User\UserOptionsManager\$queryFlagsUsedForCaching
array $queryFlagsUsedForCaching
Query flags used to retrieve options from database.
Definition: UserOptionsManager.php:78
MediaWiki\User\UserOptionsManager\loadUserOptions
loadUserOptions(UserIdentity $user, int $queryFlags=self::READ_NORMAL, array $data=null)
Loads user options either from cache or from the database.
Definition: UserOptionsManager.php:485
MediaWiki\User\UserOptionsManager\resetOptions
resetOptions(UserIdentity $user, IContextSource $context, $resetKinds=[ 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused'])
Reset certain (or all) options to the site defaults.
Definition: UserOptionsManager.php:210
MediaWiki\User\UserOptionsManager\$logger
LoggerInterface $logger
Definition: UserOptionsManager.php:66
MediaWiki\User
Definition: DefaultOptionsLookup.php:21
MediaWiki\User\UserOptionsLookup
Provides access to user options.
Definition: UserOptionsLookup.php:29
MediaWiki\User\UserOptionsManager\$serviceOptions
ServiceOptions $serviceOptions
Definition: UserOptionsManager.php:54
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
MediaWiki\User\UserOptionsManager
A service class to control user options.
Definition: UserOptionsManager.php:44
MediaWiki\User\UserIdentity\getId
getId()
HTMLCheckMatrix
A checkbox matrix Operates similarly to HTMLMultiSelectField, but instead of using an array of option...
Definition: HTMLCheckMatrix.php:27
MediaWiki\User\UserOptionsManager\$languageConverterFactory
LanguageConverterFactory $languageConverterFactory
Definition: UserOptionsManager.php:60
MediaWiki\User\UserOptionsManager\$optionsCache
array $optionsCache
Cached options by user.
Definition: UserOptionsManager.php:69
HTMLMultiSelectField
Multi-select field.
Definition: HTMLMultiSelectField.php:8
MediaWiki\User\UserOptionsManager\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: UserOptionsManager.php:49
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
LanguageCode
Methods for dealing with language codes.
Definition: LanguageCode.php:27
HTMLFormField\flattenOptions
static flattenOptions( $options)
flatten an array of options to a single array, for instance, a set of "<options>" inside "<optgroups>...
Definition: HTMLFormField.php:1135
MediaWiki\User\UserOptionsManager\canUseCachedValues
canUseCachedValues(UserIdentity $user, int $queryFlags)
Determines if it's ok to use cached options values for a given user and query flags.
Definition: UserOptionsManager.php:605
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:562
MediaWiki\$context
IContextSource $context
Definition: MediaWiki.php:40
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:56
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:66
MediaWiki\User\UserOptionsManager\__construct
__construct(ServiceOptions $options, DefaultOptionsLookup $defaultOptionsLookup, LanguageConverterFactory $languageConverterFactory, ILoadBalancer $loadBalancer, LoggerInterface $logger, HookContainer $hookContainer)
Definition: UserOptionsManager.php:88