MediaWiki master
SqlBagOStuff.php
Go to the documentation of this file.
1<?php
10namespace MediaWiki\ObjectCache;
11
12use Exception;
13use InvalidArgumentException;
16use stdClass;
17use UnexpectedValueException;
32use Wikimedia\ScopedCallback;
33use Wikimedia\Timestamp\ConvertibleTimestamp;
34use Wikimedia\Timestamp\TimestampFormat as TS;
35
53 protected $loadBalancer;
55 protected $dbDomain;
57 protected $useLB = false;
58
60 protected $serverInfos = [];
62 protected $serverTags = [];
64 protected $lastGarbageCollect = 0;
66 protected $purgePeriod = 10;
68 protected $purgeLimit = 100;
70 protected $numTableShards = 1;
72 protected $writeBatchSize = 100;
74 protected $tableName = 'objectcache';
75
77 protected $conns;
79 protected $connFailureTimes = [];
81 protected $connFailureErrors = [];
82
84 private $hasZlib;
85
87 private $dataRedundancy;
88
90 private const SAFE_CLOCK_BOUND_SEC = 15;
92 private const SAFE_PURGE_DELAY_SEC = 3600;
94 private const TOMB_SERIAL = '';
96 private const TOMB_EXPTIME = -self::SAFE_CLOCK_BOUND_SEC;
98 private const GC_DELAY_SEC = 1;
99
100 private const BLOB_VALUE = 0;
101 private const BLOB_EXPIRY = 1;
102 private const BLOB_CASTOKEN = 2;
103
110 private const INF_TIMESTAMP_PLACEHOLDER = '99991231235959';
111
139 public function __construct( $params ) {
140 parent::__construct( $params );
141
142 if ( isset( $params['servers'] ) || isset( $params['server'] ) ) {
143 // Configuration uses a direct list of servers.
144 // Object data is horizontally partitioned via key hash.
145 $index = 0;
146 foreach ( ( $params['servers'] ?? [ $params['server'] ] ) as $tag => $info ) {
147 $this->serverInfos[$index] = $info;
148 // Allow integer-indexes arrays for b/c
149 $this->serverTags[$index] = is_string( $tag ) ? $tag : "#$index";
150 ++$index;
151 }
152 } elseif ( isset( $params['loadBalancerCallback'] ) ) {
153 $this->loadBalancerCallback = $params['loadBalancerCallback'];
154 if ( !isset( $params['dbDomain'] ) ) {
155 throw new InvalidArgumentException(
156 __METHOD__ . ": 'dbDomain' is required if 'loadBalancerCallback' is given"
157 );
158 }
159 $this->dbDomain = $params['dbDomain'];
160 $this->useLB = true;
161 } else {
162 throw new InvalidArgumentException(
163 __METHOD__ . " requires 'server', 'servers', or 'loadBalancerCallback'"
164 );
165 }
166
167 $this->purgePeriod = intval( $params['purgePeriod'] ?? $this->purgePeriod );
168 $this->purgeLimit = intval( $params['purgeLimit'] ?? $this->purgeLimit );
169 $this->tableName = $params['tableName'] ?? $this->tableName;
170 $this->numTableShards = intval( $params['shards'] ?? $this->numTableShards );
171 $this->writeBatchSize = intval( $params['writeBatchSize'] ?? $this->writeBatchSize );
172 $this->dataRedundancy = min( intval( $params['dataRedundancy'] ?? 1 ), count( $this->serverTags ) );
173
175
176 $this->hasZlib = extension_loaded( 'zlib' );
177 }
178
180 protected function doGet( $key, $flags = 0, &$casToken = null ) {
181 $getToken = ( $casToken === self::PASS_BY_REF );
182 $casToken = null;
183
184 $data = $this->fetchBlobs( [ $key ], $getToken )[$key];
185 if ( $data ) {
186 $result = $this->unserialize( $data[self::BLOB_VALUE] );
187 if ( $getToken && $result !== false ) {
188 $casToken = $data[self::BLOB_CASTOKEN];
189 }
190 $valueSize = strlen( $data[self::BLOB_VALUE] );
191 } else {
192 $result = false;
193 $valueSize = false;
194 }
195
196 $this->updateOpStats( self::METRIC_OP_GET, [ $key => [ 0, $valueSize ] ] );
197
198 return $result;
199 }
200
202 protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
203 $mtime = $this->getCurrentTime();
204
205 return $this->modifyBlobs(
206 $this->modifyTableSpecificBlobsForSet( ... ),
207 $mtime,
208 [ $key => [ $value, $exptime ] ]
209 );
210 }
211
213 protected function doDelete( $key, $flags = 0 ) {
214 $mtime = $this->getCurrentTime();
215
216 return $this->modifyBlobs(
217 $this->modifyTableSpecificBlobsForDelete( ... ),
218 $mtime,
219 [ $key => [] ]
220 );
221 }
222
224 protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
225 $mtime = $this->newLockingWriteSectionModificationTimestamp( $key, $scope );
226 if ( $mtime === null ) {
227 // Timeout or I/O error during lock acquisition
228 return false;
229 }
230
231 return $this->modifyBlobs(
232 $this->modifyTableSpecificBlobsForAdd( ... ),
233 $mtime,
234 [ $key => [ $value, $exptime ] ]
235 );
236 }
237
239 protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
240 $mtime = $this->newLockingWriteSectionModificationTimestamp( $key, $scope );
241 if ( $mtime === null ) {
242 // Timeout or I/O error during lock acquisition
243 return false;
244 }
245
246 return $this->modifyBlobs(
247 $this->modifyTableSpecificBlobsForCas( ... ),
248 $mtime,
249 [ $key => [ $value, $exptime, $casToken ] ]
250 );
251 }
252
254 protected function doChangeTTL( $key, $exptime, $flags ) {
255 $mtime = $this->getCurrentTime();
256
257 return $this->modifyBlobs(
258 $this->modifyTableSpecificBlobsForChangeTTL( ... ),
259 $mtime,
260 [ $key => [ $exptime ] ]
261 );
262 }
263
265 protected function doIncrWithInit( $key, $exptime, $step, $init, $flags ) {
266 $mtime = $this->getCurrentTime();
267
268 if ( $flags & self::WRITE_BACKGROUND ) {
269 $callback = $this->modifyTableSpecificBlobsForIncrInitAsync( ... );
270 } else {
271 $callback = $this->modifyTableSpecificBlobsForIncrInit( ... );
272 }
273
274 $result = $this->modifyBlobs(
275 $callback,
276 $mtime,
277 [ $key => [ $step, $init, $exptime ] ],
278 $resByKey
279 ) ? $resByKey[$key] : false;
280
281 return $result;
282 }
283
285 protected function doGetMulti( array $keys, $flags = 0 ) {
286 $result = [];
287 $valueSizeByKey = [];
288
289 $dataByKey = $this->fetchBlobs( $keys );
290 foreach ( $keys as $key ) {
291 $data = $dataByKey[$key];
292 if ( $data ) {
293 $serialValue = $data[self::BLOB_VALUE];
294 $value = $this->unserialize( $serialValue );
295 if ( $value !== false ) {
296 $result[$key] = $value;
297 }
298 $valueSize = strlen( $serialValue );
299 } else {
300 $valueSize = false;
301 }
302 $valueSizeByKey[$key] = [ 0, $valueSize ];
303 }
304
305 $this->updateOpStats( self::METRIC_OP_GET, $valueSizeByKey );
306
307 return $result;
308 }
309
311 protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
312 $mtime = $this->getCurrentTime();
313
314 return $this->modifyBlobs(
315 $this->modifyTableSpecificBlobsForSet( ... ),
316 $mtime,
317 array_map(
318 static function ( $value ) use ( $exptime ) {
319 return [ $value, $exptime ];
320 },
321 $data
322 )
323 );
324 }
325
327 protected function doDeleteMulti( array $keys, $flags = 0 ) {
328 $mtime = $this->getCurrentTime();
329
330 return $this->modifyBlobs(
331 $this->modifyTableSpecificBlobsForDelete( ... ),
332 $mtime,
333 array_fill_keys( $keys, [] )
334 );
335 }
336
338 public function doChangeTTLMulti( array $keys, $exptime, $flags = 0 ) {
339 $mtime = $this->getCurrentTime();
340
341 return $this->modifyBlobs(
342 $this->modifyTableSpecificBlobsForChangeTTL( ... ),
343 $mtime,
344 array_fill_keys( $keys, [ $exptime ] )
345 );
346 }
347
356 private function getConnection( $shardIndex ) {
357 if ( $this->useLB ) {
358 return $this->getConnectionViaLoadBalancer();
359 }
360
361 // Don't keep timing out trying to connect if the server is down
362 if (
363 isset( $this->connFailureErrors[$shardIndex] ) &&
364 ( $this->getCurrentTime() - $this->connFailureTimes[$shardIndex] ) < 60
365 ) {
366 throw $this->connFailureErrors[$shardIndex];
367 }
368
369 if ( isset( $this->serverInfos[$shardIndex] ) ) {
370 $server = $this->serverInfos[$shardIndex];
371 $conn = $this->getConnectionFromServerInfo( $shardIndex, $server );
372 } else {
373 throw new UnexpectedValueException( "Invalid server index #$shardIndex" );
374 }
375
376 return $conn;
377 }
378
386 private function getShardIndexesForKey( $key, $fallback = false ) {
387 if ( $this->useLB || count( $this->serverTags ) === 1 ) {
388 return [ 0 ];
389 }
390
391 // Pick the same shard for sister keys
392 // Using the same hash stop as mc-router for consistency
393 [ $key ] = explode( '|#|', $key, 2 );
394
395 $sortedServers = $this->serverTags;
396 // shuffle the servers based on hashing of the keys
397 ArrayUtils::consistentHashSort( $sortedServers, $key );
398 $shardIndexes = array_keys( $sortedServers );
399 return array_slice(
400 $shardIndexes,
401 $fallback && $this->dataRedundancy === 1 ? 1 : 0,
402 $this->dataRedundancy
403 );
404 }
405
411 private function getTableNameForKey( $key ) {
412 if ( $this->numTableShards > 1 ) {
413 // Pick the same shard for sister keys
414 // Using the same hash stop as mc-router for consistency
415 [ $key ] = explode( '|#|', $key, 2 );
416
417 $hash = hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
418 $tableIndex = $hash % $this->numTableShards;
419 } else {
420 $tableIndex = null;
421 }
422
423 return $this->getTableNameByShard( $tableIndex );
424 }
425
431 private function getTableNameByShard( $index ) {
432 if ( $index !== null && $this->numTableShards > 1 ) {
433 $decimals = strlen( (string)( $this->numTableShards - 1 ) );
434
435 return $this->tableName . sprintf( "%0{$decimals}d", $index );
436 }
437
438 return $this->tableName;
439 }
440
447 private function fetchBlobs( array $keys, bool $getCasToken = false, $fallback = false ) {
448 if ( $fallback && count( $this->serverTags ) > 1 && $this->dataRedundancy > 1 ) {
449 // fallback doesn't work with data redundancy
450 return [];
451 }
452
454 $silenceScope = $this->silenceTransactionProfiler();
455
456 // Initialize order-preserved per-key results; set values for live keys below
458 $dataByKey = array_fill_keys( $keys, null );
460 $dataByKeyAndShard = [];
461
462 $readTime = (int)$this->getCurrentTime();
463 $keysByTableByShard = [];
464 foreach ( $keys as $key ) {
465 $partitionTable = $this->getTableNameForKey( $key );
466 $shardIndexes = $this->getShardIndexesForKey( $key, $fallback );
467 foreach ( $shardIndexes as $shardIndex ) {
468 $keysByTableByShard[$shardIndex][$partitionTable][] = $key;
469 }
470 }
471 $fallbackResult = [];
472
473 foreach ( $keysByTableByShard as $shardIndex => $serverKeys ) {
474 try {
475 $db = $this->getConnection( $shardIndex );
476 foreach ( $serverKeys as $partitionTable => $tableKeys ) {
477 $res = $db->newSelectQueryBuilder()
478 ->select(
479 $getCasToken
480 ? $this->addCasTokenFields( $db, [ 'keyname', 'value', 'exptime' ] )
481 : [ 'keyname', 'value', 'exptime' ] )
482 ->from( $partitionTable )
483 ->where( $this->buildExistenceConditions( $db, $tableKeys, $readTime ) )
484 ->caller( __METHOD__ )
485 ->fetchResultSet();
486 foreach ( $res as $row ) {
487 $row->shardIndex = $shardIndex;
488 $row->tableName = $partitionTable;
489 $dataByKeyAndShard[$row->keyname][$shardIndex] = $row;
490 }
491 }
492 } catch ( DBError $e ) {
493 if ( $fallback ) {
494 $this->handleDBError( $e, $shardIndex );
495 } else {
496 $fallbackResult += $this->fetchBlobs(
497 array_merge( ...array_values( $serverKeys ) ),
498 $getCasToken,
499 true
500 );
501 }
502
503 }
504 }
505 foreach ( $keys as $key ) {
506 $row = null;
507 foreach ( $dataByKeyAndShard[$key] ?? [] as $r ) {
508 if ( !$row || $r->exptime > $row->exptime ) {
509 $row = $r;
510 }
511 }
512 if ( !$row ) {
513 continue;
514 }
515
516 $this->debug( __METHOD__ . ": retrieved $key; expiry time is {$row->exptime}" );
517 try {
518 $db = $this->getConnection( $row->shardIndex );
519 $dataByKey[$key] = [
520 self::BLOB_VALUE => $this->dbDecodeSerialValue( $db, $row->value ),
521 self::BLOB_EXPIRY => $this->decodeDbExpiry( $db, $row->exptime ),
522 self::BLOB_CASTOKEN => $getCasToken
523 ? $this->getCasTokenFromRow( $db, $row )
524 : null
525 ];
526 } catch ( DBQueryError $e ) {
527 $this->handleDBError( $e, $row->shardIndex );
528 }
529 }
530
531 return array_merge( $dataByKey, $fallbackResult );
532 }
533
548 private function modifyBlobs(
549 callable $tableWriteCallback,
550 float $mtime,
551 array $argsByKey,
552 &$resByKey = [],
553 $fallback = false
554 ) {
555 if ( $fallback && count( $this->serverTags ) > 1 && $this->dataRedundancy > 1 ) {
556 // fallback doesn't work with data redundancy
557 return false;
558 }
559 // Initialize order-preserved per-key results; callbacks mark successful results
560 $resByKey = array_fill_keys( array_keys( $argsByKey ), false );
561
563 $silenceScope = $this->silenceTransactionProfiler();
564
565 $argsByKeyByTableByShard = [];
566 foreach ( $argsByKey as $key => $args ) {
567 $partitionTable = $this->getTableNameForKey( $key );
568 $shardIndexes = $this->getShardIndexesForKey( $key, $fallback );
569 foreach ( $shardIndexes as $shardIndex ) {
570 $argsByKeyByTableByShard[$shardIndex][$partitionTable][$key] = $args;
571 }
572 }
573
574 $shardIndexesAffected = [];
575 foreach ( $argsByKeyByTableByShard as $shardIndex => $argsByKeyByTables ) {
576 foreach ( $argsByKeyByTables as $table => $ptKeyArgs ) {
577 try {
578 $db = $this->getConnection( $shardIndex );
579 $shardIndexesAffected[] = $shardIndex;
580 $tableWriteCallback( $db, $table, $mtime, $ptKeyArgs, $resByKey );
581 } catch ( DBError $e ) {
582 if ( $fallback ) {
583 $this->handleDBError( $e, $shardIndex );
584 continue;
585 } else {
586 $this->modifyBlobs( $tableWriteCallback, $mtime, $ptKeyArgs, $resByKey, true );
587 }
588 }
589 }
590 }
591
592 $success = !in_array( false, $resByKey, true );
593
594 foreach ( $shardIndexesAffected as $shardIndex ) {
595 try {
596 if (
597 // Random purging is enabled
598 $this->purgePeriod >= 1 &&
599 // Only purge on one in every $this->purgePeriod writes
600 mt_rand( 1, $this->purgePeriod ) == 1 &&
601 // Avoid repeating the delete within a few seconds
602 ( $this->getCurrentTime() - $this->lastGarbageCollect ) > self::GC_DELAY_SEC
603 ) {
604 $this->garbageCollect( $shardIndex );
605 }
606 } catch ( DBError $e ) {
607 $this->handleDBError( $e, $shardIndex );
608 }
609 }
610
611 return $success;
612 }
613
624 private function modifyTableSpecificBlobsForSet(
625 IDatabase $db,
626 string $ptable,
627 float $mtime,
628 array $argsByKey,
629 array &$resByKey
630 ) {
631 $valueSizesByKey = [];
632
633 $rows = [];
634 foreach ( $argsByKey as $key => [ $value, $exptime ] ) {
635 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
636 $serialValue = $this->getSerialized( $value, $key );
637 $rows[] = $this->buildUpsertRow( $db, $key, $serialValue, $expiry );
638
639 $valueSizesByKey[$key] = [ strlen( $serialValue ), 0 ];
640 }
641
642 // T288998: use REPLACE, if possible, to avoid cluttering the binlogs
643 $db->newReplaceQueryBuilder()
644 ->replaceInto( $ptable )
645 ->rows( $rows )
646 ->uniqueIndexFields( [ 'keyname' ] )
647 ->caller( __METHOD__ )->execute();
648
649 foreach ( $argsByKey as $key => $unused ) {
650 $resByKey[$key] = true;
651 }
652
653 $this->updateOpStats( self::METRIC_OP_SET, $valueSizesByKey );
654 }
655
667 private function modifyTableSpecificBlobsForDelete(
668 IDatabase $db,
669 string $ptable,
670 float $mtime,
671 array $argsByKey,
672 array &$resByKey
673 ) {
674 // Just purge the keys since there is only one primary (e.g. "source of truth")
675 $db->newDeleteQueryBuilder()
676 ->deleteFrom( $ptable )
677 ->where( [ 'keyname' => array_keys( $argsByKey ) ] )
678 ->caller( __METHOD__ )->execute();
679
680 foreach ( $argsByKey as $key => $arg ) {
681 $resByKey[$key] = true;
682 }
683
684 $this->updateOpStats( self::METRIC_OP_DELETE, array_keys( $argsByKey ) );
685 }
686
702 private function modifyTableSpecificBlobsForAdd(
703 IDatabase $db,
704 string $ptable,
705 float $mtime,
706 array $argsByKey,
707 array &$resByKey
708 ) {
709 $valueSizesByKey = [];
710
711 // This check must happen outside the write query to respect eventual consistency
712 $existingKeys = $db->newSelectQueryBuilder()
713 ->select( 'keyname' )
714 ->from( $ptable )
715 ->where( $this->buildExistenceConditions( $db, array_keys( $argsByKey ), (int)$mtime ) )
716 ->caller( __METHOD__ )
717 ->fetchFieldValues();
718 $existingByKey = array_fill_keys( $existingKeys, true );
719
720 $rows = [];
721 foreach ( $argsByKey as $key => [ $value, $exptime ] ) {
722 if ( isset( $existingByKey[$key] ) ) {
723 $this->logger->debug( __METHOD__ . ": $key already exists" );
724 continue;
725 }
726
727 $serialValue = $this->getSerialized( $value, $key );
728 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
729 $valueSizesByKey[$key] = [ strlen( $serialValue ), 0 ];
730 $rows[] = $this->buildUpsertRow( $db, $key, $serialValue, $expiry );
731 }
732 if ( !$rows ) {
733 return;
734 }
735 $db->newInsertQueryBuilder()
736 ->insertInto( $ptable )
737 ->rows( $rows )
738 ->onDuplicateKeyUpdate()
739 ->uniqueIndexFields( [ 'keyname' ] )
740 ->set( $this->buildMultiUpsertSetForOverwrite( $db ) )
741 ->caller( __METHOD__ )->execute();
742
743 foreach ( $argsByKey as $key => $unused ) {
744 $resByKey[$key] = !isset( $existingByKey[$key] );
745 }
746
747 $this->updateOpStats( self::METRIC_OP_ADD, $valueSizesByKey );
748 }
749
765 private function modifyTableSpecificBlobsForCas(
766 IDatabase $db,
767 string $ptable,
768 float $mtime,
769 array $argsByKey,
770 array &$resByKey
771 ) {
772 $valueSizesByKey = [];
773
774 // This check must happen outside the write query to respect eventual consistency
775 $res = $db->newSelectQueryBuilder()
776 ->select( $this->addCasTokenFields( $db, [ 'keyname' ] ) )
777 ->from( $ptable )
778 ->where( $this->buildExistenceConditions( $db, array_keys( $argsByKey ), (int)$mtime ) )
779 ->caller( __METHOD__ )
780 ->fetchResultSet();
781
782 $curTokensByKey = [];
783 foreach ( $res as $row ) {
784 $curTokensByKey[$row->keyname] = $this->getCasTokenFromRow( $db, $row );
785 }
786
787 $nonMatchingByKey = [];
788 $rows = [];
789 foreach ( $argsByKey as $key => [ $value, $exptime, $casToken ] ) {
790 $curToken = $curTokensByKey[$key] ?? null;
791 if ( $curToken === null ) {
792 $nonMatchingByKey[$key] = true;
793 $this->logger->debug( __METHOD__ . ": $key does not exists" );
794 continue;
795 }
796
797 if ( $curToken !== $casToken ) {
798 $nonMatchingByKey[$key] = true;
799 $this->logger->debug( __METHOD__ . ": $key does not have a matching token" );
800 continue;
801 }
802
803 $serialValue = $this->getSerialized( $value, $key );
804 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
805 $valueSizesByKey[$key] = [ strlen( $serialValue ), 0 ];
806
807 $rows[] = $this->buildUpsertRow( $db, $key, $serialValue, $expiry );
808 }
809 if ( !$rows ) {
810 return;
811 }
812 $db->newInsertQueryBuilder()
813 ->insertInto( $ptable )
814 ->rows( $rows )
815 ->onDuplicateKeyUpdate()
816 ->uniqueIndexFields( [ 'keyname' ] )
817 ->set( $this->buildMultiUpsertSetForOverwrite( $db ) )
818 ->caller( __METHOD__ )->execute();
819
820 foreach ( $argsByKey as $key => $unused ) {
821 $resByKey[$key] = !isset( $nonMatchingByKey[$key] );
822 }
823
824 $this->updateOpStats( self::METRIC_OP_CAS, $valueSizesByKey );
825 }
826
846 private function modifyTableSpecificBlobsForChangeTTL(
847 IDatabase $db,
848 string $ptable,
849 float $mtime,
850 array $argsByKey,
851 array &$resByKey
852 ) {
853 $keysBatchesByExpiry = [];
854 foreach ( $argsByKey as $key => [ $exptime ] ) {
855 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
856 $keysBatchesByExpiry[$expiry][] = $key;
857 }
858
859 $existingCount = 0;
860 foreach ( $keysBatchesByExpiry as $expiry => $keyBatch ) {
861 $db->newUpdateQueryBuilder()
862 ->update( $ptable )
863 ->set( [ 'exptime' => $this->encodeDbExpiry( $db, $expiry ) ] )
864 ->where( $this->buildExistenceConditions( $db, $keyBatch, (int)$mtime ) )
865 ->caller( __METHOD__ )->execute();
866 $existingCount += $db->affectedRows();
867 }
868 if ( $existingCount === count( $argsByKey ) ) {
869 foreach ( $argsByKey as $key => $args ) {
870 $resByKey[$key] = true;
871 }
872 }
873
874 $this->updateOpStats( self::METRIC_OP_CHANGE_TTL, array_keys( $argsByKey ) );
875 }
876
896 private function modifyTableSpecificBlobsForIncrInit(
897 IDatabase $db,
898 string $ptable,
899 float $mtime,
900 array $argsByKey,
901 array &$resByKey
902 ) {
903 foreach ( $argsByKey as $key => [ $step, $init, $exptime ] ) {
904 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
905
906 // Use a transaction so that changes from other threads are not visible due to
907 // "consistent reads". This way, the exact post-increment value can be returned.
908 // The "live key exists" check can go inside the write query and remain safe for
909 // replication since the TTL for such keys is either indefinite or very short.
910 $atomic = $db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
911 try {
912 $db->newInsertQueryBuilder()
913 ->insertInto( $ptable )
914 ->rows( $this->buildUpsertRow( $db, $key, $init, $expiry ) )
915 ->onDuplicateKeyUpdate()
916 ->uniqueIndexFields( [ 'keyname' ] )
917 ->set( $this->buildIncrUpsertSet( $db, $step, $init, $expiry, (int)$mtime ) )
918 ->caller( __METHOD__ )->execute();
919 $affectedCount = $db->affectedRows();
920 $row = $db->newSelectQueryBuilder()
921 ->select( 'value' )
922 ->from( $ptable )
923 ->where( [ 'keyname' => $key ] )
924 ->caller( __METHOD__ )
925 ->fetchRow();
926 } catch ( Exception $e ) {
927 $db->cancelAtomic( __METHOD__, $atomic );
928 throw $e;
929 }
930 $db->endAtomic( __METHOD__ );
931
932 if ( !$affectedCount || $row === false ) {
933 $this->logger->warning( __METHOD__ . ": failed to set new $key value" );
934 continue;
935 }
936
937 $serialValue = $this->dbDecodeSerialValue( $db, $row->value );
938 if ( !$this->isInteger( $serialValue ) ) {
939 $this->logger->warning( __METHOD__ . ": got non-integer $key value" );
940 continue;
941 }
942
943 $resByKey[$key] = (int)$serialValue;
944 }
945
946 $this->updateOpStats( self::METRIC_OP_INCR, array_keys( $argsByKey ) );
947 }
948
960 private function modifyTableSpecificBlobsForIncrInitAsync(
961 IDatabase $db,
962 string $ptable,
963 float $mtime,
964 array $argsByKey,
965 array &$resByKey
966 ) {
967 foreach ( $argsByKey as $key => [ $step, $init, $exptime ] ) {
968 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
969 $db->newInsertQueryBuilder()
970 ->insertInto( $ptable )
971 ->rows( $this->buildUpsertRow( $db, $key, $init, $expiry ) )
972 ->onDuplicateKeyUpdate()
973 ->uniqueIndexFields( [ 'keyname' ] )
974 ->set( $this->buildIncrUpsertSet( $db, $step, $init, $expiry, (int)$mtime ) )
975 ->caller( __METHOD__ )->execute();
976 if ( !$db->affectedRows() ) {
977 $this->logger->warning( __METHOD__ . ": failed to set new $key value" );
978 } else {
979 $resByKey[$key] = true;
980 }
981 }
982 }
983
989 private function makeNewKeyExpiry( $exptime, int $nowTsUnix ) {
990 $expiry = $this->getExpirationAsTimestamp( $exptime );
991 // Eventual consistency requires the preservation of recently modified keys.
992 // Do not create rows with `exptime` fields so low that they might get garbage
993 // collected before being replicated.
994 if ( $expiry !== self::TTL_INDEFINITE ) {
995 $expiry = max( $expiry, $nowTsUnix - self::SAFE_CLOCK_BOUND_SEC );
996 }
997
998 return $expiry;
999 }
1000
1019 private function newLockingWriteSectionModificationTimestamp( $key, &$scope ) {
1020 if ( !$this->lock( $key, 0 ) ) {
1021 return null;
1022 }
1023
1024 $scope = new ScopedCallback( function () use ( $key ) {
1025 $this->unlock( $key );
1026 } );
1027
1028 // sprintf is used to adjust precision
1029 return (float)sprintf( '%.6F', $this->locks[$key][self::LOCK_TIME] );
1030 }
1031
1040 private function buildExistenceConditions( IDatabase $db, $keys, int $time ) {
1041 // Note that tombstones always have past expiration dates
1042 return [
1043 'keyname' => $keys,
1044 $db->expr( 'exptime', '>=', $db->timestamp( $time ) )
1045 ];
1046 }
1047
1057 private function buildUpsertRow(
1058 IDatabase $db,
1059 $key,
1060 $serialValue,
1061 int $expiry
1062 ) {
1063 $row = [
1064 'keyname' => $key,
1065 'value' => $this->dbEncodeSerialValue( $db, $serialValue ),
1066 'exptime' => $this->encodeDbExpiry( $db, $expiry )
1067 ];
1068
1069 return $row;
1070 }
1071
1078 private function buildMultiUpsertSetForOverwrite( IDatabase $db ) {
1079 $expressionsByColumn = [
1080 'value' => $db->buildExcludedValue( 'value' ),
1081 'exptime' => $db->buildExcludedValue( 'exptime' )
1082 ];
1083
1084 $set = [];
1085 foreach ( $expressionsByColumn as $column => $updateExpression ) {
1086 $set[$column] = new RawSQLValue( $updateExpression );
1087 }
1088
1089 return $set;
1090 }
1091
1102 private function buildIncrUpsertSet(
1103 IDatabase $db,
1104 int $step,
1105 int $init,
1106 int $expiry,
1107 int $mtUnixTs
1108 ) {
1109 // Map of (column => (SQL for non-expired key rows, SQL for expired key rows))
1110 $expressionsByColumn = [
1111 'value' => [
1112 $db->buildIntegerCast( 'value' ) . " + {$db->addQuotes( $step )}",
1113 $db->addQuotes( $this->dbEncodeSerialValue( $db, $init ) )
1114 ],
1115 'exptime' => [
1116 'exptime',
1117 $db->addQuotes( $this->encodeDbExpiry( $db, $expiry ) )
1118 ]
1119 ];
1120
1121 $set = [];
1122 foreach ( $expressionsByColumn as $column => [ $updateExpression, $initExpression ] ) {
1123 $rhs = $db->conditional(
1124 $db->expr( 'exptime', '>=', $db->timestamp( $mtUnixTs ) ),
1125 $updateExpression,
1126 $initExpression
1127 );
1128 $set[$column] = new RawSQLValue( $rhs );
1129 }
1130
1131 return $set;
1132 }
1133
1139 private function encodeDbExpiry( IDatabase $db, int $expiry ) {
1140 return ( $expiry === self::TTL_INDEFINITE )
1141 // Use the maximum timestamp that the column can store
1142 ? $db->timestamp( self::INF_TIMESTAMP_PLACEHOLDER )
1143 // Convert the absolute timestamp into the DB timestamp format
1144 : $db->timestamp( $expiry );
1145 }
1146
1152 private function decodeDbExpiry( IDatabase $db, string $dbExpiry ) {
1153 return ( $dbExpiry === $db->timestamp( self::INF_TIMESTAMP_PLACEHOLDER ) )
1154 ? self::TTL_INDEFINITE
1155 : (int)ConvertibleTimestamp::convert( TS::UNIX, $dbExpiry );
1156 }
1157
1163 private function dbEncodeSerialValue( IDatabase $db, $serialValue ) {
1164 return is_int( $serialValue ) ? (string)$serialValue : $db->encodeBlob( $serialValue );
1165 }
1166
1172 private function dbDecodeSerialValue( IDatabase $db, $blob ) {
1173 return $this->isInteger( $blob ) ? (int)$blob : $db->decodeBlob( $blob );
1174 }
1175
1183 private function addCasTokenFields( IDatabase $db, array $fields ) {
1184 $type = $db->getType();
1185
1186 if ( $type === 'mysql' ) {
1187 $fields['castoken'] = $db->buildConcat( [
1188 'SHA1(value)',
1189 $db->addQuotes( '@' ),
1190 'exptime'
1191 ] );
1192 } elseif ( $type === 'postgres' ) {
1193 $fields['castoken'] = $db->buildConcat( [
1194 'md5(value)',
1195 $db->addQuotes( '@' ),
1196 'exptime'
1197 ] );
1198 } else {
1199 if ( !in_array( 'value', $fields, true ) ) {
1200 $fields[] = 'value';
1201 }
1202 if ( !in_array( 'exptime', $fields, true ) ) {
1203 $fields[] = 'exptime';
1204 }
1205 }
1206
1207 return $fields;
1208 }
1209
1217 private function getCasTokenFromRow( IDatabase $db, stdClass $row ) {
1218 if ( isset( $row->castoken ) ) {
1219 $token = $row->castoken;
1220 } else {
1221 $token = sha1( $this->dbDecodeSerialValue( $db, $row->value ) ) . '@' . $row->exptime;
1222 $this->logger->debug( __METHOD__ . ": application computed hash for CAS token" );
1223 }
1224
1225 return $token;
1226 }
1227
1232 private function garbageCollect( $shardIndex ) {
1233 // set right away, avoid queuing duplicate async callbacks
1234 $this->lastGarbageCollect = $this->getCurrentTime();
1235
1236 $garbageCollector = function () use ( $shardIndex ) {
1237 $db = $this->getConnection( $shardIndex );
1239 $silenceScope = $this->silenceTransactionProfiler();
1240 $this->deleteServerObjectsExpiringBefore(
1241 $db,
1242 (int)$this->getCurrentTime(),
1243 $this->purgeLimit
1244 );
1245 $this->lastGarbageCollect = $this->getCurrentTime();
1246 };
1247
1248 if ( $this->asyncHandler ) {
1249 ( $this->asyncHandler )( $garbageCollector );
1250 } else {
1251 $garbageCollector();
1252 }
1253 }
1254
1257 $timestamp,
1258 ?callable $progress = null,
1259 $limit = INF,
1260 ?string $tag = null
1261 ) {
1263 $silenceScope = $this->silenceTransactionProfiler();
1264
1265 if ( $tag !== null ) {
1266 // Purge one server only, to support concurrent purging in large wiki farms (T282761).
1267 $shardIndexes = [];
1268 if ( !$this->serverTags ) {
1269 throw new InvalidArgumentException( "Given a tag but no tags are configured" );
1270 }
1271 foreach ( $this->serverTags as $serverShardIndex => $serverTag ) {
1272 if ( $tag === $serverTag ) {
1273 $shardIndexes[] = $serverShardIndex;
1274 break;
1275 }
1276 }
1277 if ( !$shardIndexes ) {
1278 throw new InvalidArgumentException( "Unknown server tag: $tag" );
1279 }
1280 } else {
1281 $shardIndexes = $this->getShardServerIndexes();
1282 shuffle( $shardIndexes );
1283 }
1284
1285 $ok = true;
1286 $numServers = count( $shardIndexes );
1287
1288 $keysDeletedCount = 0;
1289 foreach ( $shardIndexes as $numServersDone => $shardIndex ) {
1290 try {
1291 $db = $this->getConnection( $shardIndex );
1292
1293 // Avoid deadlock (T330377)
1294 $lockKey = "SqlBagOStuff-purge-shard:$shardIndex";
1295 if ( !$db->lock( $lockKey, __METHOD__, 0 ) ) {
1296 $this->logger->info( "SqlBagOStuff purge for shard $shardIndex already locked, skip" );
1297 continue;
1298 }
1299
1300 $this->deleteServerObjectsExpiringBefore(
1301 $db,
1302 $timestamp,
1303 $limit,
1304 $keysDeletedCount,
1305 [ 'fn' => $progress, 'serversDone' => $numServersDone, 'serversTotal' => $numServers ]
1306 );
1307 $db->unlock( $lockKey, __METHOD__ );
1308 } catch ( DBError $e ) {
1309 $this->handleDBError( $e, $shardIndex );
1310 $ok = false;
1311 }
1312 }
1313
1314 return $ok;
1315 }
1316
1326 private function deleteServerObjectsExpiringBefore(
1327 IDatabase $db,
1328 $timestamp,
1329 $limit,
1330 &$keysDeletedCount = 0,
1331 ?array $progress = null
1332 ) {
1333 $cutoffUnix = (int)ConvertibleTimestamp::convert( TS::UNIX, $timestamp );
1334 $tableIndexes = range( 0, $this->numTableShards - 1 );
1335 shuffle( $tableIndexes );
1336
1337 $batchSize = min( $this->writeBatchSize, $limit );
1338
1339 foreach ( $tableIndexes as $numShardsDone => $tableIndex ) {
1340 // don't do more than 10% of tables. To avoid overwhelming
1341 // when there are too many of them. Add one to make sure small number
1342 // of tables have been taken care of.
1343 if (
1344 $numShardsDone > ( ( $this->numTableShards / 10 ) + 1 ) &&
1345 // running in context of purge maint script. Go through all tables
1346 $limit !== INF
1347 ) {
1348 break;
1349 }
1350
1351 // The oldest expiry of a row we have deleted on this shard
1352 // (the first row that we deleted)
1353 $minExpUnix = null;
1354 // The most recent expiry time so far, from a row we have deleted on this shard
1355 $maxExp = null;
1356 // Size of the time range we'll delete, in seconds (for progress estimate)
1357 $totalSeconds = null;
1358
1359 do {
1360 $res = $db->newSelectQueryBuilder()
1361 ->select( [ 'keyname', 'exptime' ] )
1362 ->from( $this->getTableNameByShard( $tableIndex ) )
1363 ->where( $db->expr( 'exptime', '<', $db->timestamp( $cutoffUnix ) ) )
1364 ->andWhere( $maxExp ? $db->expr( 'exptime', '>=', $maxExp ) : [] )
1365 ->orderBy( 'exptime', SelectQueryBuilder::SORT_ASC )
1366 ->limit( $batchSize )
1367 ->caller( __METHOD__ )
1368 ->fetchResultSet();
1369
1370 if ( $res->numRows() ) {
1371 $row = $res->current();
1372 if ( $minExpUnix === null ) {
1373 $minExpUnix = (int)ConvertibleTimestamp::convert( TS::UNIX, $row->exptime );
1374 $totalSeconds = max( $cutoffUnix - $minExpUnix, 1 );
1375 }
1376
1377 $keys = [];
1378 foreach ( $res as $row ) {
1379 $keys[] = $row->keyname;
1380 $maxExp = $row->exptime;
1381 }
1382
1384 ->deleteFrom( $this->getTableNameByShard( $tableIndex ) )
1385 ->where( [
1386 'keyname' => $keys,
1387 $db->expr( 'exptime', '<', $db->timestamp( $cutoffUnix ) ),
1388 ] )
1389 ->caller( __METHOD__ )->execute();
1390 $keysDeletedCount += $db->affectedRows();
1391 }
1392
1393 if ( $progress && is_callable( $progress['fn'] ) ) {
1394 if ( $totalSeconds ) {
1395 $maxExpUnix = (int)ConvertibleTimestamp::convert( TS::UNIX, $maxExp );
1396 $remainingSeconds = $cutoffUnix - $maxExpUnix;
1397 $processedSeconds = max( $totalSeconds - $remainingSeconds, 0 );
1398 // For example, if we've done 1.5 table shard, and are thus half-way on the
1399 // 2nd of perhaps 5 tables on this server, then this might be:
1400 // `( 1 + ( 43200 / 86400 ) ) / 5 = 0.3`, or 30% done, of tables on this server.
1401 $tablesDoneRatio =
1402 ( $numShardsDone + ( $processedSeconds / $totalSeconds ) ) / $this->numTableShards;
1403 } else {
1404 $tablesDoneRatio = 1;
1405 }
1406
1407 // For example, if we're 30% done on the last of 10 servers, then this might be:
1408 // `( 9 / 10 ) + ( 0.3 / 10 ) = 0.93`, or 93% done, overall.
1409 $overallRatio = ( $progress['serversDone'] / $progress['serversTotal'] ) +
1410 ( $tablesDoneRatio / $progress['serversTotal'] );
1411 ( $progress['fn'] )( (int)( $overallRatio * 100 ) );
1412 }
1413 } while ( $res->numRows() && $keysDeletedCount < $limit );
1414 }
1415 }
1416
1418 public function doLock( $key, $timeout = 6, $exptime = 6 ) {
1420 $silenceScope = $this->silenceTransactionProfiler();
1421
1422 $lockTsUnix = null;
1423
1424 $shardIndexes = $this->getShardIndexesForKey( $key );
1425 foreach ( $shardIndexes as $shardIndex ) {
1426 try {
1427 $db = $this->getConnection( $shardIndex );
1428 $lockTsUnix = $db->lock( $key, __METHOD__, $timeout, $db::LOCK_TIMESTAMP );
1429 } catch ( DBError $e ) {
1430 $this->handleDBError( $e, $shardIndex );
1431 $this->logger->warning(
1432 __METHOD__ . ' failed due to I/O error for {key}.',
1433 [ 'key' => $key ]
1434 );
1435 }
1436 }
1437
1438 return $lockTsUnix;
1439 }
1440
1442 public function doUnlock( $key ) {
1444 $silenceScope = $this->silenceTransactionProfiler();
1445
1446 $shardIndexes = $this->getShardIndexesForKey( $key );
1447 $released = false;
1448 foreach ( $shardIndexes as $shardIndex ) {
1449 try {
1450 $db = $this->getConnection( $shardIndex );
1451 $released = $db->unlock( $key, __METHOD__ );
1452 } catch ( DBError $e ) {
1453 $this->handleDBError( $e, $shardIndex );
1454 $released = false;
1455 }
1456 }
1457
1458 return $released;
1459 }
1460
1462 protected function makeKeyInternal( $keyspace, $components ) {
1463 $key = strtr( $keyspace, ' ', '_' );
1464 foreach ( $components as $component ) {
1465 $component = strtr( $component ?? '', [
1466 ' ' => '_', // Avoid unnecessary misses from pre-1.35 code
1467 ':' => '%3A',
1468 ] );
1469 $key .= ':' . $component;
1470 }
1471
1472 // SQL schema for 'objectcache' specifies keys as varchar(255).
1473 // * Reserve 45 chars for prefixes used by wrappers like WANObjectCache.
1474 return $this->makeFallbackKey( $key, 205 );
1475 }
1476
1477 protected function requireConvertGenericKey(): bool {
1478 return true;
1479 }
1480
1482 protected function serialize( $value ) {
1483 if ( is_int( $value ) ) {
1484 return $value;
1485 }
1486
1487 $serial = serialize( $value );
1488 if ( $this->hasZlib ) {
1489 // On typical message and page data, this can provide a 3X storage savings
1490 $serial = gzdeflate( $serial, 9 );
1491 }
1492
1493 return $serial;
1494 }
1495
1497 protected function unserialize( $value ) {
1498 if ( $value === self::TOMB_SERIAL ) {
1499 return false; // tombstone
1500 }
1501
1502 if ( $this->isInteger( $value ) ) {
1503 return (int)$value;
1504 }
1505
1506 if ( $this->hasZlib ) {
1507 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1508 $decompressed = @gzinflate( $value );
1509
1510 if ( $decompressed !== false ) {
1511 $value = $decompressed;
1512 }
1513 }
1514
1515 return unserialize( $value );
1516 }
1517
1518 private function getLoadBalancer(): ILoadBalancer {
1519 if ( !$this->loadBalancer ) {
1520 $this->loadBalancer = ( $this->loadBalancerCallback )();
1521 }
1522 return $this->loadBalancer;
1523 }
1524
1529 private function getConnectionViaLoadBalancer() {
1530 $lb = $this->getLoadBalancer();
1531
1532 if ( $lb->getServerAttributes( ServerInfo::WRITER_INDEX )[Database::ATTR_DB_LEVEL_LOCKING] ) {
1533 // Use the main connection to avoid transaction deadlocks
1534 $conn = $lb->getMaintenanceConnectionRef( DB_PRIMARY, [], $this->dbDomain );
1535 } else {
1536 // If the RDBMS has row/table/page level locking, then use separate auto-commit
1537 // connection to avoid needless contention and deadlocks.
1538 $conn = $lb->getMaintenanceConnectionRef(
1539 DB_PRIMARY,
1540 [],
1541 $this->dbDomain,
1542 $lb::CONN_TRX_AUTOCOMMIT
1543 );
1544 }
1545
1546 // Make sure any errors are thrown now while we can more easily handle them
1547 $conn->ensureConnection();
1548 return $conn;
1549 }
1550
1557 private function getConnectionFromServerInfo( $shardIndex, array $server ) {
1558 if ( !isset( $this->conns[$shardIndex] ) ) {
1559 $server['logger'] = $this->logger;
1560 // Always use autocommit mode, even if DBO_TRX is configured
1561 $server['flags'] ??= 0;
1562 $server['flags'] &= ~( IDatabase::DBO_TRX | IDatabase::DBO_DEFAULT );
1563
1565 $conn = MediaWikiServices::getInstance()->getDatabaseFactory()
1566 ->create( $server['type'], $server );
1567
1568 // Automatically create the objectcache table for sqlite as needed
1569 if ( $conn->getType() === 'sqlite' ) {
1570 $this->initSqliteDatabase( $conn );
1571 }
1572 $this->conns[$shardIndex] = $conn;
1573 }
1574
1575 // @phan-suppress-next-line PhanTypeMismatchReturnNullable False positive
1576 return $this->conns[$shardIndex];
1577 }
1578
1585 private function handleDBError( DBError $exception, $shardIndex ) {
1586 if ( !$this->useLB && $exception instanceof DBConnectionError ) {
1587 unset( $this->conns[$shardIndex] ); // bug T103435
1588
1589 $now = $this->getCurrentTime();
1590 if ( isset( $this->connFailureTimes[$shardIndex] ) ) {
1591 if ( $now - $this->connFailureTimes[$shardIndex] >= 60 ) {
1592 unset( $this->connFailureTimes[$shardIndex] );
1593 unset( $this->connFailureErrors[$shardIndex] );
1594 } else {
1595 $this->logger->debug( __METHOD__ . ": Server #$shardIndex already down" );
1596 return;
1597 }
1598 }
1599 $this->logger->info( __METHOD__ . ": Server #$shardIndex down until " . ( $now + 60 ) );
1600 $this->connFailureTimes[$shardIndex] = $now;
1601 $this->connFailureErrors[$shardIndex] = $exception;
1602 }
1603 $this->logger->error( "DBError: {$exception->getMessage()}", [ 'exception' => $exception ] );
1604 if ( $exception instanceof DBConnectionError ) {
1605 $this->setLastError( self::ERR_UNREACHABLE );
1606 $this->logger->warning( __METHOD__ . ": ignoring connection error" );
1607 } elseif ( $exception instanceof DBQueryDisconnectedError ) {
1608 $this->setLastError( self::ERR_NO_RESPONSE );
1609 $this->logger->warning( __METHOD__ . ": ignoring connection loss" );
1610 } else {
1611 $this->setLastError( self::ERR_UNEXPECTED );
1612 $this->logger->warning( __METHOD__ . ": ignoring query error" );
1613 }
1614 }
1615
1620 private function initSqliteDatabase( IMaintainableDatabase $db ) {
1621 if ( $db->tableExists( 'objectcache', __METHOD__ ) ) {
1622 return;
1623 }
1624 // Use one table for SQLite; sharding does not seem to have much benefit
1625 $db->query( "PRAGMA journal_mode=WAL", __METHOD__ ); // this is permanent
1626 $db->startAtomic( __METHOD__ ); // atomic DDL
1627 try {
1628 $encTable = $db->tableName( 'objectcache' );
1629 $encExptimeIndex = $db->addIdentifierQuotes( $db->tablePrefix() . 'exptime' );
1630 $db->query(
1631 "CREATE TABLE $encTable (\n" .
1632 " keyname BLOB NOT NULL default '' PRIMARY KEY,\n" .
1633 " value BLOB,\n" .
1634 " exptime BLOB NOT NULL\n" .
1635 ")",
1636 __METHOD__
1637 );
1638 $db->query( "CREATE INDEX $encExptimeIndex ON $encTable (exptime)", __METHOD__ );
1639 $db->endAtomic( __METHOD__ );
1640 } catch ( DBError $e ) {
1641 $db->rollback( __METHOD__ );
1642 throw $e;
1643 }
1644 }
1645
1663 public function createTables() {
1664 foreach ( $this->getShardServerIndexes() as $shardIndex ) {
1665 $db = $this->getConnection( $shardIndex );
1666 if ( in_array( $db->getType(), [ 'mysql', 'postgres' ], true ) ) {
1667 for ( $i = 0; $i < $this->numTableShards; $i++ ) {
1668 $encBaseTable = $db->tableName( 'objectcache' );
1669 $encShardTable = $db->tableName( $this->getTableNameByShard( $i ) );
1670 $db->query( "CREATE TABLE IF NOT EXISTS $encShardTable LIKE $encBaseTable", __METHOD__ );
1671 }
1672 }
1673 }
1674 }
1675
1679 private function getShardServerIndexes() {
1680 if ( $this->useLB ) {
1681 // LoadBalancer based configuration
1682 $shardIndexes = [ 0 ];
1683 } else {
1684 // Striped array of database servers
1685 $shardIndexes = array_keys( $this->serverTags );
1686 }
1687
1688 return $shardIndexes;
1689 }
1690
1694 #[\NoDiscard]
1695 private function silenceTransactionProfiler(): ?ScopedCallback {
1696 if ( $this->serverInfos ) {
1697 return null; // no TransactionProfiler injected anyway
1698 }
1699 return Profiler::instance()->getTransactionProfiler()->silenceForScope();
1700 }
1701}
1702
1704class_alias( SqlBagOStuff::class, 'SqlBagOStuff' );
$fallback
const DB_PRIMARY
Definition defines.php:28
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:71
Service locator for MediaWiki core services.
RDBMS-based caching module.
requireConvertGenericKey()
Whether ::proxyCall() must re-encode cache keys before calling read/write methods.
doChangeTTL( $key, $exptime, $flags)
bool
__construct( $params)
Create a new backend instance from parameters injected by ObjectCache::newFromParams()
doLock( $key, $timeout=6, $exptime=6)
MediumSpecificBagOStuff::lock()float|null UNIX timestamp of acquisition; null on failure
doSetMulti(array $data, $exptime=0, $flags=0)
bool Success
doIncrWithInit( $key, $exptime, $step, $init, $flags)
int|bool New value or false on failure
float[] $connFailureTimes
Map of (shard index => UNIX timestamps)
bool $useLB
Whether to use the LoadBalancer.
serialize( $value)
string|int|false String/integer representation Special handling is usually needed for integers so inc...
doGet( $key, $flags=0, &$casToken=null)
Get an item.The CAS token should be null if the key does not exist or the value is corruptmixed Retur...
callable null $loadBalancerCallback
Injected function which returns a LoadBalancer.
unserialize( $value)
mixed Original value or false on error Special handling is usually needed for integers so incr()/decr...
int $numTableShards
Number of table shards to use on each server.
string false null $dbDomain
DB name used for keys using the LoadBalancer.
doCas( $casToken, $key, $value, $exptime=0, $flags=0)
Set an item if the current CAS token matches the provided CAS token.bool Success
createTables()
Create the shard tables on all databases.
IMaintainableDatabase[] $conns
Map of (shard index => DB handle)
DBConnectionError[] $connFailureErrors
Map of (shard index => Exception)
int $purgeLimit
Max expired rows to purge during randomized garbage collection.
makeKeyInternal( $keyspace, $components)
Make a cache key for the given keyspace and components.Subclasses may override this method to apply d...
string[] $serverTags
(server index => tag/host name)
float $lastGarbageCollect
UNIX timestamp.
array[] $serverInfos
(server index => server config)
int $purgePeriod
Average number of writes required to trigger garbage collection.
doDeleteMulti(array $keys, $flags=0)
bool Success
deleteObjectsExpiringBefore( $timestamp, ?callable $progress=null, $limit=INF, ?string $tag=null)
Delete all objects expiring before a certain date.bool Success; false if unimplemented
doAdd( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.bool Success
doDelete( $key, $flags=0)
Delete an item.bool True if the item was deleted or not found, false on failure
doSet( $key, $value, $exptime=0, $flags=0)
Set an item.bool Success
doGetMulti(array $keys, $flags=0)
Get an associative array containing the item for each of the keys that have items....
doChangeTTLMulti(array $keys, $exptime, $flags=0)
bool Success
doUnlock( $key)
MediumSpecificBagOStuff::unlock()bool Success
Profiler base class that defines the interface and some shared functionality.
Definition Profiler.php:26
A collection of static methods to play with arrays.
string $keyspace
Default keyspace; used by makeKey()
Definition BagOStuff.php:84
makeFallbackKey(string $key, int $maxLength)
Re-format a cache key that is too long.
const ATTR_DURABILITY
Key in getQoS() for durability of storage writes.
const QOS_DURABILITY_RDBMS
Storage survives on disk with high availability (SqlBagOStuff).
Helper class that implements most of BagOStuff for a backend.
unlock( $key)
Release an advisory lock on a key string.
getSerialized( $value, $key)
Get the serialized form a value, logging a warning if it involves custom classes.
lock( $key, $timeout=6, $exptime=6, $rclass='')
isInteger( $value)
Check if a value is an integer.
const PASS_BY_REF
Idiom for doGet() to return extra information by reference.
getExpirationAsTimestamp( $exptime)
Convert an optionally relative timestamp to an absolute time.
Database error base class.
Definition DBError.php:22
Raw SQL value to be used in query builders.
Build SELECT queries with a fluent interface.
Container for accessing information about the database servers in a database cluster.
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'DefaultLockManager'=> null, 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 220, 250, 300, 400,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'TrackMediaRequestProvenance'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'RestTermsOfServiceUrl'=> null, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'CodeHighlighter',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'EnableSectionShare'=> false, 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'NamespacesWithoutAutoSummaries' => [ ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider', 'services' => [ 'ConnectionProvider', 'UserFactory', ], 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'autocreateaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, 'createwithcontentmodel' => true, 'logout' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'createpreviouslyrenamedaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'RestrictUserPageEditing' => false, 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], 'managesessions' => [ 'logout' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', 'managesessions' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'BotPasswordsLimit' => 100, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'ReauthenticateForActions' => [ 'edituserjs' => 'edituserjscss', 'editusercss' => 'edituserjscss', 'editsitejs' => 'editsitejscss', 'editsitecss' => 'editsitejscss', ], 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPUseReportURIDirective' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'ApiClientErrorSampleRate' => 1.0, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: 'https: 'git@github\\.com:(.*?)(\\.git)?' => 'https: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, 'mw-edited-other-users-js' => true, 'mw-edited-other-users-css' => true, ], 'RestrictedTagViewRights' => [ ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchstarPopover' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\RecentChanges\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Promise-Non-Write-API-Action', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'RestLocalModuleTestBaseUrl' => null, 'RestModuleOverrides' => [ ], 'RestExternalModules' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'GenerateReqIDFormat' => 'rand24', 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, 'ParserOptionsLogUnsafeSampleRate' => 0, 'ReturnExperimentalPFragmentTypes' => [ ], ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'DefaultLockManager' => [ 'string', 'null', ], 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'RestTermsOfServiceUrl' => [ 'string', 'null', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'NamespacesWithoutAutoSummaries' => 'array', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'RestrictUserPageEditing' => 'boolean', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'BotPasswordsLimit' => 'integer', 'ReauthenticateForActions' => 'object', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPUseReportURIDirective' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RestrictedTagViewRights' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchstarPopover' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'RestLocalModuleTestBaseUrl' => [ 'string', 'null', ], 'RestModuleOverrides' => 'object', 'RestExternalModules' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'GenerateReqIDFormat' => 'string', 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', 'ReturnExperimentalPFragmentTypes' => 'array', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'RestModuleOverrides' => 'array_replace_recursive', 'RestExternalModules' => 'array_replace_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'UseCopyrightUpload' => [ 'deprecated' => 'since 1.47 This feature is being removed.', ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'file' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], ], ], 'RestModuleOverrides' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'mode' => [ 'type' => 'string', ], ], 'required' => [ 'mode', ], ], ], 'RestExternalModules' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'info' => [ 'type' => 'object', 'properties' => [ 'version' => [ 'type' => 'string', ], 'title' => [ 'type' => 'string', ], 'x-i18n-title' => [ 'type' => 'string', ], 'description' => [ 'type' => 'string', ], 'x-i18n-description' => [ 'type' => 'string', ], ], 'required' => [ 'version', ], ], 'base' => [ 'type' => 'string', 'format' => 'uri', ], 'spec' => [ 'type' => 'string', 'format' => 'uri', ], ], 'required' => [ 'info', 'base', 'spec', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
Interface to a relational database.
Definition IDatabase.php:31
unlock( $lockName, $method)
Release a lock.
lock( $lockName, $method, $timeout=5, $flags=0)
Acquire a named lock.
newDeleteQueryBuilder()
Get an DeleteQueryBuilder bound to this connection.
affectedRows()
Get the number of rows affected by the last query method call.
This class is a delegate to ILBFactory for a given database cluster.
Advanced database interface for IDatabase handles that include maintenance methods.
newSelectQueryBuilder()
Create an empty SelectQueryBuilder which can be used to run queries against this connection.
expr(string $field, string $op, $value)
See Expression::__construct()
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...
setLastError( $error)
This is actually implemented in the Job class.