MediaWiki REL1_35
SiteConfiguration.php
Go to the documentation of this file.
1<?php
24
122
126 public $suffixes = [];
127
131 public $wikis = [];
132
136 public $settings = [];
137
142 public $fullLoadCallback = null;
143
145 public $fullLoadDone = false;
146
161 public $siteParamsCallback = null;
162
167 protected $cfgCache = [];
168
178 public function get( $settingName, $wiki, $suffix = null, $params = [],
179 $wikiTags = []
180 ) {
181 $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
182 $overrides = $this->settings[$settingName] ?? null;
183 return $overrides ? $this->processSetting( $overrides, $wiki, $params ) : null;
184 }
185
217 private function processSetting( array $thisSetting, $wiki, array $params ) {
218 $retval = null;
219
220 if ( array_key_exists( $wiki, $thisSetting ) ) {
221 // Found override by Wiki ID.
222 $retval = $thisSetting[$wiki];
223 } else {
224 if ( array_key_exists( "+$wiki", $thisSetting ) && is_array( $thisSetting["+$wiki"] ) ) {
225 // Found mergable override by Wiki ID.
226 // We continue to look for more merge candidates.
227 $retval = $thisSetting["+$wiki"];
228 }
229
230 $done = false;
231 foreach ( $params['tags'] as $tag ) {
232 if ( array_key_exists( $tag, $thisSetting ) ) {
233 if ( is_array( $retval ) && is_array( $thisSetting[$tag] ) ) {
234 // Found a mergable override by Tag, without "+" operator.
235 // Merge it with any "+wiki" match from before, and stop the cascade.
236 $retval = self::arrayMerge( $retval, $thisSetting[$tag] );
237 } else {
238 // Found a non-mergable override by Tag.
239 // This could in theory replace a "+wiki" match, but it should never happen
240 // that a setting uses both mergable array values and non-array values.
241 $retval = $thisSetting[$tag];
242 }
243 $done = true;
244 break;
245 } elseif ( array_key_exists( "+$tag", $thisSetting ) && is_array( $thisSetting["+$tag"] ) ) {
246 // Found a mergable override by Tag with "+" operator.
247 // Merge it with any "+wiki" or "+tag" matches from before,
248 // and keep looking for more merge candidates.
249 if ( $retval === null ) {
250 $retval = [];
251 }
252 $retval = self::arrayMerge( $retval, $thisSetting["+$tag"] );
253 }
254 }
255
256 if ( !$done && array_key_exists( 'default', $thisSetting ) ) {
257 if ( is_array( $retval ) && is_array( $thisSetting['default'] ) ) {
258 // Found a mergable default
259 // Merge it with any "+wiki" or "+tag" matches from before.
260 $retval = self::arrayMerge( $retval, $thisSetting['default'] );
261 } else {
262 // Found a default
263 // If any array-based values were built up via "+wiki" or "+tag" matches,
264 // these are thrown away here. We don't support merging array values into
265 // non-array values, and the fallback here is to use the default.
266 $retval = $thisSetting['default'];
267 }
268 }
269 }
270
271 // Type-safe string replacemens, don't do replacements on non-strings.
272 if ( is_string( $retval ) ) {
273 $retval = strtr( $retval, $params['replacements'] );
274 } elseif ( is_array( $retval ) ) {
275 foreach ( $retval as $key => $val ) {
276 if ( is_string( $val ) ) {
277 $retval[$key] = strtr( $val, $params['replacements'] );
278 }
279 }
280 }
281
282 return $retval;
283 }
284
293 public function getAll( $wiki, $suffix = null, $params = [], $wikiTags = [] ) {
294 $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
295 $localSettings = [];
296 foreach ( $this->settings as $varname => $overrides ) {
297 $append = false;
298 $var = $varname;
299 if ( substr( $varname, 0, 1 ) == '+' ) {
300 $append = true;
301 $var = substr( $varname, 1 );
302 }
303
304 $value = $this->processSetting( $overrides, $wiki, $params );
305 if ( $append && is_array( $value ) && is_array( $GLOBALS[$var] ) ) {
306 $value = self::arrayMerge( $value, $GLOBALS[$var] );
307 }
308 if ( $value !== null ) {
309 $localSettings[$var] = $value;
310 }
311 }
312 return $localSettings;
313 }
314
323 public function getBool( $setting, $wiki, $suffix = null, $wikiTags = [] ) {
324 return (bool)$this->get( $setting, $wiki, $suffix, [], $wikiTags );
325 }
326
332 public function getLocalDatabases() {
333 return $this->wikis;
334 }
335
345 public function extractVar( $setting, $wiki, $suffix, &$var,
346 $params = [], $wikiTags = []
347 ) {
348 $value = $this->get( $setting, $wiki, $suffix, $params, $wikiTags );
349 if ( $value !== null ) {
350 $var = $value;
351 }
352 }
353
362 public function extractGlobal( $setting, $wiki, $suffix = null,
363 $params = [], $wikiTags = []
364 ) {
365 $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
366 $this->extractGlobalSetting( $setting, $wiki, $params );
367 }
368
374 public function extractGlobalSetting( $setting, $wiki, $params ) {
375 $overrides = $this->settings[$setting] ?? null;
376 $value = $overrides ? $this->processSetting( $overrides, $wiki, $params ) : null;
377 if ( $value !== null ) {
378 if ( substr( $setting, 0, 1 ) == '+' && is_array( $value ) ) {
379 $setting = substr( $setting, 1 );
380 if ( is_array( $GLOBALS[$setting] ) ) {
381 $GLOBALS[$setting] = self::arrayMerge( $GLOBALS[$setting], $value );
382 } else {
383 $GLOBALS[$setting] = $value;
384 }
385 } else {
386 $GLOBALS[$setting] = $value;
387 }
388 }
389 }
390
398 public function extractAllGlobals( $wiki, $suffix = null, $params = [],
399 $wikiTags = []
400 ) {
401 $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
402 foreach ( $this->settings as $varName => $setting ) {
403 $this->extractGlobalSetting( $varName, $wiki, $params );
404 }
405 }
406
415 protected function getWikiParams( $wiki ) {
416 static $default = [
417 'suffix' => null,
418 'lang' => null,
419 'tags' => [],
420 'params' => [],
421 ];
422
423 if ( !is_callable( $this->siteParamsCallback ) ) {
424 return $default;
425 }
426
427 $ret = ( $this->siteParamsCallback )( $this, $wiki );
428 # Validate the returned value
429 if ( !is_array( $ret ) ) {
430 return $default;
431 }
432
433 foreach ( $default as $name => $def ) {
434 if ( !isset( $ret[$name] ) || ( is_array( $def ) && !is_array( $ret[$name] ) ) ) {
435 $ret[$name] = $def;
436 }
437 }
438
439 return $ret;
440 }
441
454 protected function mergeParams( $wiki, $suffix, array $params, array $wikiTags ) {
455 $ret = $this->getWikiParams( $wiki );
456
457 if ( $ret['suffix'] === null ) {
458 $ret['suffix'] = $suffix;
459 }
460
461 // Make tags based on the db suffix (e.g. wiki family) automatically
462 // available for use in wgConf. The user does not have to maintain
463 // wiki tag lookups (e.g. dblists at WMF) for the wiki family.
464 $wikiTags[] = $ret['suffix'];
465
466 $ret['tags'] = array_unique( array_merge( $ret['tags'], $wikiTags ) );
467
468 $ret['params'] += $params;
469
470 // Make the $lang and $site parameters automatically available if they
471 // were provided by `siteParamsCallback` via getWikiParams()
472 if ( !isset( $ret['params']['lang'] ) && $ret['lang'] !== null ) {
473 $ret['params']['lang'] = $ret['lang'];
474 }
475 if ( !isset( $ret['params']['site'] ) && $ret['suffix'] !== null ) {
476 $ret['params']['site'] = $ret['suffix'];
477 }
478
479 // Precompute the replacements to allow re-use over hundreds of processSetting()
480 // calls, as optimisation for getAll() and extractAllGlobals().
481 $ret['replacements'] = [];
482 foreach ( $ret['params'] as $key => $value ) {
483 $ret['replacements'][ '$' . $key ] = $value;
484 }
485
486 return $ret;
487 }
488
495 public function siteFromDB( $wiki ) {
496 // Allow override
497 $def = $this->getWikiParams( $wiki );
498 if ( $def['suffix'] !== null && $def['lang'] !== null ) {
499 return [ $def['suffix'], $def['lang'] ];
500 }
501
502 $site = null;
503 $lang = null;
504 foreach ( $this->suffixes as $altSite => $suffix ) {
505 if ( $suffix === '' ) {
506 $site = '';
507 $lang = $wiki;
508 break;
509 } elseif ( substr( $wiki, -strlen( $suffix ) ) == $suffix ) {
510 $site = is_numeric( $altSite ) ? $suffix : $altSite;
511 $lang = substr( $wiki, 0, strlen( $wiki ) - strlen( $suffix ) );
512 break;
513 }
514 }
515 if ( $lang !== null ) {
516 $lang = str_replace( '_', '-', $lang );
517 }
518
519 return [ $site, $lang ];
520 }
521
533 public function getConfig( $wiki, $settings ) {
534 global $IP;
535
536 $multi = is_array( $settings );
537 $settings = (array)$settings;
538 if ( WikiMap::isCurrentWikiId( $wiki ) ) { // $wiki is this wiki
539 $res = [];
540 foreach ( $settings as $name ) {
541 if ( !preg_match( '/^wg[A-Z]/', $name ) ) {
542 throw new MWException( "Variable '$name' does start with 'wg'." );
543 } elseif ( !isset( $GLOBALS[$name] ) ) {
544 throw new MWException( "Variable '$name' is not set." );
545 }
546 $res[$name] = $GLOBALS[$name];
547 }
548 } else { // $wiki is a foreign wiki
549 if ( isset( $this->cfgCache[$wiki] ) ) {
550 $res = array_intersect_key( $this->cfgCache[$wiki], array_flip( $settings ) );
551 if ( count( $res ) == count( $settings ) ) {
552 return $multi ? $res : current( $res ); // cache hit
553 }
554 } elseif ( !in_array( $wiki, $this->wikis ) ) {
555 throw new MWException( "No such wiki '$wiki'." );
556 } else {
557 $this->cfgCache[$wiki] = [];
558 }
559 $result = Shell::makeScriptCommand(
560 "$IP/maintenance/getConfiguration.php",
561 [
562 '--wiki', $wiki,
563 '--settings', implode( ' ', $settings ),
564 '--format', 'PHP',
565 ]
566 )
567 // limit.sh breaks this call
568 ->limits( [ 'memory' => 0, 'filesize' => 0 ] )
569 ->execute();
570
571 $data = trim( $result->getStdout() );
572 if ( $result->getExitCode() || $data === '' ) {
573 throw new MWException( "Failed to run getConfiguration.php: {$result->getStdout()}" );
574 }
575 $res = unserialize( $data );
576 if ( !is_array( $res ) ) {
577 throw new MWException( "Failed to unserialize configuration array." );
578 }
579 $this->cfgCache[$wiki] += $res;
580 }
581
582 return $multi ? $res : current( $res );
583 }
584
595 private static function arrayMerge( array $array1, array $array2 ) {
596 $out = $array1;
597 foreach ( $array2 as $key => $value ) {
598 if ( isset( $out[$key] ) ) {
599 if ( is_array( $out[$key] ) && is_array( $value ) ) {
600 // Merge the new array into the existing one
601 $out[$key] = self::arrayMerge( $out[$key], $value );
602 } elseif ( is_numeric( $key ) ) {
603 // A numerical key is taken, append the value at the end instead.
604 // It is important that we generally preserve numerical keys and only
605 // fallback to appending values if there are conflicts. This is needed
606 // by configuration variables that hold associative arrays with
607 // meaningul numerical keys, such as $wgNamespacesWithSubpages,
608 // $wgNamespaceProtection, $wgNamespacesToBeSearchedDefault, etc.
609 $out[] = $value;
610 } elseif ( $out[$key] === false ) {
611 // A non-numerical key is taken and holds a false value,
612 // allow it to be overridden always. This exists mainly for the purpose
613 // merging permissions arrays, such as $wgGroupPermissions.
614 $out[$key] = $value;
615 }
616 // Else: The key is already taken and we keep the current value
617
618 } else {
619 // Add a new key.
620 $out[$key] = $value;
621 }
622 }
623
624 return $out;
625 }
626
627 public function loadFullData() {
628 if ( $this->fullLoadCallback && !$this->fullLoadDone ) {
629 ( $this->fullLoadCallback )( $this );
630 $this->fullLoadDone = true;
631 }
632 }
633}
unserialize( $serialized)
$GLOBALS['IP']
$IP
Definition WebStart.php:49
MediaWiki exception.
Executes shell commands.
Definition Shell.php:44
This is a class for holding configuration settings, particularly for multi-wiki sites.
getConfig( $wiki, $settings)
Get the resolved (post-setup) configuration of a potentially foreign wiki.
extractAllGlobals( $wiki, $suffix=null, $params=[], $wikiTags=[])
Retrieves the values of all settings, and places them in their corresponding global variables.
getLocalDatabases()
Retrieves an array of local databases.
$wikis
Array of wikis, should be the same as $wgLocalDatabases.
mergeParams( $wiki, $suffix, array $params, array $wikiTags)
Merge params between the ones passed to the function and the ones given by self::$siteParamsCallback ...
siteFromDB( $wiki)
Work out the site and language name from a database name.
array $cfgCache
Configuration cache for getConfig()
string array $fullLoadCallback
Optional callback to load full configuration data.
extractGlobal( $setting, $wiki, $suffix=null, $params=[], $wikiTags=[])
Retrieves the value of a given setting, and places it in its corresponding global variable.
string array $siteParamsCallback
A callback function that returns an array with the following keys (all optional):
extractGlobalSetting( $setting, $wiki, $params)
getAll( $wiki, $suffix=null, $params=[], $wikiTags=[])
Gets all settings for a wiki.
extractVar( $setting, $wiki, $suffix, &$var, $params=[], $wikiTags=[])
Retrieves the value of a given setting, and places it in a variable passed by reference.
getWikiParams( $wiki)
Return specific settings for $wiki See the documentation of self::$siteParamsCallback for more in-dep...
getBool( $setting, $wiki, $suffix=null, $wikiTags=[])
Retrieves a configuration setting for a given wiki, forced to a boolean.
$settings
The whole array of settings.
processSetting(array $thisSetting, $wiki, array $params)
Retrieve the configuration setting for a given wiki, based on an overrides array.
static arrayMerge(array $array1, array $array2)
Merge multiple arrays together.
$fullLoadDone
Whether or not all data has been loaded.
$suffixes
Array of suffixes, for self::siteFromDB()
if(!isset( $args[0])) $lang