MediaWiki master
ParserOutput.php
Go to the documentation of this file.
1<?php
2declare( strict_types = 1 );
3
9namespace MediaWiki\Parser;
10
11use InvalidArgumentException;
12use LogicException;
22use UnhandledMatchError;
23use Wikimedia\Assert\Assert;
24use Wikimedia\Bcp47Code\Bcp47Code;
25use Wikimedia\Bcp47Code\Bcp47CodeValue;
28use Wikimedia\Parsoid\Core\ContentMetadataCollector;
29use Wikimedia\Parsoid\Core\ContentMetadataCollectorCompat;
30use Wikimedia\Parsoid\Core\HtmlPageBundle;
31use Wikimedia\Parsoid\Core\LinkTarget as ParsoidLinkTarget;
32use Wikimedia\Parsoid\Core\MergeStrategy;
33use Wikimedia\Parsoid\Core\TOCData;
34
85class ParserOutput extends CacheTime implements ContentMetadataCollector {
86 // This is used to break cyclic dependencies and allow a measure
87 // of compatibility when new methods are added to ContentMetadataCollector
88 // by Parsoid.
89 use ContentMetadataCollectorCompat;
90
95 public const PARSOID_PAGE_BUNDLE_KEY = 'parsoid-page-bundle';
96
101 public const MW_MERGE_STRATEGY_KEY = '_mw-strategy';
102
114 public const MW_MERGE_STRATEGY_UNION = MergeStrategy::UNION;
115
116 private ContentHolder $contentHolder;
117
121 private array $mLanguageLinkMap = [];
122
126 private array $mCategories = [];
127
131 private array $mIndicators = [];
132
136 private string $mTitleText;
137
142 private array $mLinks = [];
143
148 private array $mLinksSpecial = [];
149
154 private array $mTemplates = [];
155
160 private array $mTemplateIds = [];
161
165 private array $mImages = [];
166
170 private array $mFileSearchOptions = [];
171
175 private array $mExternalLinks = [];
176
181 private array $mInterwikiLinks = [];
182
186 private array $existenceLinks = [];
187
191 private array $mHeadItems = [];
192
196 private array $mModuleSet = [];
197
201 private array $mModuleStyleSet = [];
202
206 private array $mJsConfigVars = [];
207
213 private array $mWarnings = [];
214
219 private array $mWarningMsgs = [];
220
224 private ?TOCData $mTOCData = null;
225
229 private array $mProperties = [];
230
234 private ?string $mTimestamp = null;
235
239 private array $mExtensionData = [];
240
244 private array $mLimitReportData = [];
245
247 private array $mLimitReportJSData = [];
248
250 private string $mCacheMessage = '';
251
255 private array $mParseStartTime = [];
256
260 private array $mTimeProfile = [];
261
265 private array $mExtraScriptSrcs = [];
266
270 private array $mExtraDefaultSrcs = [];
271
275 private array $mExtraStyleSrcs = [];
276
280 private $mFlags = [];
281
282 private const SPECULATIVE_FIELDS = [
283 'speculativePageIdUsed',
284 'mSpeculativeRevId',
285 'revisionTimestampUsed',
286 ];
287
289 private ?int $mSpeculativeRevId = null;
291 private ?int $speculativePageIdUsed = null;
293 private ?string $revisionTimestampUsed = null;
294
296 private ?string $revisionUsedSha1Base36 = null;
297
302 private array $mWrapperDivClasses = [];
303
308 private ?int $mMaxAdaptiveExpiry = null;
309
310 // finalizeAdaptiveCacheExpiry() uses TTL = MAX( m * PARSE_TIME + b, MIN_AR_TTL)
311 // Current values imply that m=3933.333333 and b=-333.333333
312 // See https://www.nngroup.com/articles/website-response-times/
313 private const PARSE_FAST_SEC = 0.100; // perceived "fast" page parse
314 private const PARSE_SLOW_SEC = 1.0; // perceived "slow" page parse
315 private const FAST_AR_TTL = 60; // adaptive TTL for "fast" pages
316 private const SLOW_AR_TTL = 3600; // adaptive TTL for "slow" pages
317 private const MIN_AR_TTL = 15; // min adaptive TTL (for pool counter, and edit stashing)
318
329 public function __construct( ?string $text = null, array $languageLinks = [], array $categoryLinks = [],
330 $unused = false, string $titletext = ''
331 ) {
332 if ( $text === null ) {
333 $this->contentHolder = ContentHolder::createEmpty();
334 } else {
335 $this->contentHolder = ContentHolder::createFromLegacyString( $text );
336 }
337 $this->mCategories = $categoryLinks;
338 $this->mTitleText = $titletext;
339 foreach ( $languageLinks as $ll ) {
340 $this->addLanguageLink( $ll );
341 }
342 // If the content handler does not specify an alternative (by
343 // calling ::resetParseStartTime() at a later point) then use
344 // the creation of the ParserOutput as the "start of parse" time.
345 $this->resetParseStartTime();
346 }
347
354 public function getContentHolder(): ContentHolder {
355 return $this->contentHolder;
356 }
357
362 public function setContentHolder( ContentHolder $contentHolder ) {
363 $this->contentHolder = $contentHolder;
364 }
365
376 public function hasText(): bool {
377 return $this->contentHolder->has( ContentHolder::BODY_FRAGMENT );
378 }
379
389 public function getRawText() {
390 return $this->getContentHolderText();
391 }
392
393 /*
394 * @unstable This method is transitional and will be replaced by a method
395 * in another class, maybe ContentRenderer. It allows us to break our
396 * porting work into two steps; in the first we bring ParserOptions to
397 * to each callsite to ensure it is made available to the
398 * postprocessing pipeline. In the second we move this functionality
399 * into the Content hierarchy and out of ParserOutput, which should become
400 * a pure value object.
401 *
402 * @param ParserOptions $popts
403 * @param array $options (since 1.31) Transformations to apply to the HTML
404 * - allowClone: (bool) Whether to clone the ParserOutput before
405 * applying transformations. Default is true.
406 * - allowTOC: (bool) Show the TOC, assuming there were enough headings
407 * to generate one and `__NOTOC__` wasn't used. Default is true,
408 * but might be statefully overridden.
409 * - injectTOC: (bool) Replace the TOC_PLACEHOLDER with TOC contents;
410 * otherwise the marker will be left in the article (and the skin
411 * will be responsible for replacing or removing it). Default is
412 * true.
413 * - enableSectionEditLinks: (bool) Include section edit links, assuming
414 * section edit link tokens are present in the HTML. Default is true,
415 * but might be statefully overridden.
416 * - userLang: (Language) Language object used for localizing UX messages,
417 * for example the heading of the table of contents. If omitted, will
418 * use the language of the main request context.
419 * - skin: (Skin) Skin object used for transforming section edit links.
420 * - unwrap: (bool) Return text without a wrapper div. Default is false,
421 * meaning a wrapper div will be added if getWrapperDivClass() returns
422 * a non-empty string.
423 * - wrapperDivClass: (string) Wrap the output in a div and apply the given
424 * CSS class to that div. This overrides the output of getWrapperDivClass().
425 * Setting this to an empty string has the same effect as 'unwrap' => true.
426 * - deduplicateStyles: (bool) When true, which is the default, `<style>`
427 * tags with the `data-mw-deduplicate` attribute set are deduplicated by
428 * value of the attribute: all but the first will be replaced by `<link
429 * rel="mw-deduplicated-inline-style" href="mw-data:..."/>` tags, where
430 * the scheme-specific-part of the href is the (percent-encoded) value
431 * of the `data-mw-deduplicate` attribute.
432 * - absoluteURLs: (bool) use absolute URLs in all links. Default: false
433 * - includeDebugInfo: (bool) render PP limit report in HTML. Default: false
434 * It is planned to eventually deprecate this $options array and to be able to
435 * pass its content in the $popts ParserOptions.
436 * @return ParserOutput
437 */
438 public function runOutputPipeline( ParserOptions $popts, array $options = [] ): ParserOutput {
439 $pipeline = MediaWikiServices::getInstance()->getDefaultOutputPipeline();
440 $options += [
441 'allowClone' => true,
442 'allowTOC' => true,
443 'injectTOC' => true,
444 'enableSectionEditLinks' => true,
445 'userLang' => null,
446 'skin' => null,
447 'unwrap' => false,
448 'wrapperDivClass' => $this->getWrapperDivClass(),
449 'deduplicateStyles' => true,
450 'absoluteURLs' => false,
451 'includeDebugInfo' => false,
452 ];
453 return $pipeline->run( $this, $popts, $options );
454 }
455
461 public function addCacheMessage( string $msg ): void {
462 $this->mCacheMessage .= $msg;
463 }
464
470 public function addWrapperDivClass( $class ): void {
471 $this->mWrapperDivClasses[$class] = true;
472 }
473
478 public function clearWrapperDivClass(): void {
479 $this->mWrapperDivClasses = [];
480 }
481
487 public function getWrapperDivClass(): string {
488 return implode( ' ', array_keys( $this->mWrapperDivClasses ) );
489 }
490
495 public function setSpeculativeRevIdUsed( $id ): void {
496 $this->mSpeculativeRevId = $id;
497 }
498
503 public function getSpeculativeRevIdUsed(): ?int {
504 return $this->mSpeculativeRevId;
505 }
506
511 public function setSpeculativePageIdUsed( $id ): void {
512 $this->speculativePageIdUsed = $id;
513 }
514
519 public function getSpeculativePageIdUsed() {
520 return $this->speculativePageIdUsed;
521 }
522
527 public function setRevisionTimestampUsed( $timestamp ): void {
528 $this->revisionTimestampUsed = $timestamp;
529 }
530
535 public function getRevisionTimestampUsed() {
536 return $this->revisionTimestampUsed;
537 }
538
543 public function setRevisionUsedSha1Base36( $hash ): void {
544 if ( $hash === null ) {
545 return; // e.g. RevisionRecord::getSha1() returned null
546 }
547
548 if (
549 $this->revisionUsedSha1Base36 !== null &&
550 $this->revisionUsedSha1Base36 !== $hash
551 ) {
552 $this->revisionUsedSha1Base36 = ''; // mismatched
553 } else {
554 $this->revisionUsedSha1Base36 = $hash;
555 }
556 }
557
562 public function getRevisionUsedSha1Base36() {
563 return $this->revisionUsedSha1Base36;
564 }
565
571 public function getLanguageLinks() {
572 wfDeprecated( __METHOD__, '1.43' );
573 return $this->getLanguageLinksInternal();
574 }
575
579 private function getLanguageLinksInternal(): array {
580 $result = [];
581 foreach ( $this->mLanguageLinkMap as $lang => $title ) {
582 $result[] = "$lang:$title";
583 }
584 return $result;
585 }
586
588 public function getInterwikiLinks() {
589 wfDeprecated( __METHOD__, '1.43' );
590 return $this->mInterwikiLinks;
591 }
592
600 public function getCategoryNames(): array {
601 # Note that numeric category names get converted to 'int' when
602 # stored as array keys; stringify the keys to ensure they
603 # return to original string form so as not to confuse callers.
604 return array_map( 'strval', array_keys( $this->mCategories ) );
605 }
606
618 public function getCategoryMap(): array {
619 return $this->mCategories;
620 }
621
635 public function getCategorySortKey( string $name ): ?string {
636 // This API avoids exposing the fact that numeric string category
637 // names are going to be converted to 'int' when used as array
638 // keys for the `mCategories` field.
639 return $this->mCategories[$name] ?? null;
640 }
641
646 public function getIndicators(): array {
647 return $this->mIndicators;
648 }
649
650 public function getTitleText(): string {
651 return $this->mTitleText;
652 }
653
658 public function getTOCData(): ?TOCData {
659 return $this->mTOCData;
660 }
661
666 public function getCacheMessage(): string {
667 return $this->mCacheMessage;
668 }
669
674 public function getSections(): array {
675 if ( $this->mTOCData !== null ) {
676 return $this->mTOCData->toLegacy();
677 }
678 // For compatibility
679 return [];
680 }
681
700 public function getLinkList( string|ParserOutputLinkTypes $linkType, ?int $onlyNamespace = null ): array {
701 if ( is_string( $linkType ) ) {
702 $linkType = ParserOutputLinkTypes::from( $linkType );
703 }
704 # Note that fragments are dropped for everything except language links
705 $result = [];
706 switch ( $linkType ) {
707 case ParserOutputLinkTypes::CATEGORY:
708 if ( $onlyNamespace !== null && $onlyNamespace !== NS_CATEGORY ) {
709 return [];
710 }
711 foreach ( $this->mCategories as $dbkey => $sort ) {
712 $result[] = [
713 'link' => new TitleValue( NS_CATEGORY, (string)$dbkey ),
714 'sort' => $sort,
715 ];
716 }
717 break;
718
719 case ParserOutputLinkTypes::EXISTENCE:
720 $links = $onlyNamespace === null ? $this->existenceLinks : [
721 $onlyNamespace => $this->existenceLinks[$onlyNamespace] ?? [],
722 ];
723 foreach ( $links as $ns => $titles ) {
724 foreach ( $titles as $dbkey => $unused ) {
725 $result[] = [
726 'link' => new TitleValue( $ns, (string)$dbkey )
727 ];
728 }
729 }
730 break;
731
732 case ParserOutputLinkTypes::INTERWIKI:
733 // By convention interwiki links belong to NS_MAIN
734 if ( $onlyNamespace !== null && $onlyNamespace !== NS_MAIN ) {
735 return [];
736 }
737 foreach ( $this->mInterwikiLinks as $prefix => $arr ) {
738 foreach ( $arr as $dbkey => $ignore ) {
739 $result[] = [
740 'link' => new TitleValue( NS_MAIN, (string)$dbkey, '', (string)$prefix ),
741 ];
742 }
743 }
744 break;
745
746 case ParserOutputLinkTypes::LANGUAGE:
747 // By convention language links belong to NS_MAIN
748 if ( $onlyNamespace !== null && $onlyNamespace !== NS_MAIN ) {
749 return [];
750 }
751 foreach ( $this->mLanguageLinkMap as $lang => $title ) {
752 # language links can have fragments!
753 [ $title, $frag ] = array_pad( explode( '#', $title, 2 ), 2, '' );
754 $result[] = [
755 'link' => new TitleValue( NS_MAIN, $title, $frag, (string)$lang ),
756 ];
757 }
758 break;
759
760 case ParserOutputLinkTypes::LOCAL:
761 $links = $onlyNamespace === null ? $this->mLinks : [
762 $onlyNamespace => $this->mLinks[$onlyNamespace] ?? [],
763 ];
764 foreach ( $links as $ns => $arr ) {
765 foreach ( $arr as $dbkey => $id ) {
766 $result[] = [
767 'link' => new TitleValue( $ns, (string)$dbkey ),
768 'pageid' => $id,
769 ];
770 }
771 }
772 break;
773
774 case ParserOutputLinkTypes::MEDIA:
775 if ( $onlyNamespace !== null && $onlyNamespace !== NS_FILE ) {
776 return [];
777 }
778 foreach ( $this->mImages as $dbkey => $ignore ) {
779 $extra = $this->mFileSearchOptions[$dbkey] ?? [];
780 $extra['link'] = new TitleValue( NS_FILE, (string)$dbkey );
781 $result[] = $extra;
782 }
783 break;
784
785 case ParserOutputLinkTypes::SPECIAL:
786 if ( $onlyNamespace !== null && $onlyNamespace !== NS_SPECIAL ) {
787 return [];
788 }
789 foreach ( $this->mLinksSpecial as $dbkey => $ignore ) {
790 $result[] = [
791 'link' => new TitleValue( NS_SPECIAL, (string)$dbkey ),
792 ];
793 }
794 break;
795
796 case ParserOutputLinkTypes::TEMPLATE:
797 $links = $onlyNamespace === null ? $this->mTemplates : [
798 $onlyNamespace => $this->mTemplates[$onlyNamespace] ?? [],
799 ];
800 foreach ( $links as $ns => $arr ) {
801 foreach ( $arr as $dbkey => $pageid ) {
802 $result[] = [
803 'link' => new TitleValue( $ns, (string)$dbkey ),
804 'pageid' => $pageid,
805 // default to invalid/broken revision if this is not present
806 'revid' => $this->mTemplateIds[$ns][$dbkey] ?? 0,
807 ];
808 }
809 }
810 break;
811
812 default:
813 throw new UnhandledMatchError( "Unknown link type " . $linkType->value );
814 }
815 return $result;
816 }
817
831 public function appendLinkList( string|ParserOutputLinkTypes $linkType, array $linkItem ): void {
832 if ( is_string( $linkType ) ) {
833 $linkType = ParserOutputLinkTypes::from( $linkType );
834 }
835 $link = $linkItem['link'];
836 match ( $linkType ) {
837 ParserOutputLinkTypes::CATEGORY =>
838 $this->addCategory( $link, $linkItem['sort'] ?? '' ),
839 ParserOutputLinkTypes::EXISTENCE =>
840 $this->addExistenceDependency( $link ),
841 ParserOutputLinkTypes::INTERWIKI =>
842 $this->addInterwikiLink( $link ),
843 ParserOutputLinkTypes::LANGUAGE =>
844 $this->addLanguageLink( $link ),
845 ParserOutputLinkTypes::LOCAL =>
846 $this->addLink( $link, $linkItem['pageid'] ?? null ),
847 ParserOutputLinkTypes::MEDIA =>
848 $this->addImage( $link, $linkItem['time'] ?? null, $linkItem['sha1'] ?? null ),
849 ParserOutputLinkTypes::SPECIAL =>
850 $this->addLink( $link ),
851 ParserOutputLinkTypes::TEMPLATE =>
852 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
853 $this->addTemplate( $link, $linkItem['pageid'], $linkItem['revid'] ),
854 };
855 }
856
858 public function &getLinks() {
859 wfDeprecated( __METHOD__, '1.43' );
860 return $this->mLinks;
861 }
862
869 public function hasLinks(): bool {
870 foreach ( $this->mLinks as $ns => $arr ) {
871 foreach ( $arr as $dbkey => $id ) {
872 return true;
873 }
874 }
875 return false;
876 }
877
883 public function &getLinksSpecial() {
884 wfDeprecated( __METHOD__, '1.43' );
885 return $this->mLinksSpecial;
886 }
887
889 public function &getTemplates() {
890 wfDeprecated( __METHOD__, '1.43' );
891 return $this->mTemplates;
892 }
893
895 public function &getTemplateIds() {
896 wfDeprecated( __METHOD__, '1.43' );
897 return $this->mTemplateIds;
898 }
899
901 public function &getImages() {
902 wfDeprecated( __METHOD__, '1.43' );
903 return $this->mImages;
904 }
905
911 public function hasImages(): bool {
912 return $this->mImages !== [];
913 }
914
916 public function &getFileSearchOptions() {
917 wfDeprecated( __METHOD__, '1.43' );
918 return $this->mFileSearchOptions;
919 }
920
927 public function &getExternalLinks(): array {
928 return $this->mExternalLinks;
929 }
930
935 public function setNoGallery( $value ): void {
936 $this->setOutputFlag( ParserOutputFlags::NO_GALLERY, (bool)$value );
937 }
938
943 public function getNoGallery() {
944 return $this->getOutputFlag( ParserOutputFlags::NO_GALLERY );
945 }
946
950 public function getHeadItems() {
951 return $this->mHeadItems;
952 }
953
957 public function getModules() {
958 return array_keys( $this->mModuleSet );
959 }
960
964 public function getModuleStyles() {
965 return array_keys( $this->mModuleStyleSet );
966 }
967
975 public function getJsConfigVars( bool $showStrategyKeys = false ) {
976 $result = $this->mJsConfigVars;
977 // Don't expose the internal strategy key
978 foreach ( $result as &$value ) {
979 if ( is_array( $value ) && !$showStrategyKeys ) {
980 if ( ( $value[self::MW_MERGE_STRATEGY_KEY] ?? null ) === MergeStrategy::SUM->value ) {
981 $value = $value['value'];
982 continue;
983 }
984 unset( $value[self::MW_MERGE_STRATEGY_KEY] );
985 }
986 }
987 return $result;
988 }
989
991 public function getWarnings(): array {
992 // T343048: Don't emit deprecation warnings here until the
993 // compatibility fallback in ApiParse is removed.
994 return array_keys( $this->mWarnings );
995 }
996
998 public function getWarningMsgs(): array {
999 return array_values( $this->mWarningMsgs );
1000 }
1001
1002 public function getIndexPolicy(): string {
1003 // 'noindex' wins if both are set. (T16899)
1004 if ( $this->getOutputFlag( ParserOutputFlags::NO_INDEX_POLICY ) ) {
1005 return 'noindex';
1006 } elseif ( $this->getOutputFlag( ParserOutputFlags::INDEX_POLICY ) ) {
1007 return 'index';
1008 }
1009 return '';
1010 }
1011
1015 public function getRevisionTimestamp(): ?string {
1016 return $this->mTimestamp;
1017 }
1018
1023 public function getTimestamp() {
1024 wfDeprecated( __METHOD__, '1.42' );
1025 return $this->getRevisionTimestamp();
1026 }
1027
1031 public function getLimitReportData() {
1032 return $this->mLimitReportData;
1033 }
1034
1038 public function getLimitReportJSData() {
1039 return $this->mLimitReportJSData;
1040 }
1041
1046 public function getEnableOOUI() {
1047 return $this->getOutputFlag( ParserOutputFlags::ENABLE_OOUI );
1048 }
1049
1055 public function getExtraCSPDefaultSrcs() {
1056 return $this->mExtraDefaultSrcs;
1057 }
1058
1064 public function getExtraCSPScriptSrcs() {
1065 return $this->mExtraScriptSrcs;
1066 }
1067
1073 public function getExtraCSPStyleSrcs() {
1074 return $this->mExtraStyleSrcs;
1075 }
1076
1087 public function setRawText( ?string $text ): void {
1088 $this->setContentHolderText( $text );
1089 }
1090
1103 public function setText( $text ) {
1104 wfDeprecated( __METHOD__, '1.42' );
1105 $ret = $this->hasText() ? $this->getContentHolderText() : null;
1106 $this->setContentHolderText( $text );
1107 return $ret;
1108 }
1109
1113 public function setLanguageLinks( $ll ) {
1114 wfDeprecated( __METHOD__, '1.42' );
1115 $old = $this->getLanguageLinksInternal();
1116 $this->mLanguageLinkMap = [];
1117 if ( $ll === null ) { // T376323
1118 wfDeprecated( __METHOD__ . ' with null argument', '1.43' );
1119 }
1120 foreach ( ( $ll ?? [] ) as $l ) {
1121 $this->addLanguageLink( $l );
1122 }
1123 return $old;
1124 }
1125
1127 public function clearLanguageLinks(): void {
1128 $this->mLanguageLinkMap = [];
1129 }
1130
1135 public function setTitleText( string $t ) {
1136 return wfSetVar( $this->mTitleText, $t );
1137 }
1138
1142 public function setTOCData( TOCData $tocData ): void {
1143 $this->mTOCData = $tocData;
1144 }
1145
1150 public function setSections( array $sectionArray ) {
1151 $oldValue = $this->getSections();
1152 $this->setTOCData( TOCData::fromLegacy( $sectionArray ) );
1153 return $oldValue;
1154 }
1155
1169 public function setIndexPolicy( $policy ): string {
1170 $old = $this->getIndexPolicy();
1171 if ( $policy === 'noindex' ) {
1172 $this->setOutputFlag( ParserOutputFlags::NO_INDEX_POLICY );
1173 } elseif ( $policy === 'index' ) {
1174 $this->setOutputFlag( ParserOutputFlags::INDEX_POLICY );
1175 }
1176 return $old;
1177 }
1178
1182 public function setRevisionTimestamp( ?string $timestamp ): void {
1183 $this->mTimestamp = $timestamp;
1184 }
1185
1192 public function setTimestamp( $timestamp ) {
1193 wfDeprecated( __METHOD__, '1.42' );
1194 return wfSetVar( $this->mTimestamp, $timestamp );
1195 }
1196
1212 public function addCategory( $c, $sort = '' ): void {
1213 if ( $c instanceof ParsoidLinkTarget ) {
1214 $c = $c->getDBkey();
1215 }
1216 if ( ( $this->mCategories[$c] ?? $sort ) !== $sort ) {
1217 // Overwriting a category sort key prevents selective update
1218 // [[mw:Parsoid/Internals/Handling_resource_limits]]
1219 $this->setOutputFlag( ParserOutputFlags::PREVENT_SELECTIVE_UPDATE );
1220 }
1221 $this->mCategories[$c] = $sort;
1222 }
1223
1229 public function setCategories( array $c ): void {
1230 if ( ( $this->mCategories ?: $c ) !== $c ) {
1231 // Overwriting categories prevents selective update
1232 // [[mw:Parsoid/Internals/Handling_resource_limits]]
1233 $this->setOutputFlag( ParserOutputFlags::PREVENT_SELECTIVE_UPDATE );
1234 }
1235 $this->mCategories = $c;
1236 }
1237
1244 public function setIndicator( $id, $content ): void {
1245 if ( ( $this->mIndicators[$id] ?? $content ) !== $content ) {
1246 // Overwriting an indicator prevents selective update
1247 // [[mw:Parsoid/Internals/Handling_resource_limits]]
1248 $this->setOutputFlag( ParserOutputFlags::PREVENT_SELECTIVE_UPDATE );
1249 }
1250 $this->mIndicators[$id] = $content;
1251 }
1252
1261 public function setEnableOOUI( bool $enable = false ): void {
1262 $this->setOutputFlag( ParserOutputFlags::ENABLE_OOUI, $enable );
1263 }
1264
1269 public function addLanguageLink( $t ): void {
1270 # Note that fragments are preserved
1271 if ( $t instanceof ParsoidLinkTarget ) {
1272 // Language links are unusual in using 'text' rather than 'db key'
1273 // Note that fragments are preserved.
1274 $lang = $t->getInterwiki();
1275 $title = $t->getText();
1276 if ( $t->hasFragment() ) {
1277 $title .= '#' . $t->getFragment();
1278 }
1279 } else {
1280 [ $lang, $title ] = array_pad( explode( ':', $t, 2 ), -2, '' );
1281 }
1282 if ( $lang === '' ) {
1283 throw new InvalidArgumentException( __METHOD__ . ' without prefix' );
1284 }
1285 if ( ( $this->mLanguageLinkMap[$lang] ?? $title ) !== $title ) {
1286 // Overwriting a language link prevents selective update
1287 // [[mw:Parsoid/Internals/Handling_resource_limits]]
1288 $this->setOutputFlag( ParserOutputFlags::PREVENT_SELECTIVE_UPDATE );
1289 }
1290 $this->mLanguageLinkMap[$lang] ??= $title;
1291 }
1292
1301 public function addWarningMsgVal( MessageSpecifier $mv, ?string $key = null ) {
1302 $mv = MessageValue::newFromSpecifier( $mv );
1303 $key ??= $mv->getKey();
1304 if ( array_key_exists( $key, $this->mWarningMsgs ) ) {
1305 // Overwriting a warning message prevents selective update
1306 // [[mw:Parsoid/Internals/Handling_resource_limits]]
1307 $this->setOutputFlag( ParserOutputFlags::PREVENT_SELECTIVE_UPDATE );
1308 }
1309 $this->mWarningMsgs[$key] = $mv;
1310 // Ensure callers aren't passing nonserializable arguments: T343048.
1311 $jsonCodec = MediaWikiServices::getInstance()->getJsonCodec();
1312 $path = $jsonCodec->detectNonSerializableData( $mv, true );
1313 if ( $path !== null ) {
1314 throw new InvalidArgumentException( __METHOD__ . ": nonserializable" );
1315 }
1316 // For backward compatibility with callers of ::getWarnings()
1317 // and rollback compatibility for ParserCache; don't remove
1318 // until we no longer need rollback compatiblity with MW 1.43.
1319 $s = Message::newFromSpecifier( $mv )
1320 // some callers set the title here?
1321 ->inContentLanguage() // because this ends up in cache
1322 ->text();
1323 $this->mWarnings[$s] = 1;
1324 }
1325
1334 public function addWarningMsg( string $msg, ...$args ): void {
1335 // T227447: Once MessageSpecifier is moved to a library, Parsoid would
1336 // be able to use ::addWarningMsgVal() directly and this method
1337 // could be deprecated and removed.
1338 $this->addWarningMsgVal( MessageValue::new( $msg, $args ) );
1339 }
1340
1345 public function setNewSection( $value ): void {
1346 $this->setOutputFlag( ParserOutputFlags::NEW_SECTION, (bool)$value );
1347 }
1348
1353 public function setHideNewSection( bool $value ): void {
1354 $this->setOutputFlag( ParserOutputFlags::HIDE_NEW_SECTION, $value );
1355 }
1356
1360 public function getHideNewSection(): bool {
1361 return $this->getOutputFlag( ParserOutputFlags::HIDE_NEW_SECTION );
1362 }
1363
1367 public function getNewSection(): bool {
1368 return $this->getOutputFlag( ParserOutputFlags::NEW_SECTION );
1369 }
1370
1379 public static function isLinkInternal( $internal, $url ): bool {
1380 return (bool)preg_match( '/^' .
1381 # If server is proto relative, check also for http/https links
1382 ( str_starts_with( $internal, '//' ) ? '(?:https?:)?' : '' ) .
1383 preg_quote( $internal, '/' ) .
1384 # check for query/path/anchor or end of link in each case
1385 '(?:[\?\/\#]|$)/i',
1386 $url
1387 );
1388 }
1389
1393 public function addExternalLink( $url ): void {
1394 # We don't register links pointing to our own server, unless... :-)
1395 $config = MediaWikiServices::getInstance()->getMainConfig();
1396 $server = $config->get( MainConfigNames::Server );
1397 $registerInternalExternals = $config->get( MainConfigNames::RegisterInternalExternals );
1398 $ignoreDomains = $config->get( MainConfigNames::ExternalLinksIgnoreDomains );
1399
1400 # Replace unnecessary URL escape codes with the referenced character
1401 # This prevents spammers from hiding links from the filters
1402 $url = Parser::normalizeLinkUrl( $url );
1403
1404 $registerExternalLink = true;
1405 if ( !$registerInternalExternals ) {
1406 $registerExternalLink = !self::isLinkInternal( $server, $url );
1407 }
1408 if (
1409 MediaWikiServices::getInstance()->getUrlUtils()->matchesDomainList( $url, $ignoreDomains )
1410 ) {
1411 $registerExternalLink = false;
1412 }
1413 if ( $registerExternalLink ) {
1414 $this->mExternalLinks[$url] = 1;
1415 }
1416 }
1417
1424 public function addLink( ParsoidLinkTarget $link, $id = null ): void {
1425 if ( $link->isExternal() ) {
1426 // Don't record interwikis in pagelinks
1427 $this->addInterwikiLink( $link );
1428 return;
1429 }
1430 $ns = $link->getNamespace();
1431 $dbk = $link->getDBkey();
1432 if ( $ns === NS_MEDIA ) {
1433 // Normalize this pseudo-alias if it makes it down here...
1434 $ns = NS_FILE;
1435 } elseif ( $ns === NS_SPECIAL ) {
1436 // We don't want to record Special: links in the database, so put them in a separate place.
1437 // It might actually be wise to, but we'd need to do some normalization.
1438 $this->mLinksSpecial[$dbk] = 1;
1439 return;
1440 } elseif ( $dbk === '' ) {
1441 // Don't record self links - [[#Foo]]
1442 return;
1443 }
1444 if ( $id === null ) {
1445 // T357048: This actually kills performance; we should batch these.
1446 $page = MediaWikiServices::getInstance()->getPageStore()->getPageForLink( $link );
1447 $id = $page->getId();
1448 }
1449 $this->mLinks[$ns][$dbk] = $id;
1450 }
1451
1458 public function addImage( $name, $timestamp = null, $sha1 = null ): void {
1459 if ( $name instanceof ParsoidLinkTarget ) {
1460 $name = $name->getDBkey();
1461 }
1462 $this->mImages[$name] = 1;
1463 if ( $timestamp !== null && $sha1 !== null ) {
1464 $this->mFileSearchOptions[$name] = [ 'time' => $timestamp, 'sha1' => $sha1 ];
1465 }
1466 }
1467
1475 public function addTemplate( $link, $page_id, $rev_id ): void {
1476 if ( $link->isExternal() ) {
1477 // Will throw an InvalidArgumentException in a future release.
1478 throw new InvalidArgumentException( __METHOD__ . " with interwiki link" );
1479 }
1480 $ns = $link->getNamespace();
1481 $dbk = $link->getDBkey();
1482 // T357048: Parsoid doesn't have page_id
1483 $this->mTemplates[$ns][$dbk] = $page_id;
1484 $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
1485 }
1486
1491 public function addInterwikiLink( $link ): void {
1492 if ( !$link->isExternal() ) {
1493 throw new InvalidArgumentException( 'Non-interwiki link passed, internal parser error.' );
1494 }
1495 $prefix = $link->getInterwiki();
1496 $this->mInterwikiLinks[$prefix][$link->getDBkey()] = 1;
1497 }
1498
1506 public function addExistenceDependency( ParsoidLinkTarget $link ) {
1507 $ns = $link->getNamespace();
1508 $dbk = $link->getDBkey();
1509 // Ignore some kinds of links, as in addLink()
1510 if ( $link->isExternal() || $ns === NS_SPECIAL || $dbk === '' ) {
1511 return;
1512 }
1513 if ( $ns === NS_MEDIA ) {
1514 $ns = NS_FILE;
1515 }
1516 $this->existenceLinks[$ns][$dbk] = true;
1517 }
1518
1526 public function addHeadItem( $section, $tag = false ): void {
1527 if ( $tag !== false ) {
1528 $this->mHeadItems[$tag] = $section;
1529 } else {
1530 $this->mHeadItems[] = $section;
1531 }
1532 }
1533
1538 public function addModules( array $modules ): void {
1539 $modules = array_fill_keys( $modules, true );
1540 $this->mModuleSet = array_merge( $this->mModuleSet, $modules );
1541 }
1542
1547 public function addModuleStyles( array $modules ): void {
1548 $modules = array_fill_keys( $modules, true );
1549 $this->mModuleStyleSet = array_merge( $this->mModuleStyleSet, $modules );
1550 }
1551
1562 public function addJsConfigVars( $keys, $value = null ): void {
1563 wfDeprecated( __METHOD__, '1.38' );
1564 if ( is_array( $keys ) ) {
1565 foreach ( $keys as $key => $value ) {
1566 $this->mJsConfigVars[$key] = $value;
1567 }
1568 return;
1569 }
1570
1571 $this->mJsConfigVars[$keys] = $value;
1572 }
1573
1587 public function setJsConfigVar( string $key, $value ): void {
1588 if (
1589 array_key_exists( $key, $this->mJsConfigVars ) &&
1590 $this->mJsConfigVars[$key] !== $value
1591 ) {
1592 // Ensure that a key is mapped to only a single value in order
1593 // to prevent the resulting array from varying if content
1594 // is parsed in a different order.
1595 throw new InvalidArgumentException( "Multiple conflicting values given for $key" );
1596 }
1597 $this->mJsConfigVars[$key] = $value;
1598 }
1599
1616 public function appendJsConfigVar(
1617 string $key,
1618 $value,
1619 MergeStrategy|string $strategy = MergeStrategy::UNION
1620 ): void {
1621 if ( is_string( $strategy ) ) {
1622 $strategy = MergeStrategy::from( $strategy );
1623 }
1624 $this->mJsConfigVars = self::mergeMapStrategy(
1625 $this->mJsConfigVars,
1626 [ $key => self::makeMapStrategy( $value, $strategy ) ]
1627 );
1628 }
1629
1645 public function addOutputPageMetadata( OutputPage $out ): void {
1646 // This should eventually use the same merge mechanism used
1647 // internally to merge ParserOutputs together.
1648 // (ie: $this->mergeHtmlMetaDataFrom( $out->getMetadata() )
1649 // once preventClickjacking, moduleStyles, modules, jsconfigvars,
1650 // and head items are moved to OutputPage::$metadata)
1651
1652 // Take the strictest click-jacking policy. This is to ensure any one-click features
1653 // such as patrol or rollback on the transcluded special page will result in the wiki page
1654 // disallowing embedding in cross-origin iframes. Articles are generally allowed to be
1655 // embedded. Pages that transclude special pages are expected to be user pages or
1656 // other non-content pages that content re-users won't discover or care about.
1657 $this->setOutputFlag(
1658 ParserOutputFlags::PREVENT_CLICKJACKING,
1659 $this->getOutputFlag( ParserOutputFlags::PREVENT_CLICKJACKING ) ||
1660 $out->getOutputFlag( ParserOutputFlags::PREVENT_CLICKJACKING )
1661 );
1662
1663 $this->addModuleStyles( $out->getModuleStyles() );
1664
1665 // TODO: Figure out if style modules suffice, or whether the below is needed as well.
1666 // Are there special pages that permit transcluding/including and also have JS modules
1667 // that should be activate on the host page?
1668 $this->addModules( $out->getModules() );
1669 $this->mJsConfigVars = self::mergeMapStrategy(
1670 $this->mJsConfigVars, $out->getJsConfigVars()
1671 );
1672 $this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
1673 }
1674
1686 public function setDisplayTitle( string $text ): void {
1687 $this->setTitleText( $text );
1688 $this->setPageProperty( 'displaytitle', $text );
1689 }
1690
1699 public function getDisplayTitle(): string|false {
1700 $t = $this->getTitleText();
1701 if ( $t === '' ) {
1702 return false;
1703 }
1704 return $t;
1705 }
1706
1715 public function getTitle(): ?ParsoidLinkTarget {
1716 $ns = $this->getExtensionData( 'core:title-ns' );
1717 $dbkey = $this->getExtensionData( 'core:title-dbkey' );
1718 if ( $dbkey !== null ) {
1719 return new TitleValue( $ns ?? NS_MAIN, $dbkey );
1720 }
1721 // Backward-compatibility with cache contents generated by MW < 1.46
1722 $dbkey = $this->getExtensionData( 'parsoid:title-dbkey' );
1723 if ( $dbkey !== null ) {
1724 // This is a prefixed DB key w/ localized namespace
1725 $titleFactory = MediaWikiServices::getInstance()->getTitleFactory();
1726 return $titleFactory->newFromDBkey( $dbkey );
1727 }
1728 return null;
1729 }
1730
1741 public function setTitle( ParsoidLinkTarget|PageReference $title ): void {
1742 if ( $title instanceof PageReference ) {
1743 $title->assertWiki( WikiAwareEntity::LOCAL );
1744 } else {
1745 Assert::invariant( !$title->isExternal(), "title should be local" );
1746 }
1747 $this->setExtensionData( 'core:title-ns', $title->getNamespace() );
1748 $this->setExtensionData( 'core:title-dbkey', $title->getDBkey() );
1749 }
1750
1789 public function getLanguage(): ?Bcp47Code {
1790 // This information is temporarily stored in extension data (T303329)
1791 $code = $this->getExtensionData( 'core:target-lang-variant' );
1792 // This is null if the ParserOutput was cached by MW 1.40 or earlier,
1793 // or not constructed by Parser/ParserCache.
1794 return $code === null ? null : new Bcp47CodeValue( $code );
1795 }
1796
1806 public function setLanguage( Bcp47Code $lang ): void {
1807 $this->setExtensionData( 'core:target-lang-variant', $lang->toBcp47Code() );
1808 }
1809
1816 public function getRedirectHeader(): ?string {
1817 return $this->getExtensionData( 'core:redirect-header' );
1818 }
1819
1824 public function setRedirectHeader( string $html ): void {
1825 $this->setExtensionData( 'core:redirect-header', $html );
1826 }
1827
1836 public function setRenderId( string $renderId ): void {
1837 $this->setExtensionData( 'core:render-id', $renderId );
1838 }
1839
1847 public function getRenderId(): ?string {
1848 // Backward-compatibility with old cache contents
1849 // Can be removed after parser cache contents have expired
1850 $old = $this->getExtensionData( 'parsoid-render-id' );
1851 if ( $old !== null ) {
1852 return ParsoidRenderId::newFromKey( $old )->getUniqueID();
1853 }
1854 return $this->getExtensionData( 'core:render-id' );
1855 }
1856
1862 public function getAllFlags(): array {
1863 // Before MW 1.46 this did not include NO_GALLERY, ENABLE_OOUI,
1864 // INDEX_POLICY, NO_INDEX_POLICY, NEW_SECTION, HIDE_NEW_SECTION,
1865 // and PREVENT_CLICKJACKING, but this method is only used internally.
1866 // See WikitextContentHandler::preSaveTransform() where this method
1867 // is used to transfer PST flags to the WikitextContent object, and
1868 // OutputPage::addParserOutputMetadata() where this method is used
1869 // to transfer ParserOutput flags to OutputPage::$mOutputFlags
1870 return array_keys( $this->mFlags );
1871 }
1872
1965 public function setPageProperty( string $name, string $value ): void {
1966 $this->setUnsortedPageProperty( $name, $value );
1967 }
1968
1983 public function setNumericPageProperty( string $propName, $numericValue ): void {
1984 if ( !is_numeric( $numericValue ) ) {
1985 throw new InvalidArgumentException( __METHOD__ . " with non-numeric value" );
1986 }
1987 // Coerce numeric sort key to a number.
1988 $this->mProperties[$propName] = 0 + $numericValue;
1989 }
1990
2008 public function setUnsortedPageProperty( string $propName, string $value = '' ): void {
2009 $this->mProperties[$propName] = $value;
2010 }
2011
2025 public function getPageProperty( string $name ) {
2026 return $this->mProperties[$name] ?? null;
2027 }
2028
2034 public function unsetPageProperty( string $name ): void {
2035 unset( $this->mProperties[$name] );
2036 }
2037
2043 public function getPageProperties(): array {
2044 return $this->mProperties;
2045 }
2046
2069 public function setOutputFlag( ParserOutputFlags|string $name, bool $val = true ): void {
2070 if ( is_string( $name ) ) {
2071 $flag = ParserOutputFlags::tryFrom( $name );
2072 if ( $flag === null ) {
2074 __METHOD__ . ' with non-standard flag', '1.45'
2075 );
2076 }
2077 } else {
2078 $flag = $name;
2079 $name = $flag->value;
2080 }
2081 if ( $val ) {
2082 $this->mFlags[$name] = true;
2083 } else {
2084 unset( $this->mFlags[$name] );
2085 }
2086 }
2087
2102 public function getOutputFlag( ParserOutputFlags|string $flag ): bool {
2103 // In the future we will return false if $flag doesn't correspond to a
2104 // valid ParserOutputFlag; see deprecation notice in ::setOutputFlag().
2105 $name = $flag instanceof ParserOutputFlags ? $flag->value : $flag;
2106 return $this->mFlags[$name] ?? false;
2107 }
2108
2120 public function appendOutputStrings( string|ParserOutputStringSets $name, array $value ): void {
2121 if ( is_string( $name ) ) {
2122 $name = ParserOutputStringSets::from( $name );
2123 }
2124 match ( $name ) {
2125 ParserOutputStringSets::MODULE =>
2126 $this->addModules( $value ),
2127 ParserOutputStringSets::MODULE_STYLE =>
2128 $this->addModuleStyles( $value ),
2129 ParserOutputStringSets::EXTRA_CSP_DEFAULT_SRC =>
2130 array_walk( $value, fn ( $v, $i ) =>
2131 $this->addExtraCSPDefaultSrc( $v )
2132 ),
2133 ParserOutputStringSets::EXTRA_CSP_SCRIPT_SRC =>
2134 array_walk( $value, fn ( $v, $i ) =>
2135 $this->addExtraCSPScriptSrc( $v )
2136 ),
2137 ParserOutputStringSets::EXTRA_CSP_STYLE_SRC =>
2138 array_walk( $value, fn ( $v, $i ) =>
2139 $this->addExtraCSPStyleSrc( $v )
2140 ),
2141 };
2142 }
2143
2156 public function getOutputStrings( string|ParserOutputStringSets $name ): array {
2157 if ( is_string( $name ) ) {
2158 $name = ParserOutputStringSets::from( $name );
2159 }
2160 return match ( $name ) {
2161 ParserOutputStringSets::MODULE =>
2162 $this->getModules(),
2163 ParserOutputStringSets::MODULE_STYLE =>
2164 $this->getModuleStyles(),
2165 ParserOutputStringSets::EXTRA_CSP_DEFAULT_SRC =>
2166 $this->getExtraCSPDefaultSrcs(),
2167 ParserOutputStringSets::EXTRA_CSP_SCRIPT_SRC =>
2168 $this->getExtraCSPScriptSrcs(),
2169 ParserOutputStringSets::EXTRA_CSP_STYLE_SRC =>
2170 $this->getExtraCSPStyleSrcs(),
2171 };
2172 }
2173
2223 public function setExtensionData( $key, $value ): void {
2224 if (
2225 array_key_exists( $key, $this->mExtensionData ) &&
2226 $this->mExtensionData[$key] !== $value
2227 ) {
2228 // This is discouraged, as it prevents selective update,
2229 // and was deprecated in 1.38.
2230 $this->setOutputFlag( ParserOutputFlags::PREVENT_SELECTIVE_UPDATE );
2231 }
2232 if ( $value === null ) {
2233 unset( $this->mExtensionData[$key] );
2234 } else {
2235 $this->mExtensionData[$key] = $value;
2236 }
2237 }
2238
2263 public function appendExtensionData(
2264 string $key,
2265 $value,
2266 MergeStrategy|string $strategy = MergeStrategy::UNION
2267 ): void {
2268 if ( is_string( $strategy ) ) {
2269 $strategy = MergeStrategy::from( $strategy );
2270 }
2271 $this->mExtensionData = self::mergeMapStrategy(
2272 $this->mExtensionData,
2273 [ $key => self::makeMapStrategy( $value, $strategy ) ]
2274 );
2275 }
2276
2288 public function getExtensionData( $key ) {
2289 $value = $this->mExtensionData[$key] ?? null;
2290 if ( is_array( $value ) ) {
2291 if ( ( $value[self::MW_MERGE_STRATEGY_KEY] ?? null ) === MergeStrategy::SUM->value ) {
2292 return $value['value'];
2293 }
2294 // Don't expose our internal merge strategy key.
2295 unset( $value[self::MW_MERGE_STRATEGY_KEY] );
2296 }
2297 return $value;
2298 }
2299
2300 private static function getTimes( ?string $clock = null ): array {
2301 $ret = [];
2302 if ( !$clock || $clock === 'wall' ) {
2303 $ret['wall'] = hrtime( true ) / 10 ** 9;
2304 }
2305 if ( !$clock || $clock === 'cpu' ) {
2306 $ru = getrusage( 0 /* RUSAGE_SELF */ );
2307 $ret['cpu'] = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
2308 $ret['cpu'] += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
2309 }
2310 return $ret;
2311 }
2312
2319 public function resetParseStartTime(): void {
2320 $this->mParseStartTime = self::getTimes();
2321 $this->mTimeProfile = [];
2322 }
2323
2333 public function clearParseStartTime(): void {
2334 $this->mParseStartTime = [];
2335 }
2336
2347 public function recordTimeProfile() {
2348 if ( !$this->mParseStartTime ) {
2349 // If resetParseStartTime was never called, there is nothing to record
2350 return;
2351 }
2352
2353 if ( $this->mTimeProfile !== [] ) {
2354 // Don't override the times recorded by the previous call to recordTimeProfile().
2355 return;
2356 }
2357
2358 $now = self::getTimes();
2359 $this->mTimeProfile = [
2360 'wall' => $now['wall'] - $this->mParseStartTime['wall'],
2361 'cpu' => $now['cpu'] - $this->mParseStartTime['cpu'],
2362 ];
2363 }
2364
2382 public function getTimeProfile( string $clock ) {
2383 return $this->mTimeProfile[ $clock ] ?? null;
2384 }
2385
2405 public function setLimitReportData( $key, $value ): void {
2406 $this->mLimitReportData[$key] = $value;
2407
2408 if ( is_array( $value ) ) {
2409 if ( array_keys( $value ) === [ 0, 1 ]
2410 && is_numeric( $value[0] )
2411 && is_numeric( $value[1] )
2412 ) {
2413 $data = [ 'value' => $value[0], 'limit' => $value[1] ];
2414 } else {
2415 $data = $value;
2416 }
2417 } else {
2418 $data = $value;
2419 }
2420
2421 if ( strpos( $key, '-' ) ) {
2422 [ $ns, $name ] = explode( '-', $key, 2 );
2423 $this->mLimitReportJSData[$ns][$name] = $data;
2424 } else {
2425 $this->mLimitReportJSData[$key] = $data;
2426 }
2427 }
2428
2445 public function hasReducedExpiry(): bool {
2446 if ( $this->getOutputFlag( ParserOutputFlags::HAS_ASYNC_CONTENT ) ) {
2447 // If this page has async content, then we should re-run
2448 // RefreshLinksJob whenever we regenerate the page.
2449 return true;
2450 }
2451 $parserCacheExpireTime = MediaWikiServices::getInstance()->getMainConfig()->get(
2452 MainConfigNames::ParserCacheExpireTime );
2453
2454 return $this->getCacheExpiry() < $parserCacheExpireTime;
2455 }
2456
2457 public function getCacheExpiry(): int {
2458 $expiry = parent::getCacheExpiry();
2459 if ( $this->getOutputFlag( ParserOutputFlags::ASYNC_NOT_READY ) ) {
2460 $asyncExpireTime = MediaWikiServices::getInstance()->getMainConfig()->get(
2461 MainConfigNames::ParserCacheAsyncExpireTime
2462 );
2463 $expiry = min( $expiry, $asyncExpireTime );
2464 }
2465 return $expiry;
2466 }
2467
2481 public function setPreventClickjacking( bool $flag ): void {
2482 $this->setOutputFlag( ParserOutputFlags::PREVENT_CLICKJACKING, $flag );
2483 }
2484
2493 public function getPreventClickjacking(): bool {
2494 return $this->getOutputFlag( ParserOutputFlags::PREVENT_CLICKJACKING );
2495 }
2496
2506 public function updateRuntimeAdaptiveExpiry( int $ttl, ?string $source = null ): void {
2507 $this->mMaxAdaptiveExpiry ??= $ttl;
2508 $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
2509 $this->updateCacheExpiry( $ttl, $source );
2510 }
2511
2521 public function addExtraCSPDefaultSrc( $src ): void {
2522 $this->mExtraDefaultSrcs[] = $src;
2523 }
2524
2531 public function addExtraCSPStyleSrc( $src ): void {
2532 $this->mExtraStyleSrcs[] = $src;
2533 }
2534
2543 public function addExtraCSPScriptSrc( $src ): void {
2544 $this->mExtraScriptSrcs[] = $src;
2545 }
2546
2552 public function finalizeAdaptiveCacheExpiry(): void {
2553 if ( $this->mMaxAdaptiveExpiry === null ) {
2554 return; // not set
2555 }
2556
2557 $runtime = $this->getTimeProfile( 'wall' );
2558 if ( is_float( $runtime ) ) {
2559 $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
2560 / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
2561 // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point
2562 $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
2563
2564 $adaptiveTTL = intval( $slope * $runtime + $point );
2565 $adaptiveTTL = max( $adaptiveTTL, self::MIN_AR_TTL );
2566 $adaptiveTTL = min( $adaptiveTTL, $this->mMaxAdaptiveExpiry );
2567 $this->updateCacheExpiry( $adaptiveTTL, 'adaptive-ttl' );
2568 }
2569 }
2570
2575 public function setFromParserOptions( ParserOptions $parserOptions ) {
2576 // Copied from Parser.php::parse and should probably be abstracted
2577 // into the parent base class (probably as part of T236809)
2578 // Wrap non-interface parser output in a <div> so it can be targeted
2579 // with CSS (T37247)
2580 $class = $parserOptions->getWrapOutputClass();
2581 if ( $class !== false && !$parserOptions->isMessage() ) {
2582 $this->addWrapperDivClass( $class );
2583 }
2584
2585 // Record whether we should wrap sections for collapsing them
2586 if ( $parserOptions->getCollapsibleSections() ) {
2587 $this->setOutputFlag( ParserOutputFlags::COLLAPSIBLE_SECTIONS );
2588 }
2589
2590 // Record whether this is a preview parse in the output (T341010)
2591 if ( $parserOptions->getIsPreview() ) {
2592 $this->setOutputFlag( ParserOutputFlags::IS_PREVIEW, true );
2593 // Ensure that previews aren't cacheable, just to be safe.
2594 $this->updateCacheExpiry( 0, 'preview' );
2595 }
2596
2597 // Record whether this was parsed with the legacy parser
2598 // (Unlike some other options here, this does/should fork the cache.)
2599 if ( $parserOptions->getUseParsoid() ) {
2600 $this->setOutputFlag( ParserOutputFlags::USE_PARSOID, true );
2601 }
2602 }
2603
2610 $this->mWarnings = self::mergeMap( $this->mWarnings, $source->mWarnings ); // don't use getter
2611 $this->mWarningMsgs = self::mergeMap( $this->mWarningMsgs, $source->mWarningMsgs );
2612 $this->mTimestamp = $this->useMaxValue( $this->mTimestamp, $source->getRevisionTimestamp() );
2613 if ( $source->hasCacheTime() ) {
2614 $sourceCacheTime = $source->getCacheTime();
2615 if (
2616 !$this->hasCacheTime() ||
2617 // "undocumented use of -1 to mean not cacheable"
2618 // deprecated, but still supported by ::setCacheTime()
2619 strval( $sourceCacheTime ) === '-1' ||
2620 (
2621 strval( $this->getCacheTime() ) !== '-1' &&
2622 // use newer of the two times
2623 $this->getCacheTime() < $sourceCacheTime
2624 )
2625 ) {
2626 $this->setCacheTime( $sourceCacheTime );
2627 }
2628 }
2629 if ( $source->getRenderId() !== null ) {
2630 // Final render ID should be a function of all component POs
2631 $rid = ( $this->getRenderId() ?? '' ) . $source->getRenderId();
2632 $this->setRenderId( $rid );
2633 }
2634 if ( $source->getCacheRevisionId() !== null ) {
2635 $sourceCacheRevisionId = $source->getCacheRevisionId();
2636 $thisCacheRevisionId = $this->getCacheRevisionId();
2637 if ( $thisCacheRevisionId === null ) {
2638 $this->setCacheRevisionId( $sourceCacheRevisionId );
2639 } elseif ( $sourceCacheRevisionId !== $thisCacheRevisionId ) {
2640 // May throw an exception here in the future
2642 __METHOD__ . ": conflicting revision IDs " .
2643 "$thisCacheRevisionId and $sourceCacheRevisionId"
2644 );
2645 }
2646 }
2647
2648 foreach ( self::SPECULATIVE_FIELDS as $field ) {
2649 if ( $this->$field && $source->$field && $this->$field !== $source->$field ) {
2650 wfLogWarning( __METHOD__ . ": inconsistent '$field' properties!" );
2651 }
2652 $this->$field = $this->useMaxValue( $this->$field, $source->$field );
2653 }
2654
2655 $this->mParseStartTime = $this->useEachMinValue(
2656 $this->mParseStartTime,
2657 $source->mParseStartTime
2658 );
2659
2660 $this->mTimeProfile = $this->useEachTotalValue(
2661 $this->mTimeProfile,
2662 $source->mTimeProfile
2663 );
2664
2665 $this->mFlags = self::mergeMap( $this->mFlags, $source->mFlags );
2666 $this->mParseUsedOptions = self::mergeMap( $this->mParseUsedOptions, $source->mParseUsedOptions );
2667
2668 // TODO: maintain per-slot limit reports!
2669 if ( !$this->mLimitReportData ) {
2670 $this->mLimitReportData = $source->mLimitReportData;
2671 }
2672 if ( !$this->mLimitReportJSData ) {
2673 $this->mLimitReportJSData = $source->mLimitReportJSData;
2674 }
2675 }
2676
2682 public function mergeHtmlMetaDataFrom( ParserOutput $source ): void {
2683 // HTML and HTTP
2684 $this->mHeadItems = self::mergeMixedList( $this->mHeadItems, $source->getHeadItems() );
2685 $this->addModules( $source->getModules() );
2686 $this->addModuleStyles( $source->getModuleStyles() );
2687 $this->mJsConfigVars = self::mergeMapStrategy( $this->mJsConfigVars, $source->mJsConfigVars );
2688 if ( $source->mMaxAdaptiveExpiry !== null ) {
2689 $this->updateRuntimeAdaptiveExpiry( $source->mMaxAdaptiveExpiry, $source->getCacheExpirySource() );
2690 }
2691 $this->mExtraStyleSrcs = self::mergeList(
2692 $this->mExtraStyleSrcs,
2693 $source->getExtraCSPStyleSrcs()
2694 );
2695 $this->mExtraScriptSrcs = self::mergeList(
2696 $this->mExtraScriptSrcs,
2697 $source->getExtraCSPScriptSrcs()
2698 );
2699 $this->mExtraDefaultSrcs = self::mergeList(
2700 $this->mExtraDefaultSrcs,
2701 $source->getExtraCSPDefaultSrcs()
2702 );
2703
2704 foreach ( [
2705 // "noindex" always wins!
2706 ParserOutputFlags::INDEX_POLICY,
2707 ParserOutputFlags::NO_INDEX_POLICY,
2708 // Skin control
2709 ParserOutputFlags::NEW_SECTION,
2710 ParserOutputFlags::HIDE_NEW_SECTION,
2711 ParserOutputFlags::NO_GALLERY,
2712 ParserOutputFlags::ENABLE_OOUI,
2713 ParserOutputFlags::PREVENT_CLICKJACKING,
2714 // Selective update
2715 ParserOutputFlags::PREVENT_SELECTIVE_UPDATE,
2716 ] as $flag ) {
2717 // logical OR of $this and $source
2718 if ( $source->getOutputFlag( $flag ) ) {
2719 $this->setOutputFlag( $flag );
2720 }
2721 }
2722
2723 $tocData = $this->getTOCData();
2724 $sourceTocData = $source->getTOCData();
2725 if ( $tocData !== null ) {
2726 if ( $sourceTocData !== null ) {
2727 // T327429: Section merging is broken, since it doesn't respect
2728 // global numbering, but there are tests which expect section
2729 // metadata to be concatenated.
2730 // There should eventually be a deprecation warning here.
2731 foreach ( $sourceTocData->getSections() as $s ) {
2732 $tocData->addSection( $s );
2733 }
2734 }
2735 } elseif ( $sourceTocData !== null ) {
2736 $this->setTOCData( $sourceTocData );
2737 }
2738
2739 // XXX: we don't want to concatenate title text, so first write wins.
2740 // We should use the first *modified* title text, but we don't have the original to check.
2741 if ( $this->mTitleText === '' ) {
2742 $this->mTitleText = $source->mTitleText;
2743 }
2744
2745 // class names are stored in array keys
2746 $this->mWrapperDivClasses = self::mergeMap(
2747 $this->mWrapperDivClasses,
2748 $source->mWrapperDivClasses
2749 );
2750
2751 // NOTE: last write wins, same as within one ParserOutput
2752 $this->mIndicators = self::mergeMap( $this->mIndicators, $source->getIndicators() );
2753
2754 // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
2755 // TODO: add a $mergeStrategy parameter to setExtensionData to allow different
2756 // kinds of extension data to be merged in different ways.
2757 $this->mExtensionData = self::mergeMapStrategy(
2758 $this->mExtensionData,
2759 $source->mExtensionData
2760 );
2761
2762 if ( $source->mCacheExpiry !== null ) {
2763 $this->updateCacheExpiry( $source->mCacheExpiry, $source->getCacheExpirySource() );
2764 }
2765 }
2766
2773 foreach ( ParserOutputLinkTypes::cases() as $linkType ) {
2774 foreach ( $source->getLinkList( $linkType ) as $linkItem ) {
2775 $this->appendLinkList( $linkType, $linkItem );
2776 }
2777 }
2778 $this->mExternalLinks = self::mergeMap( $this->mExternalLinks, $source->getExternalLinks() );
2779
2780 // TODO: add a $mergeStrategy parameter to setPageProperty to allow different
2781 // kinds of properties to be merged in different ways.
2782 // (Model this after ::appendJsConfigVar(); use ::mergeMapStrategy here)
2783 $this->mProperties = self::mergeMap( $this->mProperties, $source->getPageProperties() );
2784
2785 // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
2786 $this->mExtensionData = self::mergeMapStrategy(
2787 $this->mExtensionData,
2788 $source->mExtensionData
2789 );
2790 }
2791
2801 public function collectMetadata( ContentMetadataCollector $metadata ): void {
2802 // Uniform handling of all boolean flags: they are OR'ed together.
2803 $flags = array_keys(
2804 $this->mFlags + array_flip( ParserOutputFlags::values() )
2805 );
2806 foreach ( $flags as $name ) {
2807 $name = (string)$name;
2808 if ( $this->getOutputFlag( $name ) ) {
2809 $metadata->setOutputFlag( $name );
2810 }
2811 }
2812
2813 // Uniform handling of string sets: they are unioned.
2814 // (This includes modules, style modes, and CSP src.)
2815 foreach ( ParserOutputStringSets::values() as $name ) {
2816 $metadata->appendOutputStrings(
2817 $name, $this->getOutputStrings( $name )
2818 );
2819 }
2820
2821 foreach ( $this->mCategories as $cat => $key ) {
2822 // Numeric category strings are going to come out of the
2823 // `mCategories` array as ints; cast back to string.
2824 // Also convert back to a LinkTarget!
2825 $lt = TitleValue::tryNew( NS_CATEGORY, (string)$cat );
2826 $metadata->addCategory( $lt, $key );
2827 }
2828
2829 foreach ( $this->mLinks as $ns => $arr ) {
2830 foreach ( $arr as $dbk => $id ) {
2831 // Numeric titles are going to come out of the
2832 // `mLinks` array as ints; cast back to string.
2833 $lt = TitleValue::tryNew( $ns, (string)$dbk );
2834 $metadata->addLink( $lt, $id );
2835 }
2836 }
2837
2838 foreach ( $this->mInterwikiLinks as $prefix => $arr ) {
2839 foreach ( $arr as $dbk => $ignore ) {
2840 $lt = TitleValue::tryNew( NS_MAIN, (string)$dbk, '', $prefix );
2841 $metadata->addLink( $lt );
2842 }
2843 }
2844
2845 foreach ( $this->mLinksSpecial as $dbk => $ignore ) {
2846 // Numeric titles are going to come out of the
2847 // `mLinksSpecial` array as ints; cast back to string.
2848 $lt = TitleValue::tryNew( NS_SPECIAL, (string)$dbk );
2849 $metadata->addLink( $lt );
2850 }
2851
2852 foreach ( $this->mImages as $name => $ignore ) {
2853 // Numeric titles come out of mImages as ints.
2854 $lt = TitleValue::tryNew( NS_FILE, (string)$name );
2855 $props = $this->mFileSearchOptions[$name] ?? [];
2856 $metadata->addImage( $lt, $props['time'] ?? null, $props['sha1'] ?? null );
2857 }
2858
2859 foreach ( $this->mLanguageLinkMap as $lang => $title ) {
2860 # language links can have fragments!
2861 [ $title, $frag ] = array_pad( explode( '#', $title, 2 ), 2, '' );
2862 $lt = TitleValue::tryNew( NS_MAIN, $title, $frag, (string)$lang );
2863 $metadata->addLanguageLink( $lt );
2864 }
2865
2866 foreach ( $this->mJsConfigVars as $key => $value ) {
2867 // Numeric keys and items are going to come out of the
2868 // `mJsConfigVars` array as ints; cast back to string.
2869 $key = (string)$key;
2870 if ( is_array( $value ) && isset( $value[self::MW_MERGE_STRATEGY_KEY] ) ) {
2871 self::collectMapStrategy( $value, static fn ( $v, $s ) =>
2872 $metadata->appendJsConfigVar( $key, $v, $s )
2873 );
2874 } elseif ( $metadata instanceof ParserOutput &&
2875 array_key_exists( $key, $metadata->mJsConfigVars )
2876 ) {
2877 // This behavior is deprecated, will likely result in
2878 // incorrect output, and we'll eventually emit a
2879 // warning here---but at the moment this is usually
2880 // caused by limitations in Parsoid and/or use of
2881 // the ParserAfterParse hook: T303015#7770480
2882 $metadata->mJsConfigVars[$key] = $value;
2883 $metadata->setOutputFlag( ParserOutputFlags::PREVENT_SELECTIVE_UPDATE );
2884 } else {
2885 $metadata->setJsConfigVar( $key, $value );
2886 }
2887 }
2888 foreach ( $this->mExtensionData as $key => $value ) {
2889 // Numeric keys and items are going to come out of the array as
2890 // ints, cast back to string.
2891 $key = (string)$key;
2892 if ( is_array( $value ) && isset( $value[self::MW_MERGE_STRATEGY_KEY] ) ) {
2893 self::collectMapStrategy( $value, static fn ( $v, $s ) =>
2894 $metadata->appendExtensionData( $key, $v, $s )
2895 );
2896 } elseif ( $metadata instanceof ParserOutput &&
2897 array_key_exists( $key, $metadata->mExtensionData )
2898 ) {
2899 // This behavior is deprecated, will likely result in
2900 // incorrect output, and we'll eventually emit a
2901 // warning here---but at the moment this is usually
2902 // caused by limitations in Parsoid and/or use of
2903 // the ParserAfterParse hook: T303015#7770480
2904 $metadata->mExtensionData[$key] = $value;
2905 } else {
2906 $metadata->setExtensionData( $key, $value );
2907 }
2908 }
2909 foreach ( $this->mExternalLinks as $url => $ignore ) {
2910 $metadata->addExternalLink( (string)$url );
2911 }
2912 foreach ( $this->mProperties as $prop => $value ) {
2913 // Numeric properties are going to come out of the array as ints
2914 $prop = (string)$prop;
2915 if ( is_string( $value ) ) {
2916 $metadata->setUnsortedPageProperty( $prop, $value );
2917 } elseif ( is_numeric( $value ) ) {
2918 $metadata->setNumericPageProperty( $prop, $value );
2919 } else {
2920 // Deprecated, but there are still sites which call
2921 // ::setPageProperty() with "unusual" values (T374046)
2922 wfDeprecated( __METHOD__ . ' with unusual page property', '1.45' );
2923 }
2924 }
2925 foreach ( $this->mLimitReportData as $key => $value ) {
2926 $metadata->setLimitReportData( (string)$key, $value );
2927 }
2928 foreach ( $this->mIndicators as $id => $content ) {
2929 $metadata->setIndicator( (string)$id, $content );
2930 }
2931
2932 // ParserOutput-only fields; maintained "behind the curtain"
2933 // since Parsoid doesn't have to know about them.
2934 //
2935 // In production use, the $metadata supplied to this method
2936 // will almost always be an instance of ParserOutput, passed to
2937 // Parsoid by core when parsing begins and returned to core by
2938 // Parsoid as a ContentMetadataCollector (Parsoid's name for
2939 // ParserOutput) when DataAccess::parseWikitext() is called.
2940 //
2941 // We may use still Parsoid's StubMetadataCollector for testing or
2942 // when running Parsoid in standalone mode, so forcing a downcast
2943 // here would lose some flexibility.
2944
2945 if ( $metadata instanceof ParserOutput ) {
2946 foreach ( $this->getUsedOptions() as $opt ) {
2947 $metadata->recordOption( $opt );
2948 }
2949 if ( $this->mMaxAdaptiveExpiry !== null ) {
2950 $metadata->updateRuntimeAdaptiveExpiry( $this->mMaxAdaptiveExpiry, $this->getCacheExpirySource() );
2951 }
2952 if ( $this->mCacheExpiry !== null ) {
2953 $metadata->updateCacheExpiry( $this->mCacheExpiry, $this->getCacheExpirySource() );
2954 }
2955 if ( $this->mCacheTime !== '' ) {
2956 $metadata->setCacheTime( $this->mCacheTime );
2957 }
2958 if ( $this->mCacheRevisionId !== null ) {
2959 $metadata->setCacheRevisionId( $this->mCacheRevisionId );
2960 }
2961 // T293514: We should use the first *modified* title text, but
2962 // we don't have the original to check.
2963 $otherTitle = $metadata->getTitleText();
2964 if ( $otherTitle === '' ) {
2965 $metadata->setTitleText( $this->getTitleText() );
2966 }
2967 foreach (
2968 [
2969 ParserOutputLinkTypes::TEMPLATE,
2970 ParserOutputLinkTypes::EXISTENCE,
2971 ] as $linkType ) {
2972 foreach ( $this->getLinkList( $linkType ) as $linkItem ) {
2973 $metadata->appendLinkList( $linkType, $linkItem );
2974 }
2975 }
2976 foreach ( $this->mWarningMsgs as $key => $msg ) {
2977 $metadata->addWarningMsgVal( $msg, (string)$key );
2978 }
2979 }
2980 }
2981
2982 private static function mergeMixedList( array $a, array $b ): array {
2983 return array_unique( array_merge( $a, $b ), SORT_REGULAR );
2984 }
2985
2986 private static function mergeList( array $a, array $b ): array {
2987 return array_values( array_unique( array_merge( $a, $b ), SORT_REGULAR ) );
2988 }
2989
2990 private static function mergeMap( array $a, array $b ): array {
2991 return array_replace( $a, $b );
2992 }
2993
2997 private static function makeMapStrategy( string|int $value, MergeStrategy $strategy ): array {
2998 $base = [ self::MW_MERGE_STRATEGY_KEY => $strategy->value ];
2999 switch ( $strategy ) {
3000 case MergeStrategy::UNION:
3001 return [ $value => true, ...$base ];
3002 case MergeStrategy::SUM:
3003 Assert::parameterType( 'integer', $value, '$value' );
3004 return [ 'value' => $value, ...$base ];
3005 default:
3006 throw new InvalidArgumentException( "Unknown merge strategy {$strategy->value}" );
3007 }
3008 }
3009
3018 private static function collectMapStrategy( array $map, callable $f ): void {
3019 $strategy = MergeStrategy::from(
3020 $map[self::MW_MERGE_STRATEGY_KEY]
3021 );
3022 foreach ( $map as $key => $value ) {
3023 if ( $key === self::MW_MERGE_STRATEGY_KEY ) {
3024 continue;
3025 }
3026 switch ( $strategy ) {
3027 case MergeStrategy::UNION:
3028 $f( $key, $strategy ); // ignore value
3029 break;
3030 case MergeStrategy::SUM:
3031 $f( $value, $strategy ); // ignore key
3032 break;
3033 }
3034 }
3035 }
3036
3037 private static function mergeMapStrategy( array $a, array $b ): array {
3038 foreach ( $b as $key => $bValue ) {
3039 if ( !array_key_exists( $key, $a ) ) {
3040 $a[$key] = $bValue;
3041 } elseif (
3042 is_array( $a[$key] ) &&
3043 isset( $a[$key][self::MW_MERGE_STRATEGY_KEY] ) &&
3044 isset( $bValue[self::MW_MERGE_STRATEGY_KEY] )
3045 ) {
3046 $strategy = $bValue[self::MW_MERGE_STRATEGY_KEY];
3047 if ( $strategy !== $a[$key][self::MW_MERGE_STRATEGY_KEY] ) {
3048 throw new InvalidArgumentException( "Conflicting merge strategy for $key" );
3049 }
3050 $strategy = MergeStrategy::from( $strategy );
3051 switch ( $strategy ) {
3052 case MergeStrategy::UNION:
3053 // Note the array_merge is *not* safe to use here, because
3054 // the $bValue is expected to be a map from items to `true`.
3055 // If the item is a numeric string like '1' then array_merge
3056 // will convert it to an integer and renumber the array!
3057 $a[$key] = array_replace( $a[$key], $bValue );
3058 break;
3059 case MergeStrategy::SUM:
3060 $a[$key]['value'] += $b[$key]['value'];
3061 break;
3062 default:
3063 throw new InvalidArgumentException( "Unknown merge strategy {$strategy->value}" );
3064 }
3065 } else {
3066 $valuesSame = ( $a[$key] === $bValue );
3067 if ( ( !$valuesSame ) &&
3068 is_object( $a[$key] ) &&
3069 is_object( $bValue )
3070 ) {
3071 $jsonCodec = MediaWikiServices::getInstance()->getJsonCodec();
3072 $valuesSame = ( $jsonCodec->toJsonArray( $a[$key] ) === $jsonCodec->toJsonArray( $bValue ) );
3073 }
3074 if ( !$valuesSame ) {
3075 // Silently replace for now; in the future will first emit
3076 // a deprecation warning, and then (later) throw.
3077 $a[$key] = $bValue;
3078 }
3079 }
3080 }
3081 return $a;
3082 }
3083
3084 private static function useEachMinValue( array $a, array $b ): array {
3085 $values = [];
3086 $keys = array_merge( array_keys( $a ), array_keys( $b ) );
3087
3088 foreach ( $keys as $k ) {
3089 $values[$k] = min( $a[$k] ?? INF, $b[$k] ?? INF );
3090 }
3091
3092 return $values;
3093 }
3094
3095 private static function useEachTotalValue( array $a, array $b ): array {
3096 $values = [];
3097 $keys = array_merge( array_keys( $a ), array_keys( $b ) );
3098
3099 foreach ( $keys as $k ) {
3100 $values[$k] = ( $a[$k] ?? 0 ) + ( $b[$k] ?? 0 );
3101 }
3102
3103 return $values;
3104 }
3105
3111 private static function useMaxValue( $a, $b ) {
3112 if ( $a === null ) {
3113 return $b;
3114 }
3115
3116 if ( $b === null ) {
3117 return $a;
3118 }
3119
3120 return max( $a, $b );
3121 }
3122
3129 public function toJsonArray(): array {
3130 // WARNING: When changing how this class is serialized, follow the instructions
3131 // at <https://www.mediawiki.org/wiki/Manual:Parser_cache/Serialization_compatibility>!
3132 $data = [
3133 'Text' => $this->hasText() ? $this->getContentHolderText() : null,
3134 'LanguageLinks' => $this->getLanguageLinksInternal(),
3135 'Categories' => $this->mCategories,
3136 'Indicators' => $this->mIndicators,
3137 'TitleText' => $this->mTitleText,
3138 'Links' => $this->mLinks,
3139 'LinksSpecial' => $this->mLinksSpecial,
3140 'Templates' => $this->mTemplates,
3141 'TemplateIds' => $this->mTemplateIds,
3142 'Images' => $this->mImages,
3143 'FileSearchOptions' => $this->mFileSearchOptions,
3144 'ExternalLinks' => $this->mExternalLinks,
3145 'InterwikiLinks' => $this->mInterwikiLinks,
3146 'ExistenceLinks' => $this->existenceLinks,
3147 'HeadItems' => $this->mHeadItems,
3148 'Modules' => array_keys( $this->mModuleSet ),
3149 'ModuleStyles' => array_keys( $this->mModuleStyleSet ),
3150 'JsConfigVars' => $this->mJsConfigVars,
3151 'Warnings' => $this->mWarnings,
3152 'WarningMsgs' => $this->mWarningMsgs,
3153 'TOCData' => $this->mTOCData,
3154 'Properties' => self::detectAndEncodeBinary( $this->mProperties ),
3155 'Timestamp' => $this->mTimestamp,
3156 // may contain arbitrary structures!
3157 'ExtensionData' => $this->mExtensionData,
3158 'LimitReportData' => $this->mLimitReportData,
3159 'LimitReportJSData' => $this->mLimitReportJSData,
3160 'CacheMessage' => $this->mCacheMessage,
3161 'TimeProfile' => $this->mTimeProfile,
3162 'ParseStartTime' => [], // don't serialize this
3163 'ExtraScriptSrcs' => $this->mExtraScriptSrcs,
3164 'ExtraDefaultSrcs' => $this->mExtraDefaultSrcs,
3165 'ExtraStyleSrcs' => $this->mExtraStyleSrcs,
3166 'SpeculativeRevId' => $this->mSpeculativeRevId,
3167 'SpeculativePageIdUsed' => $this->speculativePageIdUsed,
3168 'RevisionTimestampUsed' => $this->revisionTimestampUsed,
3169 'RevisionUsedSha1Base36' => $this->revisionUsedSha1Base36,
3170 'WrapperDivClasses' => $this->mWrapperDivClasses,
3171 'OutputFlags' => array_keys( $this->mFlags ),
3172 ];
3173
3174 // TODO ultimately we'll change the serialization to directly
3175 // encode the ContentHolder, but let's maintain compatibility for now.
3176 if ( $this->contentHolder->isParsoidContent() ) {
3177 $pageBundle = $this->contentHolder->getBasePageBundle();
3178 $data[ 'ExtensionData' ][ self::PARSOID_PAGE_BUNDLE_KEY ] =
3179 $pageBundle->toJsonArray();
3180 }
3181
3182 // Fill in missing fields from parents. Array addition does not override existing fields.
3183 $data += parent::toJsonArray();
3184
3185 // TODO: make more fields optional!
3186
3187 if ( $this->mMaxAdaptiveExpiry !== null ) {
3188 $data['MaxAdaptiveExpiry'] = $this->mMaxAdaptiveExpiry;
3189 }
3190
3191 return $data;
3192 }
3193
3194 public static function newFromJsonArray( array $json ): ParserOutput {
3195 $parserOutput = new ParserOutput();
3196 $parserOutput->initFromJson( $json );
3197 return $parserOutput;
3198 }
3199
3204 protected function initFromJson( array $jsonData ): void {
3205 parent::initFromJson( $jsonData );
3206
3207 // WARNING: When changing how this class is serialized, follow the instructions
3208 // at <https://www.mediawiki.org/wiki/Manual:Parser_cache/Serialization_compatibility>!
3209 // (This includes changing default values when fields are missing.)
3210
3211 $pageBundleData = $jsonData['ExtensionData'][ self::PARSOID_PAGE_BUNDLE_KEY ] ?? null;
3212 if ( $pageBundleData ) {
3213 unset( $jsonData['ExtensionData'][ self::PARSOID_PAGE_BUNDLE_KEY ] );
3214 $pb = HtmlPageBundle::newFromJsonArray(
3215 $pageBundleData + [ 'html' => $jsonData[ 'Text' ] ?? '' ]
3216 );
3217 $this->contentHolder = ContentHolder::createFromParsoidPageBundle( $pb );
3218 } else {
3219 $this->contentHolder = ContentHolder::createFromLegacyString( $jsonData[ 'Text' ] ?? '' );
3220 }
3221 if ( !isset( $jsonData['Text'] ) ) {
3222 // Make the content holder empty if 'Text' was null.
3223 $this->contentHolder->setAsHtmlString( ContentHolder::BODY_FRAGMENT, null );
3224 }
3225
3226 $this->mLanguageLinkMap = [];
3227 foreach ( ( $jsonData['LanguageLinks'] ?? [] ) as $l ) {
3228 // T374736: old serialized parser cache entries may
3229 // contain invalid language links; drop them quietly.
3230 // (This code can be removed two LTS releases past 1.45.)
3231 if ( str_contains( $l, ':' ) ) {
3232 $this->addLanguageLink( $l );
3233 }
3234 }
3235 // Default values should match the property default values.
3236 $this->mCategories = $jsonData['Categories'] ?? [];
3237 $this->mIndicators = $jsonData['Indicators'] ?? [];
3238 $this->mTitleText = $jsonData['TitleText'] ?? '';
3239 $this->mLinks = $jsonData['Links'] ?? [];
3240 $this->mLinksSpecial = $jsonData['LinksSpecial'] ?? [];
3241 $this->mTemplates = $jsonData['Templates'] ?? [];
3242 $this->mTemplateIds = $jsonData['TemplateIds'] ?? [];
3243 $this->mImages = $jsonData['Images'] ?? [];
3244 $this->mFileSearchOptions = $jsonData['FileSearchOptions'] ?? [];
3245 $this->mExternalLinks = $jsonData['ExternalLinks'] ?? [];
3246 $this->mInterwikiLinks = $jsonData['InterwikiLinks'] ?? [];
3247 $this->existenceLinks = $jsonData['ExistenceLinks'] ?? [];
3248 $this->mHeadItems = $jsonData['HeadItems'] ?? [];
3249 $this->mModuleSet = array_fill_keys( $jsonData['Modules'] ?? [], true );
3250 $this->mModuleStyleSet = array_fill_keys( $jsonData['ModuleStyles'] ?? [], true );
3251 $this->mJsConfigVars = $jsonData['JsConfigVars'] ?? [];
3252 $this->mWarnings = $jsonData['Warnings'] ?? [];
3253 $this->mWarningMsgs = $jsonData['WarningMsgs'] ?? [];
3254
3255 // Set flags stored as properties (backward compatibility with MW<1.45)
3256 $this->mFlags = $jsonData['Flags'] ?? [];
3257 if ( $jsonData['NoGallery'] ?? false ) {
3258 $this->setOutputFlag( ParserOutputFlags::NO_GALLERY );
3259 }
3260 if ( $jsonData['EnableOOUI'] ?? false ) {
3261 $this->setOutputFlag( ParserOutputFlags::ENABLE_OOUI );
3262 }
3263 $this->setIndexPolicy( $jsonData['IndexPolicy'] ?? '' );
3264 if ( $jsonData['NewSection'] ?? false ) {
3265 $this->setOutputFlag( ParserOutputFlags::NEW_SECTION );
3266 }
3267 if ( $jsonData['HideNewSection'] ?? false ) {
3268 $this->setOutputFlag( ParserOutputFlags::HIDE_NEW_SECTION );
3269 }
3270 if ( $jsonData['PreventClickjacking'] ?? false ) {
3271 $this->setOutputFlag( ParserOutputFlags::PREVENT_CLICKJACKING );
3272 }
3273 // Set all generic output flags (whether stored as properties or not)
3274 // (This is effectively a logical-OR if these are also serialized
3275 // above.)
3276 foreach ( $jsonData['OutputFlags'] ?? [] as $flag ) {
3277 $this->setOutputFlag( $flag );
3278 }
3279
3280 if ( isset( $jsonData['TOCData'] ) ) {
3281 $this->mTOCData = $jsonData['TOCData'];
3282 // Backward-compatibility with old TOCData encoding (T327439)
3283 // emitted in MW < 1.45
3284 } elseif (
3285 ( $jsonData['Sections'] ?? [] ) !== [] ||
3286 // distinguish "no sections" from "sections not set"
3287 $this->getOutputFlag( 'mw:toc-set' )
3288 ) {
3289 $this->setSections( $jsonData['Sections'] ?? [] );
3290 unset( $this->mFlags['mw:toc-set'] );
3291 if ( isset( $jsonData['TOCExtensionData'] ) ) {
3292 $tocData = $this->getTOCData(); // created by setSections() above
3293 foreach ( $jsonData['TOCExtensionData'] as $key => $value ) {
3294 $tocData->setExtensionData( (string)$key, $value );
3295 }
3296 }
3297 }
3298 // backward-compatibility: convert page properties to their
3299 // 'database representation'. We haven't permitted non-string
3300 // non-numeric values since 1.45.
3301 $this->mProperties = [];
3302 foreach (
3303 self::detectAndDecodeBinary( $jsonData['Properties'] ?? [] )
3304 as $k => $v
3305 ) {
3306 if ( is_int( $v ) || is_float( $v ) || is_string( $v ) ) {
3307 $this->mProperties[$k] = $v;
3308 } elseif ( is_bool( $v ) ) {
3309 $this->mProperties[$k] = (int)$v;
3310 } elseif ( $v === null ) {
3311 $this->mProperties[$k] = '';
3312 } elseif ( is_array( $v ) ) {
3313 $this->mProperties[$k] = 'Array';
3314 } else {
3315 $this->mProperties[$k] = strval( $v );
3316 }
3317 }
3318 $this->mTimestamp = $jsonData['Timestamp'] ?? null;
3319 $this->mExtensionData = $jsonData['ExtensionData'] ?? [];
3320 $this->mLimitReportData = $jsonData['LimitReportData'] ?? [];
3321 $this->mLimitReportJSData = $jsonData['LimitReportJSData'] ?? [];
3322 $this->mCacheMessage = $jsonData['CacheMessage'] ?? '';
3323 $this->mParseStartTime = []; // invalid after reloading
3324 $this->mTimeProfile = $jsonData['TimeProfile'] ?? [];
3325 $this->mExtraScriptSrcs = $jsonData['ExtraScriptSrcs'] ?? [];
3326 $this->mExtraDefaultSrcs = $jsonData['ExtraDefaultSrcs'] ?? [];
3327 $this->mExtraStyleSrcs = $jsonData['ExtraStyleSrcs'] ?? [];
3328 $this->mSpeculativeRevId = $jsonData['SpeculativeRevId'] ?? null;
3329 $this->speculativePageIdUsed = $jsonData['SpeculativePageIdUsed'] ?? null;
3330 $this->revisionTimestampUsed = $jsonData['RevisionTimestampUsed'] ?? null;
3331 $this->revisionUsedSha1Base36 = $jsonData['RevisionUsedSha1Base36'] ?? null;
3332 $this->mWrapperDivClasses = $jsonData['WrapperDivClasses'] ?? [];
3333 $this->mMaxAdaptiveExpiry = $jsonData['MaxAdaptiveExpiry'] ?? null;
3334 }
3335
3345 private static function detectAndEncodeBinary( array $properties ) {
3346 foreach ( $properties as $key => $value ) {
3347 if ( is_string( $value ) ) {
3348 if ( !mb_detect_encoding( $value, 'UTF-8', true ) ) {
3349 $properties[$key] = [
3350 // T313818: This key name conflicts with JsonCodec
3351 '_type_' => 'string',
3352 '_encoding_' => 'base64',
3353 '_data_' => base64_encode( $value ),
3354 ];
3355 }
3356 }
3357 }
3358
3359 return $properties;
3360 }
3361
3370 private static function detectAndDecodeBinary( array $properties ) {
3371 foreach ( $properties as $key => $value ) {
3372 if ( is_array( $value ) && isset( $value['_encoding_'] ) ) {
3373 if ( $value['_encoding_'] === 'base64' ) {
3374 $properties[$key] = base64_decode( $value['_data_'] );
3375 }
3376 }
3377 }
3378
3379 return $properties;
3380 }
3381
3382 public function __serialize(): array {
3383 // Support for PHP serialization of ParserOutput for ParserCache
3384 // was turned off in 1.39 and is not guaranteed to work.
3385 wfDeprecated( "PHP serialization of ParserOutput", "1.39" );
3386 return (array)$this;
3387 }
3388
3389 public function __clone() {
3390 // It seems that very little of this object needs to be explicitly deep-cloned
3391 // while keeping copies reasonably separated.
3392 // Most of the non-scalar properties of this object are either
3393 // - (potentially multi-nested) arrays of scalars (which get deep-cloned), or
3394 // - arrays that may contain arbitrary elements (which don't necessarily get
3395 // deep-cloned), but for which no particular care elsewhere is given to
3396 // copying their references around (e.g. mJsConfigVars).
3397 // Hence, we are not going out of our way to ensure that the references to innermost
3398 // objects that may appear in a ParserOutput are unique. If that becomes the
3399 // expectation at any point, this method will require updating as well.
3400 // The exception is TOCData (which is an object), which we clone explicitly.
3401 if ( $this->mTOCData ) {
3402 $this->mTOCData = clone $this->mTOCData;
3403 }
3404 $this->contentHolder = clone $this->contentHolder;
3405 }
3406
3414 public function getContentHolderText(): string {
3415 $html = $this->contentHolder->getAsHtmlString( ContentHolder::BODY_FRAGMENT );
3416 if ( $html === null ) {
3417 throw new LogicException( 'This ParserOutput contains no text!' );
3418 }
3419 return $html;
3420 }
3421
3431 public function setContentHolderText( ?string $text ): void {
3432 $this->contentHolder->setAsHtmlString( ContentHolder::BODY_FRAGMENT, $text );
3433 }
3434
3436 private static function normalizeForObjectEquality(): array {
3437 return [
3438 'mFlags' => static function ( $v ) {
3439 ksort( $v );
3440 return $v;
3441 },
3442 ];
3443 }
3444}
3445
3447class_alias( ParserOutput::class, 'ParserOutput' );
const NS_FILE
Definition Defines.php:57
const NS_MAIN
Definition Defines.php:51
const NS_SPECIAL
Definition Defines.php:40
const NS_MEDIA
Definition Defines.php:39
const NS_CATEGORY
Definition Defines.php:65
wfSetVar(&$dest, $source, $force=false)
Sets dest to source and returns the original value of dest If source is NULL, it just returns the val...
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:69
Represents the identity of a specific rendering of a specific revision at some point in time.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
This is one of the Core classes and should be read at least once by any new developers.
getModules( $filter=false,... $args)
Get the list of modules to include on this page.
getJsConfigVars()
Get the javascript config vars to include on this page.
getModuleStyles( $filter=false,... $args)
Get the list of style-only modules to load on this page.
Parser cache specific expiry check.
Definition CacheTime.php:24
static createFromLegacyString(string $html)
Create a ContentHolder from a legacy body HTML string, typically returned by the legacy parser.
static createEmpty()
Creates an empty ContentHolder that can be used as a placeholder.
Set options of the Parser.
getWrapOutputClass()
Class to use to wrap output from Parser::parse()
getIsPreview()
Parsing the page for a "preview" operation?
getUseParsoid()
Parsoid-format HTML output, or legacy wikitext parser HTML?
getCollapsibleSections()
Should section contents be wrapped in.
ParserOutput is a rendering of a Content object or a message.
getExtraCSPDefaultSrcs()
Get extra Content-Security-Policy 'default-src' directives.
setIndexPolicy( $policy)
Update the index policy of the robots meta tag.
getJsConfigVars(bool $showStrategyKeys=false)
setLimitReportData( $key, $value)
Sets parser limit report data for a key.
setEnableOOUI(bool $enable=false)
Enables OOUI, if true, in any OutputPage instance this ParserOutput object is added to.
toJsonArray()
Returns a JSON serializable structure representing this ParserOutput instance.
unsetPageProperty(string $name)
Remove a page property.
getTimeProfile(string $clock)
Returns the time that elapsed between the most recent call to resetParseStartTime() and the first cal...
appendExtensionData(string $key, $value, MergeStrategy|string $strategy=MergeStrategy::UNION)
Appends arbitrary data to this ParserObject.
addImage( $name, $timestamp=null, $sha1=null)
Register a file dependency for this output.
setNumericPageProperty(string $propName, $numericValue)
Set a numeric page property whose value is intended to be sorted and indexed.
runOutputPipeline(ParserOptions $popts, array $options=[])
appendJsConfigVar(string $key, $value, MergeStrategy|string $strategy=MergeStrategy::UNION)
Append a value to a variable to be set in mw.config in JavaScript.
getRenderId()
Return the unique rendering id for this ParserOutput.
addTemplate( $link, $page_id, $rev_id)
Register a template dependency for this output.
addExtraCSPDefaultSrc( $src)
Add an extra value to Content-Security-Policy default-src directive.
setExtensionData( $key, $value)
Attaches arbitrary data to this ParserObject.
setRenderId(string $renderId)
Store a unique rendering id for this ParserOutput.
addOutputPageMetadata(OutputPage $out)
Accommodate very basic transcluding of a temporary OutputPage object into parser output.
addWarningMsgVal(MessageSpecifier $mv, ?string $key=null)
Add a warning to the output for this page.
addExistenceDependency(ParsoidLinkTarget $link)
Add a dependency on the existence of a page.
setUnsortedPageProperty(string $propName, string $value='')
Set a page property whose value is not intended to be sorted and indexed.
getOutputStrings(string|ParserOutputStringSets $name)
Provides a uniform interface to various boolean string sets stored in the ParserOutput.
addJsConfigVars( $keys, $value=null)
Add one or more variables to be set in mw.config in JavaScript.
setRedirectHeader(string $html)
Set an HTML prefix to be applied on redirect pages.
addCategory( $c, $sort='')
Add a category.
setJsConfigVar(string $key, $value)
Add a variable to be set in mw.config in JavaScript.
getPageProperties()
Return all the page properties set on this ParserOutput.
getContentHolderText()
Returns the body fragment text of the ParserOutput.
setRevisionTimestamp(?string $timestamp)
clearWrapperDivClass()
Clears the CSS class to use for the wrapping div, effectively disabling the wrapper div until addWrap...
getPreventClickjacking()
Get the prevent-clickjacking flag.
setPreventClickjacking(bool $flag)
Set the prevent-clickjacking flag.
getOutputFlag(ParserOutputFlags|string $flag)
Provides a uniform interface to various boolean flags stored in the ParserOutput.
addExtraCSPScriptSrc( $src)
Add an extra value to Content-Security-Policy script-src directive.
getExtraCSPStyleSrcs()
Get extra Content-Security-Policy 'style-src' directives.
getCategorySortKey(string $name)
Return the sort key for a given category name, or null if the category is not present in this ParserO...
getWrapperDivClass()
Returns the class (or classes) to be used with the wrapper div for this output.
const MW_MERGE_STRATEGY_UNION
Merge strategy to use for ParserOutput accumulators: "union" means that values are strings,...
setRawText(?string $text)
Set the raw text of the ParserOutput.
hasReducedExpiry()
Check whether the cache TTL was lowered from the site default.
getLinkList(string|ParserOutputLinkTypes $linkType, ?int $onlyNamespace=null)
Get a list of links of the given type.
addLanguageLink( $t)
Add a language link.
hasImages()
Return true if there are image dependencies registered for this ParserOutput.
addWarningMsg(string $msg,... $args)
Add a warning to the output for this page.
hasLinks()
Return true if the given parser output has local links registered in the metadata.
mergeHtmlMetaDataFrom(ParserOutput $source)
Merges HTML metadata such as head items, JS config vars, and HTTP cache control info from $source int...
recordTimeProfile()
Record the time since resetParseStartTime() was last called.
appendLinkList(string|ParserOutputLinkTypes $linkType, array $linkItem)
Append a link of the given type.
getDisplayTitle()
Get the title to be used for display.
setCategories(array $c)
Overwrite the category map.
getPageProperty(string $name)
Look up a page property.
setContentHolderText(?string $text)
Sets the body fragment text of the ParserOutput.
finalizeAdaptiveCacheExpiry()
Call this when parsing is done to lower the TTL based on low parse times.
getTitle()
Get the page used as context for creating this output.
getCategoryNames()
Return the names of the categories on this page.
mergeTrackingMetaDataFrom(ParserOutput $source)
Merges dependency tracking metadata such as backlinks, images used, and extension data from $source i...
getLanguage()
Get the primary language code of the output.
setSections(array $sectionArray)
__construct(?string $text=null, array $languageLinks=[], array $categoryLinks=[], $unused=false, string $titletext='')
addExtraCSPStyleSrc( $src)
Add an extra value to Content-Security-Policy style-src directive.
static newFromJsonArray(array $json)
mergeInternalMetaDataFrom(ParserOutput $source)
Merges internal metadata such as flags, accessed options, and profiling info from $source into this P...
addCacheMessage(string $msg)
Adds a comment notice about cache state to the text of the page.
getContentHolder()
Return the ContentHolder storing the HTML/DOM contents of this ParserOutput.
setLanguage(Bcp47Code $lang)
Set the primary language of the output.
getCategoryMap()
Return category names and sort keys as a map.
hasText()
Returns true if text was passed to the constructor, or set using setText().
setContentHolder(ContentHolder $contentHolder)
addHeadItem( $section, $tag=false)
Add some text to the "<head>".
getExtraCSPScriptSrcs()
Get extra Content-Security-Policy 'script-src' directives.
clearParseStartTime()
Unset the parse start time.
resetParseStartTime()
Resets the parse start timestamps for future calls to getTimeProfile() and recordTimeProfile().
updateRuntimeAdaptiveExpiry(int $ttl, ?string $source=null)
Lower the runtime adaptive TTL to at most this value.
setText( $text)
Set the raw text of the ParserOutput.
getExtensionData( $key)
Gets extensions data previously attached to this ParserOutput using setExtensionData().
getCacheExpiry()
Returns the number of seconds after which this object should expire.
getRedirectHeader()
Return an HTML prefix to be applied on redirect pages, or null if this is not a redirect.
setFromParserOptions(ParserOptions $parserOptions)
Transfer parser options which affect post-processing from ParserOptions to this ParserOutput.
getRawText()
Get the cacheable text with <mw:editsection> markers still in it.
setOutputFlag(ParserOutputFlags|string $name, bool $val=true)
Provides a uniform interface to various boolean flags stored in the ParserOutput.
initFromJson(array $jsonData)
Initialize member fields from an array returned by jsonSerialize().
collectMetadata(ContentMetadataCollector $metadata)
Adds the metadata collected in this ParserOutput to the supplied ContentMetadataCollector.
appendOutputStrings(string|ParserOutputStringSets $name, array $value)
Provides a uniform interface to various string sets stored in the ParserOutput.
addWrapperDivClass( $class)
Add a CSS class to use for the wrapping div.
static isLinkInternal( $internal, $url)
Checks, if a url is pointing to the own server.
setTitle(ParsoidLinkTarget|PageReference $title)
Sets the page context used to create this output.
setPageProperty(string $name, string $value)
Set a page property to be stored in the page_props database table.
setDisplayTitle(string $text)
Override the title to be used for display.
Represents the target of a wiki link.
Value object representing a message for i18n.
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'=>[], '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, 'ThumbnailStepsRatio'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, '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, 'ImageLinksSchemaMigrationStage'=> 768, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], '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,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'UseSessionCookieForBotPasswords'=> 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',],],], '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, ], '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, 'UseLegacyMediaStyles' => 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, ], ], '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, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], '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, '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, ], '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, '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' => [ ], '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, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => 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, '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, '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, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => 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', ], '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, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => 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, '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: ], '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' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], '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, ], '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, '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\\JobQueue\\Jobs\\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', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], '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, '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, 'UsePostprocCache' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => false, 'ParserOptionsLogUnsafeSampleRate' => 0, ], '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', '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', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ImageLinksSchemaMigrationStage' => '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', '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', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => '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', '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', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ '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', 'RCEngines' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => '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', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], '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', 'UsePostprocCache' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', ], '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', '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', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], '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', ], ], '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', ], ], ], ], '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.', ],]
Marker interface for entities aware of the wiki they belong to.
assertWiki( $wikiId)
Throws if $wikiId is different from the return value of getWikiId().
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
getKey()
Returns the message key.
$source