MediaWiki  master
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  $lang = str_replace( '_', '-', $lang );
516 
517  return [ $site, $lang ];
518  }
519 
531  public function getConfig( $wiki, $settings ) {
532  global $IP;
533 
534  $multi = is_array( $settings );
535  $settings = (array)$settings;
536  if ( WikiMap::isCurrentWikiId( $wiki ) ) { // $wiki is this wiki
537  $res = [];
538  foreach ( $settings as $name ) {
539  if ( !preg_match( '/^wg[A-Z]/', $name ) ) {
540  throw new MWException( "Variable '$name' does start with 'wg'." );
541  } elseif ( !isset( $GLOBALS[$name] ) ) {
542  throw new MWException( "Variable '$name' is not set." );
543  }
544  $res[$name] = $GLOBALS[$name];
545  }
546  } else { // $wiki is a foreign wiki
547  if ( isset( $this->cfgCache[$wiki] ) ) {
548  $res = array_intersect_key( $this->cfgCache[$wiki], array_flip( $settings ) );
549  if ( count( $res ) == count( $settings ) ) {
550  return $multi ? $res : current( $res ); // cache hit
551  }
552  } elseif ( !in_array( $wiki, $this->wikis ) ) {
553  throw new MWException( "No such wiki '$wiki'." );
554  } else {
555  $this->cfgCache[$wiki] = [];
556  }
557  $result = Shell::makeScriptCommand(
558  "$IP/maintenance/getConfiguration.php",
559  [
560  '--wiki', $wiki,
561  '--settings', implode( ' ', $settings ),
562  '--format', 'PHP',
563  ]
564  )
565  // limit.sh breaks this call
566  ->limits( [ 'memory' => 0, 'filesize' => 0 ] )
567  ->execute();
568 
569  $data = trim( $result->getStdout() );
570  if ( $result->getExitCode() || $data === '' ) {
571  throw new MWException( "Failed to run getConfiguration.php: {$result->getStdout()}" );
572  }
573  $res = unserialize( $data );
574  if ( !is_array( $res ) ) {
575  throw new MWException( "Failed to unserialize configuration array." );
576  }
577  $this->cfgCache[$wiki] += $res;
578  }
579 
580  return $multi ? $res : current( $res );
581  }
582 
593  private static function arrayMerge( array $array1, array $array2 ) {
594  $out = $array1;
595  foreach ( $array2 as $key => $value ) {
596  if ( isset( $out[$key] ) ) {
597  if ( is_array( $out[$key] ) && is_array( $value ) ) {
598  // Merge the new array into the existing one
599  $out[$key] = self::arrayMerge( $out[$key], $value );
600  } elseif ( is_numeric( $key ) ) {
601  // A numerical key is taken, append the value at the end instead.
602  // It is important that we generally preserve numerical keys and only
603  // fallback to appending values if there are conflicts. This is needed
604  // by configuration variables that hold associative arrays with
605  // meaningul numerical keys, such as $wgNamespacesWithSubpages,
606  // $wgNamespaceProtection, $wgNamespacesToBeSearchedDefault, etc.
607  $out[] = $value;
608  } elseif ( $out[$key] === false ) {
609  // A non-numerical key is taken and holds a false value,
610  // allow it to be overridden always. This exists mainly for the purpose
611  // merging permissions arrays, such as $wgGroupPermissions.
612  $out[$key] = $value;
613  }
614  // Else: The key is already taken and we keep the current value
615 
616  } else {
617  // Add a new key.
618  $out[$key] = $value;
619  }
620  }
621 
622  return $out;
623  }
624 
625  public function loadFullData() {
626  if ( $this->fullLoadCallback && !$this->fullLoadDone ) {
627  ( $this->fullLoadCallback )( $this );
628  $this->fullLoadDone = true;
629  }
630  }
631 }
MediaWiki\Shell\Shell
Executes shell commands.
Definition: Shell.php:44
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:345
SiteConfiguration\$fullLoadDone
$fullLoadDone
Whether or not all data has been loaded.
Definition: SiteConfiguration.php:145
SiteConfiguration
This is a class for holding configuration settings, particularly for multi-wiki sites.
Definition: SiteConfiguration.php:121
SiteConfiguration\getLocalDatabases
getLocalDatabases()
Retrieves an array of local databases.
Definition: SiteConfiguration.php:332
WikiMap\isCurrentWikiId
static isCurrentWikiId( $wikiId)
Definition: WikiMap.php:321
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
SiteConfiguration\getConfig
getConfig( $wiki, $settings)
Get the resolved (post-setup) configuration of a potentially foreign wiki.
Definition: SiteConfiguration.php:531
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:398
$res
$res
Definition: testCompression.php:57
SiteConfiguration\siteFromDB
siteFromDB( $wiki)
Work out the site and language name from a database name.
Definition: SiteConfiguration.php:495
SiteConfiguration\getBool
getBool( $setting, $wiki, $suffix=null, $wikiTags=[])
Retrieves a configuration setting for a given wiki, forced to a boolean.
Definition: SiteConfiguration.php:323
SiteConfiguration\arrayMerge
static arrayMerge(array $array1, array $array2)
Merge multiple arrays together.
Definition: SiteConfiguration.php:593
MWException
MediaWiki exception.
Definition: MWException.php:29
SiteConfiguration\$cfgCache
array $cfgCache
Configuration cache for getConfig()
Definition: SiteConfiguration.php:167
SiteConfiguration\$siteParamsCallback
string array $siteParamsCallback
A callback function that returns an array with the following keys (all optional):
Definition: SiteConfiguration.php:161
SiteConfiguration\getAll
getAll( $wiki, $suffix=null, $params=[], $wikiTags=[])
Gets all settings for a wiki.
Definition: SiteConfiguration.php:293
SiteConfiguration\processSetting
processSetting(array $thisSetting, $wiki, array $params)
Retrieve the configuration setting for a given wiki, based on an overrides array.
Definition: SiteConfiguration.php:217
unserialize
unserialize( $serialized)
Definition: ApiMessageTrait.php:146
SiteConfiguration\$wikis
$wikis
Array of wikis, should be the same as $wgLocalDatabases.
Definition: SiteConfiguration.php:131
SiteConfiguration\$fullLoadCallback
string array $fullLoadCallback
Optional callback to load full configuration data.
Definition: SiteConfiguration.php:142
SiteConfiguration\$settings
$settings
The whole array of settings.
Definition: SiteConfiguration.php:136
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:362
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:454
SiteConfiguration\loadFullData
loadFullData()
Definition: SiteConfiguration.php:625
$IP
$IP
Definition: WebStart.php:49
SiteConfiguration\extractGlobalSetting
extractGlobalSetting( $setting, $wiki, $params)
Definition: SiteConfiguration.php:374
SiteConfiguration\$suffixes
$suffixes
Array of suffixes, for self::siteFromDB()
Definition: SiteConfiguration.php:126
$GLOBALS
$GLOBALS['IP']
Definition: ComposerHookHandler.php:6
SiteConfiguration\getWikiParams
getWikiParams( $wiki)
Return specific settings for $wiki See the documentation of self::$siteParamsCallback for more in-dep...
Definition: SiteConfiguration.php:415