[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/ -> SiteConfiguration.php (source)

   1  <?php
   2  /**
   3   * Configuration holder, particularly for multi-wiki sites.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   */
  22  
  23  /**
  24   * This is a class for holding configuration settings, particularly for
  25   * multi-wiki sites.
  26   *
  27   * A basic synopsis:
  28   *
  29   * Consider a wikifarm having three sites: two production sites, one in English
  30   * and one in German, and one testing site. You can assign them easy-to-remember
  31   * identifiers - ISO 639 codes 'en' and 'de' for language wikis, and 'beta' for
  32   * the testing wiki.
  33   *
  34   * You would thus initialize the site configuration by specifying the wiki
  35   * identifiers:
  36   *
  37   * @code
  38   * $conf = new SiteConfiguration;
  39   * $conf->wikis = array( 'de', 'en', 'beta' );
  40   * @endcode
  41   *
  42   * When configuring the MediaWiki global settings (the $wg variables),
  43   * the identifiers will be available to specify settings on a per wiki basis.
  44   *
  45   * @code
  46   * $conf->settings = array(
  47   *    'wgSomeSetting' => array(
  48   *
  49   *        # production:
  50   *        'de'     => false,
  51   *        'en'     => false,
  52   *
  53   *        # test:
  54   *        'beta    => true,
  55   *    ),
  56   * );
  57   * @endcode
  58   *
  59   * With three wikis, that is easy to manage. But what about a farm with
  60   * hundreds of wikis? Site configuration provides a special keyword named
  61   * 'default' which is the value used when a wiki is not found. Hence
  62   * the above code could be written:
  63   *
  64   * @code
  65   * $conf->settings = array(
  66   *    'wgSomeSetting' => array(
  67   *
  68   *        'default' => false,
  69   *
  70   *        # Enable feature on test
  71   *        'beta'    => true,
  72   *    ),
  73   * );
  74   * @endcode
  75   *
  76   *
  77   * Since settings can contain arrays, site configuration provides a way
  78   * to merge an array with the default. This is very useful to avoid
  79   * repeating settings again and again while still maintaining specific changes
  80   * on a per wiki basis.
  81   *
  82   * @code
  83   * $conf->settings = array(
  84   *    'wgMergeSetting' = array(
  85   *        # Value that will be shared among all wikis:
  86   *        'default' => array( NS_USER => true ),
  87   *
  88   *        # Leading '+' means merging the array of value with the defaults
  89   *        '+beta' => array( NS_HELP => true ),
  90   *    ),
  91   * );
  92   *
  93   * # Get configuration for the German site:
  94   * $conf->get( 'wgMergeSetting', 'de' );
  95   * // --> array( NS_USER => true );
  96   *
  97   * # Get configuration for the testing site:
  98   * $conf->get( 'wgMergeSetting', 'beta' );
  99   * // --> array( NS_USER => true, NS_HELP => true );
 100   * @endcode
 101   *
 102   * Finally, to load all configuration settings, extract them in global context:
 103   *
 104   * @code
 105   * # Name / identifier of the wiki as set in $conf->wikis
 106   * $wikiID = 'beta';
 107   * $globals = $conf->getAll( $wikiID );
 108   * extract( $globals );
 109   * @endcode
 110   *
 111   * @todo Give examples for,
 112   * suffixes:
 113   * $conf->suffixes = array( 'wiki' );
 114   * localVHosts
 115   * callbacks!
 116   */
 117  class SiteConfiguration {
 118  
 119      /**
 120       * Array of suffixes, for self::siteFromDB()
 121       */
 122      public $suffixes = array();
 123  
 124      /**
 125       * Array of wikis, should be the same as $wgLocalDatabases
 126       */
 127      public $wikis = array();
 128  
 129      /**
 130       * The whole array of settings
 131       */
 132      public $settings = array();
 133  
 134      /**
 135       * Array of domains that are local and can be handled by the same server
 136       */
 137      public $localVHosts = array();
 138  
 139      /**
 140       * Optional callback to load full configuration data.
 141       * @var string|array
 142       */
 143      public $fullLoadCallback = null;
 144  
 145      /** Whether or not all data has been loaded */
 146      public $fullLoadDone = false;
 147  
 148      /**
 149       * A callback function that returns an array with the following keys (all
 150       * optional):
 151       * - suffix: site's suffix
 152       * - lang: site's lang
 153       * - tags: array of wiki tags
 154       * - params: array of parameters to be replaced
 155       * The function will receive the SiteConfiguration instance in the first
 156       * argument and the wiki in the second one.
 157       * if suffix and lang are passed they will be used for the return value of
 158       * self::siteFromDB() and self::$suffixes will be ignored
 159       *
 160       * @var string|array
 161       */
 162      public $siteParamsCallback = null;
 163  
 164      /**
 165       * Configuration cache for getConfig()
 166       * @var array
 167       */
 168      protected $cfgCache = array();
 169  
 170      /**
 171       * Retrieves a configuration setting for a given wiki.
 172       * @param string $settingName ID of the setting name to retrieve
 173       * @param string $wiki Wiki ID of the wiki in question.
 174       * @param string $suffix The suffix of the wiki in question.
 175       * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
 176       * @param array $wikiTags The tags assigned to the wiki.
 177       * @return mixed The value of the setting requested.
 178       */
 179  	public function get( $settingName, $wiki, $suffix = null, $params = array(),
 180          $wikiTags = array()
 181      ) {
 182          $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
 183          return $this->getSetting( $settingName, $wiki, $params );
 184      }
 185  
 186      /**
 187       * Really retrieves a configuration setting for a given wiki.
 188       *
 189       * @param string $settingName ID of the setting name to retrieve.
 190       * @param string $wiki Wiki ID of the wiki in question.
 191       * @param array $params Array of parameters.
 192       * @return mixed The value of the setting requested.
 193       */
 194  	protected function getSetting( $settingName, $wiki, array $params ) {
 195          $retval = null;
 196          if ( array_key_exists( $settingName, $this->settings ) ) {
 197              $thisSetting =& $this->settings[$settingName];
 198              do {
 199                  // Do individual wiki settings
 200                  if ( array_key_exists( $wiki, $thisSetting ) ) {
 201                      $retval = $thisSetting[$wiki];
 202                      break;
 203                  } elseif ( array_key_exists( "+$wiki", $thisSetting ) && is_array( $thisSetting["+$wiki"] ) ) {
 204                      $retval = $thisSetting["+$wiki"];
 205                  }
 206  
 207                  // Do tag settings
 208                  foreach ( $params['tags'] as $tag ) {
 209                      if ( array_key_exists( $tag, $thisSetting ) ) {
 210                          if ( is_array( $retval ) && is_array( $thisSetting[$tag] ) ) {
 211                              $retval = self::arrayMerge( $retval, $thisSetting[$tag] );
 212                          } else {
 213                              $retval = $thisSetting[$tag];
 214                          }
 215                          break 2;
 216                      } elseif ( array_key_exists( "+$tag", $thisSetting ) && is_array( $thisSetting["+$tag"] ) ) {
 217                          if ( $retval === null ) {
 218                              $retval = array();
 219                          }
 220                          $retval = self::arrayMerge( $retval, $thisSetting["+$tag"] );
 221                      }
 222                  }
 223                  // Do suffix settings
 224                  $suffix = $params['suffix'];
 225                  if ( !is_null( $suffix ) ) {
 226                      if ( array_key_exists( $suffix, $thisSetting ) ) {
 227                          if ( is_array( $retval ) && is_array( $thisSetting[$suffix] ) ) {
 228                              $retval = self::arrayMerge( $retval, $thisSetting[$suffix] );
 229                          } else {
 230                              $retval = $thisSetting[$suffix];
 231                          }
 232                          break;
 233                      } elseif ( array_key_exists( "+$suffix", $thisSetting )
 234                          && is_array( $thisSetting["+$suffix"] )
 235                      ) {
 236                          if ( $retval === null ) {
 237                              $retval = array();
 238                          }
 239                          $retval = self::arrayMerge( $retval, $thisSetting["+$suffix"] );
 240                      }
 241                  }
 242  
 243                  // Fall back to default.
 244                  if ( array_key_exists( 'default', $thisSetting ) ) {
 245                      if ( is_array( $retval ) && is_array( $thisSetting['default'] ) ) {
 246                          $retval = self::arrayMerge( $retval, $thisSetting['default'] );
 247                      } else {
 248                          $retval = $thisSetting['default'];
 249                      }
 250                      break;
 251                  }
 252              } while ( false );
 253          }
 254  
 255          if ( !is_null( $retval ) && count( $params['params'] ) ) {
 256              foreach ( $params['params'] as $key => $value ) {
 257                  $retval = $this->doReplace( '$' . $key, $value, $retval );
 258              }
 259          }
 260          return $retval;
 261      }
 262  
 263      /**
 264       * Type-safe string replace; won't do replacements on non-strings
 265       * private?
 266       *
 267       * @param string $from
 268       * @param string $to
 269       * @param string|array $in
 270       * @return string
 271       */
 272  	function doReplace( $from, $to, $in ) {
 273          if ( is_string( $in ) ) {
 274              return str_replace( $from, $to, $in );
 275          } elseif ( is_array( $in ) ) {
 276              foreach ( $in as $key => $val ) {
 277                  $in[$key] = $this->doReplace( $from, $to, $val );
 278              }
 279              return $in;
 280          } else {
 281              return $in;
 282          }
 283      }
 284  
 285      /**
 286       * Gets all settings for a wiki
 287       * @param string $wiki Wiki ID of the wiki in question.
 288       * @param string $suffix The suffix of the wiki in question.
 289       * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
 290       * @param array $wikiTags The tags assigned to the wiki.
 291       * @return array Array of settings requested.
 292       */
 293  	public function getAll( $wiki, $suffix = null, $params = array(), $wikiTags = array() ) {
 294          $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
 295          $localSettings = array();
 296          foreach ( $this->settings as $varname => $stuff ) {
 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->getSetting( $varname, $wiki, $params );
 305              if ( $append && is_array( $value ) && is_array( $GLOBALS[$var] ) ) {
 306                  $value = self::arrayMerge( $value, $GLOBALS[$var] );
 307              }
 308              if ( !is_null( $value ) ) {
 309                  $localSettings[$var] = $value;
 310              }
 311          }
 312          return $localSettings;
 313      }
 314  
 315      /**
 316       * Retrieves a configuration setting for a given wiki, forced to a boolean.
 317       * @param string $setting ID of the setting name to retrieve
 318       * @param string $wiki Wiki ID of the wiki in question.
 319       * @param string $suffix The suffix of the wiki in question.
 320       * @param array $wikiTags The tags assigned to the wiki.
 321       * @return bool The value of the setting requested.
 322       */
 323  	public function getBool( $setting, $wiki, $suffix = null, $wikiTags = array() ) {
 324          return (bool)$this->get( $setting, $wiki, $suffix, array(), $wikiTags );
 325      }
 326  
 327      /**
 328       * Retrieves an array of local databases
 329       *
 330       * @return array
 331       */
 332      function &getLocalDatabases() {
 333          return $this->wikis;
 334      }
 335  
 336      /**
 337       * Retrieves the value of a given setting, and places it in a variable passed by reference.
 338       * @param string $setting ID of the setting name to retrieve
 339       * @param string $wiki Wiki ID of the wiki in question.
 340       * @param string $suffix The suffix of the wiki in question.
 341       * @param array $var Reference The variable to insert the value into.
 342       * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
 343       * @param array $wikiTags The tags assigned to the wiki.
 344       */
 345  	public function extractVar( $setting, $wiki, $suffix, &$var,
 346          $params = array(), $wikiTags = array()
 347      ) {
 348          $value = $this->get( $setting, $wiki, $suffix, $params, $wikiTags );
 349          if ( !is_null( $value ) ) {
 350              $var = $value;
 351          }
 352      }
 353  
 354      /**
 355       * Retrieves the value of a given setting, and places it in its corresponding global variable.
 356       * @param string $setting ID of the setting name to retrieve
 357       * @param string $wiki Wiki ID of the wiki in question.
 358       * @param string $suffix The suffix of the wiki in question.
 359       * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
 360       * @param array $wikiTags The tags assigned to the wiki.
 361       */
 362  	public function extractGlobal( $setting, $wiki, $suffix = null,
 363          $params = array(), $wikiTags = array()
 364      ) {
 365          $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
 366          $this->extractGlobalSetting( $setting, $wiki, $params );
 367      }
 368  
 369      /**
 370       * @param string $setting
 371       * @param string $wiki
 372       * @param array $params
 373       */
 374  	public function extractGlobalSetting( $setting, $wiki, $params ) {
 375          $value = $this->getSetting( $setting, $wiki, $params );
 376          if ( !is_null( $value ) ) {
 377              if ( substr( $setting, 0, 1 ) == '+' && is_array( $value ) ) {
 378                  $setting = substr( $setting, 1 );
 379                  if ( is_array( $GLOBALS[$setting] ) ) {
 380                      $GLOBALS[$setting] = self::arrayMerge( $GLOBALS[$setting], $value );
 381                  } else {
 382                      $GLOBALS[$setting] = $value;
 383                  }
 384              } else {
 385                  $GLOBALS[$setting] = $value;
 386              }
 387          }
 388      }
 389  
 390      /**
 391       * Retrieves the values of all settings, and places them in their corresponding global variables.
 392       * @param string $wiki Wiki ID of the wiki in question.
 393       * @param string $suffix The suffix of the wiki in question.
 394       * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
 395       * @param array $wikiTags The tags assigned to the wiki.
 396       */
 397  	public function extractAllGlobals( $wiki, $suffix = null, $params = array(),
 398          $wikiTags = array()
 399      ) {
 400          $params = $this->mergeParams( $wiki, $suffix, $params, $wikiTags );
 401          foreach ( $this->settings as $varName => $setting ) {
 402              $this->extractGlobalSetting( $varName, $wiki, $params );
 403          }
 404      }
 405  
 406      /**
 407       * Return specific settings for $wiki
 408       * See the documentation of self::$siteParamsCallback for more in-depth
 409       * documentation about this function
 410       *
 411       * @param string $wiki
 412       * @return array
 413       */
 414  	protected function getWikiParams( $wiki ) {
 415          static $default = array(
 416              'suffix' => null,
 417              'lang' => null,
 418              'tags' => array(),
 419              'params' => array(),
 420          );
 421  
 422          if ( !is_callable( $this->siteParamsCallback ) ) {
 423              return $default;
 424          }
 425  
 426          $ret = call_user_func_array( $this->siteParamsCallback, array( $this, $wiki ) );
 427          # Validate the returned value
 428          if ( !is_array( $ret ) ) {
 429              return $default;
 430          }
 431  
 432          foreach ( $default as $name => $def ) {
 433              if ( !isset( $ret[$name] ) || ( is_array( $default[$name] ) && !is_array( $ret[$name] ) ) ) {
 434                  $ret[$name] = $default[$name];
 435              }
 436          }
 437  
 438          return $ret;
 439      }
 440  
 441      /**
 442       * Merge params between the ones passed to the function and the ones given
 443       * by self::$siteParamsCallback for backward compatibility
 444       * Values returned by self::getWikiParams() have the priority.
 445       *
 446       * @param string $wiki Wiki ID of the wiki in question.
 447       * @param string $suffix The suffix of the wiki in question.
 448       * @param array $params List of parameters. $.'key' is replaced by $value in
 449       *   all returned data.
 450       * @param array $wikiTags The tags assigned to the wiki.
 451       * @return array
 452       */
 453  	protected function mergeParams( $wiki, $suffix, array $params, array $wikiTags ) {
 454          $ret = $this->getWikiParams( $wiki );
 455  
 456          if ( is_null( $ret['suffix'] ) ) {
 457              $ret['suffix'] = $suffix;
 458          }
 459  
 460          $ret['tags'] = array_unique( array_merge( $ret['tags'], $wikiTags ) );
 461  
 462          $ret['params'] += $params;
 463  
 464          // Automatically fill that ones if needed
 465          if ( !isset( $ret['params']['lang'] ) && !is_null( $ret['lang'] ) ) {
 466              $ret['params']['lang'] = $ret['lang'];
 467          }
 468          if ( !isset( $ret['params']['site'] ) && !is_null( $ret['suffix'] ) ) {
 469              $ret['params']['site'] = $ret['suffix'];
 470          }
 471  
 472          return $ret;
 473      }
 474  
 475      /**
 476       * Work out the site and language name from a database name
 477       * @param string $db
 478       *
 479       * @return array
 480       */
 481  	public function siteFromDB( $db ) {
 482          // Allow override
 483          $def = $this->getWikiParams( $db );
 484          if ( !is_null( $def['suffix'] ) && !is_null( $def['lang'] ) ) {
 485              return array( $def['suffix'], $def['lang'] );
 486          }
 487  
 488          $site = null;
 489          $lang = null;
 490          foreach ( $this->suffixes as $altSite => $suffix ) {
 491              if ( $suffix === '' ) {
 492                  $site = '';
 493                  $lang = $db;
 494                  break;
 495              } elseif ( substr( $db, -strlen( $suffix ) ) == $suffix ) {
 496                  $site = is_numeric( $altSite ) ? $suffix : $altSite;
 497                  $lang = substr( $db, 0, strlen( $db ) - strlen( $suffix ) );
 498                  break;
 499              }
 500          }
 501          $lang = str_replace( '_', '-', $lang );
 502          return array( $site, $lang );
 503      }
 504  
 505      /**
 506       * Get the resolved (post-setup) configuration of a potentially foreign wiki.
 507       * For foreign wikis, this is expensive, and only works if maintenance
 508       * scripts are setup to handle the --wiki parameter such as in wiki farms.
 509       *
 510       * @param string $wiki
 511       * @param array|string $settings A setting name or array of setting names
 512       * @return mixed|mixed[] Array if $settings is an array, otherwise the value
 513       * @throws MWException
 514       * @since 1.21
 515       */
 516  	public function getConfig( $wiki, $settings ) {
 517          global $IP;
 518  
 519          $multi = is_array( $settings );
 520          $settings = (array)$settings;
 521          if ( $wiki === wfWikiID() ) { // $wiki is this wiki
 522              $res = array();
 523              foreach ( $settings as $name ) {
 524                  if ( !preg_match( '/^wg[A-Z]/', $name ) ) {
 525                      throw new MWException( "Variable '$name' does start with 'wg'." );
 526                  } elseif ( !isset( $GLOBALS[$name] ) ) {
 527                      throw new MWException( "Variable '$name' is not set." );
 528                  }
 529                  $res[$name] = $GLOBALS[$name];
 530              }
 531          } else { // $wiki is a foreign wiki
 532              if ( isset( $this->cfgCache[$wiki] ) ) {
 533                  $res = array_intersect_key( $this->cfgCache[$wiki], array_flip( $settings ) );
 534                  if ( count( $res ) == count( $settings ) ) {
 535                      return $multi ? $res : current( $res ); // cache hit
 536                  }
 537              } elseif ( !in_array( $wiki, $this->wikis ) ) {
 538                  throw new MWException( "No such wiki '$wiki'." );
 539              } else {
 540                  $this->cfgCache[$wiki] = array();
 541              }
 542              $retVal = 1;
 543              $cmd = wfShellWikiCmd(
 544                  "$IP/maintenance/getConfiguration.php",
 545                  array(
 546                      '--wiki', $wiki,
 547                      '--settings', implode( ' ', $settings ),
 548                      '--format', 'PHP'
 549                  )
 550              );
 551              // ulimit5.sh breaks this call
 552              $data = trim( wfShellExec( $cmd, $retVal, array(), array( 'memory' => 0 ) ) );
 553              if ( $retVal != 0 || !strlen( $data ) ) {
 554                  throw new MWException( "Failed to run getConfiguration.php." );
 555              }
 556              $res = unserialize( $data );
 557              if ( !is_array( $res ) ) {
 558                  throw new MWException( "Failed to unserialize configuration array." );
 559              }
 560              $this->cfgCache[$wiki] = $this->cfgCache[$wiki] + $res;
 561          }
 562  
 563          return $multi ? $res : current( $res );
 564      }
 565  
 566      /**
 567       * Returns true if the given vhost is handled locally.
 568       * @param string $vhost
 569       * @return bool
 570       */
 571  	public function isLocalVHost( $vhost ) {
 572          return in_array( $vhost, $this->localVHosts );
 573      }
 574  
 575      /**
 576       * Merge multiple arrays together.
 577       * On encountering duplicate keys, merge the two, but ONLY if they're arrays.
 578       * PHP's array_merge_recursive() merges ANY duplicate values into arrays,
 579       * which is not fun
 580       *
 581       * @param array $array1
 582       *
 583       * @return array
 584       */
 585  	static function arrayMerge( $array1/* ... */ ) {
 586          $out = $array1;
 587          $argsCount = func_num_args();
 588          for ( $i = 1; $i < $argsCount; $i++ ) {
 589              foreach ( func_get_arg( $i ) as $key => $value ) {
 590                  if ( isset( $out[$key] ) && is_array( $out[$key] ) && is_array( $value ) ) {
 591                      $out[$key] = self::arrayMerge( $out[$key], $value );
 592                  } elseif ( !isset( $out[$key] ) || !$out[$key] && !is_numeric( $key ) ) {
 593                      // Values that evaluate to true given precedence, for the
 594                      // primary purpose of merging permissions arrays.
 595                      $out[$key] = $value;
 596                  } elseif ( is_numeric( $key ) ) {
 597                      $out[] = $value;
 598                  }
 599              }
 600          }
 601  
 602          return $out;
 603      }
 604  
 605  	public function loadFullData() {
 606          if ( $this->fullLoadCallback && !$this->fullLoadDone ) {
 607              call_user_func( $this->fullLoadCallback, $this );
 608              $this->fullLoadDone = true;
 609          }
 610      }
 611  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1