MediaWiki master
PageUpdater.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Storage;
8
9use InvalidArgumentException;
10use LogicException;
38use Psr\Log\LoggerInterface;
39use RuntimeException;
40use Wikimedia\Assert\Assert;
41use Wikimedia\NormalizedException\NormalizedException;
45
62class PageUpdater implements PageUpdateCauses {
63
69 public const CONSTRUCTOR_OPTIONS = [
72 ];
73
77 private readonly WikiPage $wikiPage;
78 private readonly HookRunner $hookRunner;
79
84 private $useAutomaticEditSummaries = true;
85
89 private $rcPatrolStatus = RecentChange::PRC_UNPATROLLED;
90
94 private $usePageCreationLog = true;
95
99 private $forceEmptyRevision = false;
100
105 private $preventChange = false;
106
110 private $tags = [];
111
112 private readonly RevisionSlotsUpdate $slotsUpdate;
113
117 private $status = null;
118
119 private readonly EditResultBuilder $editResultBuilder;
120
124 private $editResult = null;
125
129 private $flags = 0;
130
134 private array $hints = [];
135
153 public function __construct(
154 private UserIdentity $author,
155 private readonly PageIdentity $pageIdentity,
156 private readonly DerivedPageDataUpdater $derivedDataUpdater,
157 private readonly IConnectionProvider $dbProvider,
158 private readonly RevisionStore $revisionStore,
159 private readonly SlotRoleRegistry $slotRoleRegistry,
160 private readonly IContentHandlerFactory $contentHandlerFactory,
161 private readonly HookContainer $hookContainer,
162 private readonly UserGroupManager $userGroupManager,
163 private readonly TitleFormatter $titleFormatter,
164 private readonly ServiceOptions $serviceOptions,
165 private readonly array $softwareTags,
166 private readonly LoggerInterface $logger,
167 WikiPageFactory $wikiPageFactory,
168 ) {
169 $serviceOptions->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
170
171 $this->wikiPage = $wikiPageFactory->newFromTitle( $pageIdentity );
172 $this->derivedDataUpdater->setCause( self::CAUSE_EDIT );
173
174 $this->hookRunner = new HookRunner( $hookContainer );
175
176 $this->slotsUpdate = new RevisionSlotsUpdate();
177 $this->editResultBuilder = new EditResultBuilder(
178 $revisionStore,
179 $softwareTags,
180 new ServiceOptions(
182 [
184 $serviceOptions->get( MainConfigNames::ManualRevertSearchRadius )
185 ]
186 )
187 );
188 }
189
197 public function setCause( string $cause ): self {
198 $this->derivedDataUpdater->setCause( $cause );
199 return $this;
200 }
201
210 public function setHints( array $hints ): self {
211 $this->hints = $hints + $this->hints;
212 return $this;
213 }
214
225 public function setFlags( int $flags ) {
226 $this->flags |= $flags;
227 return $this;
228 }
229
240 public function prepareUpdate( int $flags = 0 ): PreparedUpdate {
241 $this->setFlags( $flags );
242
243 // Load the data from the primary database if needed. Needed to check flags.
244 $this->grabParentRevision();
245 if ( !$this->derivedDataUpdater->isUpdatePrepared() ) {
246 // Avoid statsd noise and wasted cycles check the edit stash (T136678)
247 $useStashed = !( ( $this->flags & EDIT_INTERNAL ) || ( $this->flags & EDIT_FORCE_BOT ) );
248 // Prepare the update. This performs PST and generates the canonical ParserOutput.
249 $this->derivedDataUpdater->prepareContent(
250 $this->author,
251 $this->slotsUpdate,
252 $useStashed
253 );
254 }
255
256 return $this->derivedDataUpdater;
257 }
258
266 public function updateAuthor( UserIdentity $author ) {
267 if ( $this->author->getName() !== $author->getName() ) {
268 throw new InvalidArgumentException( 'Cannot replace the author with an author ' .
269 'of a different name, since DerivedPageDataUpdater may have stored the ' .
270 'old name.' );
271 }
272 $this->author = $author;
273 }
274
283 public function setUseAutomaticEditSummaries( $useAutomaticEditSummaries ) {
284 $this->useAutomaticEditSummaries = $useAutomaticEditSummaries;
285 return $this;
286 }
287
298 public function setRcPatrolStatus( $status ) {
299 $this->rcPatrolStatus = $status;
300 return $this;
301 }
302
311 public function setUsePageCreationLog( $use ) {
312 $this->usePageCreationLog = $use;
313 return $this;
314 }
315
334 public function setForceEmptyRevision( bool $forceEmptyRevision ): self {
335 $this->forceEmptyRevision = $forceEmptyRevision;
336
337 if ( $forceEmptyRevision ) {
338 // XXX: throw if there is no current/parent revision?
339 $original = $this->grabParentRevision();
340 $this->setOriginalRevisionId( $original ? $original->getId() : false );
341 }
342
343 $this->derivedDataUpdater->setForceEmptyRevision( $forceEmptyRevision );
344 return $this;
345 }
346
348 private function getWikiId() {
349 return $this->revisionStore->getWikiId();
350 }
351
355 public function getPage(): PageIdentity {
356 return $this->pageIdentity;
357 }
358
362 private function getTitle() {
363 // NOTE: eventually, this won't use WikiPage any more
364 return $this->wikiPage->getTitle();
365 }
366
370 private function getWikiPage() {
371 // NOTE: eventually, this won't use WikiPage any more
372 return $this->wikiPage;
373 }
374
408 public function hasEditConflict( $expectedParentRevision ) {
409 $parent = $this->grabParentRevision();
410 $parentId = $parent ? $parent->getId() : 0;
411
412 return $parentId !== $expectedParentRevision;
413 }
414
441 public function grabParentRevision() {
442 return $this->derivedDataUpdater->grabLatestRevision();
443 }
444
452 public function setContent( $role, Content $content ) {
453 $this->ensureRoleAllowed( $role );
454
455 $this->slotsUpdate->modifyContent( $role, $content );
456 return $this;
457 }
458
465 public function setSlot( SlotRecord $slot ) {
466 $this->ensureRoleAllowed( $slot->getRole() );
467
468 $this->slotsUpdate->modifySlot( $slot );
469 return $this;
470 }
471
487 public function inheritSlot( SlotRecord $originalSlot ) {
488 // NOTE: slots can be inherited even if the role is not "allowed" on the title.
489 // NOTE: this slot is inherited from some other revision, but it's
490 // a "modified" slot for the RevisionSlotsUpdate and DerivedPageDataUpdater,
491 // since it's not implicitly inherited from the parent revision.
492 $inheritedSlot = SlotRecord::newInherited( $originalSlot );
493 $this->slotsUpdate->modifySlot( $inheritedSlot );
494 return $this;
495 }
496
506 public function removeSlot( $role ) {
507 $this->ensureRoleNotRequired( $role );
508
509 $this->slotsUpdate->removeSlot( $role );
510 }
511
522 public function setOriginalRevisionId( $originalRevId ) {
523 $this->editResultBuilder->setOriginalRevision( $originalRevId );
524 return $this;
525 }
526
539 public function markAsRevert(
540 int $revertMethod,
541 int $newestRevertedRevId,
542 ?int $revertAfterRevId = null
543 ) {
544 $this->editResultBuilder->markAsRevert(
545 $revertMethod, $newestRevertedRevId, $revertAfterRevId
546 );
547 return $this;
548 }
549
557 public function getEditResult(): ?EditResult {
558 return $this->editResult;
559 }
560
568 public function addTag( string $tag ) {
569 $this->tags[] = trim( $tag );
570 return $this;
571 }
572
580 public function addTags( array $tags ) {
581 Assert::parameterElementType( 'string', $tags, '$tags' );
582 foreach ( $tags as $tag ) {
583 $this->addTag( $tag );
584 }
585 return $this;
586 }
587
596 public function addSoftwareTag( string $tag ): self {
597 if ( in_array( $tag, $this->softwareTags ) ) {
598 $this->addTag( $tag );
599 }
600 return $this;
601 }
602
608 public function getExplicitTags() {
609 return $this->tags;
610 }
611
615 private function computeEffectiveTags() {
616 $tags = $this->tags;
617 $editResult = $this->getEditResult();
618
619 foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
620 $old_content = $this->getParentContent( $role );
621
622 $handler = $this->getContentHandler( $role );
623 $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
624
625 // TODO: MCR: Do this for all slots. Also add tags for removing roles!
626 $tag = $handler->getChangeTag( $old_content, $content, $this->flags );
627 // If there is no applicable tag, null is returned, so we need to check
628 if ( $tag ) {
629 $tags[] = $tag;
630 }
631 }
632
633 $tags = array_merge( $tags, $editResult->getRevertTags() );
634
635 return array_unique( $tags );
636 }
637
645 private function getParentContent( $role ) {
646 $parent = $this->grabParentRevision();
647
648 if ( $parent && $parent->hasSlot( $role ) ) {
649 return $parent->getContent( $role, RevisionRecord::RAW );
650 }
651
652 return null;
653 }
654
659 private function getContentHandler( $role ) {
660 if ( $this->slotsUpdate->isModifiedSlot( $role ) ) {
661 $slot = $this->slotsUpdate->getModifiedSlot( $role );
662 } else {
663 $parent = $this->grabParentRevision();
664
665 if ( $parent ) {
666 $slot = $parent->getSlot( $role, RevisionRecord::RAW );
667 } else {
668 throw new RevisionAccessException(
669 'No such slot: {role}',
670 [ 'role' => $role ]
671 );
672 }
673 }
674
675 return $this->contentHandlerFactory->getContentHandler( $slot->getModel() );
676 }
677
681 private function makeAutoSummary() {
682 if ( !$this->useAutomaticEditSummaries || ( $this->flags & EDIT_AUTOSUMMARY ) === 0 ) {
683 return CommentStoreComment::newUnsavedComment( '' );
684 }
685
686 // NOTE: this generates an auto-summary for SOME RANDOM changed slot!
687 // TODO: combine auto-summaries for multiple slots!
688 // XXX: this logic should not be in the storage layer!
689 $roles = $this->slotsUpdate->getModifiedRoles();
690 $role = reset( $roles );
691
692 if ( $role === false ) {
693 return CommentStoreComment::newUnsavedComment( '' );
694 }
695
696 $handler = $this->getContentHandler( $role );
697 $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
698 $old_content = $this->getParentContent( $role );
699 $summary = $handler->getAutosummary( $old_content, $content, $this->flags );
700
701 return CommentStoreComment::newUnsavedComment( $summary );
702 }
703
719 public function saveDummyRevision( $summary, int $flags = 0 ) {
721
722 $this->setForceEmptyRevision( true );
723 $rev = $this->saveRevision( $summary, $flags );
724
725 if ( $rev === null ) {
726 throw new NormalizedException( 'Failed to create dummy revision on ' .
727 '{page} (page ID {id})',
728 [
729 'page' => (string)$this->getPage(),
730 'id' => (string)$this->getPage()->getId(),
731 ]
732 );
733 }
734
735 return $rev;
736 }
737
767 public function saveRevision( $summary, int $flags = 0 ) {
768 Assert::parameterType(
769 [ 'string', CommentStoreComment::class, ],
770 $summary,
771 '$summary'
772 );
773
774 if ( is_string( $summary ) ) {
775 $summary = CommentStoreComment::newUnsavedComment( $summary );
776 }
777
778 $this->setFlags( $flags );
779
780 if ( $this->wasCommitted() ) {
781 throw new RuntimeException(
782 'saveRevision() or updateRevision() has already been called on this PageUpdater!'
783 );
784 }
785
786 // Low-level check
787 if ( $this->getPage()->getDBkey() === '' ) {
788 throw new RuntimeException( 'Something is trying to edit an article with an empty title' );
789 }
790
791 // NOTE: slots can be inherited even if the role is not "allowed" on the title.
792 $status = PageUpdateStatus::newGood();
793 $this->checkAllRolesAllowed(
794 $this->slotsUpdate->getModifiedRoles(),
795 $status
796 );
797 $this->checkNoRolesRequired(
798 $this->slotsUpdate->getRemovedRoles(),
799 $status
800 );
801
802 if ( !$status->isOK() ) {
803 return null;
804 }
805
806 // Make sure the given content is allowed in the respective slots of this page
807 foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
808 $slot = $this->slotsUpdate->getModifiedSlot( $role );
809 $roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
810
811 if ( !$roleHandler->isAllowedModel( $slot->getModel(), $this->getPage() ) ) {
812 $contentHandler = $this->contentHandlerFactory
813 ->getContentHandler( $slot->getModel() );
814 $this->status = PageUpdateStatus::newFatal( 'content-not-allowed-here',
815 ContentHandler::getLocalizedName( $contentHandler->getModelID() ),
816 $this->titleFormatter->getPrefixedText( $this->getPage() ),
817 wfMessage( $roleHandler->getNameMessageKey() )
818 // TODO: defer message lookup to caller
819 );
820 return null;
821 }
822 }
823
824 // Load the data from the primary database if needed. Needed to check flags.
825 // NOTE: This grabs the parent revision as the CAS token, if grabParentRevision
826 // wasn't called yet. If the page is modified by another process before we are done with
827 // it, this method must fail (with status 'edit-conflict')!
828 // NOTE: The parent revision may be different from the edit's base revision.
829 $this->prepareUpdate();
830
831 // Detect whether update or creation should be performed.
832 if ( !( $this->flags & EDIT_NEW ) && !( $this->flags & EDIT_UPDATE ) ) {
833 $this->flags |= ( $this->derivedDataUpdater->pageExisted() ) ? EDIT_UPDATE : EDIT_NEW;
834 }
835
836 // Trigger pre-save hook (using provided edit summary)
837 $renderedRevision = $this->derivedDataUpdater->getRenderedRevision();
838 $hookStatus = PageUpdateStatus::newGood( [] );
839 $allowedByHook = $this->hookRunner->onMultiContentSave(
840 $renderedRevision, $this->author, $summary, $this->flags, $hookStatus
841 );
842
843 if ( !$allowedByHook ) {
844 // The hook has prevented this change from being saved.
845 if ( $hookStatus->isOK() ) {
846 // Hook returned false but didn't call fatal(); use generic message
847 $hookStatus->fatal( 'edit-hook-aborted' );
848 }
849
850 $this->status = $hookStatus;
851 $this->logger->info( 'Hook prevented page save', [ 'status' => $hookStatus ] );
852 return null;
853 }
854
855 // Provide autosummaries if one is not provided and autosummaries are enabled
856 // XXX: $summary == null seems logical, but the empty string may actually come from the user
857 // XXX: Move this logic out of the storage layer! It does not belong here! Use a callback?
858 if ( $summary->text === '' && $summary->data === null ) {
859 $summary = $this->makeAutoSummary();
860 }
861
862 // Actually create the revision and create/update the page.
863 // Do NOT yet set $this->status!
864 if ( $this->flags & EDIT_UPDATE ) {
865 $status = $this->doModify( $summary );
866 } else {
867 $status = $this->doCreate( $summary );
868 }
869
870 // Promote user to any groups they meet the criteria for
871 DeferredUpdates::addCallableUpdate( function () {
872 $this->userGroupManager->addUserToAutopromoteOnceGroups( $this->author, 'onEdit' );
873 // Also run 'onView' for backwards compatibility
874 $this->userGroupManager->addUserToAutopromoteOnceGroups( $this->author, 'onView' );
875 } );
876
877 // NOTE: set $this->status only after all hooks have been called,
878 // so wasCommitted doesn't return true when called indirectly from a hook handler!
879 $this->status = $status;
880
881 // TODO: replace bad status with Exceptions!
882 return $this->status
883 ? $this->status->getNewRevision()
884 : null;
885 }
886
898 public function updateRevision( int $revId = 0 ) {
899 if ( $this->wasCommitted() ) {
900 throw new RuntimeException(
901 'saveRevision() or updateRevision() has already been called on this PageUpdater!'
902 );
903 }
904
905 // Low-level check
906 if ( $this->getPage()->getDBkey() === '' ) {
907 throw new RuntimeException( 'Something is trying to edit an article with an empty title' );
908 }
909
910 $status = PageUpdateStatus::newGood();
911 $this->checkAllRolesAllowed(
912 $this->slotsUpdate->getModifiedRoles(),
913 $status
914 );
915 $this->checkAllRolesDerived(
916 $this->slotsUpdate->getModifiedRoles(),
917 $status
918 );
919 $this->checkAllRolesDerived(
920 $this->slotsUpdate->getRemovedRoles(),
921 $status
922 );
923
924 if ( $revId === 0 ) {
925 $revision = $this->grabParentRevision();
926 } else {
927 $revision = $this->revisionStore->getRevisionById( $revId, IDBAccessObject::READ_LATEST );
928 }
929 if ( $revision === null ) {
930 $status->fatal( 'edit-gone-missing' );
931 }
932
933 if ( !$status->isOK() ) {
934 $this->status = $status;
935 return;
936 }
937
938 // Make sure the given content is allowed in the respective slots of this page
939 foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
940 $slot = $this->slotsUpdate->getModifiedSlot( $role );
941 $roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
942
943 if ( !$roleHandler->isAllowedModel( $slot->getModel(), $this->getPage() ) ) {
944 $contentHandler = $this->contentHandlerFactory
945 ->getContentHandler( $slot->getModel() );
946 $this->status = PageUpdateStatus::newFatal(
947 'content-not-allowed-here',
948 ContentHandler::getLocalizedName( $contentHandler->getModelID() ),
949 $this->titleFormatter->getPrefixedText( $this->getPage() ),
950 wfMessage( $roleHandler->getNameMessageKey() )
951 // TODO: defer message lookup to caller
952 );
953 return;
954 }
955 }
956
957 // XXX: do we need PST?
958
959 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable revision is checked
960 $this->status = $this->doUpdate( $revision );
961 }
962
968 public function wasCommitted() {
969 return $this->status !== null;
970 }
971
995 public function getStatus(): PageUpdateStatus {
996 if ( !$this->status ) {
997 throw new LogicException(
998 'getStatus() is undefined before saveRevision() or updateRevision() have been called'
999 );
1000 }
1001 return $this->status;
1002 }
1003
1012 public function wasSuccessful() {
1013 return $this->status && $this->status->isOK();
1014 }
1015
1021 public function isNew() {
1022 return $this->status && $this->status->wasPageCreated();
1023 }
1024
1032 public function isUnchanged() {
1033 return !$this->wasRevisionCreated();
1034 }
1035
1041 public function isChange() {
1042 return $this->derivedDataUpdater->isChange();
1043 }
1044
1050 public function preventChange() {
1051 $this->preventChange = true;
1052 return $this;
1053 }
1054
1065 public function wasRevisionCreated(): bool {
1066 return $this->status
1067 && $this->status->wasRevisionCreated();
1068 }
1069
1076 public function getNewRevision() {
1077 return $this->status
1078 ? $this->status->getNewRevision()
1079 : null;
1080 }
1081
1095 private function makeNewRevision(
1096 CommentStoreComment $comment,
1097 PageUpdateStatus $status
1098 ) {
1099 $title = $this->getTitle();
1100 $parent = $this->grabParentRevision();
1101
1102 // XXX: we expect to get a MutableRevisionRecord here, but that's a bit brittle!
1103 // TODO: introduce something like an UnsavedRevisionFactory service instead!
1105 $rev = $this->derivedDataUpdater->getRevision();
1106 '@phan-var MutableRevisionRecord $rev';
1107
1108 // Avoid fatal error when the Title's ID changed, T204793
1109 if (
1110 $rev->getPageId() !== null && $title->exists()
1111 && $rev->getPageId() !== $title->getArticleID()
1112 ) {
1113 $titlePageId = $title->getArticleID();
1114 $revPageId = $rev->getPageId();
1115 $masterPageId = $title->getArticleID( IDBAccessObject::READ_LATEST );
1116
1117 if ( $revPageId === $masterPageId ) {
1118 wfWarn( __METHOD__ . ": Encountered stale Title object: old ID was $titlePageId, "
1119 . "continuing with new ID from primary DB, $masterPageId" );
1120 } else {
1121 throw new InvalidArgumentException(
1122 "Revision inherited page ID $revPageId from its parent, "
1123 . "but the provided Title object belongs to page ID $masterPageId"
1124 );
1125 }
1126 }
1127
1128 if ( $parent ) {
1129 $oldid = $parent->getId();
1130 $rev->setParentId( $oldid );
1131
1132 if ( $title->getArticleID() !== $parent->getPageId() ) {
1133 wfWarn( __METHOD__ . ': Encountered stale Title object with no page ID! '
1134 . 'Using page ID from parent revision: ' . $parent->getPageId() );
1135 }
1136 } else {
1137 $oldid = 0;
1138 }
1139
1140 $rev->setComment( $comment );
1141 $rev->setUser( $this->author );
1142 $rev->setMinorEdit( ( $this->flags & EDIT_MINOR ) > 0 );
1143
1144 foreach ( $rev->getSlots()->getSlots() as $slot ) {
1145 $content = $slot->getContent();
1146
1147 // XXX: We may push this up to the "edit controller" level, see T192777.
1148 $contentHandler = $this->contentHandlerFactory->getContentHandler( $content->getModel() );
1149 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable getId is not null here
1150 $validationParams = new ValidationParams( $this->getPage(), $this->flags, $oldid );
1151 $prepStatus = $contentHandler->validateSave( $content, $validationParams );
1152
1153 // TODO: MCR: record which problem arose in which slot.
1154 $status->merge( $prepStatus );
1155 }
1156
1157 $this->checkAllRequiredRoles(
1158 $rev->getSlotRoles(),
1159 $status
1160 );
1161
1162 return $rev;
1163 }
1164
1172 private function buildEditResult( RevisionRecord $revision, bool $isNew ) {
1173 $this->editResultBuilder->setRevisionRecord( $revision );
1174 $this->editResultBuilder->setIsNew( $isNew );
1175 $this->editResult = $this->editResultBuilder->buildEditResult();
1176 }
1177
1189 private function doUpdate( RevisionRecord $revision ): PageUpdateStatus {
1190 $currentRevision = $this->grabParentRevision();
1191 if ( !$currentRevision ) {
1192 // Article gone missing
1193 return PageUpdateStatus::newFatal( 'edit-gone-missing' );
1194 }
1195
1196 $dbw = $this->dbProvider->getPrimaryDatabase( $this->getWikiId() );
1197 $dbw->startAtomic( __METHOD__ );
1198
1199 $slots = $this->revisionStore->updateSlotsOn( $revision, $this->slotsUpdate, $dbw );
1200
1201 // Return the slots and revision to the caller
1202 $newRevisionRecord = MutableRevisionRecord::newUpdatedRevisionRecord( $revision, $slots );
1203 $status = PageUpdateStatus::newGood( [
1204 'revision-record' => $newRevisionRecord,
1205 'slots' => $slots,
1206 ] );
1207
1208 $isCurrent = $revision->getId( $this->getWikiId() ) ===
1209 $currentRevision->getId( $this->getWikiId() );
1210
1211 if ( $isCurrent ) {
1212 // Update page_touched
1213 $this->getTitle()->invalidateCache( $newRevisionRecord->getTimestamp() );
1214
1215 $this->buildEditResult( $newRevisionRecord, false );
1216
1217 // NOTE: don't trigger a PageLatestRevisionChanged event!
1218 $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1219 $this->prepareDerivedDataUpdater(
1220 $newRevisionRecord,
1221 [],
1222 [
1223 PageLatestRevisionChangedEvent::FLAG_SILENT => true,
1224 PageLatestRevisionChangedEvent::FLAG_IMPLICIT => true,
1225 'emitEvents' => false,
1226 ]
1227 );
1228
1229 $this->scheduleAtomicSectionUpdate(
1230 $dbw,
1231 $wikiPage,
1232 $newRevisionRecord,
1233 $revision->getComment(),
1234 [ 'changed' => false ]
1235 );
1236 }
1237
1238 // Mark the earliest point where the transaction round can be committed in CLI mode.
1239 // We want to make sure that the event was bound to a round of transactions. We also
1240 // want the deferred update to enqueue similarly in both web and CLI modes, in order
1241 // to simplify testing assertions.
1242 $dbw->endAtomic( __METHOD__ );
1243
1244 return $status;
1245 }
1246
1251 private function doModify( CommentStoreComment $summary ): PageUpdateStatus {
1252 $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1253
1254 // Update article, but only if changed.
1255 $status = PageUpdateStatus::newEmpty( false );
1256
1257 $oldRev = $this->grabParentRevision();
1258 $oldid = $oldRev ? $oldRev->getId() : 0;
1259
1260 if ( !$oldRev ) {
1261 // Article gone missing
1262 return $status->fatal( 'edit-gone-missing' );
1263 }
1264
1265 $newRevisionRecord = $this->makeNewRevision(
1266 $summary,
1267 $status
1268 );
1269
1270 if ( !$status->isOK() ) {
1271 return $status;
1272 }
1273
1274 $now = $newRevisionRecord->getTimestamp();
1275
1276 $changed = $this->derivedDataUpdater->isChange();
1277
1278 if ( $changed ) {
1279 if ( $this->forceEmptyRevision ) {
1280 throw new LogicException(
1281 'Content has been changed even though setForceEmptyRevision( true ) was called.'
1282 );
1283 }
1284 if ( $this->preventChange ) {
1285 throw new LogicException(
1286 'Content has been changed even though preventChange() was called.'
1287 );
1288 }
1289 }
1290
1291 // We build the EditResult before the $change if/else branch in order to pass
1292 // the correct $newRevisionRecord to EditResultBuilder. In case this is a null
1293 // edit, $newRevisionRecord will be later overridden to its parent revision, which
1294 // would confuse EditResultBuilder.
1295 if ( !$changed ) {
1296 // This is a null edit, ensure original revision ID is set properly
1297 $this->editResultBuilder->setOriginalRevision( $oldRev );
1298 }
1299 $this->buildEditResult( $newRevisionRecord, false );
1300
1301 $dbw = $this->dbProvider->getPrimaryDatabase( $this->getWikiId() );
1302 $dbw->startAtomic( __METHOD__ );
1303
1304 if ( $changed || $this->forceEmptyRevision ) {
1305 // Get the latest page_latest value while locking it.
1306 // Do a CAS style check to see if it's the same as when this method
1307 // started. If it changed then bail out before touching the DB.
1308 $latestNow = $wikiPage->lockAndGetLatest(); // TODO: move to storage service, pass DB
1309 if ( $latestNow != $oldid ) {
1310 // We don't need to roll back, since we did not modify the database yet.
1311 // XXX: Or do we want to rollback, any transaction started by calling
1312 // code will fail? If we want that, we should probably throw an exception.
1313 $dbw->endAtomic( __METHOD__ );
1314
1315 // Page updated or deleted in the mean time
1316 return $status->fatal( 'edit-conflict' );
1317 }
1318
1319 // At this point we are now committed to returning an OK
1320 // status unless some DB query error or other exception comes up.
1321 // This way callers don't have to call rollback() if $status is bad
1322 // unless they actually try to catch exceptions (which is rare).
1323
1324 // Save revision content and meta-data
1325 $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1326
1327 // Update page_latest and friends to reflect the new revision
1328 // TODO: move to storage service
1329 $wasRedirect = $this->derivedDataUpdater->wasRedirect();
1330 if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, null, $wasRedirect ) ) {
1331 throw new PageUpdateException( 'Failed to update page row to use new revision.' );
1332 }
1333
1334 $editResult = $this->getEditResult();
1335 $tags = $this->computeEffectiveTags();
1336
1337 if ( !$this->updatesSuppressed() ) {
1338 $this->hookRunner->onRevisionFromEditComplete(
1339 $wikiPage,
1340 $newRevisionRecord,
1341 $editResult->getOriginalRevisionId(),
1342 $this->author,
1343 $tags
1344 );
1345 }
1346
1347 $this->prepareDerivedDataUpdater(
1348 $newRevisionRecord,
1349 $tags
1350 );
1351
1352 // Return the new revision to the caller
1353 $status->setNewRevision( $newRevisionRecord );
1354
1355 // Notify the dispatcher of the PageLatestRevisionChangedEvent during the transaction round
1356 $this->emitEvents();
1357 } else {
1358 // T34948: revision ID must be set to page {{REVISIONID}} and
1359 // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
1360 // Since we don't insert a new revision into the database, the least
1361 // error-prone way is to reuse given old revision.
1362 $newRevisionRecord = $oldRev;
1363
1364 $this->prepareDerivedDataUpdater(
1365 $newRevisionRecord,
1366 [],
1367 [ 'changed' => false ]
1368 );
1369
1370 $status->warning( 'edit-no-change' );
1371 // Update page_touched as updateRevisionOn() was not called.
1372 // Other cache updates are managed in WikiPage::onArticleEdit()
1373 // via WikiPage::doEditUpdates().
1374 $this->getTitle()->invalidateCache( $now );
1375
1376 // Notify the dispatcher of the PageLatestRevisionChangedEvent during the transaction round
1377 $this->emitEvents();
1378 }
1379
1380 // Schedule the secondary updates to run after the transaction round commits.
1381 // NOTE: the updates have to be processed before sending the response to the client
1382 // (DeferredUpdates::PRESEND), otherwise the client may already be following the
1383 // HTTP redirect to the standard view before derived data has been created - most
1384 // importantly, before the parser cache has been updated. This would cause the
1385 // content to be parsed a second time, or may cause stale content to be shown.
1386 $this->scheduleAtomicSectionUpdate(
1387 $dbw,
1388 $wikiPage,
1389 $newRevisionRecord,
1390 $summary,
1391 [ 'changed' => $changed, ]
1392 );
1393
1394 // Mark the earliest point where the transaction round can be committed in CLI mode.
1395 // We want to make sure that the event was bound to a round of transactions. We also
1396 // want the deferred update to enqueue similarly in both web and CLI modes, in order
1397 // to simplify testing assertions.
1398 $dbw->endAtomic( __METHOD__ );
1399
1400 return $status;
1401 }
1402
1407 private function doCreate( CommentStoreComment $summary ): PageUpdateStatus {
1408 if ( $this->preventChange ) {
1409 throw new LogicException(
1410 'Content was changed even though preventChange is true.'
1411 );
1412 }
1413 $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1414
1415 if ( !$this->derivedDataUpdater->getSlots()->hasSlot( SlotRecord::MAIN ) ) {
1416 throw new PageUpdateException( 'Must provide a main slot when creating a page!' );
1417 }
1418
1419 $status = PageUpdateStatus::newEmpty( true );
1420
1421 $newRevisionRecord = $this->makeNewRevision(
1422 $summary,
1423 $status
1424 );
1425
1426 if ( !$status->isOK() ) {
1427 return $status;
1428 }
1429
1430 $this->buildEditResult( $newRevisionRecord, true );
1431 $now = $newRevisionRecord->getTimestamp();
1432
1433 $dbw = $this->dbProvider->getPrimaryDatabase( $this->getWikiId() );
1434 $dbw->startAtomic( __METHOD__ );
1435
1436 // Add the page record unless one already exists for the title
1437 // TODO: move to storage service
1438 $newid = $wikiPage->insertOn( $dbw );
1439 if ( $newid === false ) {
1440 $dbw->endAtomic( __METHOD__ );
1441 return $status->fatal( 'edit-already-exists' );
1442 }
1443
1444 // At this point we are now committed to returning an OK
1445 // status unless some DB query error or other exception comes up.
1446 // This way callers don't have to call rollback() if $status is bad
1447 // unless they actually try to catch exceptions (which is rare).
1448 $newRevisionRecord->setPageId( $newid );
1449
1450 // Save the revision text...
1451 $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1452
1453 // Update the page record with revision data
1454 // TODO: move to storage service
1455 if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, 0, false ) ) {
1456 throw new PageUpdateException( 'Failed to update page row to use new revision.' );
1457 }
1458
1459 $tags = $this->computeEffectiveTags();
1460 if ( !$this->updatesSuppressed() ) {
1461 $this->hookRunner->onRevisionFromEditComplete(
1462 $wikiPage, $newRevisionRecord, false, $this->author, $tags
1463 );
1464 }
1465
1466 if ( $this->usePageCreationLog ) {
1467 // Log the page creation
1468 // @TODO: Do we want a 'recreate' action?
1469 $logEntry = new ManualLogEntry( 'create', 'create' );
1470 $logEntry->setPerformer( $this->author );
1471 $logEntry->setTarget( $this->getPage() );
1472 $logEntry->setComment( $summary->text );
1473 $logEntry->setTimestamp( $now );
1474 $logEntry->setAssociatedRevId( $newRevisionRecord->getId() );
1475 $logEntry->insert();
1476 // Note that we don't publish page creation events to recentchanges
1477 // (i.e. $logEntry->publish()) since this would create duplicate entries,
1478 // one for the edit and one for the page creation.
1479 }
1480
1481 $this->prepareDerivedDataUpdater(
1482 $newRevisionRecord,
1483 $tags
1484 );
1485
1486 // Return the new revision to the caller
1487 $status->setNewRevision( $newRevisionRecord );
1488
1489 // Notify the dispatcher of the PageLatestRevisionChangedEvent during the transaction round
1490 $this->emitEvents();
1491
1492 // Schedule the secondary updates to run after the transaction round commits
1493 $this->scheduleAtomicSectionUpdate(
1494 $dbw,
1495 $wikiPage,
1496 $newRevisionRecord,
1497 $summary,
1498 [ 'created' => true ]
1499 );
1500
1501 // Mark the earliest point where the transaction round can be committed in CLI mode.
1502 // We want to make sure that the event was bound to a round of transactions. We also
1503 // want the deferred update to enqueue similarly in both web and CLI modes, in order
1504 // to simplify testing assertions.
1505 $dbw->endAtomic( __METHOD__ );
1506
1507 return $status;
1508 }
1509
1510 private function prepareDerivedDataUpdater(
1511 RevisionRecord $newRevisionRecord,
1512 array $tags,
1513 array $hintOverrides = []
1514 ) {
1515 static $flagMap = [
1516 EDIT_SILENT => PageLatestRevisionChangedEvent::FLAG_SILENT,
1517 EDIT_FORCE_BOT => PageLatestRevisionChangedEvent::FLAG_BOT,
1518 EDIT_IMPLICIT => PageLatestRevisionChangedEvent::FLAG_IMPLICIT,
1519 ];
1520
1521 $hints = $this->hints;
1522 foreach ( $flagMap as $bit => $name ) {
1523 $hints[$name] = ( $this->flags & $bit ) === $bit;
1524 }
1525
1526 $hints += PageLatestRevisionChangedEvent::DEFAULT_FLAGS;
1527 $hints = $hintOverrides + $hints;
1528
1529 // set debug data
1530 $hints['causeAction'] = 'edit-page';
1531 $hints['causeAgent'] = $this->author->getName();
1532
1533 $editResult = $this->getEditResult();
1534 $hints['editResult'] = $editResult;
1535
1536 // Prepare to update links tables, site stats, etc.
1537 $hints['rcPatrolStatus'] = $this->rcPatrolStatus;
1538 $hints['tags'] = $tags;
1539
1540 $this->derivedDataUpdater->setPerformer( $this->author );
1541 $this->derivedDataUpdater->prepareUpdate( $newRevisionRecord, $hints );
1542 }
1543
1544 private function updatesSuppressed(): bool {
1545 return $this->hints['suppressDerivedDataUpdates'] ?? false;
1546 }
1547
1548 private function emitEvents(): void {
1549 if ( $this->updatesSuppressed() ) {
1550 return;
1551 }
1552
1553 $this->derivedDataUpdater->emitEvents();
1554 }
1555
1556 private function scheduleAtomicSectionUpdate(
1557 IDatabase $dbw,
1558 WikiPage $wikiPage,
1559 RevisionRecord $newRevisionRecord,
1560 CommentStoreComment $summary,
1561 array $hints = []
1562 ): void {
1563 if ( $this->updatesSuppressed() ) {
1564 return;
1565 }
1566
1567 DeferredUpdates::addUpdate(
1568 $this->getAtomicSectionUpdate(
1569 $dbw,
1570 $wikiPage,
1571 $newRevisionRecord,
1572 $summary,
1573 $hints
1574 ),
1575 DeferredUpdates::PRESEND
1576 );
1577 }
1578
1579 private function getAtomicSectionUpdate(
1580 IDatabase $dbw,
1581 WikiPage $wikiPage,
1582 RevisionRecord $newRevisionRecord,
1583 CommentStoreComment $summary,
1584 array $hints = []
1585 ): AtomicSectionUpdate {
1586 return new AtomicSectionUpdate(
1587 $dbw,
1588 __METHOD__,
1589 function () use (
1590 $wikiPage, $newRevisionRecord,
1591 $summary, $hints
1592 ) {
1593 $this->derivedDataUpdater->doUpdates();
1594
1595 $created = $hints['created'] ?? false;
1596 $this->flags |= ( $created ? EDIT_NEW : EDIT_UPDATE );
1597
1598 // PageSaveComplete replaced old PageContentInsertComplete and
1599 // PageContentSaveComplete hooks since 1.35
1600 $this->hookRunner->onPageSaveComplete(
1601 $wikiPage,
1602 $this->author,
1603 $summary->text,
1604 $this->flags,
1605 $newRevisionRecord,
1606 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Not null already checked
1607 $this->getEditResult()
1608 );
1609 }
1610 );
1611 }
1612
1616 private function getRequiredSlotRoles() {
1617 return $this->slotRoleRegistry->getRequiredRoles( $this->getPage() );
1618 }
1619
1623 private function getAllowedSlotRoles() {
1624 return $this->slotRoleRegistry->getAllowedRoles( $this->getPage() );
1625 }
1626
1627 private function ensureRoleAllowed( string $role ) {
1628 $allowedRoles = $this->getAllowedSlotRoles();
1629 if ( !in_array( $role, $allowedRoles ) ) {
1630 throw new PageUpdateException( "Slot role `$role` is not allowed." );
1631 }
1632 }
1633
1634 private function ensureRoleNotRequired( string $role ) {
1635 $requiredRoles = $this->getRequiredSlotRoles();
1636 if ( in_array( $role, $requiredRoles ) ) {
1637 throw new PageUpdateException( "Slot role `$role` is required." );
1638 }
1639 }
1640
1641 private function checkAllRolesAllowed( array $roles, PageUpdateStatus $status ) {
1642 $allowedRoles = $this->getAllowedSlotRoles();
1643
1644 $forbidden = array_diff( $roles, $allowedRoles );
1645 if ( $forbidden ) {
1646 $status->error(
1647 'edit-slots-cannot-add',
1648 count( $forbidden ),
1649 implode( ', ', $forbidden )
1650 );
1651 }
1652 }
1653
1654 private function checkAllRolesDerived( array $roles, PageUpdateStatus $status ) {
1655 $notDerived = array_filter(
1656 $roles,
1657 function ( $role ) {
1658 return !$this->slotRoleRegistry->getRoleHandler( $role )->isDerived();
1659 }
1660 );
1661 if ( $notDerived ) {
1662 $status->error(
1663 'edit-slots-not-derived',
1664 count( $notDerived ),
1665 implode( ', ', $notDerived )
1666 );
1667 }
1668 }
1669
1670 private function checkNoRolesRequired( array $roles, PageUpdateStatus $status ) {
1671 $requiredRoles = $this->getRequiredSlotRoles();
1672
1673 $needed = array_diff( $roles, $requiredRoles );
1674 if ( $needed ) {
1675 $status->error(
1676 'edit-slots-cannot-remove',
1677 count( $needed ),
1678 implode( ', ', $needed )
1679 );
1680 }
1681 }
1682
1683 private function checkAllRequiredRoles( array $roles, PageUpdateStatus $status ) {
1684 $requiredRoles = $this->getRequiredSlotRoles();
1685
1686 $missing = array_diff( $requiredRoles, $roles );
1687 if ( $missing ) {
1688 $status->error(
1689 'edit-slots-missing',
1690 count( $missing ),
1691 implode( ', ', $missing )
1692 );
1693 }
1694 }
1695
1696}
const EDIT_FORCE_BOT
Mark the edit a "bot" edit regardless of user rights.
Definition Defines.php:129
const EDIT_INTERNAL
Signal that the page retrieve/save cycle happened entirely in this request.
Definition Defines.php:138
const EDIT_UPDATE
Article is assumed to be pre-existing, fail if it doesn't exist.
Definition Defines.php:117
const EDIT_IMPLICIT
The edit is a side effect and does not represent an active user contribution.
Definition Defines.php:141
const EDIT_SILENT
Do not notify other users (e.g.
Definition Defines.php:123
const EDIT_MINOR
Mark this edit minor, if the user is allowed to do so.
Definition Defines.php:120
const EDIT_AUTOSUMMARY
Fill in blank summaries with generated text where possible.
Definition Defines.php:135
const EDIT_NEW
Article is assumed to be non-existent, fail if it exists.
Definition Defines.php:114
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:69
Value object for a comment stored by CommentStore.
A class for passing options to services.
Base class for content handling.
Deferrable Update for closure/callback updates via IDatabase::doAtomicSection()
Defer callable updates to run later in the PHP process.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Class for creating new log entries and inserting them into the database.
A class containing constants representing the names of configuration variables.
const UseRCPatrol
Name constant for the UseRCPatrol setting, for use with Config::get()
const ManualRevertSearchRadius
Name constant for the ManualRevertSearchRadius setting, for use with Config::get()
Domain event representing a change to the page's latest revision.
Service for creating WikiPage objects.
newFromTitle(PageReference $pageReference)
Create a WikiPage object from a title.
Base representation for an editable wiki page.
Definition WikiPage.php:82
Utility class for creating and reading rows in the recentchanges table.
Exception representing a failure to look up a revision.
Page revision base class.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
getRole()
Returns the role of the slot.
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
A handle for managing updates for derived page data on edit, import, purge, etc.
Builder class for the EditResult object.
Object for storing information about the effects of an edit.
Status object representing the outcome of a page update.
Controller-like object for creating and updating pages by creating new revisions.
getStatus()
The Status object indicating whether saveRevision() was successful.
setCause(string $cause)
Set the cause of the update.
inheritSlot(SlotRecord $originalSlot)
Explicitly inherit a slot from some earlier revision.
wasSuccessful()
Whether saveRevision() completed successfully.
getEditResult()
Returns the EditResult associated with this PageUpdater.
const CONSTRUCTOR_OPTIONS
Options that have to be present in the ServiceOptions object passed to the constructor.
isNew()
Whether saveRevision() was called and created a new page.
setFlags(int $flags)
Sets any flags to use when performing the update.
isUnchanged()
Whether saveRevision() did create a revision because the content didn't change: (null-edit).
updateAuthor(UserIdentity $author)
After creation of the user during the save process, update the stored UserIdentity.
__construct(private UserIdentity $author, private readonly PageIdentity $pageIdentity, private readonly DerivedPageDataUpdater $derivedDataUpdater, private readonly IConnectionProvider $dbProvider, private readonly RevisionStore $revisionStore, private readonly SlotRoleRegistry $slotRoleRegistry, private readonly IContentHandlerFactory $contentHandlerFactory, private readonly HookContainer $hookContainer, private readonly UserGroupManager $userGroupManager, private readonly TitleFormatter $titleFormatter, private readonly ServiceOptions $serviceOptions, private readonly array $softwareTags, private readonly LoggerInterface $logger, WikiPageFactory $wikiPageFactory,)
updateRevision(int $revId=0)
Updates derived slots of an existing article.
prepareUpdate(int $flags=0)
Prepare the update.
getNewRevision()
The new revision created by saveRevision(), or null if saveRevision() has not yet been called,...
saveDummyRevision( $summary, int $flags=0)
Creates a dummy revision that does not change the content.
markAsRevert(int $revertMethod, int $newestRevertedRevId, ?int $revertAfterRevId=null)
Marks this edit as a revert and applies relevant information.
setUsePageCreationLog( $use)
Whether to create a log entry for new page creations.
removeSlot( $role)
Removes the slot with the given role.
setOriginalRevisionId( $originalRevId)
Sets the ID of an earlier revision that is being repeated or restored by this update.
addTags(array $tags)
Sets tags to apply to this update.
setUseAutomaticEditSummaries( $useAutomaticEditSummaries)
Can be used to enable or disable automatic summaries that are applied to certain kinds of changes,...
grabParentRevision()
Returns the revision that was the page's latest revision when grabParentRevision() was first called.
hasEditConflict( $expectedParentRevision)
Checks whether this update conflicts with another update performed between the client loading data to...
setRcPatrolStatus( $status)
Sets the "patrolled" status of the edit.
addSoftwareTag(string $tag)
Sets software tag to this update.
saveRevision( $summary, int $flags=0)
Change an existing article or create a new article.
wasRevisionCreated()
Whether saveRevision() did create a revision.
addTag(string $tag)
Sets a tag to apply to this update.
wasCommitted()
Whether saveRevision() has been called on this instance.
setForceEmptyRevision(bool $forceEmptyRevision)
Set whether null-edits should create a revision.
isChange()
Whether the prepared edit is a change compared to the previous revision.
getExplicitTags()
Returns the list of tags set using the addTag() method.
preventChange()
Disable new revision creation, throwing an exception if it is attempted.
getPage()
Get the page we're currently updating.
setSlot(SlotRecord $slot)
Set the new slot for the given slot role.
setContent( $role, Content $content)
Set the new content for the given slot role.
Value object representing a modification of revision slots.
A title formatter service for MediaWiki.
Represents a title within MediaWiki.
Definition Title.php:69
Manage user group memberships.
isOK()
Returns whether the operation completed.
fatal( $message,... $parameters)
Add an error and set OK to false, indicating that the operation as a whole was fatal.
merge( $other, $overwriteValue=false)
Merge another status object into this one.
warning( $message,... $parameters)
Add a new warning.
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, 'TrackMediaRequestProvenance'=> 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, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], '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,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], '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, 'createwithcontentmodel' => 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, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], ], '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, 'ApiClientErrorSampleRate' => 1.0, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, ], '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, 'GenerateReqIDFormat' => 'rand24', 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, 'ParserOptionsLogUnsafeSampleRate' => 0, ], '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', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', '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', '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', ], 'GenerateReqIDFormat' => 'string', 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', ], '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.', ],]
Content objects represent page content, e.g.
Definition Content.php:28
Interface for objects (potentially) representing an editable wiki page.
Constants for representing well known causes for page updates.
An object representing a page update during an edit.
Interface for objects representing user identity.
Provide primary and replica IDatabase connections.
Interface for database access objects.
Interface to a relational database.
Definition IDatabase.php:31