Configuration database
From MediaWiki.org
Contents |
[edit] Problems to solve
- .php config files are fragile
- easy to break by mistake
- no edit history unless you explicit do external version control
- Editing config files is difficult/inconvenient for many sites
- requires shell access
- can't easily expose limited settings to admins on the wiki, making serious bottlenecks for site config issues on a big wiki farm
- Handling config for multi-wiki farms right now is very difficult
- SiteConfig/InitaliseSettings stuff is weirdly Wikimedia-specific and just plain ugly
- querying settings from a sister site (even simply translating dbname <-> URL and getting descriptive names) is very difficult, unreliable (various settings may be in CommonSettings and not show up in the config arrays)
- extensions are difficult to configure since the array is read in before the extensions and their defaults are loaded
[edit] Requirements
- Should work cleanly for one-off installs
- Should handle Wikimedia-style farms:
- global configuration
- configurations shared among project groups
- configurations specific to a wiki
- need to be able to fetch config info from other wikis cleanly
- Varying user permissions; not all settings should be editable by everyone
- Metadata...
- For each config (core or ext) provide:
- type
- validation rules
- friendly name
- more detailed description, examples
- Default
- For each config (core or ext) provide:
- Needs high-level read/write support, so we can abstract backends more easily (CDB,SQL,etc)
Possibly use HtmlForm or similar framework for defining UI paging? Similar to prefs rewrite?
- We can adapt some of the form field types in Configure (that's where I got the idea for HTMLForm) for the new configuration (there are some specialist types like group permissions arrays).
[edit] Data types
- bool
- int
- string
- URL
- local path
- message key
- other
- wiki reference
- array of strings
- array of items
- ?
[edit] Internal interface
Common case: local lookups:
Conf::get( 'serverUrl' )(shortcut)Conf::singleton()->reallyGet( 'serverUrl' )
Other-local-site info lookups:
Conf::get( 'serverUrl', 'dewiki' );
abstract class Conf { // All of the configuration variables we've loaded thus far protected $data; // The Wiki ID (usually $wgDBname) private $wikiId; // Singleton private static $__instance; protected function __construct( $conf ) { $this->config = $conf['id']; } /** * Initialize a new child class based on a configuration array * @param $conf Array of configuration settings, see $wgConfiguration * for details * @return unknown_type */ private static function newFromSettings( $conf ) { $class = ucfirst( $conf['type'] ) . 'Conf'; if( !class_exists( $class ) ) { throw new MWException( '$wgConf misconfigured with invalid "type"' ); } return new $class( $conf ); } /** * Get the singleton if we don't want a specific wiki * @param $wiki String An id for a remote wiki * @return Conf child */ public static function load( $wiki = false ) { if( !self::$__instance ) { global $wgConfiguration; self::$__instance = self::newFromSettings( $wgConfiguration ); } if( $wiki && $wiki != self::$__instace->getWikiId() ) { // Load configuration for a different wiki, not sure how // we're gonna do this yet return null; } return self::$__instance; } /** * Get a property from the configuration database, falling back * to DefaultSettings if undefined * @param $key String Any possible configuration string * @param $wiki String A wiki ID of a remote configuration [no-op] * @return Mixed */ public static function get( $key, $wiki = false ) { return self::load( $wiki )->reallyGet( $key ); } /** * Set a property to the configuration database * @param $key String Any possible configuration string * @param $value Mixed Anything * @param $write boolean If true, write to database * @param $wiki String A wiki ID of a remote configuration [no-op] * @return Boolean true on successful write */ public static function set( $key, $value, $write = false, $wiki = false ) { return self::load( $wiki )->reallySet( $key, $value, $write ); } /** * What is the wiki ID for this site? * @return String */ public function getWikiId() { return $this->wikiId; } /** * Actually retrieve the data. Check local var cache before * trying the database and then default settings * @param $key String a configuration key * @return Mixed */ protected function reallyGet( $key ) { if( !isset( $this->data[$key] ) ) { $val = $this->retrieveSetting( $key ); if( !$val ) { $val = DefaultSettings::$ds[$key]; } $this->data[$key] = $this->stringToBoolean( $val ); } return $this->data[$key]; } /** * Set the var to the local cache, then potentially to the database itself * @param $key a configuration key * @param $value Mixed * @param $write Boolean true to write to the database, false for request-only * @return unknown_type */ protected function reallySet( $key, $value, $write ) { if( count( $this->data ) > 100 ) { $this->data = array(); } $this->data[$key] = $value; if( $write ) { return $this->applySetting( $key, $this->booleanToString( $value ) ); } return true; } /** * This is the implementation-specific part of the get() process. * Child classes will need to implement this (eg: SQL queries, DBA * calls, etc) * @param $key String a configuration key * @return Mixed */ abstract protected function retrieveSetting( $key ); /** * This is the implementation-specific part of the set() process. * Child classes will need to implement this (eg: SQL queries, DBA * calls, etc) * @param $key String a configuration key * @param $value Mixed * @return Boolean success on write */ abstract protected function applySetting( $key, $value ); /** * Turn special-case * @return boolean (or original value, if not special-case) */ private function stringToBoolean( $v ) { if( $v === 'boolean-true' ) { return true; } elseif( $v === 'boolean-false' ) { return false; } return $v; } /** * Munge a boolean into a string * Some databases (like CDB) don't like storing non-strings. * Since booleans are commonly used in configuration, we * munge it to our 'boolean-true|false' strings. * @param $v Mixed, anything to go in the database * @return String (or original value, if not special-case) */ private function booleanToString( $v ) { if( is_boolean( $v ) ) { return $v ? 'boolean-true' : 'boolean-false'; } return $v; } }
[edit] User interface
There's a lot that can be adapted or copied from Configure.
[edit] Bugs that could be solved / New features
- bug 12518 - Cross-wiki userrights reflects groups on local wiki, not target wiki
- Maybe add a iw_wiki col in the interwiki table that references the wiki ID:
- bug 11 - Red interwiki links -- check for page existence across wikis
- bug 9890 - Reasonably efficient interwiki transclusion
- (these sorts of things benefit from being able to look up things like remote namespaces cleanly, so we can pull data from a db directly instead of doing some nasty api hack)
- Drop $wgLocalInterwiki and check if iw_wiki == wfWikiId().
- Maybe run internal HTTP requests (transwiki imports, fetching remote file description page, interwiki transclusion) internally?
- Drop $wgLocalDatabases and replace this with a query in the database.
[edit] See also
- module registry idea notes
- Extension:Configure experience and lessons
- Zend_Config