MediaWiki master
LocalisationCache.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Language;
8
9use CLDRPluralRuleParser\Error as CLDRPluralRuleError;
10use CLDRPluralRuleParser\Evaluator;
11use DOMDocument;
12use InvalidArgumentException;
13use LogicException;
25use Psr\Log\LoggerInterface;
26use RuntimeException;
27use UnexpectedValueException;
29use Wikimedia\Leximorph\Provider\PluralRules as LeximorphPluralRulesProvider;
30
48 public const VERSION = 7;
49
57 private $manualRecache;
58
67 protected $data = [];
68
74 protected $sourceLanguage = [];
75
76 private HookRunner $hookRunner;
78 private $clearStoreCallbacks;
79
89 private $loadedItems = [];
90
97 private $loadedSubitems = [];
98
106 private $initialisedLangs = [];
107
115 private $shallowFallbacks = [];
116
121 private $fallbackCodes = [];
122
128 private $recachedLangs = [];
129
140 private $coreDataLoaded = [];
141
145 public const ALL_KEYS = [
146 'fallback', 'namespaceNames', 'bookstoreList',
147 'magicWords', 'messages', 'rtl',
148 'digitTransformTable', 'separatorTransformTable',
149 'minimumGroupingDigits', 'numberingSystem', 'fallback8bitEncoding',
150 'linkPrefixExtension', 'linkTrail', 'linkPrefixCharset',
151 'namespaceAliases', 'dateFormats', 'jsDateFormats', 'datePreferences',
152 'datePreferenceMigrationMap', 'defaultDateFormat',
153 'specialPageAliases', 'imageFiles', 'preloadedMessages',
154 'namespaceGenderAliases', 'digitGroupingPattern', 'pluralRules',
155 'pluralRuleTypes', 'compiledPluralRules', 'formalityIndex'
156 ];
157
165 private const CORE_ONLY_KEYS = [
166 'fallback', 'rtl', 'digitTransformTable', 'separatorTransformTable',
167 'minimumGroupingDigits', 'numberingSystem',
168 'fallback8bitEncoding', 'linkPrefixExtension',
169 'linkTrail', 'linkPrefixCharset', 'datePreferences',
170 'datePreferenceMigrationMap', 'defaultDateFormat', 'digitGroupingPattern',
171 'formalityIndex',
172 ];
173
182 private const ALL_EXCEPT_CORE_ONLY_KEYS = [
183 'namespaceNames', 'bookstoreList', 'magicWords', 'messages',
184 'namespaceAliases', 'dateFormats', 'jsDateFormats', 'specialPageAliases',
185 'imageFiles', 'preloadedMessages', 'namespaceGenderAliases',
186 'pluralRules', 'pluralRuleTypes', 'compiledPluralRules',
187 ];
188
190 public const ALL_ALIAS_KEYS = [ 'specialPageAliases' ];
191
196 private const MERGEABLE_MAP_KEYS = [ 'messages', 'namespaceNames',
197 'namespaceAliases', 'dateFormats', 'jsDateFormats', 'imageFiles', 'preloadedMessages'
198 ];
199
204 private const MERGEABLE_ALIAS_LIST_KEYS = [ 'specialPageAliases' ];
205
211 private const OPTIONAL_MERGE_KEYS = [ 'bookstoreList' ];
212
216 private const MAGIC_WORD_KEYS = [ 'magicWords' ];
217
221 private const SPLIT_KEYS = [ 'messages' ];
222
227 private const SOURCE_PREFIX_KEYS = [ 'messages' ];
228
232 private const SOURCEPREFIX_SEPARATOR = ':';
233
237 private const PRELOADED_KEYS = [ 'dateFormats', 'namespaceNames' ];
238
242 private const META_KEYS = [ 'deps', 'list', 'preload' ];
243
244 private const PLURAL_FILES = [
245 // Load CLDR plural rules
246 MW_INSTALL_PATH . '/languages/data/plurals.xml',
247 // Override or extend with MW-specific rules
248 MW_INSTALL_PATH . '/languages/data/plurals-mediawiki.xml',
249 ];
250
257 private static $pluralRules = null;
258
273 private static $pluralRuleTypes = null;
274
280 private $expiredReason = [];
281
290 public static function getStoreFromConf( array $conf, $fallbackCacheDir ): LCStore {
291 $storeArg = [
292 'directory' => $conf['storeDirectory'] ?: $fallbackCacheDir,
293 ];
294
295 // Custom classes are supported. For core builtins, short names are stable (since 1.23).
296 $storeClass = match ( $conf['storeClass'] ) {
297 'LCStoreCDB' => LCStoreCDB::class,
298 'LCStoreDB' => LCStoreDB::class,
299 'LCStoreNull' => LCStoreNull::class,
300 'LCStoreStaticArray' => LCStoreStaticArray::class,
301 default => $conf['storeClass'],
302 };
303 if ( !$storeClass ) {
304 if (
305 $conf['store'] === 'files'
306 || $conf['store'] === 'file'
307 || ( $conf['store'] === 'detect' && $storeArg['directory'] )
308 ) {
309 $storeClass = LCStoreCDB::class;
310 } elseif ( $conf['store'] === 'db' || $conf['store'] === 'detect' ) {
311 $storeClass = LCStoreDB::class;
312 $storeArg['server'] = $conf['storeServer'] ?? [];
313 } elseif ( $conf['store'] === 'array' ) {
314 $storeClass = LCStoreStaticArray::class;
315 } else {
316 throw new ConfigException(
317 'Please set $wgLocalisationCacheConf[\'store\'] to something sensible.'
318 );
319 }
320 }
321
322 return new $storeClass( $storeArg );
323 }
324
328 public const CONSTRUCTOR_OPTIONS = [
329 // True to treat all files as expired until they are regenerated by this object.
330 'forceRecache',
331 'manualRecache',
336 ];
337
351 public function __construct(
352 private readonly ServiceOptions $options,
353 private LCStore $store,
354 private readonly LoggerInterface $logger,
355 array $clearStoreCallbacks,
356 private readonly LanguageNameUtils $langNameUtils,
357 HookContainer $hookContainer,
358 ) {
359 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
360
361 $this->clearStoreCallbacks = $clearStoreCallbacks;
362 $this->hookRunner = new HookRunner( $hookContainer );
363
364 // Keep this separate from $this->options so that it can be mutable
365 $this->manualRecache = $options->get( 'manualRecache' );
366 }
367
374 private static function isMergeableKey( string $key ): bool {
375 static $mergeableKeys;
376 $mergeableKeys ??= array_fill_keys( [
377 ...self::MERGEABLE_MAP_KEYS,
378 ...self::MERGEABLE_ALIAS_LIST_KEYS,
379 ...self::OPTIONAL_MERGE_KEYS,
380 ...self::MAGIC_WORD_KEYS,
381 ], true );
382 return isset( $mergeableKeys[$key] );
383 }
384
394 public function getItem( $code, $key ) {
395 if ( !isset( $this->loadedItems[$code][$key] ) ) {
396 $this->loadItem( $code, $key );
397 }
398
399 if ( $key === 'fallback' && isset( $this->shallowFallbacks[$code] ) ) {
400 return $this->shallowFallbacks[$code];
401 }
402
403 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
404 return $this->data[$code][$key];
405 }
406
414 public function getSubitem( $code, $key, $subkey ) {
415 if ( !isset( $this->loadedSubitems[$code][$key][$subkey] ) &&
416 !isset( $this->loadedItems[$code][$key] )
417 ) {
418 $this->loadSubitem( $code, $key, $subkey );
419 }
420
421 return $this->data[$code][$key][$subkey] ?? null;
422 }
423
433 public function getSubitemWithSource( $code, $key, $subkey ) {
434 $subitem = $this->getSubitem( $code, $key, $subkey );
435 // Undefined in the backend.
436 if ( $subitem === null ) {
437 return null;
438 }
439
440 // The source language should have been set, but to avoid a Phan error and to be double sure.
441 return [ $subitem, $this->sourceLanguage[$code][$key][$subkey] ?? $code ];
442 }
443
457 public function getSubitemList( $code, $key ) {
458 if ( in_array( $key, self::SPLIT_KEYS ) ) {
459 return $this->getSubitem( $code, 'list', $key );
460 } else {
461 $item = $this->getItem( $code, $key );
462 if ( is_array( $item ) ) {
463 return array_keys( $item );
464 } else {
465 return false;
466 }
467 }
468 }
469
476 private function loadItem( $code, $key ) {
477 if ( isset( $this->loadedItems[$code][$key] ) ) {
478 return;
479 }
480
481 if (
482 in_array( $key, self::CORE_ONLY_KEYS, true ) ||
483 // "synthetic" keys added by loadCoreData based on "fallback"
484 $key === 'fallbackSequence' ||
485 $key === 'originalFallbackSequence'
486 ) {
487 if ( $this->langNameUtils->isValidBuiltInCode( $code ) ) {
488 $this->loadCoreData( $code );
489 return;
490 }
491 }
492
493 if ( !isset( $this->initialisedLangs[$code] ) ) {
494 $this->initLanguage( $code );
495
496 // Check to see if initLanguage() loaded it for us
497 if ( isset( $this->loadedItems[$code][$key] ) ) {
498 return;
499 }
500 }
501
502 if ( isset( $this->shallowFallbacks[$code] ) ) {
503 $this->loadItem( $this->shallowFallbacks[$code], $key );
504
505 return;
506 }
507
508 if ( in_array( $key, self::SPLIT_KEYS ) ) {
509 $subkeyList = $this->getSubitem( $code, 'list', $key );
510 foreach ( $subkeyList as $subkey ) {
511 if ( isset( $this->data[$code][$key][$subkey] ) ) {
512 continue;
513 }
514 $this->loadSubitem( $code, $key, $subkey );
515 }
516 } else {
517 $this->data[$code][$key] = $this->getFromStore( $code, $key );
518 }
519
520 $this->loadedItems[$code][$key] = true;
521 }
522
529 private function getFromStore( string $code, string $key ) {
530 if ( $this->store->lateFallback() ) {
531 $result = null;
532 foreach ( $this->getFallbackCodes( $code ) as $langCode ) {
533 $value = $this->store->get( $langCode, $key );
534 $this->mergeItem( $key, $result, $value );
535 if ( in_array( $key, self::META_KEYS ) ) {
536 break;
537 }
538 if ( is_string( $result ) ) {
539 // No need to do merges or look further
540 break;
541 }
542 }
543 return $result;
544 }
545 return $this->store->get( $code, $key );
546 }
547
554 protected function getFallbackCodes( string $code ): array {
555 if ( !array_key_exists( $code, $this->fallbackCodes ) ) {
556 $this->fallbackCodes[$code] = [
557 $code,
558 ...MediaWikiServices::getInstance()->getLanguageFallback()->getAll( $code )
559 ];
560 }
561 return $this->fallbackCodes[$code];
562 }
563
571 private function loadSubitem( $code, $key, $subkey ) {
572 if ( !in_array( $key, self::SPLIT_KEYS ) ) {
573 $this->loadItem( $code, $key );
574
575 return;
576 }
577
578 if ( !isset( $this->initialisedLangs[$code] ) ) {
579 $this->initLanguage( $code );
580 }
581
582 // Check to see if initLanguage() loaded it for us
583 if ( isset( $this->loadedItems[$code][$key] ) ||
584 isset( $this->loadedSubitems[$code][$key][$subkey] )
585 ) {
586 return;
587 }
588
589 if ( isset( $this->shallowFallbacks[$code] ) ) {
590 $this->loadSubitem( $this->shallowFallbacks[$code], $key, $subkey );
591
592 return;
593 }
594
595 $value = $this->getFromStore( $code, "$key:$subkey" );
596 if ( $value !== null && in_array( $key, self::SOURCE_PREFIX_KEYS ) ) {
597 [
598 $this->sourceLanguage[$code][$key][$subkey],
599 $this->data[$code][$key][$subkey]
600 ] = explode( self::SOURCEPREFIX_SEPARATOR, $value, 2 );
601 } else {
602 $this->data[$code][$key][$subkey] = $value;
603 }
604
605 $this->loadedSubitems[$code][$key][$subkey] = true;
606 }
607
615 public function isExpired( $code ) {
616 if ( $this->options->get( 'forceRecache' ) && !isset( $this->recachedLangs[$code] ) ) {
617 $this->logger->debug( __METHOD__ . "($code): forced reload" );
618
619 $this->expiredReason[$code] = "Forced rebuild requested";
620 return true;
621 }
622
623 $deps = $this->getFromStore( $code, 'deps' );
624 $keys = $this->getFromStore( $code, 'list' );
625 $preload = $this->getFromStore( $code, 'preload' );
626 // Different keys may expire separately for some stores
627 if ( $deps === null || $keys === null || $preload === null ) {
628 $this->logger->debug( __METHOD__ . "($code): cache missing, need to make one" );
629
630 $this->expiredReason[$code] = "No existing cache";
631 return true;
632 }
633
634 $expiredReason = '';
635
636 $collectExpiredReason = static function ( $reason ) use ( &$expiredReason ) {
637 $expiredReason = $reason;
638 };
639
640 foreach ( $deps as $dep ) {
641 // Because we're unserializing stuff from cache, we
642 // could receive objects of classes that don't exist
643 // anymore (e.g., uninstalled extensions)
644 // When this happens, always expire the cache
645 if ( !$dep instanceof CacheDependency ) {
646 $this->expiredReason[$code] = get_class( $dep ) . " is not a subtype of CacheDependency";
647 return true;
648 }
649
650 if ( $dep->isExpired( $collectExpiredReason ) ) {
651 $this->logger->debug( __METHOD__ . "($code): cache for $code expired due to " .
652 get_class( $dep ) );
653 $this->expiredReason[$code] = $expiredReason;
654 return true;
655 }
656 }
657
658 return false;
659 }
660
667 public function getExpiredReason( $code ) {
668 return $this->expiredReason[$code] ?? null;
669 }
670
676 private function initLanguage( $langCode ) {
677 foreach ( array_reverse( $this->getFallbackCodes( $langCode ) ) as $code ) {
678 if ( isset( $this->initialisedLangs[$code] ) ) {
679 continue;
680 }
681
682 $this->initialisedLangs[$code] = true;
683
684 # If the code is of the wrong form for a Messages*.php file, do a shallow fallback
685 if ( !$this->langNameUtils->isValidBuiltInCode( $code ) ) {
686 $this->initShallowFallback( $code, 'en' );
687
688 continue;
689 }
690
691 # Re-cache the data if necessary
692 if ( !$this->manualRecache && $this->isExpired( $code ) ) {
693 if ( $this->langNameUtils->isSupportedLanguage( $code ) ) {
694 $this->recache( $code );
695 } elseif ( $code === 'en' ) {
696 throw new RuntimeException( 'MessagesEn.php is missing.' );
697 } else {
698 $this->initShallowFallback( $code, 'en' );
699 }
700
701 continue;
702 }
703
704 # Preload some stuff
705 $preload = $this->getItem( $code, 'preload' );
706 if ( $preload === null ) {
707 if ( $this->manualRecache ) {
708 // No Messages*.php file. Do shallow fallback to en.
709 if ( $code === 'en' ) {
710 throw new RuntimeException( 'No localisation cache found for English. ' .
711 'Please run maintenance/rebuildLocalisationCache.php.' );
712 }
713 $this->initShallowFallback( $code, 'en' );
714
715 break;
716 } else {
717 throw new RuntimeException( 'Invalid or missing localisation cache.' );
718 }
719 }
720
721 foreach ( self::SOURCE_PREFIX_KEYS as $key ) {
722 if ( !isset( $preload[$key] ) ) {
723 continue;
724 }
725 foreach ( $preload[$key] as $subkey => $value ) {
726 if ( $value !== null ) {
727 [
728 $this->sourceLanguage[$code][$key][$subkey],
729 $preload[$key][$subkey]
730 ] = explode( self::SOURCEPREFIX_SEPARATOR, $value, 2 );
731 } else {
732 $preload[$key][$subkey] = null;
733 }
734 }
735 }
736
737 if ( isset( $this->data[$code] ) ) {
738 foreach ( $preload as $key => $value ) {
739 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable -- see isset() above
740 $this->mergeItem( $key, $this->data[$code][$key], $value );
741 }
742 } else {
743 $this->data[$code] = $preload;
744 }
745 foreach ( $preload as $key => $item ) {
746 if ( in_array( $key, self::SPLIT_KEYS ) ) {
747 foreach ( $item as $subkey => $subitem ) {
748 $this->loadedSubitems[$code][$key][$subkey] = true;
749 }
750 } else {
751 $this->loadedItems[$code][$key] = true;
752 }
753 }
754 }
755 }
756
764 private function initShallowFallback( $primaryCode, $fallbackCode ) {
765 $this->data[$primaryCode] =& $this->data[$fallbackCode];
766 $this->loadedItems[$primaryCode] =& $this->loadedItems[$fallbackCode];
767 $this->loadedSubitems[$primaryCode] =& $this->loadedSubitems[$fallbackCode];
768 $this->shallowFallbacks[$primaryCode] = $fallbackCode;
769 $this->coreDataLoaded[$primaryCode] =& $this->coreDataLoaded[$fallbackCode];
770 }
771
779 protected function readPHPFile( $_fileName, $_fileType ) {
780 include $_fileName;
781
782 $data = [];
783 if ( $_fileType == 'core' ) {
784 foreach ( self::ALL_KEYS as $key ) {
785 // Not all keys are set in language files, so
786 // check they exist first
787 // @phan-suppress-next-line MediaWikiNoIssetIfDefined May be set in the included file
788 if ( isset( $$key ) ) {
789 $data[$key] = $$key;
790 }
791 }
792 } elseif ( $_fileType == 'extension' ) {
793 foreach ( self::ALL_EXCEPT_CORE_ONLY_KEYS as $key ) {
794 // @phan-suppress-next-line MediaWikiNoIssetIfDefined May be set in the included file
795 if ( isset( $$key ) ) {
796 $data[$key] = $$key;
797 }
798 }
799 } elseif ( $_fileType == 'aliases' ) {
800 // @phan-suppress-next-line PhanImpossibleCondition May be set in the included file
801 if ( isset( $aliases ) ) {
802 $data['aliases'] = $aliases;
803 }
804 } else {
805 throw new InvalidArgumentException( __METHOD__ . ": Invalid file type: $_fileType" );
806 }
807
808 return $data;
809 }
810
817 private function readJSONFile( $fileName ) {
818 if ( !is_readable( $fileName ) ) {
819 return [];
820 }
821
822 $json = file_get_contents( $fileName );
823 if ( $json === false ) {
824 return [];
825 }
826
827 $data = FormatJson::decode( $json, true );
828 if ( $data === null ) {
829 throw new RuntimeException( __METHOD__ . ": Invalid JSON file: $fileName" );
830 }
831
832 // Remove keys starting with '@'; they are reserved for metadata and non-message data
833 foreach ( $data as $key => $unused ) {
834 if ( $key === '' || $key[0] === '@' ) {
835 unset( $data[$key] );
836 }
837 }
838
839 return $data;
840 }
841
849 private function getCompiledPluralRules( $code ) {
850 $rules = $this->getPluralRules( $code );
851 if ( $rules === null ) {
852 return null;
853 }
854 try {
855 $compiledRules = Evaluator::compile( $rules );
856 } catch ( CLDRPluralRuleError $e ) {
857 $this->logger->debug( $e->getMessage() );
858
859 return [];
860 }
861
862 return $compiledRules;
863 }
864
874 private function getPluralRules( $code ) {
875 if ( self::$pluralRules === null ) {
876 self::loadPluralFiles();
877 }
878 return self::$pluralRules[$code] ?? null;
879 }
880
890 private function getPluralRuleTypes( $code ) {
891 if ( self::$pluralRuleTypes === null ) {
892 self::loadPluralFiles();
893 }
894 return self::$pluralRuleTypes[$code] ?? null;
895 }
896
900 private static function loadPluralFiles() {
901 foreach ( self::PLURAL_FILES as $fileName ) {
902 self::loadPluralFile( $fileName );
903 }
904 }
905
912 private static function loadPluralFile( $fileName ) {
913 // Use file_get_contents instead of DOMDocument::load (T58439)
914 $xml = file_get_contents( $fileName );
915 if ( !$xml ) {
916 throw new RuntimeException( "Unable to read plurals file $fileName" );
917 }
918 $doc = new DOMDocument;
919 $doc->loadXML( $xml );
920 $rulesets = $doc->getElementsByTagName( "pluralRules" );
921 foreach ( $rulesets as $ruleset ) {
922 $codes = $ruleset->getAttribute( 'locales' );
923 $rules = [];
924 $ruleTypes = [];
925 $ruleElements = $ruleset->getElementsByTagName( "pluralRule" );
926 foreach ( $ruleElements as $elt ) {
927 $ruleType = $elt->getAttribute( 'count' );
928 if ( $ruleType === 'other' ) {
929 // Don't record "other" rules, which have an empty condition
930 continue;
931 }
932 $rules[] = $elt->nodeValue;
933 $ruleTypes[] = $ruleType;
934 }
935 foreach ( explode( ' ', $codes ) as $code ) {
936 self::$pluralRules[$code] = $rules;
937 self::$pluralRuleTypes[$code] = $ruleTypes;
938 }
939 }
940 }
941
950 private function readSourceFilesAndRegisterDeps( $code, &$deps ) {
951 // This reads in the PHP i18n file with non-messages l10n data
952 $fileName = $this->langNameUtils->getMessagesFileName( $code );
953 if ( !is_file( $fileName ) ) {
954 $data = [];
955 } else {
956 $deps[] = new FileDependency( $fileName );
957 $data = $this->readPHPFile( $fileName, 'core' );
958 }
959
960 return $data;
961 }
962
971 private function readPluralFilesAndRegisterDeps( $code, &$deps ) {
972 if ( $this->isLeximorphEnabled() ) {
973 $provider = ( new LeximorphProvider( $code, $this->logger ) )->getPluralProvider();
974 $pluralRules = $provider->getPluralRules();
975 $compiledPluralRules = $provider->getCompiledPluralRules();
976 $pluralRuleTypes = $provider->getPluralRuleTypes();
978 } else {
979 $pluralRules = $this->getPluralRules( $code );
980 $compiledPluralRules = $this->getCompiledPluralRules( $code );
981 $pluralRuleTypes = $this->getPluralRuleTypes( $code );
982 $depFiles = self::PLURAL_FILES;
983 }
984
985 foreach ( $depFiles as $fileName ) {
986 $deps[] = new FileDependency( $fileName );
987 }
988
989 return [
990 // Load CLDR plural rules for JavaScript
991 'pluralRules' => $pluralRules,
992 // And for PHP
993 'compiledPluralRules' => $compiledPluralRules,
994 // Load plural rule types
995 'pluralRuleTypes' => $pluralRuleTypes,
996 ];
997 }
998
1007 private function mergeItem( $key, &$value, $fallbackValue ) {
1008 if ( $value !== null ) {
1009 if ( $fallbackValue !== null ) {
1010 if ( in_array( $key, self::MERGEABLE_MAP_KEYS ) ) {
1011 $value += $fallbackValue;
1012 } elseif ( in_array( $key, self::MERGEABLE_ALIAS_LIST_KEYS ) ) {
1013 $value = array_merge_recursive( $value, $fallbackValue );
1014 } elseif ( in_array( $key, self::OPTIONAL_MERGE_KEYS ) ) {
1015 if ( !empty( $value['inherit'] ) ) {
1016 $value = array_merge( $fallbackValue, $value );
1017 }
1018
1019 unset( $value['inherit'] );
1020 } elseif ( in_array( $key, self::MAGIC_WORD_KEYS ) ) {
1021 $this->mergeMagicWords( $value, $fallbackValue );
1022 }
1023 }
1024 } else {
1025 $value = $fallbackValue;
1026 }
1027 }
1028
1029 private function mergeMagicWords( array &$value, array $fallbackValue ): void {
1030 foreach ( $fallbackValue as $magicName => $fallbackInfo ) {
1031 if ( !isset( $value[$magicName] ) ) {
1032 $value[$magicName] = $fallbackInfo;
1033 } else {
1034 $value[$magicName] = [
1035 $fallbackInfo[0],
1036 ...array_unique( [
1037 // First value is 1 if the magic word is case-sensitive, 0 if not
1038 ...array_slice( $value[$magicName], 1 ),
1039 ...array_slice( $fallbackInfo, 1 ),
1040 ] )
1041 ];
1042 }
1043 }
1044 }
1045
1053 public function getMessagesDirs() {
1054 global $IP;
1055
1056 return [
1057 'core' => "$IP/languages/i18n",
1058 'botpasswords' => "$IP/languages/i18n/botpasswords",
1059 'codex' => "$IP/languages/i18n/codex",
1060 'datetime' => "$IP/languages/i18n/datetime",
1061 'exif' => "$IP/languages/i18n/exif",
1062 'languageconverter' => "$IP/languages/i18n/languageconverter",
1063 'interwiki' => "$IP/languages/i18n/interwiki",
1064 'preferences' => "$IP/languages/i18n/preferences",
1065 'userrights' => "$IP/languages/i18n/userrights",
1066
1067 'nontranslatable' => "$IP/languages/i18n/nontranslatable",
1068
1069 'api' => "$IP/includes/Api/i18n",
1070 'rest' => "$IP/includes/Rest/i18n",
1071 'oojs-ui' => "$IP/resources/lib/ooui/i18n",
1072 'paramvalidator' => "$IP/includes/libs/ParamValidator/i18n",
1073 'installer' => "$IP/includes/Installer/i18n",
1074 ] + $this->options->get( MainConfigNames::MessagesDirs );
1075 }
1076
1087 private function loadCoreData( string $code ) {
1088 if ( !$code ) {
1089 throw new InvalidArgumentException( "Invalid language code requested" );
1090 }
1091 if ( $this->coreDataLoaded[$code] ?? false ) {
1092 return;
1093 }
1094
1095 $coreData = array_fill_keys( self::CORE_ONLY_KEYS, null );
1096 $deps = [];
1097
1098 # Load the primary localisation from the source file
1099 $data = $this->readSourceFilesAndRegisterDeps( $code, $deps );
1100 $this->logger->debug( __METHOD__ . ": got localisation for $code from source" );
1101
1102 # Merge primary localisation
1103 foreach ( $data as $key => $value ) {
1104 $this->mergeItem( $key, $coreData[ $key ], $value );
1105 }
1106
1107 # Fill in the fallback if it's not there already
1108 // @phan-suppress-next-line PhanRedundantValueComparison
1109 if ( ( $coreData['fallback'] === null || $coreData['fallback'] === false ) && $code === 'en' ) {
1110 $coreData['fallback'] = false;
1111 $coreData['originalFallbackSequence'] = $coreData['fallbackSequence'] = [];
1112 } else {
1113 if ( $coreData['fallback'] !== null ) {
1114 $coreData['fallbackSequence'] = array_map( 'trim', explode( ',', $coreData['fallback'] ) );
1115 } else {
1116 $coreData['fallbackSequence'] = [];
1117 }
1118 $len = count( $coreData['fallbackSequence'] );
1119
1120 # Before we add the 'en' fallback for messages, keep a copy of
1121 # the original fallback sequence
1122 $coreData['originalFallbackSequence'] = $coreData['fallbackSequence'];
1123
1124 # Ensure that the sequence ends at 'en' for messages
1125 if ( !$len || $coreData['fallbackSequence'][$len - 1] !== 'en' ) {
1126 $coreData['fallbackSequence'][] = 'en';
1127 }
1128 }
1129
1130 foreach ( $coreData['fallbackSequence'] as $fbCode ) {
1131 // load core fallback data
1132 $fbData = $this->readSourceFilesAndRegisterDeps( $fbCode, $deps );
1133 foreach ( self::CORE_ONLY_KEYS as $key ) {
1134 // core-only keys are not mergeable, only set if not present in core data yet
1135 if ( isset( $fbData[$key] ) && !isset( $coreData[$key] ) ) {
1136 $coreData[$key] = $fbData[$key];
1137 }
1138 }
1139 }
1140
1141 $coreData['deps'] = $deps;
1142 foreach ( $coreData as $key => $item ) {
1143 $this->data[$code][$key] ??= null;
1144 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable -- we just set a default null
1145 $this->mergeItem( $key, $this->data[$code][$key], $item );
1146 if (
1147 in_array( $key, self::CORE_ONLY_KEYS, true ) ||
1148 // "synthetic" keys based on "fallback" (see above)
1149 $key === 'fallbackSequence' ||
1150 $key === 'originalFallbackSequence'
1151 ) {
1152 // only mark core-only keys as loaded;
1153 // we may have loaded additional ones from the source file,
1154 // but they are not fully loaded yet, since recache()
1155 // may have to merge in additional values from fallback languages
1156 $this->loadedItems[$code][$key] = true;
1157 }
1158 }
1159
1160 $this->coreDataLoaded[$code] = true;
1161 }
1162
1169 public function recache( $code ) {
1170 if ( !$code ) {
1171 throw new InvalidArgumentException( "Invalid language code requested" );
1172 }
1173 $this->recachedLangs[ $code ] = true;
1174
1175 # Initial values
1176 $initialData = array_fill_keys( self::ALL_KEYS, null );
1177 $this->data[$code] = [];
1178 $this->loadedItems[$code] = [];
1179 $this->loadedSubitems[$code] = [];
1180 $this->coreDataLoaded[$code] = false;
1181 $this->loadCoreData( $code );
1182 $coreData = $this->data[$code];
1183 $deps = $coreData['deps'];
1184 $coreData += $this->readPluralFilesAndRegisterDeps( $code, $deps );
1185
1186 if ( $this->store->lateFallback() ) {
1187 // This LCStore can handle multiple queries efficiently and
1188 // requests to merge fallback languages at read time.
1189 $codeSequence = [ $code ];
1190 } else {
1191 // Our LCStore prefers to cache pre-combined data with all
1192 // the fallback paths filled out to reduce query count.
1193 $codeSequence = [ $code, ...$coreData['fallbackSequence'] ];
1194 }
1195 $messageDirs = $this->getMessagesDirs();
1196 $translationAliasesDirs = $this->options->get( MainConfigNames::TranslationAliasesDirs );
1197
1198 # Load non-JSON localisation data for extensions
1199 $extensionData = array_fill_keys( $codeSequence, $initialData );
1200 foreach ( $this->options->get( MainConfigNames::ExtensionMessagesFiles ) as $extension => $fileName ) {
1201 if ( isset( $messageDirs[$extension] ) || isset( $translationAliasesDirs[$extension] ) ) {
1202 # This extension has JSON message data; skip the PHP shim
1203 continue;
1204 }
1205
1206 $data = $this->readPHPFile( $fileName, 'extension' );
1207 $used = false;
1208
1209 foreach ( $data as $key => $item ) {
1210 foreach ( $codeSequence as $csCode ) {
1211 if ( isset( $item[$csCode] ) ) {
1212 // Keep the behaviour the same as for json messages.
1213 // TODO: Consider deprecating using a PHP file for messages.
1214 if ( in_array( $key, self::SOURCE_PREFIX_KEYS ) ) {
1215 foreach ( $item[$csCode] as $subkey => $_ ) {
1216 $this->sourceLanguage[$code][$key][$subkey] ??= $csCode;
1217 }
1218 }
1219 $this->mergeItem( $key, $extensionData[$csCode][$key], $item[$csCode] );
1220 $used = true;
1221 }
1222 }
1223 }
1224
1225 if ( $used ) {
1226 $deps[] = new FileDependency( $fileName );
1227 }
1228 }
1229
1230 # Load the localisation data for each fallback, then merge it into the full array
1231 $allData = $initialData;
1232 foreach ( $codeSequence as $csCode ) {
1233 $csData = $initialData;
1234
1235 # Load core messages and the extension localisations.
1236 foreach ( $messageDirs as $dirs ) {
1237 foreach ( (array)$dirs as $dir ) {
1238 $fileName = "$dir/$csCode.json";
1239 $messages = $this->readJSONFile( $fileName );
1240
1241 foreach ( $messages as $subkey => $_ ) {
1242 $this->sourceLanguage[$code]['messages'][$subkey] ??= $csCode;
1243 }
1244 $this->mergeItem( 'messages', $csData['messages'], $messages );
1245
1246 $deps[] = new FileDependency( $fileName );
1247 }
1248 }
1249
1250 foreach ( $translationAliasesDirs as $dirs ) {
1251 foreach ( (array)$dirs as $dir ) {
1252 $fileName = "$dir/$csCode.json";
1253 $data = $this->readJSONFile( $fileName );
1254
1255 foreach ( $data as $key => $item ) {
1256 // We allow the key in the JSON to be specified in PascalCase similar to key definitions in
1257 // extension.json, but eventually they are stored in camelCase
1258 $normalizedKey = lcfirst( $key );
1259
1260 if ( $normalizedKey === '@metadata' ) {
1261 // Don't store @metadata information in extension data.
1262 continue;
1263 }
1264
1265 if ( !in_array( $normalizedKey, self::ALL_ALIAS_KEYS ) ) {
1266 throw new UnexpectedValueException(
1267 "Invalid key: \"$key\" for " . MainConfigNames::TranslationAliasesDirs . ". " .
1268 'Valid keys: ' . implode( ', ', self::ALL_ALIAS_KEYS )
1269 );
1270 }
1271
1272 $this->mergeItem( $normalizedKey, $extensionData[$csCode][$normalizedKey], $item );
1273 }
1274
1275 $deps[] = new FileDependency( $fileName );
1276 }
1277 }
1278
1279 # Merge non-JSON extension data
1280 if ( isset( $extensionData[$csCode] ) ) {
1281 foreach ( $extensionData[$csCode] as $key => $item ) {
1282 $this->mergeItem( $key, $csData[$key], $item );
1283 }
1284 }
1285
1286 if ( $csCode === $code ) {
1287 # Merge core data into extension data
1288 foreach ( $coreData as $key => $item ) {
1289 $this->mergeItem( $key, $csData[$key], $item );
1290 }
1291 } else {
1292 # Load the secondary localisation from the source file to
1293 # avoid infinite cycles on cyclic fallbacks
1294 $fbData = $this->readSourceFilesAndRegisterDeps( $csCode, $deps );
1295 $fbData += $this->readPluralFilesAndRegisterDeps( $csCode, $deps );
1296 # Only merge the keys that make sense to merge
1297 foreach ( self::ALL_KEYS as $key ) {
1298 if ( !isset( $fbData[ $key ] ) ) {
1299 continue;
1300 }
1301
1302 if ( !isset( $coreData[ $key ] ) || self::isMergeableKey( $key ) ) {
1303 $this->mergeItem( $key, $csData[ $key ], $fbData[ $key ] );
1304 }
1305 }
1306 }
1307
1308 # Allow extensions an opportunity to adjust the data for this fallback
1309 $this->hookRunner->onLocalisationCacheRecacheFallback( $this, $csCode, $csData );
1310
1311 # Merge the data for this fallback into the final array
1312 if ( $csCode === $code ) {
1313 $allData = $csData;
1314 } else {
1315 foreach ( self::ALL_KEYS as $key ) {
1316 if ( !isset( $csData[$key] ) ) {
1317 continue;
1318 }
1319
1320 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
1321 if ( $allData[$key] === null || self::isMergeableKey( $key ) ) {
1322 $this->mergeItem( $key, $allData[$key], $csData[$key] );
1323 }
1324 }
1325 }
1326 }
1327
1328 if ( !isset( $allData['rtl'] ) ) {
1329 throw new RuntimeException( __METHOD__ . ': Localisation data failed validation check! ' .
1330 'Check that your languages/messages/MessagesEn.php file is intact.' );
1331 }
1332
1333 // Add cache dependencies for any referenced configs
1334 // We use the keys prefixed with 'wg' for historical reasons.
1335 $deps['wgExtensionMessagesFiles'] =
1336 new MainConfigDependency( MainConfigNames::ExtensionMessagesFiles );
1337 $deps['wgMessagesDirs'] =
1338 new MainConfigDependency( MainConfigNames::MessagesDirs );
1339 $deps['wgUseLeximorph'] =
1340 new MainConfigDependency( MainConfigNames::UseLeximorph );
1341 $deps['version'] = new ConstantDependency( self::class . '::VERSION' );
1342
1343 # Add dependencies to the cache entry
1344 $allData['deps'] = $deps;
1345
1346 # Replace spaces with underscores in namespace names
1347 if ( isset( $allData['namespaceNames'] ) ) {
1348 $allData['namespaceNames'] = str_replace( ' ', '_', $allData['namespaceNames'] );
1349 }
1350
1351 # And do the same for special page aliases. $page is an array.
1352 if ( isset( $allData['specialPageAliases'] ) ) {
1353 foreach ( $allData['specialPageAliases'] as &$page ) {
1354 $page = str_replace( ' ', '_', $page );
1355 }
1356 }
1357 # Decouple the reference to prevent accidental damage
1358 unset( $page );
1359
1360 # If there were no plural rules, return an empty array
1361 $allData['pluralRules'] ??= [];
1362 $allData['compiledPluralRules'] ??= [];
1363 # If there were no plural rule types, return an empty array
1364 $allData['pluralRuleTypes'] ??= [];
1365
1366 # Set the list keys
1367 $allData['list'] = [];
1368 foreach ( self::SPLIT_KEYS as $key ) {
1369 $allData['list'][$key] = array_keys( $allData[$key] );
1370 }
1371 # Run hooks
1372 $unused = true; // Used to be $purgeBlobs, removed in 1.34
1373 $this->hookRunner->onLocalisationCacheRecache( $this, $code, $allData, $unused );
1374
1375 if ( $this->store->lateFallback() ) {
1376 // Our in-process cache stores merged data, so let it be reloaded
1377 // from the new cache as backend declares it's cheap to do so.
1378 } else {
1379 // Save to the process cache and register the items loaded
1380 $this->data[$code] = $allData;
1381 $this->loadedItems[$code] = [];
1382 $this->loadedSubitems[$code] = [];
1383 foreach ( $allData as $key => $item ) {
1384 $this->loadedItems[$code][$key] = true;
1385 }
1386 }
1387
1388 # Prefix each item with its source language code before save
1389 foreach ( self::SOURCE_PREFIX_KEYS as $key ) {
1390 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
1391 foreach ( $allData[$key] as $subKey => $value ) {
1392 // The source language should have been set, but to avoid Phan error and be double sure.
1393 $allData[$key][$subKey] = ( $this->sourceLanguage[$code][$key][$subKey] ?? $code ) .
1394 self::SOURCEPREFIX_SEPARATOR . $value;
1395 }
1396 }
1397
1398 # Set the preload key
1399 $allData['preload'] = $this->buildPreload( $allData );
1400
1401 # Save to the persistent cache
1402 $this->store->startWrite( $code );
1403 foreach ( $allData as $key => $value ) {
1404 if ( in_array( $key, self::SPLIT_KEYS ) ) {
1405 foreach ( $value as $subkey => $subvalue ) {
1406 $this->store->set( "$key:$subkey", $subvalue );
1407 }
1408 } else {
1409 $this->store->set( $key, $value );
1410 }
1411 }
1412 $this->store->finishWrite();
1413
1414 # Clear out the MessageBlobStore
1415 # HACK: If using a null (i.e., disabled) storage backend, we
1416 # can't write to the MessageBlobStore either
1417 if ( !$this->store instanceof LCStoreNull ) {
1418 foreach ( $this->clearStoreCallbacks as $callback ) {
1419 $callback();
1420 }
1421 }
1422 }
1423
1433 private function buildPreload( $data ) {
1434 $preload = [ 'messages' => [] ];
1435 foreach ( self::PRELOADED_KEYS as $key ) {
1436 if ( isset( $data[$key] ) ) {
1437 $preload[$key] = $data[$key];
1438 }
1439 }
1440
1441 foreach ( $data['preloadedMessages'] ?? [] as $subkey ) {
1442 if ( isset( $data['messages'][$subkey] ) ) {
1443 $preload['messages'][$subkey] = $data['messages'][$subkey];
1444 }
1445 }
1446
1447 return $preload;
1448 }
1449
1457 public function unload( $code ) {
1458 unset( $this->data[$code] );
1459 unset( $this->loadedItems[$code] );
1460 unset( $this->loadedSubitems[$code] );
1461 unset( $this->initialisedLangs[$code] );
1462 unset( $this->shallowFallbacks[$code] );
1463 unset( $this->sourceLanguage[$code] );
1464 unset( $this->coreDataLoaded[$code] );
1465
1466 foreach ( $this->shallowFallbacks as $shallowCode => $fbCode ) {
1467 if ( $fbCode === $code ) {
1468 $this->unload( $shallowCode );
1469 }
1470 }
1471 }
1472
1476 public function unloadAll() {
1477 foreach ( $this->initialisedLangs as $lang => $unused ) {
1478 $this->unload( $lang );
1479 }
1480 }
1481
1485 public function disableBackend() {
1486 $this->store = new LCStoreNull;
1487 $this->manualRecache = false;
1488 }
1489
1493 private function isLeximorphEnabled(): bool {
1494 return (bool)$this->options->get( MainConfigNames::UseLeximorph );
1495 }
1496
1500 public function setSubitemForTesting(
1501 string $code, string $key, string $subkey, mixed $value
1502 ): void {
1503 if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
1504 throw new LogicException( __METHOD__ . '() should be called only from tests!' );
1505 }
1506 if ( !isset( $this->initialisedLangs[$code] ) ) {
1507 $this->initLanguage( $code );
1508 }
1509 $this->data[$code][$key][$subkey] = $value;
1510 $this->loadedSubitems[$code][$key][$subkey] = true;
1511 }
1512}
1513
1515class_alias( LocalisationCache::class, 'LocalisationCache' );
if(!defined('MEDIAWIKI')) if(!defined( 'MW_ENTRY_POINT')) $IP
Environment checks.
Definition Setup.php:102
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:71
Compatibility class for unserialize of php code from cache.
Exceptions for config failures.
A class for passing options to services.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
JSON formatter wrapper class.
Base class to represent dependencies for LocalisationCache entries.
Depend on a MediaWiki configuration variable from the global config.
Null store backend, used to avoid DB errors during MediaWiki installation.
A service that provides utilities to do with language names and codes.
Caching for the contents of localisation files.
getFallbackCodes(string $code)
Get the set of language codes, including the current language code and any fallbacks to read from in ...
static getStoreFromConf(array $conf, $fallbackCacheDir)
Return a suitable LCStore as specified by the given configuration.
recache( $code)
Load localisation data for a given language for both core and extensions and save it to the persisten...
disableBackend()
Disable the storage backend.
__construct(private readonly ServiceOptions $options, private LCStore $store, private readonly LoggerInterface $logger, array $clearStoreCallbacks, private readonly LanguageNameUtils $langNameUtils, HookContainer $hookContainer,)
For constructor parameters, \MediaWiki\MainConfigSchema::LocalisationCacheConf.
getSubitemList( $code, $key)
Get the list of subitem keys for a given item.
getMessagesDirs()
Gets the combined list of messages dirs from core and extensions.
readPHPFile( $_fileName, $_fileType)
Read a PHP file containing localisation data.
array< string, array< string, array< string, string > > > $sourceLanguage
The source language of cached data items.
isExpired( $code)
Returns true if the cache identified by $code is missing or expired.
setSubitemForTesting(string $code, string $key, string $subkey, mixed $value)
Override a subitem value.
const ALL_ALIAS_KEYS
Keys for items which can be localized.
array< string, array > $data
The cache data.
unload( $code)
Unload the data for a given language from the object cache.
getSubitem( $code, $key, $subkey)
Get a subitem, for instance a single message for a given language.
getItem( $code, $key)
Get a cache item.
getSubitemWithSource( $code, $key, $subkey)
Get a subitem with its source language.
getExpiredReason( $code)
Returns a string describing the reason a call to isExpired ( $code ) returned true.
A class containing constants representing the names of configuration variables.
const MessagesDirs
Name constant for the MessagesDirs setting, for use with Config::get()
const ExtensionMessagesFiles
Name constant for the ExtensionMessagesFiles setting, for use with Config::get()
const TranslationAliasesDirs
Name constant for the TranslationAliasesDirs setting, for use with Config::get()
const UseLeximorph
Name constant for the UseLeximorph setting, for use with Config::get()
Service locator for MediaWiki core services.
const array PLURAL_FILES
The paths to the plural rules XML files.
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'DefaultLockManager'=> null, 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 220, 250, 300, 400,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'TrackMediaRequestProvenance'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'RestTermsOfServiceUrl'=> null, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'CodeHighlighter',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'EnableSectionShare'=> false, 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'NamespacesWithoutAutoSummaries' => [ ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider', 'services' => [ 'ConnectionProvider', 'UserFactory', ], 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'autocreateaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, 'createwithcontentmodel' => true, 'logout' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'createpreviouslyrenamedaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'RestrictUserPageEditing' => false, 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], 'managesessions' => [ 'logout' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', 'managesessions' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'BotPasswordsLimit' => 100, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'ReauthenticateForActions' => [ 'edituserjs' => 'edituserjscss', 'editusercss' => 'edituserjscss', 'editsitejs' => 'editsitejscss', 'editsitecss' => 'editsitejscss', ], 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPUseReportURIDirective' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'ApiClientErrorSampleRate' => 1.0, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: 'https: 'git@github\\.com:(.*?)(\\.git)?' => 'https: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, 'mw-edited-other-users-js' => true, 'mw-edited-other-users-css' => true, ], 'RestrictedTagViewRights' => [ ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchstarPopover' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\RecentChanges\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Promise-Non-Write-API-Action', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'RestLocalModuleTestBaseUrl' => null, 'RestModuleOverrides' => [ ], 'RestExternalModules' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'GenerateReqIDFormat' => 'rand24', 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, 'ParserOptionsLogUnsafeSampleRate' => 0, 'ReturnExperimentalPFragmentTypes' => [ ], ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'DefaultLockManager' => [ 'string', 'null', ], 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'RestTermsOfServiceUrl' => [ 'string', 'null', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'NamespacesWithoutAutoSummaries' => 'array', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'RestrictUserPageEditing' => 'boolean', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'BotPasswordsLimit' => 'integer', 'ReauthenticateForActions' => 'object', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPUseReportURIDirective' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RestrictedTagViewRights' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchstarPopover' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'RestLocalModuleTestBaseUrl' => [ 'string', 'null', ], 'RestModuleOverrides' => 'object', 'RestExternalModules' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'GenerateReqIDFormat' => 'string', 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', 'ReturnExperimentalPFragmentTypes' => 'array', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'RestModuleOverrides' => 'array_replace_recursive', 'RestExternalModules' => 'array_replace_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'UseCopyrightUpload' => [ 'deprecated' => 'since 1.47 This feature is being removed.', ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'file' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], ], ], 'RestModuleOverrides' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'mode' => [ 'type' => 'string', ], ], 'required' => [ 'mode', ], ], ], 'RestExternalModules' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'info' => [ 'type' => 'object', 'properties' => [ 'version' => [ 'type' => 'string', ], 'title' => [ 'type' => 'string', ], 'x-i18n-title' => [ 'type' => 'string', ], 'description' => [ 'type' => 'string', ], 'x-i18n-description' => [ 'type' => 'string', ], ], 'required' => [ 'version', ], ], 'base' => [ 'type' => 'string', 'format' => 'uri', ], 'spec' => [ 'type' => 'string', 'format' => 'uri', ], ], 'required' => [ 'info', 'base', 'spec', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
Interface for the persistence layer of LocalisationCache.
Definition LCStore.php:28