MediaWiki master
SiteConfiguration.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Config;
22
127
131 public $suffixes = [];
132
136 public $wikis = [];
137
144 public $settings = [];
145
150 public $fullLoadCallback = null;
151
153 public $fullLoadDone = false;
154
169 public $siteParamsCallback = null;
170
175 protected $cfgCache = [];
176
186 public function get(
187 $settingName,
188 $wiki,
189 $site = null,
190 $params = [],
191 $wikiTags = []
192 ) {
193 $params = $this->mergeParams( $wiki, $site, $params, $wikiTags );
194 $overrides = $this->settings[$settingName] ?? null;
195 $value = $overrides ? $this->processSetting( $overrides, $wiki, $params['tags'] ) : null;
196 if ( !array_key_exists( '@replaceableSettings', $this->settings )
197 || in_array( $settingName, $this->settings['@replaceableSettings'] )
198 ) {
199 $this->doReplacements( $value, $params['replacements'] );
200 }
201 return $value;
202 }
203
235 private function processSetting( $thisSetting, $wiki, $tags ) {
236 // Optimization: Avoid native type hint on private method called by hot getAll()
237 // <https://gerrit.wikimedia.org/r/c/mediawiki/core/+/820244>
238
239 $retval = null;
240
241 if ( array_key_exists( $wiki, $thisSetting ) ) {
242 // Found override by Wiki ID.
243 $retval = $thisSetting[$wiki];
244 } else {
245 if ( array_key_exists( "+$wiki", $thisSetting ) ) {
246 // Found mergable override by Wiki ID.
247 // We continue to look for more merge candidates.
248 $retval = $thisSetting["+$wiki"];
249 }
250
251 foreach ( $tags as $tag ) {
252 if ( array_key_exists( $tag, $thisSetting ) ) {
253 if ( is_array( $retval ) && is_array( $thisSetting[$tag] ) ) {
254 // Found a mergable override by Tag, without "+" operator.
255 // Merge it with any "+wiki" match from before, and stop the cascade.
256 $retval = self::arrayMerge( $retval, $thisSetting[$tag] );
257 } else {
258 // Found a non-mergable override by Tag.
259 // This could in theory replace a "+wiki" match, but it should never happen
260 // that a setting uses both mergable array values and non-array values.
261 $retval = $thisSetting[$tag];
262 }
263 return $retval;
264 } elseif ( array_key_exists( "+$tag", $thisSetting ) ) {
265 // Found a mergable override by Tag with "+" operator.
266 // Merge it with any "+wiki" or "+tag" matches from before,
267 // and keep looking for more merge candidates.
268 $retval = self::arrayMerge( $retval ?? [], $thisSetting["+$tag"] );
269 }
270 }
271
272 if ( array_key_exists( 'default', $thisSetting ) ) {
273 if ( is_array( $retval ) && is_array( $thisSetting['default'] ) ) {
274 // Found a mergable default
275 // Merge it with any "+wiki" or "+tag" matches from before.
276 $retval = self::arrayMerge( $retval, $thisSetting['default'] );
277 } else {
278 // Found a default
279 // If any array-based values were built up via "+wiki" or "+tag" matches,
280 // these are thrown away here. We don't support merging array values into
281 // non-array values, and the fallback here is to use the default.
282 $retval = $thisSetting['default'];
283 }
284 }
285 }
286 return $retval;
287 }
288
295 private function doReplacements( &$value, $replacements ) {
296 // Optimization: Avoid native type hint on private method called by hot getAll()
297 // <https://gerrit.wikimedia.org/r/c/mediawiki/core/+/820244>
298
299 if ( is_string( $value ) ) {
300 $value = strtr( $value, $replacements );
301 } elseif ( is_array( $value ) ) {
302 foreach ( $value as &$val ) {
303 if ( is_string( $val ) ) {
304 $val = strtr( $val, $replacements );
305 }
306 }
307 }
308 }
309
318 public function getAll( $wiki, $site = null, $params = [], $wikiTags = [] ) {
319 $params = $this->mergeParams( $wiki, $site, $params, $wikiTags );
320 $tags = $params['tags'];
321 $localSettings = [];
322 foreach ( $this->settings as $varname => $overrides ) {
323 $value = $this->processSetting( $overrides, $wiki, $tags );
324 if ( $varname[0] === '+' ) {
325 $varname = substr( $varname, 1 );
326 if ( is_array( $value ) && is_array( $GLOBALS[$varname] ) ) {
327 $value = self::arrayMerge( $value, $GLOBALS[$varname] );
328 }
329 }
330 if ( $value !== null ) {
331 $localSettings[$varname] = $value;
332 }
333 }
334
335 $replacements = $params['replacements'];
336 if ( array_key_exists( '@replaceableSettings', $this->settings ) ) {
337 foreach ( $this->settings['@replaceableSettings'] as $varname ) {
338 if ( array_key_exists( $varname, $localSettings ) ) {
339 $this->doReplacements( $localSettings[$varname], $replacements );
340 }
341 }
342 } else {
343 foreach ( $localSettings as &$value ) {
344 $this->doReplacements( $value, $replacements );
345 }
346 }
347 return $localSettings;
348 }
349
359 public function getBool( $setting, $wiki, $site = null, $wikiTags = [] ) {
360 return (bool)$this->get( $setting, $wiki, $site, [], $wikiTags );
361 }
362
368 public function getLocalDatabases() {
369 return $this->wikis;
370 }
371
383 public function extractVar(
384 $setting,
385 $wiki,
386 $site,
387 &$var,
388 $params = [],
389 $wikiTags = []
390 ) {
391 wfDeprecated( __METHOD__, '1.41' );
392 $value = $this->get( $setting, $wiki, $site, $params, $wikiTags );
393 if ( $value !== null ) {
394 $var = $value;
395 }
396 }
397
408 public function extractGlobal(
409 $setting,
410 $wiki,
411 $site = null,
412 $params = [],
413 $wikiTags = []
414 ) {
415 wfDeprecated( __METHOD__, '1.41' );
416 $params = $this->mergeParams( $wiki, $site, $params, $wikiTags );
417 $this->extractGlobalSetting( $setting, $wiki, $params );
418 }
419
425 public function extractGlobalSetting( $setting, $wiki, $params ) {
426 $overrides = $this->settings[$setting] ?? null;
427 $value = $overrides ? $this->processSetting( $overrides, $wiki, $params['tags'] ) : null;
428 if ( !array_key_exists( '@replaceableSettings', $this->settings )
429 || in_array( $setting, $this->settings['@replaceableSettings'] )
430 ) {
431 $this->doReplacements( $value, $params['replacements'] );
432 }
433 if ( $value !== null ) {
434 if ( substr( $setting, 0, 1 ) == '+' && is_array( $value ) ) {
435 $setting = substr( $setting, 1 );
436 if ( is_array( $GLOBALS[$setting] ) ) {
437 $GLOBALS[$setting] = self::arrayMerge( $GLOBALS[$setting], $value );
438 } else {
439 $GLOBALS[$setting] = $value;
440 }
441 } else {
442 $GLOBALS[$setting] = $value;
443 }
444 }
445 }
446
454 public function extractAllGlobals(
455 $wiki,
456 $site = null,
457 $params = [],
458 $wikiTags = []
459 ) {
460 $params = $this->mergeParams( $wiki, $site, $params, $wikiTags );
461 foreach ( $this->settings as $varName => $setting ) {
462 $this->extractGlobalSetting( $varName, $wiki, $params );
463 }
464 }
465
474 protected function getWikiParams( $wiki ) {
475 static $default = [
476 'suffix' => null,
477 'lang' => null,
478 'tags' => [],
479 'params' => [],
480 ];
481
482 if ( !is_callable( $this->siteParamsCallback ) ) {
483 return $default;
484 }
485
486 $ret = ( $this->siteParamsCallback )( $this, $wiki );
487 # Validate the returned value
488 if ( !is_array( $ret ) ) {
489 return $default;
490 }
491
492 foreach ( $default as $name => $def ) {
493 if ( !isset( $ret[$name] ) || ( is_array( $def ) && !is_array( $ret[$name] ) ) ) {
494 $ret[$name] = $def;
495 }
496 }
497
498 return $ret;
499 }
500
513 protected function mergeParams( $wiki, $site, array $params, array $wikiTags ) {
514 $ret = $this->getWikiParams( $wiki );
515
516 $ret['suffix'] ??= $site;
517
518 // Make tags based on the db suffix (e.g. wiki family) automatically
519 // available for use in wgConf. The user does not have to maintain
520 // wiki tag lookups (e.g. dblists at WMF) for the wiki family.
521 $wikiTags[] = $ret['suffix'];
522
523 $ret['tags'] = array_unique( array_merge( $ret['tags'], $wikiTags ) );
524
525 $ret['params'] += $params;
526
527 // Make the $lang and $site parameters automatically available if they
528 // were provided by `siteParamsCallback` via getWikiParams()
529 if ( !isset( $ret['params']['lang'] ) && $ret['lang'] !== null ) {
530 $ret['params']['lang'] = $ret['lang'];
531 }
532 if ( !isset( $ret['params']['site'] ) && $ret['suffix'] !== null ) {
533 $ret['params']['site'] = $ret['suffix'];
534 }
535
536 // Optimization: For hot getAll() code path, precompute replacements to re-use
537 // over hundreds of processSetting() calls.
538 $ret['replacements'] = [];
539 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive
540 foreach ( $ret['params'] as $key => $value ) {
541 $ret['replacements']['$' . $key] = $value;
542 }
543
544 return $ret;
545 }
546
553 public function siteFromDB( $wiki ) {
554 // Allow override
555 $def = $this->getWikiParams( $wiki );
556 if ( $def['suffix'] !== null && $def['lang'] !== null ) {
557 return [ $def['suffix'], $def['lang'] ];
558 }
559
560 $languageCode = str_replace( '_', '-', $wiki );
561 foreach ( $this->suffixes as $altSite => $suffix ) {
562 if ( $suffix === '' ) {
563 return [ '', $languageCode ];
564 } elseif ( str_ends_with( $wiki, $suffix ) ) {
565 $site = is_string( $altSite ) ? $altSite : $suffix;
566 $languageCode = substr( $languageCode, 0, -strlen( $suffix ) );
567 return [ $site, $languageCode ];
568 }
569 }
570
571 return [ null, null ];
572 }
573
584 private static function arrayMerge( array $array1, array $array2 ) {
585 $out = $array1;
586 foreach ( $array2 as $key => $value ) {
587 if ( isset( $out[$key] ) ) {
588 if ( is_array( $out[$key] ) && is_array( $value ) ) {
589 // Merge the new array into the existing one
590 $out[$key] = self::arrayMerge( $out[$key], $value );
591 } elseif ( is_numeric( $key ) ) {
592 // A numerical key is taken, append the value at the end instead.
593 // It is important that we generally preserve numerical keys and only
594 // fallback to appending values if there are conflicts. This is needed
595 // by configuration variables that hold associative arrays with
596 // meaningful numerical keys, such as $wgNamespacesWithSubpages,
597 // $wgNamespaceProtection, $wgNamespacesToBeSearchedDefault, etc.
598 $out[] = $value;
599 } elseif ( $out[$key] === false ) {
600 // A non-numerical key is taken and holds a false value,
601 // allow it to be overridden always. This exists mainly for the purpose
602 // merging permissions arrays, such as $wgGroupPermissions.
603 $out[$key] = $value;
604 }
605 // Else: The key is already taken and we keep the current value
606
607 } else {
608 // Add a new key.
609 $out[$key] = $value;
610 }
611 }
612
613 return $out;
614 }
615
616 public function loadFullData() {
617 if ( $this->fullLoadCallback && !$this->fullLoadDone ) {
618 ( $this->fullLoadCallback )( $this );
619 $this->fullLoadDone = true;
620 }
621 }
622}
623
625class_alias( SiteConfiguration::class, 'SiteConfiguration' );
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
array $params
The job parameters.
Configuration holder, particularly for multi-wiki sites.
extractGlobalSetting( $setting, $wiki, $params)
$suffixes
Array of suffixes, for self::siteFromDB()
extractVar( $setting, $wiki, $site, &$var, $params=[], $wikiTags=[])
Retrieves the value of a given setting, and places it in a variable passed by reference.
$fullLoadDone
Whether or not all data has been loaded.
getLocalDatabases()
Retrieves an array of local databases.
getWikiParams( $wiki)
Return specific settings for $wiki See the documentation of self::$siteParamsCallback for more in-dep...
siteFromDB( $wiki)
Work out the site and language name from a database name.
extractAllGlobals( $wiki, $site=null, $params=[], $wikiTags=[])
Retrieves the values of all settings, and places them in their corresponding global variables.
array $cfgCache
Configuration cache for getConfig()
string array $fullLoadCallback
Optional callback to load full configuration data.
$wikis
Array of wikis, should be the same as $wgLocalDatabases.
mergeParams( $wiki, $site, array $params, array $wikiTags)
Merge params between the ones passed to the function and the ones given by self::$siteParamsCallback ...
$settings
The whole array of settings.
getAll( $wiki, $site=null, $params=[], $wikiTags=[])
Gets all settings for a wiki.
callable null $siteParamsCallback
A callback function that returns an array with the following keys (all optional):
getBool( $setting, $wiki, $site=null, $wikiTags=[])
Retrieves a configuration setting for a given wiki, forced to a boolean.
extractGlobal( $setting, $wiki, $site=null, $params=[], $wikiTags=[])
Retrieves the value of a given setting, and places it in its corresponding global variable.