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