MediaWiki master
SqlBagOStuff.php
Go to the documentation of this file.
1<?php
24use Wikimedia\ScopedCallback;
25use Wikimedia\Timestamp\ConvertibleTimestamp;
26use Wikimedia\Timestamp\TimestampFormat as TS;
27
45 protected $loadBalancer;
47 protected $dbDomain;
49 protected $useLB = false;
50
52 protected $serverInfos = [];
54 protected $serverTags = [];
56 protected $lastGarbageCollect = 0;
58 protected $purgePeriod = 10;
60 protected $purgeLimit = 100;
62 protected $numTableShards = 1;
64 protected $writeBatchSize = 100;
66 protected $tableName = 'objectcache';
67
69 protected $conns;
71 protected $connFailureTimes = [];
73 protected $connFailureErrors = [];
74
76 private $hasZlib;
77
79 private $dataRedundancy;
80
82 private const SAFE_CLOCK_BOUND_SEC = 15;
84 private const SAFE_PURGE_DELAY_SEC = 3600;
86 private const TOMB_SERIAL = '';
88 private const TOMB_EXPTIME = -self::SAFE_CLOCK_BOUND_SEC;
90 private const GC_DELAY_SEC = 1;
91
92 private const BLOB_VALUE = 0;
93 private const BLOB_EXPIRY = 1;
94 private const BLOB_CASTOKEN = 2;
95
102 private const INF_TIMESTAMP_PLACEHOLDER = '99991231235959';
103
131 public function __construct( $params ) {
132 parent::__construct( $params );
133
134 if ( isset( $params['servers'] ) || isset( $params['server'] ) ) {
135 // Configuration uses a direct list of servers.
136 // Object data is horizontally partitioned via key hash.
137 $index = 0;
138 foreach ( ( $params['servers'] ?? [ $params['server'] ] ) as $tag => $info ) {
139 $this->serverInfos[$index] = $info;
140 // Allow integer-indexes arrays for b/c
141 $this->serverTags[$index] = is_string( $tag ) ? $tag : "#$index";
142 ++$index;
143 }
144 } elseif ( isset( $params['loadBalancerCallback'] ) ) {
145 $this->loadBalancerCallback = $params['loadBalancerCallback'];
146 if ( !isset( $params['dbDomain'] ) ) {
147 throw new InvalidArgumentException(
148 __METHOD__ . ": 'dbDomain' is required if 'loadBalancerCallback' is given"
149 );
150 }
151 $this->dbDomain = $params['dbDomain'];
152 $this->useLB = true;
153 } else {
154 throw new InvalidArgumentException(
155 __METHOD__ . " requires 'server', 'servers', or 'loadBalancerCallback'"
156 );
157 }
158
159 $this->purgePeriod = intval( $params['purgePeriod'] ?? $this->purgePeriod );
160 $this->purgeLimit = intval( $params['purgeLimit'] ?? $this->purgeLimit );
161 $this->tableName = $params['tableName'] ?? $this->tableName;
162 $this->numTableShards = intval( $params['shards'] ?? $this->numTableShards );
163 $this->writeBatchSize = intval( $params['writeBatchSize'] ?? $this->writeBatchSize );
164 $this->dataRedundancy = min( intval( $params['dataRedundancy'] ?? 1 ), count( $this->serverTags ) );
165
166 $this->attrMap[self::ATTR_DURABILITY] = self::QOS_DURABILITY_RDBMS;
167
168 $this->hasZlib = extension_loaded( 'zlib' );
169 }
170
172 protected function doGet( $key, $flags = 0, &$casToken = null ) {
173 $getToken = ( $casToken === self::PASS_BY_REF );
174 $casToken = null;
175
176 $data = $this->fetchBlobs( [ $key ], $getToken )[$key];
177 if ( $data ) {
178 $result = $this->unserialize( $data[self::BLOB_VALUE] );
179 if ( $getToken && $result !== false ) {
180 $casToken = $data[self::BLOB_CASTOKEN];
181 }
182 $valueSize = strlen( $data[self::BLOB_VALUE] );
183 } else {
184 $result = false;
185 $valueSize = false;
186 }
187
188 $this->updateOpStats( self::METRIC_OP_GET, [ $key => [ 0, $valueSize ] ] );
189
190 return $result;
191 }
192
194 protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
195 $mtime = $this->getCurrentTime();
196
197 return $this->modifyBlobs(
198 $this->modifyTableSpecificBlobsForSet( ... ),
199 $mtime,
200 [ $key => [ $value, $exptime ] ]
201 );
202 }
203
205 protected function doDelete( $key, $flags = 0 ) {
206 $mtime = $this->getCurrentTime();
207
208 return $this->modifyBlobs(
209 $this->modifyTableSpecificBlobsForDelete( ... ),
210 $mtime,
211 [ $key => [] ]
212 );
213 }
214
216 protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
217 $mtime = $this->newLockingWriteSectionModificationTimestamp( $key, $scope );
218 if ( $mtime === null ) {
219 // Timeout or I/O error during lock acquisition
220 return false;
221 }
222
223 return $this->modifyBlobs(
224 $this->modifyTableSpecificBlobsForAdd( ... ),
225 $mtime,
226 [ $key => [ $value, $exptime ] ]
227 );
228 }
229
231 protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
232 $mtime = $this->newLockingWriteSectionModificationTimestamp( $key, $scope );
233 if ( $mtime === null ) {
234 // Timeout or I/O error during lock acquisition
235 return false;
236 }
237
238 return $this->modifyBlobs(
239 $this->modifyTableSpecificBlobsForCas( ... ),
240 $mtime,
241 [ $key => [ $value, $exptime, $casToken ] ]
242 );
243 }
244
246 protected function doChangeTTL( $key, $exptime, $flags ) {
247 $mtime = $this->getCurrentTime();
248
249 return $this->modifyBlobs(
250 $this->modifyTableSpecificBlobsForChangeTTL( ... ),
251 $mtime,
252 [ $key => [ $exptime ] ]
253 );
254 }
255
257 protected function doIncrWithInit( $key, $exptime, $step, $init, $flags ) {
258 $mtime = $this->getCurrentTime();
259
260 if ( $flags & self::WRITE_BACKGROUND ) {
261 $callback = $this->modifyTableSpecificBlobsForIncrInitAsync( ... );
262 } else {
263 $callback = $this->modifyTableSpecificBlobsForIncrInit( ... );
264 }
265
266 $result = $this->modifyBlobs(
267 $callback,
268 $mtime,
269 [ $key => [ $step, $init, $exptime ] ],
270 $resByKey
271 ) ? $resByKey[$key] : false;
272
273 return $result;
274 }
275
277 protected function doGetMulti( array $keys, $flags = 0 ) {
278 $result = [];
279 $valueSizeByKey = [];
280
281 $dataByKey = $this->fetchBlobs( $keys );
282 foreach ( $keys as $key ) {
283 $data = $dataByKey[$key];
284 if ( $data ) {
285 $serialValue = $data[self::BLOB_VALUE];
286 $value = $this->unserialize( $serialValue );
287 if ( $value !== false ) {
288 $result[$key] = $value;
289 }
290 $valueSize = strlen( $serialValue );
291 } else {
292 $valueSize = false;
293 }
294 $valueSizeByKey[$key] = [ 0, $valueSize ];
295 }
296
297 $this->updateOpStats( self::METRIC_OP_GET, $valueSizeByKey );
298
299 return $result;
300 }
301
303 protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
304 $mtime = $this->getCurrentTime();
305
306 return $this->modifyBlobs(
307 $this->modifyTableSpecificBlobsForSet( ... ),
308 $mtime,
309 array_map(
310 static function ( $value ) use ( $exptime ) {
311 return [ $value, $exptime ];
312 },
313 $data
314 )
315 );
316 }
317
319 protected function doDeleteMulti( array $keys, $flags = 0 ) {
320 $mtime = $this->getCurrentTime();
321
322 return $this->modifyBlobs(
323 $this->modifyTableSpecificBlobsForDelete( ... ),
324 $mtime,
325 array_fill_keys( $keys, [] )
326 );
327 }
328
330 public function doChangeTTLMulti( array $keys, $exptime, $flags = 0 ) {
331 $mtime = $this->getCurrentTime();
332
333 return $this->modifyBlobs(
334 $this->modifyTableSpecificBlobsForChangeTTL( ... ),
335 $mtime,
336 array_fill_keys( $keys, [ $exptime ] )
337 );
338 }
339
348 private function getConnection( $shardIndex ) {
349 if ( $this->useLB ) {
350 return $this->getConnectionViaLoadBalancer();
351 }
352
353 // Don't keep timing out trying to connect if the server is down
354 if (
355 isset( $this->connFailureErrors[$shardIndex] ) &&
356 ( $this->getCurrentTime() - $this->connFailureTimes[$shardIndex] ) < 60
357 ) {
358 throw $this->connFailureErrors[$shardIndex];
359 }
360
361 if ( isset( $this->serverInfos[$shardIndex] ) ) {
362 $server = $this->serverInfos[$shardIndex];
363 $conn = $this->getConnectionFromServerInfo( $shardIndex, $server );
364 } else {
365 throw new UnexpectedValueException( "Invalid server index #$shardIndex" );
366 }
367
368 return $conn;
369 }
370
378 private function getShardIndexesForKey( $key, $fallback = false ) {
379 // Pick the same shard for sister keys
380 // Using the same hash stop as mc-router for consistency
381 if ( str_contains( $key, '|#|' ) ) {
382 $key = explode( '|#|', $key )[0];
383 }
384 if ( $this->useLB ) {
385 // LoadBalancer based configuration
386 return [ 0 ];
387 } else {
388 // Striped array of database servers
389 if ( count( $this->serverTags ) == 1 ) {
390 return [ 0 ];
391 } else {
392 $sortedServers = $this->serverTags;
393 // shuffle the servers based on hashing of the keys
394 ArrayUtils::consistentHashSort( $sortedServers, $key );
395 $shardIndexes = array_keys( $sortedServers );
396 if ( $this->dataRedundancy === 1 ) {
397 if ( $fallback ) {
398 return [ $shardIndexes[1] ];
399 } else {
400 return [ $shardIndexes[0] ];
401 }
402 } else {
403 return array_slice( $shardIndexes, 0, $this->dataRedundancy );
404 }
405 }
406 }
407 }
408
414 private function getTableNameForKey( $key ) {
415 // Pick the same shard for sister keys
416 // Using the same hash stop as mc-router for consistency
417 if ( str_contains( $key, '|#|' ) ) {
418 $key = explode( '|#|', $key )[0];
419 }
420
421 if ( $this->numTableShards > 1 ) {
422 $hash = hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff;
423 $tableIndex = $hash % $this->numTableShards;
424 } else {
425 $tableIndex = null;
426 }
427
428 return $this->getTableNameByShard( $tableIndex );
429 }
430
436 private function getTableNameByShard( $index ) {
437 if ( $index !== null && $this->numTableShards > 1 ) {
438 $decimals = strlen( (string)( $this->numTableShards - 1 ) );
439
440 return $this->tableName . sprintf( "%0{$decimals}d", $index );
441 }
442
443 return $this->tableName;
444 }
445
452 private function fetchBlobs( array $keys, bool $getCasToken = false, $fallback = false ) {
453 if ( $fallback && count( $this->serverTags ) > 1 && $this->dataRedundancy > 1 ) {
454 // fallback doesn't work with data redundancy
455 return [];
456 }
457
459 $silenceScope = $this->silenceTransactionProfiler();
460
461 // Initialize order-preserved per-key results; set values for live keys below
462 $dataByKey = array_fill_keys( $keys, null );
463 $dataByKeyAndShard = [];
464
465 $readTime = (int)$this->getCurrentTime();
466 $keysByTableByShard = [];
467 foreach ( $keys as $key ) {
468 $partitionTable = $this->getTableNameForKey( $key );
469 $shardIndexes = $this->getShardIndexesForKey( $key, $fallback );
470 foreach ( $shardIndexes as $shardIndex ) {
471 $keysByTableByShard[$shardIndex][$partitionTable][] = $key;
472 }
473 }
474 $fallbackResult = [];
475
476 foreach ( $keysByTableByShard as $shardIndex => $serverKeys ) {
477 try {
478 $db = $this->getConnection( $shardIndex );
479 foreach ( $serverKeys as $partitionTable => $tableKeys ) {
480 $res = $db->newSelectQueryBuilder()
481 ->select(
482 $getCasToken
483 ? $this->addCasTokenFields( $db, [ 'keyname', 'value', 'exptime' ] )
484 : [ 'keyname', 'value', 'exptime' ] )
485 ->from( $partitionTable )
486 ->where( $this->buildExistenceConditions( $db, $tableKeys, $readTime ) )
487 ->caller( __METHOD__ )
488 ->fetchResultSet();
489 foreach ( $res as $row ) {
490 $row->shardIndex = $shardIndex;
491 $row->tableName = $partitionTable;
492 $dataByKeyAndShard[$row->keyname][$shardIndex] = $row;
493 }
494 }
495 } catch ( DBError $e ) {
496 if ( $fallback ) {
497 $this->handleDBError( $e, $shardIndex );
498 } else {
499 $fallbackResult += $this->fetchBlobs(
500 array_merge( ...array_values( $serverKeys ) ),
501 $getCasToken,
502 true
503 );
504 }
505
506 }
507 }
508 foreach ( $keys as $key ) {
509 $rowsByShard = $dataByKeyAndShard[$key] ?? null;
510 if ( !$rowsByShard ) {
511 continue;
512 }
513
514 // One response, no point of consistency checks
515 if ( count( $rowsByShard ) == 1 ) {
516 $row = array_values( $rowsByShard )[0];
517 } else {
518 usort( $rowsByShard, static function ( $a, $b ) {
519 if ( $a->exptime == $b->exptime ) {
520 return 0;
521 }
522 return ( $a->exptime < $b->exptime ) ? 1 : -1;
523 } );
524 $row = array_values( $rowsByShard )[0];
525 }
526
527 $this->debug( __METHOD__ . ": retrieved $key; expiry time is {$row->exptime}" );
528 try {
529 $db = $this->getConnection( $row->shardIndex );
530 $dataByKey[$key] = [
531 self::BLOB_VALUE => $this->dbDecodeSerialValue( $db, $row->value ),
532 self::BLOB_EXPIRY => $this->decodeDbExpiry( $db, $row->exptime ),
533 self::BLOB_CASTOKEN => $getCasToken
534 ? $this->getCasTokenFromRow( $db, $row )
535 : null
536 ];
537 } catch ( DBQueryError $e ) {
538 $this->handleDBError( $e, $row->shardIndex );
539 }
540 }
541
542 return array_merge( $dataByKey, $fallbackResult );
543 }
544
559 private function modifyBlobs(
560 callable $tableWriteCallback,
561 float $mtime,
562 array $argsByKey,
563 &$resByKey = [],
564 $fallback = false
565 ) {
566 if ( $fallback && count( $this->serverTags ) > 1 && $this->dataRedundancy > 1 ) {
567 // fallback doesn't work with data redundancy
568 return false;
569 }
570 // Initialize order-preserved per-key results; callbacks mark successful results
571 $resByKey = array_fill_keys( array_keys( $argsByKey ), false );
572
574 $silenceScope = $this->silenceTransactionProfiler();
575
576 $argsByKeyByTableByShard = [];
577 foreach ( $argsByKey as $key => $args ) {
578 $partitionTable = $this->getTableNameForKey( $key );
579 $shardIndexes = $this->getShardIndexesForKey( $key, $fallback );
580 foreach ( $shardIndexes as $shardIndex ) {
581 $argsByKeyByTableByShard[$shardIndex][$partitionTable][$key] = $args;
582 }
583 }
584
585 $shardIndexesAffected = [];
586 foreach ( $argsByKeyByTableByShard as $shardIndex => $argsByKeyByTables ) {
587 foreach ( $argsByKeyByTables as $table => $ptKeyArgs ) {
588 try {
589 $db = $this->getConnection( $shardIndex );
590 $shardIndexesAffected[] = $shardIndex;
591 $tableWriteCallback( $db, $table, $mtime, $ptKeyArgs, $resByKey );
592 } catch ( DBError $e ) {
593 if ( $fallback ) {
594 $this->handleDBError( $e, $shardIndex );
595 continue;
596 } else {
597 $this->modifyBlobs( $tableWriteCallback, $mtime, $ptKeyArgs, $resByKey, true );
598 }
599 }
600 }
601 }
602
603 $success = !in_array( false, $resByKey, true );
604
605 foreach ( $shardIndexesAffected as $shardIndex ) {
606 try {
607 if (
608 // Random purging is enabled
609 $this->purgePeriod >= 1 &&
610 // Only purge on one in every $this->purgePeriod writes
611 mt_rand( 1, $this->purgePeriod ) == 1 &&
612 // Avoid repeating the delete within a few seconds
613 ( $this->getCurrentTime() - $this->lastGarbageCollect ) > self::GC_DELAY_SEC
614 ) {
615 $this->garbageCollect( $shardIndex );
616 }
617 } catch ( DBError $e ) {
618 $this->handleDBError( $e, $shardIndex );
619 }
620 }
621
622 return $success;
623 }
624
635 private function modifyTableSpecificBlobsForSet(
636 IDatabase $db,
637 string $ptable,
638 float $mtime,
639 array $argsByKey,
640 array &$resByKey
641 ) {
642 $valueSizesByKey = [];
643
644 $rows = [];
645 foreach ( $argsByKey as $key => [ $value, $exptime ] ) {
646 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
647 $serialValue = $this->getSerialized( $value, $key );
648 $rows[] = $this->buildUpsertRow( $db, $key, $serialValue, $expiry );
649
650 $valueSizesByKey[$key] = [ strlen( $serialValue ), 0 ];
651 }
652
653 // T288998: use REPLACE, if possible, to avoid cluttering the binlogs
655 ->replaceInto( $ptable )
656 ->rows( $rows )
657 ->uniqueIndexFields( [ 'keyname' ] )
658 ->caller( __METHOD__ )->execute();
659
660 foreach ( $argsByKey as $key => $unused ) {
661 $resByKey[$key] = true;
662 }
663
664 $this->updateOpStats( self::METRIC_OP_SET, $valueSizesByKey );
665 }
666
677 private function modifyTableSpecificBlobsForDelete(
678 IDatabase $db,
679 string $ptable,
680 float $mtime,
681 array $argsByKey,
682 array &$resByKey
683 ) {
684 // Just purge the keys since there is only one primary (e.g. "source of truth")
686 ->deleteFrom( $ptable )
687 ->where( [ 'keyname' => array_keys( $argsByKey ) ] )
688 ->caller( __METHOD__ )->execute();
689
690 foreach ( $argsByKey as $key => $arg ) {
691 $resByKey[$key] = true;
692 }
693
694 $this->updateOpStats( self::METRIC_OP_DELETE, array_keys( $argsByKey ) );
695 }
696
712 private function modifyTableSpecificBlobsForAdd(
713 IDatabase $db,
714 string $ptable,
715 float $mtime,
716 array $argsByKey,
717 array &$resByKey
718 ) {
719 $valueSizesByKey = [];
720
721 // This check must happen outside the write query to respect eventual consistency
722 $existingKeys = $db->newSelectQueryBuilder()
723 ->select( 'keyname' )
724 ->from( $ptable )
725 ->where( $this->buildExistenceConditions( $db, array_keys( $argsByKey ), (int)$mtime ) )
726 ->caller( __METHOD__ )
727 ->fetchFieldValues();
728 $existingByKey = array_fill_keys( $existingKeys, true );
729
730 $rows = [];
731 foreach ( $argsByKey as $key => [ $value, $exptime ] ) {
732 if ( isset( $existingByKey[$key] ) ) {
733 $this->logger->debug( __METHOD__ . ": $key already exists" );
734 continue;
735 }
736
737 $serialValue = $this->getSerialized( $value, $key );
738 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
739 $valueSizesByKey[$key] = [ strlen( $serialValue ), 0 ];
740 $rows[] = $this->buildUpsertRow( $db, $key, $serialValue, $expiry );
741 }
742 if ( !$rows ) {
743 return;
744 }
746 ->insertInto( $ptable )
747 ->rows( $rows )
748 ->onDuplicateKeyUpdate()
749 ->uniqueIndexFields( [ 'keyname' ] )
750 ->set( $this->buildMultiUpsertSetForOverwrite( $db ) )
751 ->caller( __METHOD__ )->execute();
752
753 foreach ( $argsByKey as $key => $unused ) {
754 $resByKey[$key] = !isset( $existingByKey[$key] );
755 }
756
757 $this->updateOpStats( self::METRIC_OP_ADD, $valueSizesByKey );
758 }
759
775 private function modifyTableSpecificBlobsForCas(
776 IDatabase $db,
777 string $ptable,
778 float $mtime,
779 array $argsByKey,
780 array &$resByKey
781 ) {
782 $valueSizesByKey = [];
783
784 // This check must happen outside the write query to respect eventual consistency
785 $res = $db->newSelectQueryBuilder()
786 ->select( $this->addCasTokenFields( $db, [ 'keyname' ] ) )
787 ->from( $ptable )
788 ->where( $this->buildExistenceConditions( $db, array_keys( $argsByKey ), (int)$mtime ) )
789 ->caller( __METHOD__ )
790 ->fetchResultSet();
791
792 $curTokensByKey = [];
793 foreach ( $res as $row ) {
794 $curTokensByKey[$row->keyname] = $this->getCasTokenFromRow( $db, $row );
795 }
796
797 $nonMatchingByKey = [];
798 $rows = [];
799 foreach ( $argsByKey as $key => [ $value, $exptime, $casToken ] ) {
800 $curToken = $curTokensByKey[$key] ?? null;
801 if ( $curToken === null ) {
802 $nonMatchingByKey[$key] = true;
803 $this->logger->debug( __METHOD__ . ": $key does not exists" );
804 continue;
805 }
806
807 if ( $curToken !== $casToken ) {
808 $nonMatchingByKey[$key] = true;
809 $this->logger->debug( __METHOD__ . ": $key does not have a matching token" );
810 continue;
811 }
812
813 $serialValue = $this->getSerialized( $value, $key );
814 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
815 $valueSizesByKey[$key] = [ strlen( $serialValue ), 0 ];
816
817 $rows[] = $this->buildUpsertRow( $db, $key, $serialValue, $expiry );
818 }
819 if ( !$rows ) {
820 return;
821 }
823 ->insertInto( $ptable )
824 ->rows( $rows )
825 ->onDuplicateKeyUpdate()
826 ->uniqueIndexFields( [ 'keyname' ] )
827 ->set( $this->buildMultiUpsertSetForOverwrite( $db ) )
828 ->caller( __METHOD__ )->execute();
829
830 foreach ( $argsByKey as $key => $unused ) {
831 $resByKey[$key] = !isset( $nonMatchingByKey[$key] );
832 }
833
834 $this->updateOpStats( self::METRIC_OP_CAS, $valueSizesByKey );
835 }
836
856 private function modifyTableSpecificBlobsForChangeTTL(
857 IDatabase $db,
858 string $ptable,
859 float $mtime,
860 array $argsByKey,
861 array &$resByKey
862 ) {
863 $keysBatchesByExpiry = [];
864 foreach ( $argsByKey as $key => [ $exptime ] ) {
865 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
866 $keysBatchesByExpiry[$expiry][] = $key;
867 }
868
869 $existingCount = 0;
870 foreach ( $keysBatchesByExpiry as $expiry => $keyBatch ) {
872 ->update( $ptable )
873 ->set( [ 'exptime' => $this->encodeDbExpiry( $db, $expiry ) ] )
874 ->where( $this->buildExistenceConditions( $db, $keyBatch, (int)$mtime ) )
875 ->caller( __METHOD__ )->execute();
876 $existingCount += $db->affectedRows();
877 }
878 if ( $existingCount === count( $argsByKey ) ) {
879 foreach ( $argsByKey as $key => $args ) {
880 $resByKey[$key] = true;
881 }
882 }
883
884 $this->updateOpStats( self::METRIC_OP_CHANGE_TTL, array_keys( $argsByKey ) );
885 }
886
906 private function modifyTableSpecificBlobsForIncrInit(
907 IDatabase $db,
908 string $ptable,
909 float $mtime,
910 array $argsByKey,
911 array &$resByKey
912 ) {
913 foreach ( $argsByKey as $key => [ $step, $init, $exptime ] ) {
914 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
915
916 // Use a transaction so that changes from other threads are not visible due to
917 // "consistent reads". This way, the exact post-increment value can be returned.
918 // The "live key exists" check can go inside the write query and remain safe for
919 // replication since the TTL for such keys is either indefinite or very short.
920 $atomic = $db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
921 try {
923 ->insertInto( $ptable )
924 ->rows( $this->buildUpsertRow( $db, $key, $init, $expiry ) )
925 ->onDuplicateKeyUpdate()
926 ->uniqueIndexFields( [ 'keyname' ] )
927 ->set( $this->buildIncrUpsertSet( $db, $step, $init, $expiry, (int)$mtime ) )
928 ->caller( __METHOD__ )->execute();
929 $affectedCount = $db->affectedRows();
930 $row = $db->newSelectQueryBuilder()
931 ->select( 'value' )
932 ->from( $ptable )
933 ->where( [ 'keyname' => $key ] )
934 ->caller( __METHOD__ )
935 ->fetchRow();
936 } catch ( Exception $e ) {
937 $db->cancelAtomic( __METHOD__, $atomic );
938 throw $e;
939 }
940 $db->endAtomic( __METHOD__ );
941
942 if ( !$affectedCount || $row === false ) {
943 $this->logger->warning( __METHOD__ . ": failed to set new $key value" );
944 continue;
945 }
946
947 $serialValue = $this->dbDecodeSerialValue( $db, $row->value );
948 if ( !$this->isInteger( $serialValue ) ) {
949 $this->logger->warning( __METHOD__ . ": got non-integer $key value" );
950 continue;
951 }
952
953 $resByKey[$key] = (int)$serialValue;
954 }
955
956 $this->updateOpStats( self::METRIC_OP_INCR, array_keys( $argsByKey ) );
957 }
958
970 private function modifyTableSpecificBlobsForIncrInitAsync(
971 IDatabase $db,
972 string $ptable,
973 float $mtime,
974 array $argsByKey,
975 array &$resByKey
976 ) {
977 foreach ( $argsByKey as $key => [ $step, $init, $exptime ] ) {
978 $expiry = $this->makeNewKeyExpiry( $exptime, (int)$mtime );
980 ->insertInto( $ptable )
981 ->rows( $this->buildUpsertRow( $db, $key, $init, $expiry ) )
982 ->onDuplicateKeyUpdate()
983 ->uniqueIndexFields( [ 'keyname' ] )
984 ->set( $this->buildIncrUpsertSet( $db, $step, $init, $expiry, (int)$mtime ) )
985 ->caller( __METHOD__ )->execute();
986 if ( !$db->affectedRows() ) {
987 $this->logger->warning( __METHOD__ . ": failed to set new $key value" );
988 } else {
989 $resByKey[$key] = true;
990 }
991 }
992 }
993
999 private function makeNewKeyExpiry( $exptime, int $nowTsUnix ) {
1000 $expiry = $this->getExpirationAsTimestamp( $exptime );
1001 // Eventual consistency requires the preservation of recently modified keys.
1002 // Do not create rows with `exptime` fields so low that they might get garbage
1003 // collected before being replicated.
1004 if ( $expiry !== self::TTL_INDEFINITE ) {
1005 $expiry = max( $expiry, $nowTsUnix - self::SAFE_CLOCK_BOUND_SEC );
1006 }
1007
1008 return $expiry;
1009 }
1010
1029 private function newLockingWriteSectionModificationTimestamp( $key, &$scope ) {
1030 if ( !$this->lock( $key, 0 ) ) {
1031 return null;
1032 }
1033
1034 $scope = new ScopedCallback( function () use ( $key ) {
1035 $this->unlock( $key );
1036 } );
1037
1038 // sprintf is used to adjust precision
1039 return (float)sprintf( '%.6F', $this->locks[$key][self::LOCK_TIME] );
1040 }
1041
1050 private function buildExistenceConditions( IDatabase $db, $keys, int $time ) {
1051 // Note that tombstones always have past expiration dates
1052 return [
1053 'keyname' => $keys,
1054 $db->expr( 'exptime', '>=', $db->timestamp( $time ) )
1055 ];
1056 }
1057
1067 private function buildUpsertRow(
1068 IDatabase $db,
1069 $key,
1070 $serialValue,
1071 int $expiry
1072 ) {
1073 $row = [
1074 'keyname' => $key,
1075 'value' => $this->dbEncodeSerialValue( $db, $serialValue ),
1076 'exptime' => $this->encodeDbExpiry( $db, $expiry )
1077 ];
1078
1079 return $row;
1080 }
1081
1088 private function buildMultiUpsertSetForOverwrite( IDatabase $db ) {
1089 $expressionsByColumn = [
1090 'value' => $db->buildExcludedValue( 'value' ),
1091 'exptime' => $db->buildExcludedValue( 'exptime' )
1092 ];
1093
1094 $set = [];
1095 foreach ( $expressionsByColumn as $column => $updateExpression ) {
1096 $set[$column] = new RawSQLValue( $updateExpression );
1097 }
1098
1099 return $set;
1100 }
1101
1112 private function buildIncrUpsertSet(
1113 IDatabase $db,
1114 int $step,
1115 int $init,
1116 int $expiry,
1117 int $mtUnixTs
1118 ) {
1119 // Map of (column => (SQL for non-expired key rows, SQL for expired key rows))
1120 $expressionsByColumn = [
1121 'value' => [
1122 $db->buildIntegerCast( 'value' ) . " + {$db->addQuotes( $step )}",
1123 $db->addQuotes( $this->dbEncodeSerialValue( $db, $init ) )
1124 ],
1125 'exptime' => [
1126 'exptime',
1127 $db->addQuotes( $this->encodeDbExpiry( $db, $expiry ) )
1128 ]
1129 ];
1130
1131 $set = [];
1132 foreach ( $expressionsByColumn as $column => [ $updateExpression, $initExpression ] ) {
1133 $rhs = $db->conditional(
1134 $db->expr( 'exptime', '>=', $db->timestamp( $mtUnixTs ) ),
1135 $updateExpression,
1136 $initExpression
1137 );
1138 $set[$column] = new RawSQLValue( $rhs );
1139 }
1140
1141 return $set;
1142 }
1143
1149 private function encodeDbExpiry( IDatabase $db, int $expiry ) {
1150 return ( $expiry === self::TTL_INDEFINITE )
1151 // Use the maximum timestamp that the column can store
1152 ? $db->timestamp( self::INF_TIMESTAMP_PLACEHOLDER )
1153 // Convert the absolute timestamp into the DB timestamp format
1154 : $db->timestamp( $expiry );
1155 }
1156
1162 private function decodeDbExpiry( IDatabase $db, string $dbExpiry ) {
1163 return ( $dbExpiry === $db->timestamp( self::INF_TIMESTAMP_PLACEHOLDER ) )
1164 ? self::TTL_INDEFINITE
1165 : (int)ConvertibleTimestamp::convert( TS::UNIX, $dbExpiry );
1166 }
1167
1173 private function dbEncodeSerialValue( IDatabase $db, $serialValue ) {
1174 return is_int( $serialValue ) ? (string)$serialValue : $db->encodeBlob( $serialValue );
1175 }
1176
1182 private function dbDecodeSerialValue( IDatabase $db, $blob ) {
1183 return $this->isInteger( $blob ) ? (int)$blob : $db->decodeBlob( $blob );
1184 }
1185
1193 private function addCasTokenFields( IDatabase $db, array $fields ) {
1194 $type = $db->getType();
1195
1196 if ( $type === 'mysql' ) {
1197 $fields['castoken'] = $db->buildConcat( [
1198 'SHA1(value)',
1199 $db->addQuotes( '@' ),
1200 'exptime'
1201 ] );
1202 } elseif ( $type === 'postgres' ) {
1203 $fields['castoken'] = $db->buildConcat( [
1204 'md5(value)',
1205 $db->addQuotes( '@' ),
1206 'exptime'
1207 ] );
1208 } else {
1209 if ( !in_array( 'value', $fields, true ) ) {
1210 $fields[] = 'value';
1211 }
1212 if ( !in_array( 'exptime', $fields, true ) ) {
1213 $fields[] = 'exptime';
1214 }
1215 }
1216
1217 return $fields;
1218 }
1219
1227 private function getCasTokenFromRow( IDatabase $db, stdClass $row ) {
1228 if ( isset( $row->castoken ) ) {
1229 $token = $row->castoken;
1230 } else {
1231 $token = sha1( $this->dbDecodeSerialValue( $db, $row->value ) ) . '@' . $row->exptime;
1232 $this->logger->debug( __METHOD__ . ": application computed hash for CAS token" );
1233 }
1234
1235 return $token;
1236 }
1237
1242 private function garbageCollect( $shardIndex ) {
1243 // set right away, avoid queuing duplicate async callbacks
1244 $this->lastGarbageCollect = $this->getCurrentTime();
1245
1246 $garbageCollector = function () use ( $shardIndex ) {
1247 $db = $this->getConnection( $shardIndex );
1249 $silenceScope = $this->silenceTransactionProfiler();
1250 $this->deleteServerObjectsExpiringBefore(
1251 $db,
1252 (int)$this->getCurrentTime(),
1253 $this->purgeLimit
1254 );
1255 $this->lastGarbageCollect = $this->getCurrentTime();
1256 };
1257
1258 if ( $this->asyncHandler ) {
1259 ( $this->asyncHandler )( $garbageCollector );
1260 } else {
1261 $garbageCollector();
1262 }
1263 }
1264
1267 $timestamp,
1268 ?callable $progress = null,
1269 $limit = INF,
1270 ?string $tag = null
1271 ) {
1273 $silenceScope = $this->silenceTransactionProfiler();
1274
1275 if ( $tag !== null ) {
1276 // Purge one server only, to support concurrent purging in large wiki farms (T282761).
1277 $shardIndexes = [];
1278 if ( !$this->serverTags ) {
1279 throw new InvalidArgumentException( "Given a tag but no tags are configured" );
1280 }
1281 foreach ( $this->serverTags as $serverShardIndex => $serverTag ) {
1282 if ( $tag === $serverTag ) {
1283 $shardIndexes[] = $serverShardIndex;
1284 break;
1285 }
1286 }
1287 if ( !$shardIndexes ) {
1288 throw new InvalidArgumentException( "Unknown server tag: $tag" );
1289 }
1290 } else {
1291 $shardIndexes = $this->getShardServerIndexes();
1292 shuffle( $shardIndexes );
1293 }
1294
1295 $ok = true;
1296 $numServers = count( $shardIndexes );
1297
1298 $keysDeletedCount = 0;
1299 foreach ( $shardIndexes as $numServersDone => $shardIndex ) {
1300 try {
1301 $db = $this->getConnection( $shardIndex );
1302
1303 // Avoid deadlock (T330377)
1304 $lockKey = "SqlBagOStuff-purge-shard:$shardIndex";
1305 if ( !$db->lock( $lockKey, __METHOD__, 0 ) ) {
1306 $this->logger->info( "SqlBagOStuff purge for shard $shardIndex already locked, skip" );
1307 continue;
1308 }
1309
1310 $this->deleteServerObjectsExpiringBefore(
1311 $db,
1312 $timestamp,
1313 $limit,
1314 $keysDeletedCount,
1315 [ 'fn' => $progress, 'serversDone' => $numServersDone, 'serversTotal' => $numServers ]
1316 );
1317 $db->unlock( $lockKey, __METHOD__ );
1318 } catch ( DBError $e ) {
1319 $this->handleDBError( $e, $shardIndex );
1320 $ok = false;
1321 }
1322 }
1323
1324 return $ok;
1325 }
1326
1336 private function deleteServerObjectsExpiringBefore(
1337 IDatabase $db,
1338 $timestamp,
1339 $limit,
1340 &$keysDeletedCount = 0,
1341 ?array $progress = null
1342 ) {
1343 $cutoffUnix = (int)ConvertibleTimestamp::convert( TS::UNIX, $timestamp );
1344 $tableIndexes = range( 0, $this->numTableShards - 1 );
1345 shuffle( $tableIndexes );
1346
1347 $batchSize = min( $this->writeBatchSize, $limit );
1348
1349 foreach ( $tableIndexes as $numShardsDone => $tableIndex ) {
1350 // don't do more than 10% of tables. To avoid overwhelming
1351 // when there are too many of them. Add one to make sure small number
1352 // of tables have been taken care of.
1353 if (
1354 $numShardsDone > ( ( $this->numTableShards / 10 ) + 1 ) &&
1355 // running in context of purge maint script. Go through all tables
1356 $limit !== INF
1357 ) {
1358 break;
1359 }
1360
1361 // The oldest expiry of a row we have deleted on this shard
1362 // (the first row that we deleted)
1363 $minExpUnix = null;
1364 // The most recent expiry time so far, from a row we have deleted on this shard
1365 $maxExp = null;
1366 // Size of the time range we'll delete, in seconds (for progress estimate)
1367 $totalSeconds = null;
1368
1369 do {
1370 $res = $db->newSelectQueryBuilder()
1371 ->select( [ 'keyname', 'exptime' ] )
1372 ->from( $this->getTableNameByShard( $tableIndex ) )
1373 ->where( $db->expr( 'exptime', '<', $db->timestamp( $cutoffUnix ) ) )
1374 ->andWhere( $maxExp ? $db->expr( 'exptime', '>=', $maxExp ) : [] )
1375 ->orderBy( 'exptime', SelectQueryBuilder::SORT_ASC )
1376 ->limit( $batchSize )
1377 ->caller( __METHOD__ )
1378 ->fetchResultSet();
1379
1380 if ( $res->numRows() ) {
1381 $row = $res->current();
1382 if ( $minExpUnix === null ) {
1383 $minExpUnix = (int)ConvertibleTimestamp::convert( TS::UNIX, $row->exptime );
1384 $totalSeconds = max( $cutoffUnix - $minExpUnix, 1 );
1385 }
1386
1387 $keys = [];
1388 foreach ( $res as $row ) {
1389 $keys[] = $row->keyname;
1390 $maxExp = $row->exptime;
1391 }
1392
1394 ->deleteFrom( $this->getTableNameByShard( $tableIndex ) )
1395 ->where( [
1396 'keyname' => $keys,
1397 $db->expr( 'exptime', '<', $db->timestamp( $cutoffUnix ) ),
1398 ] )
1399 ->caller( __METHOD__ )->execute();
1400 $keysDeletedCount += $db->affectedRows();
1401 }
1402
1403 if ( $progress && is_callable( $progress['fn'] ) ) {
1404 if ( $totalSeconds ) {
1405 $maxExpUnix = (int)ConvertibleTimestamp::convert( TS::UNIX, $maxExp );
1406 $remainingSeconds = $cutoffUnix - $maxExpUnix;
1407 $processedSeconds = max( $totalSeconds - $remainingSeconds, 0 );
1408 // For example, if we've done 1.5 table shard, and are thus half-way on the
1409 // 2nd of perhaps 5 tables on this server, then this might be:
1410 // `( 1 + ( 43200 / 86400 ) ) / 5 = 0.3`, or 30% done, of tables on this server.
1411 $tablesDoneRatio =
1412 ( $numShardsDone + ( $processedSeconds / $totalSeconds ) ) / $this->numTableShards;
1413 } else {
1414 $tablesDoneRatio = 1;
1415 }
1416
1417 // For example, if we're 30% done on the last of 10 servers, then this might be:
1418 // `( 9 / 10 ) + ( 0.3 / 10 ) = 0.93`, or 93% done, overall.
1419 $overallRatio = ( $progress['serversDone'] / $progress['serversTotal'] ) +
1420 ( $tablesDoneRatio / $progress['serversTotal'] );
1421 ( $progress['fn'] )( (int)( $overallRatio * 100 ) );
1422 }
1423 } while ( $res->numRows() && $keysDeletedCount < $limit );
1424 }
1425 }
1426
1428 public function doLock( $key, $timeout = 6, $exptime = 6 ) {
1430 $silenceScope = $this->silenceTransactionProfiler();
1431
1432 $lockTsUnix = null;
1433
1434 $shardIndexes = $this->getShardIndexesForKey( $key );
1435 foreach ( $shardIndexes as $shardIndex ) {
1436 try {
1437 $db = $this->getConnection( $shardIndex );
1438 $lockTsUnix = $db->lock( $key, __METHOD__, $timeout, $db::LOCK_TIMESTAMP );
1439 } catch ( DBError $e ) {
1440 $this->handleDBError( $e, $shardIndex );
1441 $this->logger->warning(
1442 __METHOD__ . ' failed due to I/O error for {key}.',
1443 [ 'key' => $key ]
1444 );
1445 }
1446 }
1447
1448 return $lockTsUnix;
1449 }
1450
1452 public function doUnlock( $key ) {
1454 $silenceScope = $this->silenceTransactionProfiler();
1455
1456 $shardIndexes = $this->getShardIndexesForKey( $key );
1457 $released = false;
1458 foreach ( $shardIndexes as $shardIndex ) {
1459 try {
1460 $db = $this->getConnection( $shardIndex );
1461 $released = $db->unlock( $key, __METHOD__ );
1462 } catch ( DBError $e ) {
1463 $this->handleDBError( $e, $shardIndex );
1464 $released = false;
1465 }
1466 }
1467
1468 return $released;
1469 }
1470
1472 protected function makeKeyInternal( $keyspace, $components ) {
1473 $key = strtr( $keyspace, ' ', '_' );
1474 foreach ( $components as $component ) {
1475 $component = strtr( $component ?? '', [
1476 ' ' => '_', // Avoid unnecessary misses from pre-1.35 code
1477 ':' => '%3A',
1478 ] );
1479 $key .= ':' . $component;
1480 }
1481
1482 // SQL schema for 'objectcache' specifies keys as varchar(255).
1483 // * Reserve 45 chars for prefixes used by wrappers like WANObjectCache.
1484 return $this->makeFallbackKey( $key, 205 );
1485 }
1486
1487 protected function requireConvertGenericKey(): bool {
1488 return true;
1489 }
1490
1492 protected function serialize( $value ) {
1493 if ( is_int( $value ) ) {
1494 return $value;
1495 }
1496
1497 $serial = serialize( $value );
1498 if ( $this->hasZlib ) {
1499 // On typical message and page data, this can provide a 3X storage savings
1500 $serial = gzdeflate( $serial );
1501 }
1502
1503 return $serial;
1504 }
1505
1507 protected function unserialize( $value ) {
1508 if ( $value === self::TOMB_SERIAL ) {
1509 return false; // tombstone
1510 }
1511
1512 if ( $this->isInteger( $value ) ) {
1513 return (int)$value;
1514 }
1515
1516 if ( $this->hasZlib ) {
1517 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1518 $decompressed = @gzinflate( $value );
1519
1520 if ( $decompressed !== false ) {
1521 $value = $decompressed;
1522 }
1523 }
1524
1525 return unserialize( $value );
1526 }
1527
1528 private function getLoadBalancer(): ILoadBalancer {
1529 if ( !$this->loadBalancer ) {
1530 $this->loadBalancer = ( $this->loadBalancerCallback )();
1531 }
1532 return $this->loadBalancer;
1533 }
1534
1539 private function getConnectionViaLoadBalancer() {
1540 $lb = $this->getLoadBalancer();
1541
1542 if ( $lb->getServerAttributes( ServerInfo::WRITER_INDEX )[Database::ATTR_DB_LEVEL_LOCKING] ) {
1543 // Use the main connection to avoid transaction deadlocks
1544 $conn = $lb->getMaintenanceConnectionRef( DB_PRIMARY, [], $this->dbDomain );
1545 } else {
1546 // If the RDBMS has row/table/page level locking, then use separate auto-commit
1547 // connection to avoid needless contention and deadlocks.
1548 $conn = $lb->getMaintenanceConnectionRef(
1549 DB_PRIMARY,
1550 [],
1551 $this->dbDomain,
1552 $lb::CONN_TRX_AUTOCOMMIT
1553 );
1554 }
1555
1556 // Make sure any errors are thrown now while we can more easily handle them
1557 $conn->ensureConnection();
1558 return $conn;
1559 }
1560
1567 private function getConnectionFromServerInfo( $shardIndex, array $server ) {
1568 if ( !isset( $this->conns[$shardIndex] ) ) {
1569 $server['logger'] = $this->logger;
1570 // Always use autocommit mode, even if DBO_TRX is configured
1571 $server['flags'] ??= 0;
1572 $server['flags'] &= ~( IDatabase::DBO_TRX | IDatabase::DBO_DEFAULT );
1573
1575 $conn = MediaWikiServices::getInstance()->getDatabaseFactory()
1576 ->create( $server['type'], $server );
1577
1578 // Automatically create the objectcache table for sqlite as needed
1579 if ( $conn->getType() === 'sqlite' ) {
1580 $this->initSqliteDatabase( $conn );
1581 }
1582 $this->conns[$shardIndex] = $conn;
1583 }
1584
1585 // @phan-suppress-next-line PhanTypeMismatchReturnNullable False positive
1586 return $this->conns[$shardIndex];
1587 }
1588
1595 private function handleDBError( DBError $exception, $shardIndex ) {
1596 if ( !$this->useLB && $exception instanceof DBConnectionError ) {
1597 unset( $this->conns[$shardIndex] ); // bug T103435
1598
1599 $now = $this->getCurrentTime();
1600 if ( isset( $this->connFailureTimes[$shardIndex] ) ) {
1601 if ( $now - $this->connFailureTimes[$shardIndex] >= 60 ) {
1602 unset( $this->connFailureTimes[$shardIndex] );
1603 unset( $this->connFailureErrors[$shardIndex] );
1604 } else {
1605 $this->logger->debug( __METHOD__ . ": Server #$shardIndex already down" );
1606 return;
1607 }
1608 }
1609 $this->logger->info( __METHOD__ . ": Server #$shardIndex down until " . ( $now + 60 ) );
1610 $this->connFailureTimes[$shardIndex] = $now;
1611 $this->connFailureErrors[$shardIndex] = $exception;
1612 }
1613 $this->logger->error( "DBError: {$exception->getMessage()}", [ 'exception' => $exception ] );
1614 if ( $exception instanceof DBConnectionError ) {
1615 $this->setLastError( self::ERR_UNREACHABLE );
1616 $this->logger->warning( __METHOD__ . ": ignoring connection error" );
1617 } else {
1618 $this->setLastError( self::ERR_UNEXPECTED );
1619 $this->logger->warning( __METHOD__ . ": ignoring query error" );
1620 }
1621 }
1622
1627 private function initSqliteDatabase( IMaintainableDatabase $db ) {
1628 if ( $db->tableExists( 'objectcache', __METHOD__ ) ) {
1629 return;
1630 }
1631 // Use one table for SQLite; sharding does not seem to have much benefit
1632 $db->query( "PRAGMA journal_mode=WAL", __METHOD__ ); // this is permanent
1633 $db->startAtomic( __METHOD__ ); // atomic DDL
1634 try {
1635 $encTable = $db->tableName( 'objectcache' );
1636 $encExptimeIndex = $db->addIdentifierQuotes( $db->tablePrefix() . 'exptime' );
1637 $db->query(
1638 "CREATE TABLE $encTable (\n" .
1639 " keyname BLOB NOT NULL default '' PRIMARY KEY,\n" .
1640 " value BLOB,\n" .
1641 " exptime BLOB NOT NULL\n" .
1642 ")",
1643 __METHOD__
1644 );
1645 $db->query( "CREATE INDEX $encExptimeIndex ON $encTable (exptime)", __METHOD__ );
1646 $db->endAtomic( __METHOD__ );
1647 } catch ( DBError $e ) {
1648 $db->rollback( __METHOD__ );
1649 throw $e;
1650 }
1651 }
1652
1670 public function createTables() {
1671 foreach ( $this->getShardServerIndexes() as $shardIndex ) {
1672 $db = $this->getConnection( $shardIndex );
1673 if ( in_array( $db->getType(), [ 'mysql', 'postgres' ], true ) ) {
1674 for ( $i = 0; $i < $this->numTableShards; $i++ ) {
1675 $encBaseTable = $db->tableName( 'objectcache' );
1676 $encShardTable = $db->tableName( $this->getTableNameByShard( $i ) );
1677 $db->query( "CREATE TABLE IF NOT EXISTS $encShardTable LIKE $encBaseTable", __METHOD__ );
1678 }
1679 }
1680 }
1681 }
1682
1686 private function getShardServerIndexes() {
1687 if ( $this->useLB ) {
1688 // LoadBalancer based configuration
1689 $shardIndexes = [ 0 ];
1690 } else {
1691 // Striped array of database servers
1692 $shardIndexes = array_keys( $this->serverTags );
1693 }
1694
1695 return $shardIndexes;
1696 }
1697
1701 #[\NoDiscard]
1702 private function silenceTransactionProfiler(): ?ScopedCallback {
1703 if ( $this->serverInfos ) {
1704 return null; // no TransactionProfiler injected anyway
1705 }
1706 return Profiler::instance()->getTransactionProfiler()->silenceForScope();
1707 }
1708}
$fallback
const DB_PRIMARY
Definition defines.php:28
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:68
Service locator for MediaWiki core services.
RDBMS-based caching module.
ILoadBalancer null $loadBalancer
doDelete( $key, $flags=0)
Delete an item.bool True if the item was deleted or not found, false on failure
float $lastGarbageCollect
UNIX timestamp.
string[] $serverTags
(server index => tag/host name)
doSetMulti(array $data, $exptime=0, $flags=0)
bool Success
createTables()
Create the shard tables on all databases.
DBConnectionError[] $connFailureErrors
Map of (shard index => Exception)
serialize( $value)
string|int|false String/integer representation Special handling is usually needed for integers so inc...
requireConvertGenericKey()
Whether ::proxyCall() must re-encode cache keys before calling read/write methods.
int $purgePeriod
Average number of writes required to trigger garbage collection.
doChangeTTL( $key, $exptime, $flags)
bool
unserialize( $value)
mixed Original value or false on error Special handling is usually needed for integers so incr()/decr...
array[] $serverInfos
(server index => server config)
callable null $loadBalancerCallback
Injected function which returns a LoadBalancer.
doDeleteMulti(array $keys, $flags=0)
bool Success
int $numTableShards
Number of table shards to use on each server.
int $purgeLimit
Max expired rows to purge during randomized garbage collection.
__construct( $params)
Create a new backend instance from parameters injected by ObjectCache::newFromParams()
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...
doAdd( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.bool Success
doSet( $key, $value, $exptime=0, $flags=0)
Set an item.bool Success
makeKeyInternal( $keyspace, $components)
Make a cache key for the given keyspace and components.Subclasses may override this method to apply d...
doLock( $key, $timeout=6, $exptime=6)
MediumSpecificBagOStuff::lock()float|null UNIX timestamp of acquisition; null on failure
doIncrWithInit( $key, $exptime, $step, $init, $flags)
int|bool New value or false on failure
IMaintainableDatabase[] $conns
Map of (shard index => DB handle)
string $tableName
doUnlock( $key)
MediumSpecificBagOStuff::unlock()bool Success
doGetMulti(array $keys, $flags=0)
Get an associative array containing the item for each of the keys that have items....
bool $useLB
Whether to use the LoadBalancer.
deleteObjectsExpiringBefore( $timestamp, ?callable $progress=null, $limit=INF, ?string $tag=null)
Delete all objects expiring before a certain date.bool Success; false if unimplemented
doCas( $casToken, $key, $value, $exptime=0, $flags=0)
Set an item if the current CAS token matches the provided CAS token.bool Success
string false null $dbDomain
DB name used for keys using the LoadBalancer.
doChangeTTLMulti(array $keys, $exptime, $flags=0)
bool Success
float[] $connFailureTimes
Map of (shard index => UNIX timestamps)
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.
Helper classs 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.
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'=>[], '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, 250, 300,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailStepsRatio'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'EnableSpecialMute'=> false, 'EnableUserEmailMuteList'=> false, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'UserEmailConfirmationUseHTML'=> false, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ImageLinksSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'PHPSessionHandling'=> 'warn', 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'UseLegacyMediaStyles' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCEngines' => [ 'redis' => 'MediaWiki\\RCFeed\\RedisPubSubFeedEngine', 'udp' => 'MediaWiki\\RCFeed\\UDPRCFeedEngine', ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCache' => false, 'ParserOptionsLogUnsafeSampleRate' => 0, ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'UserEmailConfirmationUseHTML' => 'boolean', 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ImageLinksSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'PHPSessionHandling' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'RCEngines' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCache' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], 'required' => [ 'url', ], ], ], '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.', ],]
addQuotes( $s)
Escape and quote a raw value string for use in a SQL query.
Interface to a relational database.
Definition IDatabase.php:31
rollback( $fname=__METHOD__, $flush=self::FLUSHING_ONE)
Rollback a transaction previously started using begin()
unlock( $lockName, $method)
Release a lock.
endAtomic( $fname=__METHOD__)
Ends an atomic section of SQL statements.
lock( $lockName, $method, $timeout=5, $flags=0)
Acquire a named lock.
newUpdateQueryBuilder()
Get an UpdateQueryBuilder bound to this connection.
newDeleteQueryBuilder()
Get an DeleteQueryBuilder bound to this connection.
affectedRows()
Get the number of rows affected by the last query method call.
newReplaceQueryBuilder()
Get an ReplaceQueryBuilder bound to this connection.
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query statement and return the result.
cancelAtomic( $fname=__METHOD__, ?AtomicSectionIdentifier $sectionId=null)
Cancel an atomic section of SQL statements.
startAtomic( $fname=__METHOD__, $cancelable=self::ATOMIC_NOT_CANCELABLE)
Begin an atomic section of SQL statements.
newInsertQueryBuilder()
Get an InsertQueryBuilder bound to this connection.
This class is a delegate to ILBFactory for a given database cluster.
Advanced database interface for IDatabase handles that include maintenance methods.
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
getType()
Get the RDBMS type of the server (e.g.
newSelectQueryBuilder()
Create an empty SelectQueryBuilder which can be used to run queries against this connection.
tablePrefix( $prefix=null)
Get/set the table prefix.
expr(string $field, string $op, $value)
See Expression::__construct()
tableName(string $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.
buildExcludedValue( $column)
Build a reference to a column value from the conflicting proposed upsert() row.
conditional( $cond, $caseTrueExpression, $caseFalseExpression)
Returns an SQL expression for a simple conditional.
addIdentifierQuotes( $s)
Escape a SQL identifier (e.g.
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by ConvertibleTimestamp to the format used for ins...
buildConcat( $stringList)
Build a concatenation list to feed into a SQL query.
setLastError( $error)
This is actually implemented in the Job class.