r52503 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r52502‎ | r52503 | r52504 >
Date:07:11, 28 June 2009
Author:tstarling
Status:resolved (Comments)
Tags:
Comment:
* Introduced a new system for localisation caching. The system is based around fast fetches of individual messages, minimising memory overhead and startup time in the typical case. It handles both core messages (formerly in Language.php) and extension messages (formerly in MessageCache.php). Profiling indicates a significant win for average throughput.
* The serialized message cache, which would have been redundant, has been removed. Similar performance characteristics can be achieved with $wgLocalisationCacheConf['manualRecache'] = true;
* Added a maintenance script rebuildLocalisationCache.php for offline rebuilding of the localisation cache.
* Extension i18n files can now contain any of the variables which can be set in Messages*.php. It is possible, and recommended, to use this feature instead of the hooks for special page aliases and magic words.
* $wgExtensionAliasesFiles, LanguageGetMagic and LanguageGetSpecialPageAliases are retained for backwards compatibility. $wgMessageCache->addMessages() and related functions have been removed. wfLoadExtensionMessages() is a no-op and can continue to be called for b/c.
* Introduced $wgCacheDirectory as a default location for the various local caches that have accumulated. Suggested $IP/cache as a good place for it in the default LocalSettings.php and created this directory with a deny-all .htaccess.
* Patched Exception.php to avoid using the message cache when an exception is thrown from within LocalisationCache, since this tends to fail horribly.
* Removed Language::getLocalisationArray(), Language::loadLocalisation(), Language::load()
* Fixed FileDependency::__sleep()
* In Cdb.php, fixed newlines in debug messages

In MessageCache::get():
* Replaced calls to $wgContLang capitalisation functions with plain PHP functions, reducing the typical case from 99us to 93us. Message cache keys are already documented as being restricted to ASCII.
* Implemented a more efficient way to filter out bogus language codes, reducing the "foo/en" case from 430us to 101us
* Optimised wfRunHooks() in the typical do-nothing case, from ~30us to ~3us. This reduced MessageCache::get() typical case time from 93us to 38us.
* Removed hook MessageNotInMwNs to save an extra 3us per cache hit. Reimplemented the only user (LocalisationUpdate) using the new hook LocalisationCacheRecache.
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/cache (added) (history)
  • /trunk/phase3/cache/.htaccess (added) (history)
  • /trunk/phase3/config/index.php (modified) (history)
  • /trunk/phase3/docs/hooks.txt (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/CacheDependency.php (modified) (history)
  • /trunk/phase3/includes/Cdb.php (modified) (history)
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/Exception.php (modified) (history)
  • /trunk/phase3/includes/GlobalFunctions.php (modified) (history)
  • /trunk/phase3/includes/HTMLFileCache.php (modified) (history)
  • /trunk/phase3/includes/Hooks.php (modified) (history)
  • /trunk/phase3/includes/LocalisationCache.php (added) (history)
  • /trunk/phase3/includes/MagicWord.php (modified) (history)
  • /trunk/phase3/includes/MessageCache.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQuerySiteinfo.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialAllmessages.php (modified) (history)
  • /trunk/phase3/languages/Language.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/maintenance/archives/patch-l10n_cache.sql (added) (history)
  • /trunk/phase3/maintenance/rebuildLocalisationCache.php (added) (history)
  • /trunk/phase3/maintenance/tables.sql (modified) (history)
  • /trunk/phase3/maintenance/updaters.inc (modified) (history)
  • /trunk/phase3/serialized/Makefile (modified) (history)
  • /trunk/phase3/serialized/README (deleted) (history)
  • /trunk/phase3/serialized/serialize-localisation.php (deleted) (history)

Diff [purge]

Index: trunk/phase3/maintenance/archives/patch-l10n_cache.sql
@@ -0,0 +1,8 @@
 2+-- Table for storing localisation data
 3+CREATE TABLE /*_*/l10n_cache (
 4+ lc_lang varbinary(32) NOT NULL,
 5+ lc_key varchar(255) NOT NULL,
 6+ lc_value mediumblob NOT NULL
 7+);
 8+CREATE INDEX /*i*/lc_lang_key ON /*_*/l10n_cache (lc_lang, lc_key);
 9+
Property changes on: trunk/phase3/maintenance/archives/patch-l10n_cache.sql
___________________________________________________________________
Added: svn:eol-style
110 + native
Index: trunk/phase3/maintenance/updaters.inc
@@ -161,6 +161,7 @@
162162 array( 'add_table', 'log_search', 'patch-log_search.sql' ),
163163 array( 'do_log_search_population' ),
164164 array( 'add_field', 'logging', 'log_user_text', 'patch-log_user_text.sql' ),
 165+ array( 'add_table', 'l10n_cache', 'patch-l10n_cache.sql' ),
165166 ),
166167
167168 'sqlite' => array(
@@ -180,6 +181,7 @@
181182 array( 'add_table', 'log_search', 'patch-log_search.sql' ),
182183 array( 'do_log_search_population' ),
183184 array( 'add_field', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ),
 185+ array( 'add_table', 'l10n_cache', 'patch-l10n_cache.sql' ),
184186 ),
185187 );
186188
Index: trunk/phase3/maintenance/rebuildLocalisationCache.php
@@ -0,0 +1,41 @@
 2+<?php
 3+
 4+/**
 5+ * Rebuild the localisation cache. Useful if you disabled automatic updates
 6+ * using $wgLocalisationCacheConf['manualRecache'] = true;
 7+ *
 8+ * Usage:
 9+ * php rebuildLocalisationCache.php [--force]
 10+ *
 11+ * Use --force to rebuild all files, even the ones that are not out of date.
 12+ */
 13+
 14+require( dirname(__FILE__).'/commandLine.inc' );
 15+ini_set( 'memory_limit', '200M' );
 16+
 17+$force = isset( $options['force'] );
 18+
 19+$conf = $wgLocalisationCacheConf;
 20+$conf['manualRecache'] = false; // Allow fallbacks to create CDB files
 21+if ( $force ) {
 22+ $conf['forceRecache'] = true;
 23+}
 24+$lc = new LocalisationCache_BulkLoad( $conf );
 25+
 26+$codes = array_keys( Language::getLanguageNames( true ) );
 27+sort( $codes );
 28+$numRebuilt = 0;
 29+foreach ( $codes as $code ) {
 30+ if ( $force || $lc->isExpired( $code ) ) {
 31+ echo "Rebuilding $code...\n";
 32+ $lc->recache( $code );
 33+ $numRebuilt++;
 34+ }
 35+}
 36+echo "$numRebuilt languages rebuilt out of " . count( $codes ) . ".\n";
 37+if ( $numRebuilt == 0 ) {
 38+ echo "Use --force to rebuild the caches which are still fresh.\n";
 39+}
 40+
 41+
 42+
Property changes on: trunk/phase3/maintenance/rebuildLocalisationCache.php
___________________________________________________________________
Added: svn:eol-style
143 + native
Index: trunk/phase3/maintenance/tables.sql
@@ -1310,4 +1310,15 @@
13111311 vt_tag varchar(255) NOT NULL PRIMARY KEY
13121312 ) /*$wgDBTableOptions*/;
13131313
 1314+-- Table for storing localisation data
 1315+CREATE TABLE /*_*/l10n_cache (
 1316+ -- Language code
 1317+ lc_lang varbinary(32) NOT NULL,
 1318+ -- Cache key
 1319+ lc_key varchar(255) NOT NULL,
 1320+ -- Value
 1321+ lc_value mediumblob NOT NULL
 1322+);
 1323+CREATE INDEX /*i*/lc_lang_key ON /*_*/l10n_cache (lc_lang, lc_key);
 1324+
13141325 -- vim: sw=2 sts=2 et
Index: trunk/phase3/docs/hooks.txt
@@ -833,13 +833,15 @@
834834 &$result: Set this to either true (passes) or the key for a message error
835835 $user: User the password is being validated for
836836
837 -'LanguageGetMagic': Use this to define synonyms of magic words depending
838 -of the language
 837+'LanguageGetMagic': DEPRECATED, use $magicWords in a file listed in
 838+$wgExtensionMessagesFiles instead.
 839+Use this to define synonyms of magic words depending of the language
839840 $magicExtensions: associative array of magic words synonyms
840841 $lang: laguage code (string)
841842
842 -'LanguageGetSpecialPageAliases': Use to define aliases of special pages
843 -names depending of the language
 843+'LanguageGetSpecialPageAliases': DEPRECATED, use $specialPageAliases in a file
 844+listed in $wgExtensionMessagesFiles instead.
 845+Use to define aliases of special pages names depending of the language
844846 $specialPageAliases: associative array of magic words synonyms
845847 $lang: laguage code (string)
846848
@@ -900,10 +902,6 @@
901903 'ListDefinedTags': When trying to find all defined tags.
902904 &$tags: The list of tags.
903905
904 -'LoadAllMessages': called by MessageCache::loadAllMessages() to load extensions
905 -messages
906 -&$messageCache: The MessageCache object
907 -
908906 'LoadExtensionSchemaUpdates': called by maintenance/updaters.inc when upgrading
909907 database schema
910908
@@ -1000,13 +998,6 @@
1001999 $title: name of the page changed.
10021000 $text: new contents of the page.
10031001
1004 -'MessageNotInMwNs': When trying to get a message that isn't found in the
1005 -MediaWiki namespace (but before checking the message files)
1006 -&$message: message's content; can be changed
1007 -$lckey: message's name
1008 -$langcode: language code
1009 -$isFullKey: specifies whether $lckey is a two part key "msg/lang"
1010 -
10111002 'MonoBookTemplateToolboxEnd': Called by Monobook skin after toolbox links have
10121003 been rendered (useful for adding more)
10131004 Note: this is only run for the Monobook skin. To add items to the toolbox
Index: trunk/phase3/cache/.htaccess
@@ -0,0 +1 @@
 2+Deny from all
Index: trunk/phase3/includes/GlobalFunctions.php
@@ -2932,42 +2932,9 @@
29332933
29342934 /**
29352935 * Load an extension messages file
2936 - *
2937 - * @param string $extensionName Name of extension to load messages from\for.
2938 - * @param string $langcode Language to load messages for, or false for default
2939 - * behvaiour (en, content language and user language).
2940 - * @since r24808 (v1.11) Using this method of loading extension messages will not work
2941 - * on MediaWiki prior to that
 2936+ * @deprecated
29422937 */
29432938 function wfLoadExtensionMessages( $extensionName, $langcode = false ) {
2944 - global $wgExtensionMessagesFiles, $wgMessageCache, $wgLang, $wgContLang;
2945 -
2946 - #For recording whether extension message files have been loaded in a given language.
2947 - static $loaded = array();
2948 -
2949 - if( !array_key_exists( $extensionName, $loaded ) ) {
2950 - $loaded[$extensionName] = array();
2951 - }
2952 -
2953 - if ( !isset($wgExtensionMessagesFiles[$extensionName]) ) {
2954 - throw new MWException( "Messages file for extensions $extensionName is not defined" );
2955 - }
2956 -
2957 - if( !$langcode && !array_key_exists( '*', $loaded[$extensionName] ) ) {
2958 - # Just do en, content language and user language.
2959 - $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], false );
2960 - # Mark that they have been loaded.
2961 - $loaded[$extensionName]['en'] = true;
2962 - $loaded[$extensionName][$wgLang->getCode()] = true;
2963 - $loaded[$extensionName][$wgContLang->getCode()] = true;
2964 - # Mark that this part has been done to avoid weird if statements.
2965 - $loaded[$extensionName]['*'] = true;
2966 - } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $loaded[$extensionName] ) ) {
2967 - # Load messages for specified language.
2968 - $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], $langcode );
2969 - # Mark that they have been loaded.
2970 - $loaded[$extensionName][$langcode] = true;
2971 - }
29722939 }
29732940
29742941 /**
Index: trunk/phase3/includes/LocalisationCache.php
@@ -0,0 +1,880 @@
 2+<?php
 3+
 4+define( 'MW_LC_VERSION', 1 );
 5+
 6+/**
 7+ * Class for caching the contents of localisation files, Messages*.php
 8+ * and *.i18n.php.
 9+ *
 10+ * An instance of this class is available using Language::getLocalisationCache().
 11+ *
 12+ * The values retrieved from here are merged, containing items from extension
 13+ * files, core messages files and the language fallback sequence (e.g. zh-cn ->
 14+ * zh-hans -> en ). Some common errors are corrected, for example namespace
 15+ * names with spaces instead of underscores, but heavyweight processing, such
 16+ * as grammatical transformation, is done by the caller.
 17+ */
 18+class LocalisationCache {
 19+ /** Configuration associative array */
 20+ var $conf;
 21+
 22+ /**
 23+ * True if recaching should only be done on an explicit call to recache().
 24+ * Setting this reduces the overhead of cache freshness checking, which
 25+ * requires doing a stat() for every extension i18n file.
 26+ */
 27+ var $manualRecache = false;
 28+
 29+ /**
 30+ * True to treat all files as expired until they are regenerated by this object.
 31+ */
 32+ var $forceRecache = false;
 33+
 34+ /**
 35+ * The cache data. 3-d array, where the first key is the language code,
 36+ * the second key is the item key e.g. 'messages', and the third key is
 37+ * an item specific subkey index. Some items are not arrays and so for those
 38+ * items, there are no subkeys.
 39+ */
 40+ var $data = array();
 41+
 42+ /**
 43+ * The persistent store object. An instance of LCStore.
 44+ */
 45+ var $store;
 46+
 47+ /**
 48+ * A 2-d associative array, code/key, where presence indicates that the item
 49+ * is loaded. Value arbitrary.
 50+ *
 51+ * For split items, if set, this indicates that all of the subitems have been
 52+ * loaded.
 53+ */
 54+ var $loadedItems = array();
 55+
 56+ /**
 57+ * A 3-d associative array, code/key/subkey, where presence indicates that
 58+ * the subitem is loaded. Only used for the split items, i.e. messages.
 59+ */
 60+ var $loadedSubitems = array();
 61+
 62+ /**
 63+ * An array where presence of a key indicates that that language has been
 64+ * initialised. Initialisation includes checking for cache expiry and doing
 65+ * any necessary updates.
 66+ */
 67+ var $initialisedLangs = array();
 68+
 69+ /**
 70+ * An array mapping non-existent pseudo-languages to fallback languages. This
 71+ * is filled by initShallowFallback() when data is requested from a language
 72+ * that lacks a Messages*.php file.
 73+ */
 74+ var $shallowFallbacks = array();
 75+
 76+ /**
 77+ * An array where the keys are codes that have been recached by this instance.
 78+ */
 79+ var $recachedLangs = array();
 80+
 81+ /**
 82+ * All item keys
 83+ */
 84+ static public $allKeys = array(
 85+ 'fallback', 'namespaceNames', 'mathNames', 'bookstoreList',
 86+ 'magicWords', 'messages', 'rtl', 'capitalizeAllNouns', 'digitTransformTable',
 87+ 'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
 88+ 'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases',
 89+ 'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
 90+ 'defaultDateFormat', 'extraUserToggles', 'specialPageAliases',
 91+ 'imageFiles', 'preloadedMessages',
 92+ );
 93+
 94+ /**
 95+ * Keys for items which consist of associative arrays, which may be merged
 96+ * by a fallback sequence.
 97+ */
 98+ static public $mergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames',
 99+ 'dateFormats', 'defaultUserOptionOverrides', 'magicWords', 'imageFiles',
 100+ 'preloadedMessages',
 101+ );
 102+
 103+ /**
 104+ * Keys for items which are a numbered array.
 105+ */
 106+ static public $mergeableListKeys = array( 'extraUserToggles' );
 107+
 108+ /**
 109+ * Keys for items which contain an array of arrays of equivalent aliases
 110+ * for each subitem. The aliases may be merged by a fallback sequence.
 111+ */
 112+ static public $mergeableAliasListKeys = array( 'specialPageAliases' );
 113+
 114+ /**
 115+ * Keys for items which contain an associative array, and may be merged if
 116+ * the primary value contains the special array key "inherit". That array
 117+ * key is removed after the first merge.
 118+ */
 119+ static public $optionalMergeKeys = array( 'bookstoreList' );
 120+
 121+ /**
 122+ * Keys for items where the subitems are stored in the backend separately.
 123+ */
 124+ static public $splitKeys = array( 'messages' );
 125+
 126+ /**
 127+ * Keys which are loaded automatically by initLanguage()
 128+ */
 129+ static public $preloadedKeys = array( 'dateFormats', 'namespaceNames',
 130+ 'defaultUserOptionOverrides' );
 131+
 132+ /**
 133+ * Constructor.
 134+ * For constructor parameters, see the documentation in DefaultSettings.php
 135+ * for $wgLocalisationCacheConf.
 136+ */
 137+ function __construct( $conf ) {
 138+ global $wgCacheDirectory;
 139+
 140+ $this->conf = $conf;
 141+ $this->data = array();
 142+ $this->loadedItems = array();
 143+ $this->loadedSubitems = array();
 144+ $this->initialisedLangs = array();
 145+ if ( !empty( $conf['storeClass'] ) ) {
 146+ $storeClass = $conf['storeClass'];
 147+ } else {
 148+ switch ( $conf['store'] ) {
 149+ case 'files':
 150+ case 'file':
 151+ $storeClass = 'LCStore_CDB';
 152+ break;
 153+ case 'db':
 154+ $storeClass = 'LCStore_DB';
 155+ break;
 156+ case 'detect':
 157+ $storeClass = $wgCacheDirectory ? 'LCStore_CDB' : 'LCStore_DB';
 158+ break;
 159+ default:
 160+ throw new MWException(
 161+ 'Please set $wgLocalisationConf[\'store\'] to something sensible.' );
 162+ }
 163+ }
 164+
 165+ wfDebug( get_class( $this ) . ": using store $storeClass\n" );
 166+ $this->store = new $storeClass;
 167+ foreach ( array( 'manualRecache', 'forceRecache' ) as $var ) {
 168+ if ( isset( $conf[$var] ) ) {
 169+ $this->$var = $conf[$var];
 170+ }
 171+ }
 172+ }
 173+
 174+ /**
 175+ * Returns true if the given key is mergeable, that is, if it is an associative
 176+ * array which can be merged through a fallback sequence.
 177+ */
 178+ public function isMergeableKey( $key ) {
 179+ if ( !isset( $this->mergeableKeys ) ) {
 180+ $this->mergeableKeys = array_flip( array_merge(
 181+ self::$mergeableMapKeys,
 182+ self::$mergeableListKeys,
 183+ self::$mergeableAliasListKeys,
 184+ self::$optionalMergeKeys
 185+ ) );
 186+ }
 187+ return isset( $this->mergeableKeys[$key] );
 188+ }
 189+
 190+ /**
 191+ * Get a cache item.
 192+ *
 193+ * Warning: this may be slow for split items (messages), since it will
 194+ * need to fetch all of the subitems from the cache individually.
 195+ */
 196+ public function getItem( $code, $key ) {
 197+ if ( !isset( $this->loadedItems[$code][$key] ) ) {
 198+ wfProfileIn( __METHOD__.'-load' );
 199+ $this->loadItem( $code, $key );
 200+ wfProfileOut( __METHOD__.'-load' );
 201+ }
 202+ if ( $key === 'fallback' && isset( $this->shallowFallbacks[$code] ) ) {
 203+ return $this->shallowFallbacks[$code];
 204+ }
 205+ return $this->data[$code][$key];
 206+ }
 207+
 208+ /**
 209+ * Get a subitem, for instance a single message for a given language.
 210+ */
 211+ public function getSubitem( $code, $key, $subkey ) {
 212+ if ( !isset( $this->loadedSubitems[$code][$key][$subkey] ) ) {
 213+ if ( isset( $this->loadedItems[$code][$key] ) ) {
 214+ if ( isset( $this->data[$code][$key][$subkey] ) ) {
 215+ return $this->data[$code][$key][$subkey];
 216+ } else {
 217+ return null;
 218+ }
 219+ } else {
 220+ wfProfileIn( __METHOD__.'-load' );
 221+ $this->loadSubitem( $code, $key, $subkey );
 222+ wfProfileOut( __METHOD__.'-load' );
 223+ }
 224+ }
 225+ return $this->data[$code][$key][$subkey];
 226+ }
 227+
 228+ /**
 229+ * Load an item into the cache.
 230+ */
 231+ protected function loadItem( $code, $key ) {
 232+ if ( !isset( $this->initialisedLangs[$code] ) ) {
 233+ $this->initLanguage( $code );
 234+ }
 235+ // Check to see if initLanguage() loaded it for us
 236+ if ( isset( $this->loadedItems[$code][$key] ) ) {
 237+ return;
 238+ }
 239+ if ( isset( $this->shallowFallbacks[$code] ) ) {
 240+ $this->loadItem( $this->shallowFallbacks[$code], $key );
 241+ return;
 242+ }
 243+ if ( in_array( $key, self::$splitKeys ) ) {
 244+ $subkeyList = $this->getSubitem( $code, 'list', $key );
 245+ foreach ( $subkeyList as $subkey ) {
 246+ if ( isset( $this->data[$code][$key][$subkey] ) ) {
 247+ continue;
 248+ }
 249+ $this->data[$code][$key][$subkey] = $this->getSubitem( $code, $key, $subkey );
 250+ }
 251+ } else {
 252+ $this->data[$code][$key] = $this->store->get( $code, $key );
 253+ }
 254+ $this->loadedItems[$code][$key] = true;
 255+ }
 256+
 257+ /**
 258+ * Load a subitem into the cache
 259+ */
 260+ protected function loadSubitem( $code, $key, $subkey ) {
 261+ if ( !in_array( $key, self::$splitKeys ) ) {
 262+ $this->loadItem( $code, $key );
 263+ return;
 264+ }
 265+ if ( !isset( $this->initialisedLangs[$code] ) ) {
 266+ $this->initLanguage( $code );
 267+ }
 268+ // Check to see if initLanguage() loaded it for us
 269+ if ( isset( $this->loadedSubitems[$code][$key][$subkey] ) ) {
 270+ return;
 271+ }
 272+ if ( isset( $this->shallowFallbacks[$code] ) ) {
 273+ $this->loadSubitem( $this->shallowFallbacks[$code], $key, $subkey );
 274+ return;
 275+ }
 276+ $value = $this->store->get( $code, "$key:$subkey" );
 277+ $this->data[$code][$key][$subkey] = $value;
 278+ $this->loadedSubitems[$code][$key][$subkey] = true;
 279+ }
 280+
 281+ /**
 282+ * Returns true if the cache identified by $code is missing or expired.
 283+ */
 284+ public function isExpired( $code ) {
 285+ if ( $this->forceRecache && !isset( $this->recachedLangs[$code] ) ) {
 286+ wfDebug( __METHOD__."($code): forced reload\n" );
 287+ return true;
 288+ }
 289+
 290+ $deps = $this->store->get( $code, 'deps' );
 291+ if ( $deps === null ) {
 292+ wfDebug( __METHOD__."($code): cache missing, need to make one\n" );
 293+ return true;
 294+ }
 295+ foreach ( $deps as $dep ) {
 296+ if ( $dep->isExpired() ) {
 297+ wfDebug( __METHOD__."($code): cache for $code expired due to " .
 298+ get_class( $dep ) . "\n" );
 299+ return true;
 300+ }
 301+ }
 302+ return false;
 303+ }
 304+
 305+ /**
 306+ * Initialise a language in this object. Rebuild the cache if necessary.
 307+ */
 308+ protected function initLanguage( $code ) {
 309+ if ( isset( $this->initialisedLangs[$code] ) ) {
 310+ return;
 311+ }
 312+ $this->initialisedLangs[$code] = true;
 313+
 314+ # Recache the data if necessary
 315+ if ( !$this->manualRecache && $this->isExpired( $code ) ) {
 316+ if ( file_exists( Language::getMessagesFileName( $code ) ) ) {
 317+ $this->recache( $code );
 318+ } elseif ( $code === 'en' ) {
 319+ throw new MWException( 'MessagesEn.php is missing.' );
 320+ } else {
 321+ $this->initShallowFallback( $code, 'en' );
 322+ }
 323+ return;
 324+ }
 325+
 326+ # Preload some stuff
 327+ $preload = $this->getItem( $code, 'preload' );
 328+ if ( $preload === null ) {
 329+ if ( $this->manualRecache ) {
 330+ // No Messages*.php file. Do shallow fallback to en.
 331+ if ( $code === 'en' ) {
 332+ throw new MWException( 'No localisation cache found for English. ' .
 333+ 'Please run maintenance/rebuildLocalisationCache.php.' );
 334+ }
 335+ $this->initShallowFallback( $code, 'en' );
 336+ return;
 337+ } else {
 338+ throw new MWException( 'Invalid or missing localisation cache.' );
 339+ }
 340+ }
 341+ $this->data[$code] = $preload;
 342+ foreach ( $preload as $key => $item ) {
 343+ if ( in_array( $key, self::$splitKeys ) ) {
 344+ foreach ( $item as $subkey => $subitem ) {
 345+ $this->loadedSubitems[$code][$key][$subkey] = true;
 346+ }
 347+ } else {
 348+ $this->loadedItems[$code][$key] = true;
 349+ }
 350+ }
 351+ }
 352+
 353+ /**
 354+ * Create a fallback from one language to another, without creating a
 355+ * complete persistent cache.
 356+ */
 357+ public function initShallowFallback( $primaryCode, $fallbackCode ) {
 358+ $this->data[$primaryCode] =& $this->data[$fallbackCode];
 359+ $this->loadedItems[$primaryCode] =& $this->loadedItems[$fallbackCode];
 360+ $this->loadedSubitems[$primaryCode] =& $this->loadedSubitems[$fallbackCode];
 361+ $this->shallowFallbacks[$primaryCode] = $fallbackCode;
 362+ }
 363+
 364+ /**
 365+ * Read a PHP file containing localisation data.
 366+ */
 367+ protected function readPHPFile( $_fileName, $_fileType ) {
 368+ // Disable APC caching
 369+ $_apcEnabled = ini_set( 'apc.enabled', '0' );
 370+ include( $_fileName );
 371+ ini_set( 'apc.enabled', $_apcEnabled );
 372+
 373+ if ( $_fileType == 'core' || $_fileType == 'extension' ) {
 374+ $data = compact( self::$allKeys );
 375+ } elseif ( $_fileType == 'aliases' ) {
 376+ $data = compact( 'aliases' );
 377+ } else {
 378+ throw new MWException( __METHOD__.": Invalid file type: $_fileType" );
 379+ }
 380+ return $data;
 381+ }
 382+
 383+ /**
 384+ * Merge two localisation values, a primary and a fallback, overwriting the
 385+ * primary value in place.
 386+ */
 387+ protected function mergeItem( $key, &$value, $fallbackValue ) {
 388+ if ( !is_null( $value ) ) {
 389+ if ( !is_null( $fallbackValue ) ) {
 390+ if ( in_array( $key, self::$mergeableMapKeys ) ) {
 391+ $value = $value + $fallbackValue;
 392+ } elseif ( in_array( $key, self::$mergeableListKeys ) ) {
 393+ $value = array_unique( array_merge( $fallbackValue, $value ) );
 394+ } elseif ( in_array( $key, self::$mergeableAliasListKeys ) ) {
 395+ $value = array_merge_recursive( $value, $fallbackValue );
 396+ } elseif ( in_array( $key, self::$optionalMergeKeys ) ) {
 397+ if ( !empty( $value['inherit'] ) ) {
 398+ $value = array_merge( $fallbackValue, $value );
 399+ }
 400+ if ( isset( $value['inherit'] ) ) {
 401+ unset( $value['inherit'] );
 402+ }
 403+ }
 404+ }
 405+ } else {
 406+ $value = $fallbackValue;
 407+ }
 408+ }
 409+
 410+ /**
 411+ * Given an array mapping language code to localisation value, such as is
 412+ * found in extension *.i18n.php files, iterate through a fallback sequence
 413+ * to merge the given data with an existing primary value.
 414+ *
 415+ * Returns true if any data from the extension array was used, false
 416+ * otherwise.
 417+ */
 418+ protected function mergeExtensionItem( $codeSequence, $key, &$value, $fallbackValue ) {
 419+ $used = false;
 420+ foreach ( $codeSequence as $code ) {
 421+ if ( isset( $fallbackValue[$code] ) ) {
 422+ $this->mergeItem( $key, $value, $fallbackValue[$code] );
 423+ $used = true;
 424+ }
 425+ }
 426+ return $used;
 427+ }
 428+
 429+ /**
 430+ * Load localisation data for a given language for both core and extensions
 431+ * and save it to the persistent cache store and the process cache
 432+ */
 433+ public function recache( $code ) {
 434+ static $recursionGuard = array();
 435+ global $wgExtensionMessagesFiles, $wgExtensionAliasesFiles;
 436+ wfProfileIn( __METHOD__ );
 437+
 438+ if ( !$code ) {
 439+ throw new MWException( "Invalid language code requested" );
 440+ }
 441+ $this->recachedLangs[$code] = true;
 442+
 443+ # Initial values
 444+ $initialData = array_combine(
 445+ self::$allKeys,
 446+ array_fill( 0, count( self::$allKeys ), null ) );
 447+ $coreData = $initialData;
 448+ $deps = array();
 449+
 450+ # Load the primary localisation from the source file
 451+ $fileName = Language::getMessagesFileName( $code );
 452+ if ( !file_exists( $fileName ) ) {
 453+ wfDebug( __METHOD__.": no localisation file for $code, using fallback to en\n" );
 454+ $coreData['fallback'] = 'en';
 455+ } else {
 456+ $deps[] = new FileDependency( $fileName );
 457+ $data = $this->readPHPFile( $fileName, 'core' );
 458+ wfDebug( __METHOD__.": got localisation for $code from source\n" );
 459+
 460+ # Merge primary localisation
 461+ foreach ( $data as $key => $value ) {
 462+ $this->mergeItem( $key, $coreData[$key], $value );
 463+ }
 464+ }
 465+
 466+ # Fill in the fallback if it's not there already
 467+ if ( is_null( $coreData['fallback'] ) ) {
 468+ $coreData['fallback'] = $code === 'en' ? false : 'en';
 469+ }
 470+
 471+ if ( $coreData['fallback'] !== false ) {
 472+ # Guard against circular references
 473+ if ( isset( $recursionGuard[$code] ) ) {
 474+ throw new MWException( "Error: Circular fallback reference in language code $code" );
 475+ }
 476+ $recursionGuard[$code] = true;
 477+
 478+ # Load the fallback localisation item by item and merge it
 479+ $deps = array_merge( $deps, $this->getItem( $coreData['fallback'], 'deps' ) );
 480+ foreach ( self::$allKeys as $key ) {
 481+ if ( is_null( $coreData[$key] ) || $this->isMergeableKey( $key ) ) {
 482+ $fallbackValue = $this->getItem( $coreData['fallback'], $key );
 483+ $this->mergeItem( $key, $coreData[$key], $fallbackValue );
 484+ }
 485+ }
 486+ $fallbackSequence = $this->getItem( $coreData['fallback'], 'fallbackSequence' );
 487+ array_unshift( $fallbackSequence, $coreData['fallback'] );
 488+ $coreData['fallbackSequence'] = $fallbackSequence;
 489+ unset( $recursionGuard[$code] );
 490+ } else {
 491+ $coreData['fallbackSequence'] = array();
 492+ }
 493+ $codeSequence = array_merge( array( $code ), $coreData['fallbackSequence'] );
 494+
 495+ # Load the extension localisations
 496+ # This is done after the core because we know the fallback sequence now.
 497+ # But it has a higher precedence for merging so that we can support things
 498+ # like site-specific message overrides.
 499+ $allData = $initialData;
 500+ foreach ( $wgExtensionMessagesFiles as $fileName ) {
 501+ $data = $this->readPHPFile( $fileName, 'extension' );
 502+ $used = false;
 503+ foreach ( $data as $key => $item ) {
 504+ $used = $used ||
 505+ $this->mergeExtensionItem( $codeSequence, $key, $allData[$key], $item );
 506+ }
 507+ if ( $used ) {
 508+ $deps[] = new FileDependency( $fileName );
 509+ }
 510+ }
 511+
 512+ # Load deprecated $wgExtensionAliasesFiles
 513+ foreach ( $wgExtensionAliasesFiles as $fileName ) {
 514+ $data = $this->readPHPFile( $fileName, 'aliases' );
 515+ if ( !isset( $data['aliases'] ) ) {
 516+ continue;
 517+ }
 518+ $used = $this->mergeExtensionItem( $codeSequence, 'specialPageAliases',
 519+ $allData['specialPageAliases'], $data['aliases'] );
 520+ if ( $used ) {
 521+ $deps[] = new FileDependency( $fileName );
 522+ }
 523+ }
 524+
 525+ # Merge core data into extension data
 526+ foreach ( $coreData as $key => $item ) {
 527+ $this->mergeItem( $key, $allData[$key], $item );
 528+ }
 529+
 530+ # Add cache dependencies for any referenced globals
 531+ $deps['wgExtensionMessagesFiles'] = new GlobalDependency( 'wgExtensionMessagesFiles' );
 532+ $deps['wgExtensionAliasesFiles'] = new GlobalDependency( 'wgExtensionAliasesFiles' );
 533+ $deps['version'] = new ConstantDependency( 'MW_LC_VERSION' );
 534+
 535+ # Add dependencies to the cache entry
 536+ $allData['deps'] = $deps;
 537+
 538+ # Replace spaces with underscores in namespace names
 539+ $allData['namespaceNames'] = str_replace( ' ', '_', $allData['namespaceNames'] );
 540+
 541+ # And do the same for special page aliases. $page is an array.
 542+ foreach ( $allData['specialPageAliases'] as &$page ) {
 543+ $page = str_replace( ' ', '_', $page );
 544+ }
 545+ # Decouple the reference to prevent accidental damage
 546+ unset($page);
 547+
 548+ # Fix broken defaultUserOptionOverrides
 549+ if ( !is_array( $allData['defaultUserOptionOverrides'] ) ) {
 550+ $allData['defaultUserOptionOverrides'] = array();
 551+ }
 552+
 553+ # Set the preload key
 554+ $allData['preload'] = $this->buildPreload( $allData );
 555+
 556+ # Set the list keys
 557+ $allData['list'] = array();
 558+ foreach ( self::$splitKeys as $key ) {
 559+ $allData['list'][$key] = array_keys( $allData[$key] );
 560+ }
 561+
 562+ # Run hooks
 563+ wfRunHooks( 'LocalisationCacheRecache', array( $this, $code, &$allData ) );
 564+
 565+ if ( is_null( $allData['defaultUserOptionOverrides'] ) ) {
 566+ throw new MWException( __METHOD__.': Localisation data failed sanity check! ' .
 567+ 'Check that your languages/messages/MessagesEn.php file is intact.' );
 568+ }
 569+
 570+ # Save to the process cache and register the items loaded
 571+ $this->data[$code] = $allData;
 572+ foreach ( $allData as $key => $item ) {
 573+ $this->loadedItems[$code][$key] = true;
 574+ }
 575+
 576+ # Save to the persistent cache
 577+ $this->store->startWrite( $code );
 578+ foreach ( $allData as $key => $value ) {
 579+ if ( in_array( $key, self::$splitKeys ) ) {
 580+ foreach ( $value as $subkey => $subvalue ) {
 581+ $this->store->set( "$key:$subkey", $subvalue );
 582+ }
 583+ } else {
 584+ $this->store->set( $key, $value );
 585+ }
 586+ }
 587+ $this->store->finishWrite();
 588+
 589+ wfProfileOut( __METHOD__ );
 590+ }
 591+
 592+ /**
 593+ * Build the preload item from the given pre-cache data.
 594+ *
 595+ * The preload item will be loaded automatically, improving performance
 596+ * for the commonly-requested items it contains.
 597+ */
 598+ protected function buildPreload( $data ) {
 599+ $preload = array( 'messages' => array() );
 600+ foreach ( self::$preloadedKeys as $key ) {
 601+ $preload[$key] = $data[$key];
 602+ }
 603+ foreach ( $data['preloadedMessages'] as $subkey ) {
 604+ if ( isset( $data['messages'][$subkey] ) ) {
 605+ $subitem = $data['messages'][$subkey];
 606+ } else {
 607+ $subitem = null;
 608+ }
 609+ $preload['messages'][$subkey] = $subitem;
 610+ }
 611+ return $preload;
 612+ }
 613+
 614+ /**
 615+ * Unload the data for a given language from the object cache.
 616+ * Reduces memory usage.
 617+ */
 618+ public function unload( $code ) {
 619+ unset( $this->data[$code] );
 620+ unset( $this->loadedItems[$code] );
 621+ unset( $this->loadedSubitems[$code] );
 622+ unset( $this->initialisedLangs[$code] );
 623+ foreach ( $this->shallowFallbacks as $shallowCode => $fbCode ) {
 624+ if ( $fbCode === $code ) {
 625+ $this->unload( $shallowCode );
 626+ }
 627+ }
 628+ }
 629+}
 630+
 631+/**
 632+ * Interface for the persistence layer of LocalisationCache.
 633+ *
 634+ * The persistence layer is two-level hierarchical cache. The first level
 635+ * is the language, the second level is the item or subitem.
 636+ *
 637+ * Since the data for a whole language is rebuilt in one operation, it needs
 638+ * to have a fast and atomic method for deleting or replacing all of the
 639+ * current data for a given language. The interface reflects this bulk update
 640+ * operation. Callers writing to the cache must first call startWrite(), then
 641+ * will call set() a couple of thousand times, then will call finishWrite()
 642+ * to commit the operation. When finishWrite() is called, the cache is
 643+ * expected to delete all data previously stored for that language.
 644+ *
 645+ * The values stored are PHP variables suitable for serialize(). Implementations
 646+ * of LCStore are responsible for serializing and unserializing.
 647+ */
 648+interface LCStore {
 649+ /**
 650+ * Get a value.
 651+ * @param $code Language code
 652+ * @param $key Cache key
 653+ */
 654+ public function get( $code, $key );
 655+
 656+ /**
 657+ * Start a write transaction.
 658+ * @param $code Language code
 659+ */
 660+ public function startWrite( $code );
 661+
 662+ /**
 663+ * Finish a write transaction.
 664+ */
 665+ public function finishWrite();
 666+
 667+ /**
 668+ * Set a key to a given value. startWrite() must be called before this
 669+ * is called, and finishWrite() must be called afterwards.
 670+ */
 671+ public function set( $key, $value );
 672+
 673+}
 674+
 675+/**
 676+ * LCStore implementation which uses the standard DB functions to store data.
 677+ * This will work on any MediaWiki installation.
 678+ */
 679+class LCStore_DB implements LCStore {
 680+ var $currentLang;
 681+ var $writesDone = false;
 682+ var $dbw, $batch;
 683+
 684+ public function get( $code, $key ) {
 685+ if ( $this->writesDone ) {
 686+ $db = wfGetDB( DB_MASTER );
 687+ } else {
 688+ $db = wfGetDB( DB_SLAVE );
 689+ }
 690+ $row = $db->selectRow( 'l10n_cache', array( 'lc_value' ),
 691+ array( 'lc_lang' => $code, 'lc_key' => $key ), __METHOD__ );
 692+ if ( $row ) {
 693+ return unserialize( $row->lc_value );
 694+ } else {
 695+ return null;
 696+ }
 697+ }
 698+
 699+ public function startWrite( $code ) {
 700+ if ( !$code ) {
 701+ throw new MWException( __METHOD__.": Invalid language \"$code\"" );
 702+ }
 703+ $this->dbw = wfGetDB( DB_MASTER );
 704+ $this->dbw->begin();
 705+ $this->dbw->delete( 'l10n_cache', array( 'lc_lang' => $code ), __METHOD__ );
 706+ $this->currentLang = $code;
 707+ $this->batch = array();
 708+ }
 709+
 710+ public function finishWrite() {
 711+ if ( $this->batch ) {
 712+ $this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ );
 713+ }
 714+ $this->dbw->commit();
 715+ $this->currentLang = null;
 716+ $this->dbw = null;
 717+ $this->batch = array();
 718+ $this->writesDone = true;
 719+ }
 720+
 721+ public function set( $key, $value ) {
 722+ if ( is_null( $this->currentLang ) ) {
 723+ throw new MWException( __CLASS__.': must call startWrite() before calling set()' );
 724+ }
 725+ $this->batch[] = array(
 726+ 'lc_lang' => $this->currentLang,
 727+ 'lc_key' => $key,
 728+ 'lc_value' => serialize( $value ) );
 729+ if ( count( $this->batch ) >= 100 ) {
 730+ $this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ );
 731+ $this->batch = array();
 732+ }
 733+ }
 734+}
 735+
 736+/**
 737+ * LCStore implementation which stores data as a collection of CDB files in the
 738+ * directory given by $wgCacheDirectory. If $wgCacheDirectory is not set, this
 739+ * will throw an exception.
 740+ *
 741+ * Profiling indicates that on Linux, this implementation outperforms MySQL if
 742+ * the directory is on a local filesystem and there is ample kernel cache
 743+ * space. The performance advantage is greater when the DBA extension is
 744+ * available than it is with the PHP port.
 745+ *
 746+ * See Cdb.php and http://cr.yp.to/cdb.html
 747+ */
 748+class LCStore_CDB implements LCStore {
 749+ var $readers, $writer, $currentLang;
 750+
 751+ public function get( $code, $key ) {
 752+ if ( !isset( $this->readers[$code] ) ) {
 753+ $fileName = $this->getFileName( $code );
 754+ if ( !file_exists( $fileName ) ) {
 755+ $this->readers[$code] = false;
 756+ } else {
 757+ $this->readers[$code] = CdbReader::open( $fileName );
 758+ }
 759+ }
 760+ if ( !$this->readers[$code] ) {
 761+ return null;
 762+ } else {
 763+ $value = $this->readers[$code]->get( $key );
 764+ if ( $value === false ) {
 765+ return null;
 766+ }
 767+ return unserialize( $value );
 768+ }
 769+ }
 770+
 771+ public function startWrite( $code ) {
 772+ $this->writer = CdbWriter::open( $this->getFileName( $code ) );
 773+ $this->currentLang = $code;
 774+ }
 775+
 776+ public function finishWrite() {
 777+ // Close the writer
 778+ $this->writer->close();
 779+ $this->writer = null;
 780+
 781+ // Reopen the reader
 782+ if ( !empty( $this->readers[$this->currentLang] ) ) {
 783+ $this->readers[$this->currentLang]->close();
 784+ }
 785+ unset( $this->readers[$this->currentLang] );
 786+ $this->currentLang = null;
 787+ }
 788+
 789+ public function set( $key, $value ) {
 790+ if ( is_null( $this->writer ) ) {
 791+ throw new MWException( __CLASS__.': must call startWrite() before calling set()' );
 792+ }
 793+ $this->writer->set( $key, serialize( $value ) );
 794+ }
 795+
 796+ protected function getFileName( $code ) {
 797+ global $wgCacheDirectory;
 798+ if ( !$code || strpos( $code, '/' ) !== false ) {
 799+ throw new MWException( __METHOD__.": Invalid language \"$code\"" );
 800+ }
 801+ return "$wgCacheDirectory/l10n_cache-$code.cdb";
 802+ }
 803+}
 804+
 805+/**
 806+ * A localisation cache optimised for loading large amounts of data for many
 807+ * languages. Used by rebuildLocalisationCache.php.
 808+ */
 809+class LocalisationCache_BulkLoad extends LocalisationCache {
 810+ /**
 811+ * A cache of the contents of data files.
 812+ * Core files are serialized to avoid using ~1GB of RAM during a recache.
 813+ */
 814+ var $fileCache = array();
 815+
 816+ /**
 817+ * Most recently used languages. Uses the linked-list aspect of PHP hashtables
 818+ * to keep the most recently used language codes at the end of the array, and
 819+ * the language codes that are ready to be deleted at the beginning.
 820+ */
 821+ var $mruLangs = array();
 822+
 823+ /**
 824+ * Maximum number of languages that may be loaded into $this->data
 825+ */
 826+ var $maxLoadedLangs = 10;
 827+
 828+ protected function readPHPFile( $fileName, $fileType ) {
 829+ $serialize = $fileType === 'core';
 830+ if ( !isset( $this->fileCache[$fileName][$fileType] ) ) {
 831+ $data = parent::readPHPFile( $fileName, $fileType );
 832+ if ( $serialize ) {
 833+ $encData = serialize( $data );
 834+ } else {
 835+ $encData = $data;
 836+ }
 837+ $this->fileCache[$fileName][$fileType] = $encData;
 838+ return $data;
 839+ } elseif ( $serialize ) {
 840+ return unserialize( $this->fileCache[$fileName][$fileType] );
 841+ } else {
 842+ return $this->fileCache[$fileName][$fileType];
 843+ }
 844+ }
 845+
 846+ public function getItem( $code, $key ) {
 847+ unset( $this->mruLangs[$code] );
 848+ $this->mruLangs[$code] = true;
 849+ return parent::getItem( $code, $key );
 850+ }
 851+
 852+ public function getSubitem( $code, $key, $subkey ) {
 853+ unset( $this->mruLangs[$code] );
 854+ $this->mruLangs[$code] = true;
 855+ return parent::getSubitem( $code, $key, $subkey );
 856+ }
 857+
 858+ public function recache( $code ) {
 859+ parent::recache( $code );
 860+ unset( $this->mruLangs[$code] );
 861+ $this->mruLangs[$code] = true;
 862+ $this->trimCache();
 863+ }
 864+
 865+ public function unload( $code ) {
 866+ unset( $this->mruLangs[$code] );
 867+ parent::unload( $code );
 868+ }
 869+
 870+ /**
 871+ * Unload cached languages until there are less than $this->maxLoadedLangs
 872+ */
 873+ protected function trimCache() {
 874+ while ( count( $this->data ) > $this->maxLoadedLangs && count( $this->mruLangs ) ) {
 875+ reset( $this->mruLangs );
 876+ $code = key( $this->mruLangs );
 877+ wfDebug( __METHOD__.": unloading $code\n" );
 878+ $this->unload( $code );
 879+ }
 880+ }
 881+}
Property changes on: trunk/phase3/includes/LocalisationCache.php
___________________________________________________________________
Added: svn:eol-style
1882 + native
Index: trunk/phase3/includes/MessageCache.php
@@ -23,9 +23,6 @@
2424
2525 var $mUseCache, $mDisable, $mExpiry;
2626 var $mKeys, $mParserOptions, $mParser;
27 - var $mExtensionMessages = array();
28 - var $mInitialised = false;
29 - var $mAllMessagesLoaded = array(); // Extension messages
3027
3128 // Variable for tracking which variables are loaded
3229 var $mLoadedLanguages = array();
@@ -37,7 +34,6 @@
3835 $this->mExpiry = $expiry;
3936 $this->mDisableTransform = false;
4037 $this->mKeys = false; # initialised on demand
41 - $this->mInitialised = true;
4238 $this->mParser = null;
4339 }
4440
@@ -62,9 +58,9 @@
6359 * @return false on failure.
6460 */
6561 function loadFromLocal( $hash, $code ) {
66 - global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
 62+ global $wgCacheDirectory, $wgLocalMessageCacheSerialized;
6763
68 - $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code";
 64+ $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code";
6965
7066 # Check file existence
7167 wfSuppressWarnings();
@@ -106,10 +102,10 @@
107103 * Save the cache to a local file.
108104 */
109105 function saveToLocal( $serialized, $hash, $code ) {
110 - global $wgLocalMessageCache;
 106+ global $wgCacheDirectory;
111107
112 - $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code";
113 - wfMkdirParents( $wgLocalMessageCache ); // might fail
 108+ $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code";
 109+ wfMkdirParents( $wgCacheDirectory ); // might fail
114110
115111 wfSuppressWarnings();
116112 $file = fopen( $filename, 'w' );
@@ -126,11 +122,11 @@
127123 }
128124
129125 function saveToScript( $array, $hash, $code ) {
130 - global $wgLocalMessageCache;
 126+ global $wgCacheDirectory;
131127
132 - $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code";
 128+ $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code";
133129 $tempFilename = $filename . '.tmp';
134 - wfMkdirParents( $wgLocalMessageCache ); // might fail
 130+ wfMkdirParents( $wgCacheDirectory ); // might fail
135131
136132 wfSuppressWarnings();
137133 $file = fopen( $tempFilename, 'w');
@@ -174,7 +170,7 @@
175171
176172 /**
177173 * Loads messages from caches or from database in this order:
178 - * (1) local message cache (if $wgLocalMessageCache is enabled)
 174+ * (1) local message cache (if $wgUseLocalMessageCache is enabled)
179175 * (2) memcached
180176 * (3) from the database.
181177 *
@@ -191,7 +187,7 @@
192188 * @param $code String: language to which load messages
193189 */
194190 function load( $code = false ) {
195 - global $wgLocalMessageCache;
 191+ global $wgUseLocalMessageCache;
196192
197193 if ( !$this->mUseCache ) {
198194 return true;
@@ -227,7 +223,7 @@
228224 # (1) local cache
229225 # Hash of the contents is stored in memcache, to detect if local cache goes
230226 # out of date (due to update in other thread?)
231 - if ( $wgLocalMessageCache !== false ) {
 227+ if ( $wgUseLocalMessageCache ) {
232228 wfProfileIn( __METHOD__ . '-fromlocal' );
233229
234230 $hash = $this->mMemc->get( wfMemcKey( 'messages', $code, 'hash' ) );
@@ -423,7 +419,7 @@
424420 */
425421 protected function saveToCaches( $cache, $memc = true, $code = false ) {
426422 wfProfileIn( __METHOD__ );
427 - global $wgLocalMessageCache, $wgLocalMessageCacheSerialized;
 423+ global $wgUseLocalMessageCache, $wgLocalMessageCacheSerialized;
428424
429425 $cacheKey = wfMemcKey( 'messages', $code );
430426
@@ -440,7 +436,7 @@
441437 }
442438
443439 # Save to local cache
444 - if ( $wgLocalMessageCache !== false ) {
 440+ if ( $wgUseLocalMessageCache ) {
445441 $serialized = serialize( $cache );
446442 $hash = md5( $serialized );
447443 $this->mMemc->set( wfMemcKey( 'messages', $code, 'hash' ), $hash, $this->mExpiry );
@@ -508,36 +504,22 @@
509505 $lang = wfGetLangObj( $langcode );
510506 $langcode = $lang->getCode();
511507
512 - # If uninitialised, someone is trying to call this halfway through Setup.php
513 - if( !$this->mInitialised ) {
514 - return '&lt;' . htmlspecialchars($key) . '&gt;';
515 - }
516 -
517508 $message = false;
518509
519510 # Normalise title-case input
520 - $lckey = $wgContLang->lcfirst( $key );
521 - $lckey = str_replace( ' ', '_', $lckey );
 511+ $lckey = str_replace( ' ', '_', $key );
 512+ $lckey[0] = strtolower( $lckey[0] );
 513+ $uckey = ucfirst( $lckey );
522514
523515 # Try the MediaWiki namespace
524516 if( !$this->mDisable && $useDB ) {
525 - $title = $wgContLang->ucfirst( $lckey );
 517+ $title = $uckey;
526518 if(!$isFullKey && ( $langcode != $wgContLanguageCode ) ) {
527519 $title .= '/' . $langcode;
528520 }
529521 $message = $this->getMsgFromNamespace( $title, $langcode );
530522 }
531 - if( $message === false )
532 - wfRunHooks( 'MessageNotInMwNs', array( &$message, $lckey, $langcode, $isFullKey ) );
533523
534 - # Try the extension array
535 - if ( $message === false && isset( $this->mExtensionMessages[$langcode][$lckey] ) ) {
536 - $message = $this->mExtensionMessages[$langcode][$lckey];
537 - }
538 - if ( $message === false && isset( $this->mExtensionMessages['en'][$lckey] ) ) {
539 - $message = $this->mExtensionMessages['en'][$lckey];
540 - }
541 -
542524 # Try the array in the language object
543525 if ( $message === false ) {
544526 $message = $lang->getMessage( $lckey );
@@ -547,19 +529,15 @@
548530 }
549531
550532 # Try the array of another language
551 - $pos = strrpos( $lckey, '/' );
552 - if( $message === false && $pos !== false) {
553 - $mkey = substr( $lckey, 0, $pos );
554 - $code = substr( $lckey, $pos+1 );
555 - if ( $code ) {
556 - # We may get calls for things that are http-urls from sidebar
557 - # Let's not load nonexistent languages for those
558 - $validCodes = array_keys( Language::getLanguageNames() );
559 - if ( in_array( $code, $validCodes ) ) {
560 - $message = Language::getMessageFor( $mkey, $code );
561 - if ( is_null( $message ) ) {
562 - $message = false;
563 - }
 533+ if( $message === false ) {
 534+ $parts = explode( '/', $lckey );
 535+ # We may get calls for things that are http-urls from sidebar
 536+ # Let's not load nonexistent languages for those
 537+ # They usually have more than one slash.
 538+ if ( count( $parts ) == 2 && $parts[1] !== '' ) {
 539+ $message = Language::getMessageFor( $parts[0], $parts[1] );
 540+ if ( is_null( $message ) ) {
 541+ $message = false;
564542 }
565543 }
566544 }
@@ -568,7 +546,7 @@
569547 if( ($message === false || $message === '-' ) &&
570548 !$this->mDisable && $useDB &&
571549 !$isFullKey && ($langcode != $wgContLanguageCode) ) {
572 - $message = $this->getMsgFromNamespace( $wgContLang->ucfirst( $lckey ), $wgContLanguageCode );
 550+ $message = $this->getMsgFromNamespace( $uckey, $wgContLanguageCode );
573551 }
574552
575553 # Final fallback
@@ -662,7 +640,7 @@
663641 }
664642
665643 function transform( $message, $interface = false, $language = null ) {
666 - // Avoid creating parser if nothing to transfrom
 644+ // Avoid creating parser if nothing to transform
667645 if( strpos( $message, '{{' ) === false ) {
668646 return $message;
669647 }
@@ -709,71 +687,6 @@
710688 }
711689
712690 /**
713 - * Add a message to the cache
714 - *
715 - * @param mixed $key
716 - * @param mixed $value
717 - * @param string $lang The messages language, English by default
718 - */
719 - function addMessage( $key, $value, $lang = 'en' ) {
720 - global $wgContLang;
721 - # Normalise title-case input
722 - $lckey = str_replace( ' ', '_', $wgContLang->lcfirst( $key ) );
723 - $this->mExtensionMessages[$lang][$lckey] = $value;
724 - }
725 -
726 - /**
727 - * Add an associative array of message to the cache
728 - *
729 - * @param array $messages An associative array of key => values to be added
730 - * @param string $lang The messages language, English by default
731 - */
732 - function addMessages( $messages, $lang = 'en' ) {
733 - wfProfileIn( __METHOD__ );
734 - if ( !is_array( $messages ) ) {
735 - throw new MWException( __METHOD__.': Invalid message array' );
736 - }
737 - if ( isset( $this->mExtensionMessages[$lang] ) ) {
738 - $this->mExtensionMessages[$lang] = $messages + $this->mExtensionMessages[$lang];
739 - } else {
740 - $this->mExtensionMessages[$lang] = $messages;
741 - }
742 - wfProfileOut( __METHOD__ );
743 - }
744 -
745 - /**
746 - * Add a 2-D array of messages by lang. Useful for extensions.
747 - *
748 - * @param array $messages The array to be added
749 - */
750 - function addMessagesByLang( $messages ) {
751 - wfProfileIn( __METHOD__ );
752 - foreach ( $messages as $key => $value ) {
753 - $this->addMessages( $value, $key );
754 - }
755 - wfProfileOut( __METHOD__ );
756 - }
757 -
758 - /**
759 - * Get the extension messages for a specific language. Only English, interface
760 - * and content language are guaranteed to be loaded.
761 - *
762 - * @param string $lang The messages language, English by default
763 - */
764 - function getExtensionMessagesFor( $lang = 'en' ) {
765 - wfProfileIn( __METHOD__ );
766 - $messages = array();
767 - if ( isset( $this->mExtensionMessages[$lang] ) ) {
768 - $messages = $this->mExtensionMessages[$lang];
769 - }
770 - if ( $lang != 'en' ) {
771 - $messages = $messages + $this->mExtensionMessages['en'];
772 - }
773 - wfProfileOut( __METHOD__ );
774 - return $messages;
775 - }
776 -
777 - /**
778691 * Clear all stored messages. Mainly used after a mass rebuild.
779692 */
780693 function clear() {
@@ -788,83 +701,18 @@
789702 }
790703 }
791704
 705+ /**
 706+ * @deprecated
 707+ */
792708 function loadAllMessages( $lang = false ) {
793 - global $wgExtensionMessagesFiles;
794 - $key = $lang === false ? '*' : $lang;
795 - if ( isset( $this->mAllMessagesLoaded[$key] ) ) {
796 - return;
797 - }
798 - $this->mAllMessagesLoaded[$key] = true;
799 -
800 - # Some extensions will load their messages when you load their class file
801 - wfLoadAllExtensions();
802 - # Others will respond to this hook
803 - wfRunHooks( 'LoadAllMessages', array( $this ) );
804 - # Some register their messages in $wgExtensionMessagesFiles
805 - foreach ( $wgExtensionMessagesFiles as $name => $file ) {
806 - wfLoadExtensionMessages( $name, $lang );
807 - }
808 - # Still others will respond to neither, they are EVIL. We sometimes need to know!
809709 }
810710
811711 /**
812 - * Load messages from a given file
813 - *
814 - * @param string $filename Filename of file to load.
815 - * @param string $langcode Language to load messages for, or false for
816 - * default behvaiour (en, content language and user
817 - * language).
 712+ * @deprecated
818713 */
819714 function loadMessagesFile( $filename, $langcode = false ) {
820 - global $wgLang, $wgContLang;
821 - wfProfileIn( __METHOD__ );
822 - $messages = $magicWords = false;
823 - require( $filename );
824 -
825 - $validCodes = Language::getLanguageNames();
826 - if( is_string( $langcode ) && array_key_exists( $langcode, $validCodes ) ) {
827 - # Load messages for given language code.
828 - $this->processMessagesArray( $messages, $langcode );
829 - } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $validCodes ) ) {
830 - wfDebug( "Invalid language '$langcode' code passed to MessageCache::loadMessagesFile()" );
831 - } else {
832 - # Load only languages that are usually used, and merge all
833 - # fallbacks, except English.
834 - $langs = array_unique( array( 'en', $wgContLang->getCode(), $wgLang->getCode() ) );
835 - foreach( $langs as $code ) {
836 - $this->processMessagesArray( $messages, $code );
837 - }
838 - }
839 -
840 - if ( $magicWords !== false ) {
841 - global $wgContLang;
842 - $wgContLang->addMagicWordsByLang( $magicWords );
843 - }
844 - wfProfileOut( __METHOD__ );
845715 }
846716
847 - /**
848 - * Process an array of messages, loading it into the message cache.
849 - *
850 - * @param array $messages Messages array.
851 - * @param string $langcode Language code to process.
852 - */
853 - function processMessagesArray( $messages, $langcode ) {
854 - wfProfileIn( __METHOD__ );
855 - $fallbackCode = $langcode;
856 - $mergedMessages = array();
857 - do {
858 - if ( isset($messages[$fallbackCode]) ) {
859 - $mergedMessages += $messages[$fallbackCode];
860 - }
861 - $fallbackCode = Language::getFallbackfor( $fallbackCode );
862 - } while( $fallbackCode && $fallbackCode !== 'en' );
863 -
864 - if ( !empty($mergedMessages) )
865 - $this->addMessages( $mergedMessages, $langcode );
866 - wfProfileOut( __METHOD__ );
867 - }
868 -
869717 public function figureMessage( $key ) {
870718 global $wgContLanguageCode;
871719 $pieces = explode( '/', $key );
Index: trunk/phase3/includes/CacheDependency.php
@@ -134,6 +134,11 @@
135135 $this->timestamp = $timestamp;
136136 }
137137
 138+ function __sleep() {
 139+ $this->loadDependencyValues();
 140+ return array( 'filename', 'timestamp' );
 141+ }
 142+
138143 function loadDependencyValues() {
139144 if ( is_null( $this->timestamp ) ) {
140145 if ( !file_exists( $this->filename ) ) {
Index: trunk/phase3/includes/api/ApiQuerySiteinfo.php
@@ -185,8 +185,7 @@
186186
187187 protected function appendNamespaceAliases( $property ) {
188188 global $wgNamespaceAliases, $wgContLang;
189 - $wgContLang->load();
190 - $aliases = array_merge( $wgNamespaceAliases, $wgContLang->namespaceAliases );
 189+ $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
191190 $namespaces = $wgContLang->getNamespaces();
192191 $data = array();
193192 foreach( $aliases as $title => $ns ) {
Index: trunk/phase3/includes/Hooks.php
@@ -32,15 +32,16 @@
3333
3434 global $wgHooks;
3535
 36+ // Return quickly in the most common case
 37+ if ( !isset( $wgHooks[$event] ) ) {
 38+ return true;
 39+ }
 40+
3641 if (!is_array($wgHooks)) {
3742 throw new MWException("Global hooks array is not an array!\n");
3843 return false;
3944 }
4045
41 - if (!array_key_exists($event, $wgHooks)) {
42 - return true;
43 - }
44 -
4546 if (!is_array($wgHooks[$event])) {
4647 throw new MWException("Hooks array for event '$event' is not an array!\n");
4748 return false;
Index: trunk/phase3/includes/Cdb.php
@@ -13,7 +13,7 @@
1414 if ( self::haveExtension() ) {
1515 return new CdbReader_DBA( $fileName );
1616 } else {
17 - wfDebug( 'Warning: no dba extension found, using emulation.' );
 17+ wfDebug( "Warning: no dba extension found, using emulation.\n" );
1818 return new CdbReader_PHP( $fileName );
1919 }
2020 }
@@ -61,7 +61,7 @@
6262 if ( CdbReader::haveExtension() ) {
6363 return new CdbWriter_DBA( $fileName );
6464 } else {
65 - wfDebug( 'Warning: no dba extension found, using emulation.' );
 65+ wfDebug( "Warning: no dba extension found, using emulation.\n" );
6666 return new CdbWriter_PHP( $fileName );
6767 }
6868 }
Index: trunk/phase3/includes/AutoLoader.php
@@ -115,6 +115,8 @@
116116 'Interwiki' => 'includes/Interwiki.php',
117117 'IP' => 'includes/IP.php',
118118 'Job' => 'includes/JobQueue.php',
 119+ 'LCStore_DB' => 'includes/LocalisationCache.php',
 120+ 'LCStore_CDB' => 'includes/LocalisationCache.php',
119121 'License' => 'includes/Licenses.php',
120122 'Licenses' => 'includes/Licenses.php',
121123 'LinkBatch' => 'includes/LinkBatch.php',
@@ -122,6 +124,8 @@
123125 'Linker' => 'includes/Linker.php',
124126 'LinkFilter' => 'includes/LinkFilter.php',
125127 'LinksUpdate' => 'includes/LinksUpdate.php',
 128+ 'LocalisationCache' => 'includes/LocalisationCache.php',
 129+ 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php',
126130 'LogPage' => 'includes/LogPage.php',
127131 'LogPager' => 'includes/LogEventsList.php',
128132 'LogEventsList' => 'includes/LogEventsList.php',
Index: trunk/phase3/includes/MagicWord.php
@@ -185,7 +185,7 @@
186186 */
187187 static function &get( $id ) {
188188 wfProfileIn( __METHOD__ );
189 - if (!array_key_exists( $id, self::$mObjects ) ) {
 189+ if ( !isset( self::$mObjects[$id] ) ) {
190190 $mw = new MagicWord();
191191 $mw->load( $id );
192192 self::$mObjects[$id] = $mw;
Index: trunk/phase3/includes/HTMLFileCache.php
@@ -14,6 +14,7 @@
1515 * - $wgCachePages
1616 * - $wgCacheEpoch
1717 * - $wgUseFileCache
 18+ * - $wgCacheDirectory
1819 * - $wgFileCacheDirectory
1920 * - $wgUseGzip
2021 *
@@ -30,7 +31,16 @@
3132
3233 public function fileCacheName() {
3334 if( !$this->mFileCache ) {
34 - global $wgFileCacheDirectory, $wgRequest;
 35+ global $wgCacheDirectory, $wgFileCacheDirectory, $wgRequest;
 36+
 37+ if ( $wgFileCacheDirectory ) {
 38+ $dir = $wgFileCacheDirectory;
 39+ } elseif ( $wgCacheDirectory ) {
 40+ $dir = "$wgCacheDirectory/html";
 41+ } else {
 42+ throw new MWException( 'Please set $wgCacheDirectory in LocalSettings.php if you wish to use the HTML file cache' );
 43+ }
 44+
3545 # Store raw pages (like CSS hits) elsewhere
3646 $subdir = ($this->mType === 'raw') ? 'raw/' : '';
3747 $key = $this->mTitle->getPrefixedDbkey();
Index: trunk/phase3/includes/DefaultSettings.php
@@ -165,6 +165,12 @@
166166 /**@}*/
167167
168168 /**
 169+ * Directory for caching data in the local filesystem. Should not be accessible
 170+ * from the web.Set this to false to not use any local caches.
 171+ */
 172+$wgCacheDirectory = false;
 173+
 174+/**
169175 * Default value for chmoding of new directories.
170176 */
171177 $wgDirectoryMode = 0777;
@@ -755,16 +761,36 @@
756762 /**@}*/
757763
758764 /**
759 - * Directory for local copy of message cache, for use in addition to memcached
 765+ * Set this to true to make a local copy of the message cache, for use in
 766+ * addition to memcached. The files will be put in $wgCacheDirectory.
760767 */
761 -$wgLocalMessageCache = false;
 768+$wgUseLocalMessageCache = false;
 769+
762770 /**
763 - * Defines format of local cache
764 - * true - Serialized object
765 - * false - PHP source file (Warning - security risk)
 771+ * Localisation cache configuration. Associative array with keys:
 772+ * class: The class to use. May be overridden by extensions.
 773+ *
 774+ * store: The location to store cache data. May be 'files', 'db' or
 775+ * 'detect'. If set to "files", data will be in CDB files in
 776+ * the directory specified by $wgCacheDirectory. If set to "db",
 777+ * data will be stored to the database. If set to "detect", files
 778+ * will be used if $wgCacheDirectory is set, otherwise the
 779+ * database will be used.
 780+ *
 781+ * storeClass: The class name for the underlying storage. If set to a class
 782+ * name, it overrides the "store" setting.
 783+ *
 784+ * manualRecache: Set this to true to disable cache updates on web requests.
 785+ * Use maintenance/rebuildLocalisationCache.php instead.
766786 */
767 -$wgLocalMessageCacheSerialized = true;
 787+$wgLocalisationCacheConf = array(
 788+ 'class' => 'LocalisationCache',
 789+ 'store' => 'detect',
 790+ 'storeClass' => false,
 791+ 'manualRecache' => false,
 792+);
768793
 794+
769795 # Language settings
770796 #
771797 /** Site language code, should be one of ./languages/Language(.*).php */
@@ -872,20 +898,6 @@
873899 */
874900 $wgMaxMsgCacheEntrySize = 10000;
875901
876 -/**
877 - * If true, serialized versions of the messages arrays will be
878 - * read from the 'serialized' subdirectory if they are present.
879 - * Set to false to always use the Messages files, regardless of
880 - * whether they are up to date or not.
881 - */
882 -$wgEnableSerializedMessages = true;
883 -
884 -/**
885 - * Set to false if you are thorough system admin who always remembers to keep
886 - * serialized files up to date to save few mtime calls.
887 - */
888 -$wgCheckSerialized = true;
889 -
890902 /** Whether to enable language variant conversion. */
891903 $wgDisableLangConversion = false;
892904
@@ -1509,7 +1521,7 @@
15101522 $wgUseFileCache = false;
15111523
15121524 /** Directory where the cached page will be saved */
1513 -$wgFileCacheDirectory = false; ///< defaults to "{$wgUploadDirectory}/cache";
 1525+$wgFileCacheDirectory = false; ///< defaults to "$wgCacheDirectory/html";
15141526
15151527 /**
15161528 * When using the file cache, we can store the cached HTML gzipped to save disk
@@ -2550,11 +2562,16 @@
25512563 $wgSkinExtensionFunctions = array();
25522564
25532565 /**
2554 - * Extension messages files
2555 - * Associative array mapping extension name to the filename where messages can be found.
2556 - * The file must create a variable called $messages.
2557 - * When the messages are needed, the extension should call wfLoadExtensionMessages().
 2566+ * Extension messages files.
25582567 *
 2568+ * Associative array mapping extension name to the filename where messages can be
 2569+ * found. The file should contain variable assignments. Any of the variables
 2570+ * present in languages/messages/MessagesEn.php may be defined, but $messages
 2571+ * is the most common.
 2572+ *
 2573+ * Variables defined in extensions will override conflicting variables defined
 2574+ * in the core.
 2575+ *
25592576 * Example:
25602577 * $wgExtensionMessagesFiles['ConfirmEdit'] = dirname(__FILE__).'/ConfirmEdit.i18n.php';
25612578 *
@@ -2563,13 +2580,7 @@
25642581
25652582 /**
25662583 * Aliases for special pages provided by extensions.
2567 - * Associative array mapping special page to array of aliases. First alternative
2568 - * for each special page will be used as the normalised name for it. English
2569 - * aliases will be added to the end of the list so that they always work. The
2570 - * file must define a variable $aliases.
2571 - *
2572 - * Example:
2573 - * $wgExtensionAliasesFiles['Translate'] = dirname(__FILE__).'/Translate.alias.php';
 2584+ * @deprecated Use $specialPageAliases in a file referred to by $wgExtensionMessagesFiles
25742585 */
25752586 $wgExtensionAliasesFiles = array();
25762587
Index: trunk/phase3/includes/specials/SpecialAllmessages.php
@@ -29,8 +29,7 @@
3030
3131 $wgMessageCache->loadAllMessages();
3232
33 - $sortedArray = array_merge( Language::getMessagesFor( 'en' ),
34 - $wgMessageCache->getExtensionMessagesFor( 'en' ) );
 33+ $sortedArray = Language::getMessagesFor( 'en' );
3534 ksort( $sortedArray );
3635
3736 $messages = array();
Index: trunk/phase3/includes/Exception.php
@@ -8,13 +8,13 @@
99 * @ingroup Exception
1010 */
1111 class MWException extends Exception {
12 -
1312 /**
1413 * Should the exception use $wgOut to output the error ?
1514 * @return bool
1615 */
1716 function useOutputPage() {
18 - return !empty( $GLOBALS['wgFullyInitialised'] ) &&
 17+ return $this->useMessageCache() &&
 18+ !empty( $GLOBALS['wgFullyInitialised'] ) &&
1919 ( !empty( $GLOBALS['wgArticle'] ) || ( !empty( $GLOBALS['wgOut'] ) && !$GLOBALS['wgOut']->isArticle() ) ) &&
2020 !empty( $GLOBALS['wgTitle'] );
2121 }
@@ -25,6 +25,11 @@
2626 */
2727 function useMessageCache() {
2828 global $wgLang;
 29+ foreach ( $this->getTrace() as $frame ) {
 30+ if ( $frame['class'] == 'LocalisationCache' ) {
 31+ return false;
 32+ }
 33+ }
2934 return is_object( $wgLang );
3035 }
3136
Index: trunk/phase3/serialized/serialize-localisation.php
@@ -1,35 +0,0 @@
2 -<?php
3 -
4 -$wgNoDBParam = true;
5 -$optionsWithArgs = array( 'o' );
6 -require_once( dirname(__FILE__).'/../maintenance/commandLine.inc' );
7 -require_once( dirname(__FILE__).'/serialize.php' );
8 -
9 -$stderr = fopen( 'php://stderr', 'w' );
10 -if ( !isset( $args[0] ) ) {
11 - fwrite( $stderr, "No input file specified\n" );
12 - exit( 1 );
13 -}
14 -$file = $args[0];
15 -$code = str_replace( 'Messages', '', basename( $file ) );
16 -$code = str_replace( '.php', '', $code );
17 -$code = strtolower( str_replace( '_', '-', $code ) );
18 -
19 -$localisation = Language::getLocalisationArray( $code, true );
20 -if ( wfIsWindows() ) {
21 - $localisation = unixLineEndings( $localisation );
22 -}
23 -
24 -if ( isset( $options['o'] ) ) {
25 - $out = fopen( $options['o'], 'wb' );
26 - if ( !$out ) {
27 - fwrite( $stderr, "Unable to open file \"{$options['o']}\" for output\n" );
28 - exit( 1 );
29 - }
30 -} else {
31 - $out = fopen( 'php://stdout', 'wb' );
32 -}
33 -
34 -fwrite( $out, serialize( $localisation ) );
35 -
36 -
Index: trunk/phase3/serialized/README
@@ -1,37 +0,0 @@
2 -This directory contains data files in the format of PHP's serialize() function.
3 -The source data are typically array literals in PHP source files. We have
4 -observed that unserialize(file_get_contents(...)) is faster than executing such
5 -a file from an oparray cache like APC, and very much faster than loading it by
6 -parsing the source file without such a cache. It should also be faster than
7 -loading the data across the network with memcached, as long as you are careful
8 -to put your MediaWiki root directory on a local hard drive rather than on NFS.
9 -This is a good idea for performance in any case.
10 -
11 -To generate all data files:
12 -
13 - cd /path/to/wiki/serialized
14 - make
15 -
16 -This requires GNU Make. At present, the only serialized data file which is
17 -strictly required is Utf8Case.ser. This contains UTF-8 case conversion tables,
18 -which have essentially never changed since MediaWiki was invented.
19 -
20 -The Messages*.ser files are localisation files, containing user interface text
21 -and various other data related to language-specific behaviour. Because they
22 -are merged with the fallback language (usually English) before caching, they
23 -are all quite large, about 140 KB each at the time of writing. If you generate
24 -all of them, they take up about 20 MB. Hence, I don't expect we will include
25 -all of them in the release tarballs. However, to obtain optimum performance,
26 -YOU SHOULD GENERATE ALL THE LOCALISATION FILES THAT YOU WILL BE USING ON YOUR
27 -WIKIS.
28 -
29 -You can generate individual files by typing a command such as:
30 - cd /path/to/wiki/serialized
31 - make MessagesAr.ser
32 -
33 -If you change a Messages*.php source file, you must recompile any serialized
34 -data files which are present. If you change MessagesEn.php, this will
35 -invalidate *all* Messages*.ser files.
36 -
37 -I think we should distribute a few Messages*.ser files in the release tarballs,
38 -specifically the ones created by "make dist".
Index: trunk/phase3/serialized/Makefile
@@ -1,20 +1,12 @@
22
3 -MESSAGE_SOURCES=$(wildcard ../languages/messages/Messages*.php)
4 -MESSAGE_TARGETS=$(patsubst ../languages/messages/Messages%.php, Messages%.ser, $(MESSAGE_SOURCES))
53 SPECIAL_TARGETS=Utf8Case.ser
6 -ALL_TARGETS=$(MESSAGE_TARGETS) $(SPECIAL_TARGETS)
7 -DIST_TARGETS=$(SPECIAL_TARGETS) \
8 - MessagesDe.ser \
9 - MessagesEn.ser \
10 - MessagesFr.ser \
11 - MessagesJa.ser \
12 - MessagesNl.ser \
13 - MessagesPl.ser \
14 - MessagesSv.ser
 4+ALL_TARGETS=$(SPECIAL_TARGETS)
 5+DIST_TARGETS=$(SPECIAL_TARGETS)
156
167 .PHONY: all dist clean
178
189 all: $(ALL_TARGETS)
 10+ @echo 'Warning: messages are no longer serialized by this makefile.'
1911
2012 dist: $(DIST_TARGETS)
2113
@@ -24,5 +16,3 @@
2517 Utf8Case.ser : ../includes/normal/Utf8Case.php
2618 php serialize.php -o $@ $<
2719
28 -Messages%.ser : ../languages/messages/Messages%.php ../languages/messages/MessagesEn.php
29 - php serialize-localisation.php -o $@ $<
Index: trunk/phase3/config/index.php
@@ -1924,6 +1924,11 @@
19251925 ## you can enable inline LaTeX equations:
19261926 \$wgUseTeX = false;
19271927
 1928+## Set \$wgCacheDirectory to a writable directory on the web server
 1929+## to make your wiki go slightly faster. The directory should not
 1930+## be publically accessible from the web.
 1931+#\$wgCacheDirectory = \"\$IP/cache\";
 1932+
19281933 \$wgLocalInterwiki = strtolower( \$wgSitename );
19291934
19301935 \$wgLanguageCode = \"{$slconf['LanguageCode']}\";
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -474,6 +474,109 @@
475475 'button-hr' => 'button_hr.png',
476476 );
477477
 478+/**
 479+ * A list of messages to preload for each request.
 480+ * We add messages here which are needed for a typical anonymous parser cache hit.
 481+ */
 482+$preloadedMessages = array(
 483+ 'aboutpage',
 484+ 'aboutsite',
 485+ 'accesskey-ca-edit',
 486+ 'accesskey-ca-history',
 487+ 'accesskey-ca-nstab-main',
 488+ 'accesskey-ca-talk',
 489+ 'accesskey-n-currentevents',
 490+ 'accesskey-n-help',
 491+ 'accesskey-n-mainpage-description',
 492+ 'accesskey-n-portal',
 493+ 'accesskey-n-randompage',
 494+ 'accesskey-n-recentchanges',
 495+ 'accesskey-n-sitesupport',
 496+ 'accesskey-p-logo',
 497+ 'accesskey-pt-login',
 498+ 'accesskey-search',
 499+ 'accesskey-search-fulltext',
 500+ 'accesskey-search-go',
 501+ 'accesskey-t-permalink',
 502+ 'accesskey-t-print',
 503+ 'accesskey-t-recentchangeslinked',
 504+ 'accesskey-t-specialpages',
 505+ 'accesskey-t-whatlinkshere',
 506+ 'anonnotice',
 507+ 'catseparator',
 508+ 'colon-separator',
 509+ 'currentevents',
 510+ 'currentevents-url',
 511+ 'disclaimerpage',
 512+ 'disclaimers',
 513+ 'edit',
 514+ 'help',
 515+ 'helppage',
 516+ 'history_short',
 517+ 'jumpto',
 518+ 'jumptonavigation',
 519+ 'jumptosearch',
 520+ 'lastmodifiedat',
 521+ 'mainpage',
 522+ 'mainpage-description',
 523+ 'nav-login-createaccount',
 524+ 'navigation',
 525+ 'nstab-main',
 526+ 'opensearch-desc',
 527+ 'pagecategories',
 528+ 'pagecategorieslink',
 529+ 'pagetitle',
 530+ 'pagetitle-view-mainpage',
 531+ 'permalink',
 532+ 'personaltools',
 533+ 'portal',
 534+ 'portal-url',
 535+ 'printableversion',
 536+ 'privacy',
 537+ 'privacypage',
 538+ 'randompage',
 539+ 'randompage-url',
 540+ 'recentchanges',
 541+ 'recentchanges-url',
 542+ 'recentchangeslinked-toolbox',
 543+ 'retrievedfrom',
 544+ 'search',
 545+ 'searcharticle',
 546+ 'searchbutton',
 547+ 'sidebar',
 548+ 'site-atom-feed',
 549+ 'site-rss-feed',
 550+ 'sitenotice',
 551+ 'specialpages',
 552+ 'tagline',
 553+ 'talk',
 554+ 'toolbox',
 555+ 'tooltip-ca-edit',
 556+ 'tooltip-ca-history',
 557+ 'tooltip-ca-nstab-main',
 558+ 'tooltip-ca-talk',
 559+ 'tooltip-n-currentevents',
 560+ 'tooltip-n-help',
 561+ 'tooltip-n-mainpage-description',
 562+ 'tooltip-n-portal',
 563+ 'tooltip-n-randompage',
 564+ 'tooltip-n-recentchanges',
 565+ 'tooltip-n-sitesupport',
 566+ 'tooltip-p-logo',
 567+ 'tooltip-p-navigation',
 568+ 'tooltip-pt-login',
 569+ 'tooltip-search',
 570+ 'tooltip-search-fulltext',
 571+ 'tooltip-search-go',
 572+ 'tooltip-t-permalink',
 573+ 'tooltip-t-print',
 574+ 'tooltip-t-recentchangeslinked',
 575+ 'tooltip-t-specialpages',
 576+ 'tooltip-t-whatlinkshere',
 577+ 'views',
 578+ 'whatlinkshere',
 579+);
 580+
478581 #-------------------------------------------------------------------
479582 # Default messages
480583 #-------------------------------------------------------------------
Index: trunk/phase3/languages/Language.php
@@ -57,24 +57,12 @@
5858 var $mConverter, $mVariants, $mCode, $mLoaded = false;
5959 var $mMagicExtensions = array(), $mMagicHookDone = false;
6060
61 - static public $mLocalisationKeys = array(
62 - 'fallback', 'namespaceNames', 'mathNames', 'bookstoreList',
63 - 'magicWords', 'messages', 'rtl', 'capitalizeAllNouns', 'digitTransformTable',
64 - 'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
65 - 'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases',
66 - 'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
67 - 'defaultDateFormat', 'extraUserToggles', 'specialPageAliases',
68 - 'imageFiles'
69 - );
 61+ var $mNamespaceIds, $namespaceNames, $namespaceAliases;
 62+ var $dateFormatStrings = array();
 63+ var $minSearchLength;
 64+ var $mExtendedSpecialPageAliases;
7065
71 - static public $mMergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames',
72 - 'dateFormats', 'defaultUserOptionOverrides', 'magicWords', 'imageFiles' );
73 -
74 - static public $mMergeableListKeys = array( 'extraUserToggles' );
75 -
76 - static public $mMergeableAliasListKeys = array( 'specialPageAliases' );
77 -
78 - static public $mLocalisationCache = array();
 66+ static public $dataCache;
7967 static public $mLangObjCache = array();
8068
8169 static public $mWeekdayMsgs = array(
@@ -180,6 +168,15 @@
181169 return $lang;
182170 }
183171
 172+ public static function getLocalisationCache() {
 173+ if ( is_null( self::$dataCache ) ) {
 174+ global $wgLocalisationCacheConf;
 175+ $class = $wgLocalisationCacheConf['class'];
 176+ self::$dataCache = new $class( $wgLocalisationCacheConf );
 177+ }
 178+ return self::$dataCache;
 179+ }
 180+
184181 function __construct() {
185182 $this->mConverter = new FakeConverter($this);
186183 // Set the code to the name of the descendant
@@ -188,6 +185,7 @@
189186 } else {
190187 $this->mCode = str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) );
191188 }
 189+ self::getLocalisationCache();
192190 }
193191
194192 /**
@@ -215,7 +213,11 @@
216214 }
217215
218216 function getFallbackLanguageCode() {
219 - return self::getFallbackFor( $this->mCode );
 217+ if ( $this->mCode === 'en' ) {
 218+ return false;
 219+ } else {
 220+ return self::$dataCache->getItem( $this->mCode, 'fallback' );
 221+ }
220222 }
221223
222224 /**
@@ -223,15 +225,34 @@
224226 * @return array
225227 */
226228 function getBookstoreList() {
227 - $this->load();
228 - return $this->bookstoreList;
 229+ return self::$dataCache->getItem( $this->mCode, 'bookstoreList' );
229230 }
230231
231232 /**
232233 * @return array
233234 */
234235 function getNamespaces() {
235 - $this->load();
 236+ if ( is_null( $this->namespaceNames ) ) {
 237+ global $wgExtraNamespaces, $wgMetaNamespace, $wgMetaNamespaceTalk;
 238+
 239+ $this->namespaceNames = self::$dataCache->getItem( $this->mCode, 'namespaceNames' );
 240+ if ( $wgExtraNamespaces ) {
 241+ $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames;
 242+ }
 243+
 244+ $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
 245+ if ( $wgMetaNamespaceTalk ) {
 246+ $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
 247+ } else {
 248+ $talk = $this->namespaceNames[NS_PROJECT_TALK];
 249+ $this->namespaceNames[NS_PROJECT_TALK] =
 250+ $this->fixVariableInNamespace( $talk );
 251+ }
 252+
 253+ # The above mixing may leave namespaces out of canonical order.
 254+ # Re-order by namespace ID number...
 255+ ksort( $this->namespaceNames );
 256+ }
236257 return $this->namespaceNames;
237258 }
238259
@@ -287,11 +308,54 @@
288309 * @return mixed An integer if $text is a valid value otherwise false
289310 */
290311 function getLocalNsIndex( $text ) {
291 - $this->load();
292312 $lctext = $this->lc($text);
293 - return isset( $this->mNamespaceIds[$lctext] ) ? $this->mNamespaceIds[$lctext] : false;
 313+ $ids = $this->getNamespaceIds();
 314+ return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
294315 }
295316
 317+ function getNamespaceAliases() {
 318+ if ( is_null( $this->namespaceAliases ) ) {
 319+ $aliases = self::$dataCache->getItem( $this->mCode, 'namespaceAliases' );
 320+ if ( !$aliases ) {
 321+ $aliases = array();
 322+ } else {
 323+ foreach ( $aliases as $name => $index ) {
 324+ if ( $index === NS_PROJECT_TALK ) {
 325+ unset( $aliases[$name] );
 326+ $name = $this->fixVariableInNamespace( $name );
 327+ $aliases[$name] = $index;
 328+ }
 329+ }
 330+ }
 331+ $this->namespaceAliases = $aliases;
 332+ }
 333+ return $this->namespaceAliases;
 334+ }
 335+
 336+ function getNamespaceIds() {
 337+ if ( is_null( $this->mNamespaceIds ) ) {
 338+ global $wgNamespaceAliases;
 339+ # Put namespace names and aliases into a hashtable.
 340+ # If this is too slow, then we should arrange it so that it is done
 341+ # before caching. The catch is that at pre-cache time, the above
 342+ # class-specific fixup hasn't been done.
 343+ $this->mNamespaceIds = array();
 344+ foreach ( $this->getNamespaces() as $index => $name ) {
 345+ $this->mNamespaceIds[$this->lc($name)] = $index;
 346+ }
 347+ foreach ( $this->getNamespaceAliases() as $name => $index ) {
 348+ $this->mNamespaceIds[$this->lc($name)] = $index;
 349+ }
 350+ if ( $wgNamespaceAliases ) {
 351+ foreach ( $wgNamespaceAliases as $name => $index ) {
 352+ $this->mNamespaceIds[$this->lc($name)] = $index;
 353+ }
 354+ }
 355+ }
 356+ return $this->mNamespaceIds;
 357+ }
 358+
 359+
296360 /**
297361 * Get a namespace key by value, case insensitive. Canonical namespace
298362 * names override custom ones defined for the current language.
@@ -300,10 +364,12 @@
301365 * @return mixed An integer if $text is a valid value otherwise false
302366 */
303367 function getNsIndex( $text ) {
304 - $this->load();
305368 $lctext = $this->lc($text);
306 - if( ( $ns = MWNamespace::getCanonicalIndex( $lctext ) ) !== null ) return $ns;
307 - return isset( $this->mNamespaceIds[$lctext] ) ? $this->mNamespaceIds[$lctext] : false;
 369+ if ( ( $ns = MWNamespace::getCanonicalIndex( $lctext ) ) !== null ) {
 370+ return $ns;
 371+ }
 372+ $ids = $this->getNamespaceIds();
 373+ return isset( $ids[$lctext] ) ? $ids[$lctext] : false;
308374 }
309375
310376 /**
@@ -335,48 +401,41 @@
336402 }
337403
338404 function getMathNames() {
339 - $this->load();
340 - return $this->mathNames;
 405+ return self::$dataCache->getItem( $this->mCode, 'mathNames' );
341406 }
342407
343408 function getDatePreferences() {
344 - $this->load();
345 - return $this->datePreferences;
 409+ return self::$dataCache->getItem( $this->mCode, 'datePreferences' );
346410 }
347411
348412 function getDateFormats() {
349 - $this->load();
350 - return $this->dateFormats;
 413+ return self::$dataCache->getItem( $this->mCode, 'dateFormats' );
351414 }
352415
353416 function getDefaultDateFormat() {
354 - $this->load();
355 - return $this->defaultDateFormat;
 417+ $df = self::$dataCache->getItem( $this->mCode, 'defaultDateFormat' );
 418+ if ( $df === 'dmy or mdy' ) {
 419+ global $wgAmericanDates;
 420+ return $wgAmericanDates ? 'mdy' : 'dmy';
 421+ } else {
 422+ return $df;
 423+ }
356424 }
357425
358426 function getDatePreferenceMigrationMap() {
359 - $this->load();
360 - return $this->datePreferenceMigrationMap;
 427+ return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap' );
361428 }
362429
363430 function getImageFile( $image ) {
364 - $this->load();
365 - return $this->imageFiles[$image];
 431+ return self::$dataCache->getSubitem( $this->mCode, 'imageFiles', $image );
366432 }
367433
368434 function getDefaultUserOptionOverrides() {
369 - $this->load();
370 - # XXX - apparently some languageas get empty arrays, didn't get to it yet -- midom
371 - if (is_array($this->defaultUserOptionOverrides)) {
372 - return $this->defaultUserOptionOverrides;
373 - } else {
374 - return array();
375 - }
 435+ return self::$dataCache->getItem( $this->mCode, 'defaultUserOptionOverrides' );
376436 }
377437
378438 function getExtraUserToggles() {
379 - $this->load();
380 - return $this->extraUserToggles;
 439+ return self::$dataCache->getItem( $this->mCode, 'extraUserToggles' );
381440 }
382441
383442 function getUserToggle( $tog ) {
@@ -1319,6 +1378,28 @@
13201379 }
13211380
13221381 /**
 1382+ * Get a format string for a given type and preference
 1383+ * @param $type May be date, time or both
 1384+ * @param $pref The format name as it appears in Messages*.php
 1385+ */
 1386+ function getDateFormatString( $type, $pref ) {
 1387+ if ( !isset( $this->dateFormatStrings[$type][$pref] ) ) {
 1388+ if ( $pref == 'default' ) {
 1389+ $pref = $this->getDefaultDateFormat();
 1390+ $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
 1391+ } else {
 1392+ $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
 1393+ if ( is_null( $df ) ) {
 1394+ $pref = $this->getDefaultDateFormat();
 1395+ $df = self::$dataCache->getSubitem( $this->mCode, 'dateFormats', "$pref $type" );
 1396+ }
 1397+ }
 1398+ $this->dateFormatStrings[$type][$pref] = $df;
 1399+ }
 1400+ return $this->dateFormatStrings[$type][$pref];
 1401+ }
 1402+
 1403+ /**
13231404 * @param $ts Mixed: the time format which needs to be turned into a
13241405 * date('YmdHis') format with wfTimestamp(TS_MW,$ts)
13251406 * @param $adj Bool: whether to adjust the time output according to the
@@ -1329,16 +1410,11 @@
13301411 * @return string
13311412 */
13321413 function date( $ts, $adj = false, $format = true, $timecorrection = false ) {
1333 - $this->load();
13341414 if ( $adj ) {
13351415 $ts = $this->userAdjust( $ts, $timecorrection );
13361416 }
1337 -
1338 - $pref = $this->dateFormat( $format );
1339 - if( $pref == 'default' || !isset( $this->dateFormats["$pref date"] ) ) {
1340 - $pref = $this->defaultDateFormat;
1341 - }
1342 - return $this->sprintfDate( $this->dateFormats["$pref date"], $ts );
 1417+ $df = $this->getDateFormatString( 'date', $this->dateFormat( $format ) );
 1418+ return $this->sprintfDate( $df, $ts );
13431419 }
13441420
13451421 /**
@@ -1352,16 +1428,11 @@
13531429 * @return string
13541430 */
13551431 function time( $ts, $adj = false, $format = true, $timecorrection = false ) {
1356 - $this->load();
13571432 if ( $adj ) {
13581433 $ts = $this->userAdjust( $ts, $timecorrection );
13591434 }
1360 -
1361 - $pref = $this->dateFormat( $format );
1362 - if( $pref == 'default' || !isset( $this->dateFormats["$pref time"] ) ) {
1363 - $pref = $this->defaultDateFormat;
1364 - }
1365 - return $this->sprintfDate( $this->dateFormats["$pref time"], $ts );
 1435+ $df = $this->getDateFormatString( 'time', $this->dateFormat( $format ) );
 1436+ return $this->sprintfDate( $df, $ts );
13661437 }
13671438
13681439 /**
@@ -1376,30 +1447,20 @@
13771448 * @return string
13781449 */
13791450 function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false) {
1380 - $this->load();
1381 -
13821451 $ts = wfTimestamp( TS_MW, $ts );
1383 -
13841452 if ( $adj ) {
13851453 $ts = $this->userAdjust( $ts, $timecorrection );
13861454 }
1387 -
1388 - $pref = $this->dateFormat( $format );
1389 - if( $pref == 'default' || !isset( $this->dateFormats["$pref both"] ) ) {
1390 - $pref = $this->defaultDateFormat;
1391 - }
1392 -
1393 - return $this->sprintfDate( $this->dateFormats["$pref both"], $ts );
 1455+ $df = $this->getDateFormatString( 'both', $this->dateFormat( $format ) );
 1456+ return $this->sprintfDate( $df, $ts );
13941457 }
13951458
13961459 function getMessage( $key ) {
1397 - $this->load();
1398 - return isset( $this->messages[$key] ) ? $this->messages[$key] : null;
 1460+ return self::$dataCache->getSubitem( $this->mCode, 'messages', $key );
13991461 }
14001462
14011463 function getAllMessages() {
1402 - $this->load();
1403 - return $this->messages;
 1464+ return self::$dataCache->getItem( $this->mCode, 'messages' );
14041465 }
14051466
14061467 function iconv( $in, $out, $string ) {
@@ -1590,8 +1651,7 @@
15911652 }
15921653
15931654 function fallback8bitEncoding() {
1594 - $this->load();
1595 - return $this->fallback8bitEncoding;
 1655+ return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' );
15961656 }
15971657
15981658 /**
@@ -1669,7 +1729,7 @@
16701730 * if we need to pad short words...
16711731 */
16721732 protected function minSearchLength() {
1673 - if( !isset( $this->minSearchLength ) ) {
 1733+ if( is_null( $this->minSearchLength ) ) {
16741734 $sql = "show global variables like 'ft\\_min\\_word\\_len'";
16751735 $dbr = wfGetDB( DB_SLAVE );
16761736 $result = $dbr->query( $sql );
@@ -1789,8 +1849,7 @@
17901850 * @return bool
17911851 */
17921852 function isRTL() {
1793 - $this->load();
1794 - return $this->rtl;
 1853+ return self::$dataCache->getItem( $this->mCode, 'rtl' );
17951854 }
17961855
17971856 /**
@@ -1803,8 +1862,7 @@
18041863 }
18051864
18061865 function capitalizeAllNouns() {
1807 - $this->load();
1808 - return $this->capitalizeAllNouns;
 1866+ return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' );
18091867 }
18101868
18111869 /**
@@ -1822,13 +1880,11 @@
18231881 * @return bool
18241882 */
18251883 function linkPrefixExtension() {
1826 - $this->load();
1827 - return $this->linkPrefixExtension;
 1884+ return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' );
18281885 }
18291886
1830 - function &getMagicWords() {
1831 - $this->load();
1832 - return $this->magicWords;
 1887+ function getMagicWords() {
 1888+ return self::$dataCache->getItem( $this->mCode, 'magicWords' );
18331889 }
18341890
18351891 # Fill a MagicWord object with data from here
@@ -1840,16 +1896,11 @@
18411897 if ( isset( $this->mMagicExtensions[$mw->mId] ) ) {
18421898 $rawEntry = $this->mMagicExtensions[$mw->mId];
18431899 } else {
1844 - $magicWords =& $this->getMagicWords();
 1900+ $magicWords = $this->getMagicWords();
18451901 if ( isset( $magicWords[$mw->mId] ) ) {
18461902 $rawEntry = $magicWords[$mw->mId];
18471903 } else {
1848 - # Fall back to English if local list is incomplete
1849 - $magicWords =& Language::getMagicWords();
1850 - if ( !isset($magicWords[$mw->mId]) ) {
1851 - throw new MWException("Magic word '{$mw->mId}' not found" );
1852 - }
1853 - $rawEntry = $magicWords[$mw->mId];
 1904+ $rawEntry = false;
18541905 }
18551906 }
18561907
@@ -1887,43 +1938,11 @@
18881939 * case folded alias => real name
18891940 */
18901941 function getSpecialPageAliases() {
1891 - $this->load();
1892 -
18931942 // Cache aliases because it may be slow to load them
1894 - if ( !isset( $this->mExtendedSpecialPageAliases ) ) {
1895 -
 1943+ if ( is_null( $this->mExtendedSpecialPageAliases ) ) {
18961944 // Initialise array
1897 - $this->mExtendedSpecialPageAliases = $this->specialPageAliases;
1898 -
1899 - global $wgExtensionAliasesFiles;
1900 - foreach ( $wgExtensionAliasesFiles as $file ) {
1901 -
1902 - // Fail fast
1903 - if ( !file_exists($file) )
1904 - throw new MWException( "Aliases file does not exist: $file" );
1905 -
1906 - $aliases = array();
1907 - require($file);
1908 -
1909 - // Check the availability of aliases
1910 - if ( !isset($aliases['en']) )
1911 - throw new MWException( "Malformed aliases file: $file" );
1912 -
1913 - // Merge all aliases in fallback chain
1914 - $code = $this->getCode();
1915 - do {
1916 - if ( !isset($aliases[$code]) ) continue;
1917 -
1918 - $aliases[$code] = $this->fixSpecialPageAliases( $aliases[$code] );
1919 - /* Merge the aliases, THIS will break if there is special page name
1920 - * which looks like a numerical key, thanks to PHP...
1921 - * See the array_merge_recursive manual entry */
1922 - $this->mExtendedSpecialPageAliases = array_merge_recursive(
1923 - $this->mExtendedSpecialPageAliases, $aliases[$code] );
1924 -
1925 - } while ( $code = self::getFallbackFor( $code ) );
1926 - }
1927 -
 1945+ $this->mExtendedSpecialPageAliases =
 1946+ self::$dataCache->getItem( $this->mCode, 'specialPageAliases' );
19281947 wfRunHooks( 'LanguageGetSpecialPageAliases',
19291948 array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) );
19301949 }
@@ -1932,20 +1951,6 @@
19331952 }
19341953
19351954 /**
1936 - * Function to fix special page aliases. Will convert the first letter to
1937 - * upper case and spaces to underscores. Can be given a full aliases array,
1938 - * in which case it will recursively fix all aliases.
1939 - */
1940 - public function fixSpecialPageAliases( $mixed ) {
1941 - // Work recursively until in string level
1942 - if ( is_array($mixed) ) {
1943 - $callback = array( $this, 'fixSpecialPageAliases' );
1944 - return array_map( $callback, $mixed );
1945 - }
1946 - return str_replace( ' ', '_', $this->ucfirst( $mixed ) );
1947 - }
1948 -
1949 - /**
19501955 * Italic is unsuitable for some languages
19511956 *
19521957 * @param $text String: the text to be emphasized.
@@ -2017,13 +2022,11 @@
20182023 }
20192024
20202025 function digitTransformTable() {
2021 - $this->load();
2022 - return $this->digitTransformTable;
 2026+ return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' );
20232027 }
20242028
20252029 function separatorTransformTable() {
2026 - $this->load();
2027 - return $this->separatorTransformTable;
 2030+ return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' );
20282031 }
20292032
20302033
@@ -2380,8 +2383,7 @@
23812384 * @return string
23822385 */
23832386 function linkTrail() {
2384 - $this->load();
2385 - return $this->linkTrail;
 2387+ return self::$dataCache->getItem( $this->mCode, 'linkTrail' );
23862388 }
23872389
23882390 function getLangObj() {
@@ -2413,308 +2415,33 @@
24142416 return self::getFileName( "$IP/languages/classes/Language", $code, '.php' );
24152417 }
24162418
2417 - static function getLocalisationArray( $code, $disableCache = false ) {
2418 - self::loadLocalisation( $code, $disableCache );
2419 - return self::$mLocalisationCache[$code];
2420 - }
2421 -
24222419 /**
2423 - * Load localisation data for a given code into the static cache
2424 - *
2425 - * @return array Dependencies, map of filenames to mtimes
2426 - */
2427 - static function loadLocalisation( $code, $disableCache = false ) {
2428 - static $recursionGuard = array();
2429 - global $wgMemc, $wgEnableSerializedMessages, $wgCheckSerialized;
2430 -
2431 - if ( !$code ) {
2432 - throw new MWException( "Invalid language code requested" );
2433 - }
2434 -
2435 - if ( !$disableCache ) {
2436 - # Try the per-process cache
2437 - if ( isset( self::$mLocalisationCache[$code] ) ) {
2438 - return self::$mLocalisationCache[$code]['deps'];
2439 - }
2440 -
2441 - wfProfileIn( __METHOD__ );
2442 -
2443 - # Try the serialized directory
2444 - if( $wgEnableSerializedMessages ) {
2445 - $cache = wfGetPrecompiledData( self::getFileName( "Messages", $code, '.ser' ) );
2446 - if ( $cache ) {
2447 - if ( $wgCheckSerialized && self::isLocalisationOutOfDate( $cache ) ) {
2448 - $cache = false;
2449 - wfDebug( "Language::loadLocalisation(): precompiled data file for $code is out of date\n" );
2450 - } else {
2451 - self::$mLocalisationCache[$code] = $cache;
2452 - wfDebug( "Language::loadLocalisation(): got localisation for $code from precompiled data file\n" );
2453 - wfProfileOut( __METHOD__ );
2454 - return self::$mLocalisationCache[$code]['deps'];
2455 - }
2456 - }
2457 - } else {
2458 - $cache = false;
2459 - }
2460 -
2461 - # Try the global cache
2462 - $memcKey = wfMemcKey('localisation', $code );
2463 - $fbMemcKey = wfMemcKey('fallback', $cache['fallback'] );
2464 - $cache = $wgMemc->get( $memcKey );
2465 - if ( $cache ) {
2466 - if ( self::isLocalisationOutOfDate( $cache ) ) {
2467 - $wgMemc->delete( $memcKey );
2468 - $wgMemc->delete( $fbMemcKey );
2469 - $cache = false;
2470 - wfDebug( "Language::loadLocalisation(): localisation cache for $code had expired\n" );
2471 - } else {
2472 - self::$mLocalisationCache[$code] = $cache;
2473 - wfDebug( "Language::loadLocalisation(): got localisation for $code from cache\n" );
2474 - wfProfileOut( __METHOD__ );
2475 - return $cache['deps'];
2476 - }
2477 - }
2478 - } else {
2479 - wfProfileIn( __METHOD__ );
2480 - }
2481 -
2482 - # Default fallback, may be overridden when the messages file is included
2483 - if ( $code != 'en' ) {
2484 - $fallback = 'en';
2485 - } else {
2486 - $fallback = false;
2487 - }
2488 -
2489 - # Load the primary localisation from the source file
2490 - $filename = self::getMessagesFileName( $code );
2491 - if ( !file_exists( $filename ) ) {
2492 - wfDebug( "Language::loadLocalisation(): no localisation file for $code, using implicit fallback to en\n" );
2493 - $cache = compact( self::$mLocalisationKeys ); // Set correct fallback
2494 - $deps = array();
2495 - } else {
2496 - $deps = array( $filename => filemtime( $filename ) );
2497 - require( $filename );
2498 - $cache = compact( self::$mLocalisationKeys );
2499 - wfDebug( "Language::loadLocalisation(): got localisation for $code from source\n" );
2500 - }
2501 -
2502 - # Load magic word source file
2503 - global $IP;
2504 - $filename = "$IP/includes/MagicWord.php";
2505 - $newDeps = array( $filename => filemtime( $filename ) );
2506 - $deps = array_merge( $deps, $newDeps );
2507 -
2508 - if ( !empty( $fallback ) ) {
2509 - # Load the fallback localisation, with a circular reference guard
2510 - if ( isset( $recursionGuard[$code] ) ) {
2511 - throw new MWException( "Error: Circular fallback reference in language code $code" );
2512 - }
2513 - $recursionGuard[$code] = true;
2514 - $newDeps = self::loadLocalisation( $fallback, $disableCache );
2515 - unset( $recursionGuard[$code] );
2516 -
2517 - $secondary = self::$mLocalisationCache[$fallback];
2518 - $deps = array_merge( $deps, $newDeps );
2519 -
2520 - # Merge the fallback localisation with the current localisation
2521 - foreach ( self::$mLocalisationKeys as $key ) {
2522 - if ( isset( $cache[$key] ) ) {
2523 - if ( isset( $secondary[$key] ) ) {
2524 - if ( in_array( $key, self::$mMergeableMapKeys ) ) {
2525 - $cache[$key] = $cache[$key] + $secondary[$key];
2526 - } elseif ( in_array( $key, self::$mMergeableListKeys ) ) {
2527 - $cache[$key] = array_merge( $secondary[$key], $cache[$key] );
2528 - } elseif ( in_array( $key, self::$mMergeableAliasListKeys ) ) {
2529 - $cache[$key] = array_merge_recursive( $cache[$key], $secondary[$key] );
2530 - }
2531 - }
2532 - } else {
2533 - $cache[$key] = $secondary[$key];
2534 - }
2535 - }
2536 -
2537 - # Merge bookstore lists if requested
2538 - if ( !empty( $cache['bookstoreList']['inherit'] ) ) {
2539 - $cache['bookstoreList'] = array_merge( $cache['bookstoreList'], $secondary['bookstoreList'] );
2540 - }
2541 - if ( isset( $cache['bookstoreList']['inherit'] ) ) {
2542 - unset( $cache['bookstoreList']['inherit'] );
2543 - }
2544 - }
2545 -
2546 - # Add dependencies to the cache entry
2547 - $cache['deps'] = $deps;
2548 -
2549 - # Replace spaces with underscores in namespace names
2550 - $cache['namespaceNames'] = str_replace( ' ', '_', $cache['namespaceNames'] );
2551 -
2552 - # And do the same for specialpage aliases. $page is an array.
2553 - foreach ( $cache['specialPageAliases'] as &$page ) {
2554 - $page = str_replace( ' ', '_', $page );
2555 - }
2556 - # Decouple the reference to prevent accidental damage
2557 - unset($page);
2558 -
2559 - # Save to both caches
2560 - self::$mLocalisationCache[$code] = $cache;
2561 - if ( !$disableCache ) {
2562 - $wgMemc->set( $memcKey, $cache );
2563 - $wgMemc->set( $fbMemcKey, (string) $cache['fallback'] );
2564 - }
2565 -
2566 - wfProfileOut( __METHOD__ );
2567 - return $deps;
2568 - }
2569 -
2570 - /**
2571 - * Test if a given localisation cache is out of date with respect to the
2572 - * source Messages files. This is done automatically for the global cache
2573 - * in $wgMemc, but is only done on certain occasions for the serialized
2574 - * data file.
2575 - *
2576 - * @param $cache mixed Either a language code or a cache array
2577 - */
2578 - static function isLocalisationOutOfDate( $cache ) {
2579 - if ( !is_array( $cache ) ) {
2580 - self::loadLocalisation( $cache );
2581 - $cache = self::$mLocalisationCache[$cache];
2582 - }
2583 - // At least one language file and the MagicWord file needed
2584 - if( count($cache['deps']) < 2 ) {
2585 - return true;
2586 - }
2587 - $expired = false;
2588 - foreach ( $cache['deps'] as $file => $mtime ) {
2589 - if ( !file_exists( $file ) || filemtime( $file ) > $mtime ) {
2590 - $expired = true;
2591 - break;
2592 - }
2593 - }
2594 - return $expired;
2595 - }
2596 -
2597 - /**
25982420 * Get the fallback for a given language
25992421 */
26002422 static function getFallbackFor( $code ) {
2601 - // Shortcut
2602 - if ( $code === 'en' ) return false;
2603 -
2604 - // Local cache
2605 - static $cache = array();
2606 - // Quick return
2607 - if ( isset($cache[$code]) ) return $cache[$code];
2608 -
2609 - // Try memcache
2610 - global $wgMemc;
2611 - $memcKey = wfMemcKey( 'fallback', $code );
2612 - $fbcode = $wgMemc->get( $memcKey );
2613 -
2614 - if ( is_string($fbcode) ) {
2615 - // False is stored as a string to detect failures in memcache properly
2616 - if ( $fbcode === '' ) $fbcode = false;
2617 -
2618 - // Update local cache and return
2619 - $cache[$code] = $fbcode;
2620 - return $fbcode;
 2423+ if ( $code === 'en' ) {
 2424+ // Shortcut
 2425+ return false;
 2426+ } else {
 2427+ return self::getLocalisationCache()->getItem( $code, 'fallback' );
26212428 }
2622 -
2623 - // Nothing in caches, load and and update both caches
2624 - self::loadLocalisation( $code );
2625 - $fbcode = self::$mLocalisationCache[$code]['fallback'];
2626 -
2627 - $cache[$code] = $fbcode;
2628 - $wgMemc->set( $memcKey, (string) $fbcode );
2629 -
2630 - return $fbcode;
26312429 }
26322430
26332431 /**
26342432 * Get all messages for a given language
 2433+ * WARNING: this may take a long time
26352434 */
26362435 static function getMessagesFor( $code ) {
2637 - self::loadLocalisation( $code );
2638 - return self::$mLocalisationCache[$code]['messages'];
 2436+ return self::getLocalisationCache()->getItem( $code, 'messages' );
26392437 }
26402438
26412439 /**
26422440 * Get a message for a given language
26432441 */
26442442 static function getMessageFor( $key, $code ) {
2645 - self::loadLocalisation( $code );
2646 - return isset( self::$mLocalisationCache[$code]['messages'][$key] ) ? self::$mLocalisationCache[$code]['messages'][$key] : null;
 2443+ return self::getLocalisationCache()->getSubitem( $code, 'messages', $key );
26472444 }
26482445
2649 - /**
2650 - * Load localisation data for this object
2651 - */
2652 - function load() {
2653 - if ( !$this->mLoaded ) {
2654 - self::loadLocalisation( $this->getCode() );
2655 - $cache =& self::$mLocalisationCache[$this->getCode()];
2656 - foreach ( self::$mLocalisationKeys as $key ) {
2657 - $this->$key = $cache[$key];
2658 - }
2659 - $this->mLoaded = true;
2660 -
2661 - $this->fixUpSettings();
2662 - }
2663 - }
2664 -
2665 - /**
2666 - * Do any necessary post-cache-load settings adjustment
2667 - */
2668 - function fixUpSettings() {
2669 - global $wgExtraNamespaces, $wgMetaNamespace, $wgMetaNamespaceTalk,
2670 - $wgNamespaceAliases, $wgAmericanDates;
2671 - wfProfileIn( __METHOD__ );
2672 - if ( $wgExtraNamespaces ) {
2673 - $this->namespaceNames = $wgExtraNamespaces + $this->namespaceNames;
2674 - }
2675 -
2676 - $this->namespaceNames[NS_PROJECT] = $wgMetaNamespace;
2677 - if ( $wgMetaNamespaceTalk ) {
2678 - $this->namespaceNames[NS_PROJECT_TALK] = $wgMetaNamespaceTalk;
2679 - } else {
2680 - $talk = $this->namespaceNames[NS_PROJECT_TALK];
2681 - $this->namespaceNames[NS_PROJECT_TALK] =
2682 - $this->fixVariableInNamespace( $talk );
2683 - }
2684 -
2685 - # The above mixing may leave namespaces out of canonical order.
2686 - # Re-order by namespace ID number...
2687 - ksort( $this->namespaceNames );
2688 -
2689 - # Put namespace names and aliases into a hashtable.
2690 - # If this is too slow, then we should arrange it so that it is done
2691 - # before caching. The catch is that at pre-cache time, the above
2692 - # class-specific fixup hasn't been done.
2693 - $this->mNamespaceIds = array();
2694 - foreach ( $this->namespaceNames as $index => $name ) {
2695 - $this->mNamespaceIds[$this->lc($name)] = $index;
2696 - }
2697 - if ( $this->namespaceAliases ) {
2698 - foreach ( $this->namespaceAliases as $name => $index ) {
2699 - if ( $index === NS_PROJECT_TALK ) {
2700 - unset( $this->namespaceAliases[$name] );
2701 - $name = $this->fixVariableInNamespace( $name );
2702 - $this->namespaceAliases[$name] = $index;
2703 - }
2704 - $this->mNamespaceIds[$this->lc($name)] = $index;
2705 - }
2706 - }
2707 - if ( $wgNamespaceAliases ) {
2708 - foreach ( $wgNamespaceAliases as $name => $index ) {
2709 - $this->mNamespaceIds[$this->lc($name)] = $index;
2710 - }
2711 - }
2712 -
2713 - if ( $this->defaultDateFormat == 'dmy or mdy' ) {
2714 - $this->defaultDateFormat = $wgAmericanDates ? 'mdy' : 'dmy';
2715 - }
2716 - wfProfileOut( __METHOD__ );
2717 - }
2718 -
27192446 function fixVariableInNamespace( $talk ) {
27202447 if ( strpos( $talk, '$1' ) === false ) return $talk;
27212448
Index: trunk/phase3/RELEASE-NOTES
@@ -41,6 +41,15 @@
4242 appropriate privileges. Creating this user with web-install page requires
4343 oci8.privileged_connect set to On in php.ini.
4444 * Removed UserrightsChangeableGroups hook introduced in 1.14
 45+* Added $wgCacheDirectory, to replace $wgFileCacheDirectory,
 46+ $wgLocalMessageCache, and any other local caches which need a place to put
 47+ files.
 48+* $wgFileCacheDirectory is no longer set to anything by default, and so either
 49+ needs to be set explicitly, or $wgCacheDirectory needs to be set instead.
 50+* $wgLocalMessageCache has been removed. Instead, set $wgUseLocalMessageCache
 51+ to true
 52+* Removed $wgEnableSerializedMessages and $wgCheckSerialized. Similar
 53+ functionality is now available via $wgLocalisationCacheConf.
4554
4655 === New features in 1.16 ===
4756
@@ -93,6 +102,12 @@
94103 the DBA extension is not available.
95104 * (bug 14611) Added support showing the version of the image thumbnailing
96105 engine and diff/diff3 engine.
 106+* Introduced a new system for localisation caching. The system is based around
 107+ fast fetches of individual messages, minimising memory overhead and startup
 108+ time in the typical case. The database backend will be used by default, but
 109+ set $wgCacheDirectory to get a faster CDB-based implementation.
 110+* Expanded the number of variables which can be set in the extension messages
 111+ files.
97112
98113 === Bug fixes in 1.16 ===
99114

Follow-up revisions

RevisionCommit summaryAuthorDate
r52574readded $wgLocalMessageCacheSerialized (was removed in r52503), it's still us...ialex20:43, 29 June 2009
r52618(bug 19428) PG and MySQL followups to r52503. Patch by OverlordQdemon00:55, 1 July 2009
r52727* Re-added $wgMessageCache->addMessages(), there are still some extensions (i...tstarling06:19, 3 July 2009
r53042Fixed bug reported on CR r52503, invalid date format passed to $wgLang->date(...tstarling11:45, 10 July 2009
r53043Made wfMsg('') and wfMsg(null) silently return &lt;&gt; as it did before r525...tstarling11:54, 10 July 2009
r54936Simplistic reorganisation for compatibility with r52503. Load messages pre-ca...tstarling14:16, 13 August 2009
r70401Remove all calls to $wgMessageCache->loadAllMessages()...platonides19:15, 3 August 2010
r70691Removed $wgEnableSerializedMessages and $wgCheckSerialized again....nikerabbit13:06, 8 August 2010
r111349Updated the message preload list for Vector and other changes since r52503tstarling06:17, 13 February 2012

Comments

#Comment by Tim Starling (talk | contribs)   07:17, 28 June 2009

The DB patch is not required on WM, but $wgCacheDirectory needs to be set to /tmp/mediawiki, and the scap scripts need to be updated to run maintenance/rebuildLocalisationCache.php instead of serialized/Makefile.

#Comment by Voice of All (talk | contribs)   12:36, 28 June 2009

I keep getting the following: Notice: Array to string conversion in B:\www\MW_changes\includes\MessageCache.php on line 533

Notice: Undefined offset: -1 in B:\www\MW_changes\languages\Language.php on line 491

Notice: Uninitialized string offset: 0 in B:\www\MW_changes\includes\MessageCache.php on line 511

Notice: Array to string conversion in B:\www\MW_changes\includes\MessageCache.php on line 512

Warning: Illegal offset type in isset or empty in B:\www\MW_changes\includes\LocalisationCache.php on line 211

Warning: Illegal offset type in isset or empty in B:\www\MW_changes\includes\LocalisationCache.php on line 268

Warning: Illegal offset type in B:\www\MW_changes\includes\LocalisationCache.php on line 276

Warning: Illegal offset type in B:\www\MW_changes\includes\LocalisationCache.php on line 277

Warning: Illegal offset type in B:\www\MW_changes\includes\LocalisationCache.php on line 224

Notice: Array to string conversion in B:\www\MW_changes\includes\MessageCache.php on line 533

#Comment by Nikerabbit (talk | contribs)   18:12, 28 June 2009

Did r52524 fix any of those?

#Comment by Aaron Schulz (talk | contribs)   20:00, 28 June 2009

No. It seems to mainly effect reviewed pages.

#Comment by Tim Starling (talk | contribs)   06:45, 2 July 2009

Can't reproduce, but I will replace some of those warnings with exceptions so that they can be debugged more easily. Obviously message keys shouldn't be arrays.

#Comment by Voice of All (talk | contribs)   12:35, 2 July 2009

Try testing the Template:United States infobox template from enwikinews. I think I've actually narrowed it down to something with that.

#Comment by Voice of All (talk | contribs)   14:52, 6 July 2009

Also:

MessageCache::get: Invalid message key of type NULL

Backtrace:

  1. 0 B:\www\MW_changes\includes\GlobalFunctions.php(646): MessageCache->get(NULL, true, Object(Language))
  2. 1 B:\www\MW_changes\includes\GlobalFunctions.php(779): wfMsgGetKey(NULL, true, Object(Language), false)
  3. 2 B:\www\MW_changes\languages\Language.php(482): wfMsgExt(NULL, Array)
  4. 3 B:\www\MW_changes\languages\Language.php(494): Language->getMessageFromDB(NULL)
  5. 4 B:\www\MW_changes\languages\Language.php(748): Language->getMonthName('-0')
  6. 5 B:\www\MW_changes\languages\Language.php(1420): Language->sprintfDate('j F Y', '2009-06-28 12:3...')
  7. 6 B:\www\MW_changes\extensions\intersection\DynamicPageList.php(449): Language->date('2009-06-28 12:3...')
#Comment by Happy-melon (talk | contribs)   15:42, 30 June 2009

I'm pretty sure this is responsible for all the extension messages suddenly failing to load on my test wiki. What needs to be changed in extensions to get messages to load correctly? Who is going to make said changes?

#Comment by Tim Starling (talk | contribs)   15:28, 1 July 2009

Sorry about that. Someone led me to believe that $wgMessageCache->addMessages() had been removed from the extensions that used it, long ago. I didn't bother to check.

#Comment by Happy-melon (talk | contribs)   17:35, 1 July 2009

These are extensions like Oversight; I don't think this is just a case of using deprecated accessors.

#Comment by OverlordQ (talk | contribs)   20:56, 30 June 2009

Few database followups to this on bug #19428

#Comment by Shinjiman (talk | contribs)   15:09, 1 July 2009

I've got the following errors when the $wgLanguageCode in LocalSettings.php is to one of the LanguageConverter enabled code, e.g. zh, ar, kk, etc. Not sure whether was that happened for this revision or not:

  • PHP Fatal error: Maximum execution time of 30 seconds exceeded in C:\\Users\\Man\\Website\\mediawiki_svn\\includes\\db\\Database.php on line 916
  • PHP Fatal error: Maximum execution time of 30 seconds exceeded in C:\\Users\\Man\\Website\\mediawiki_svn\\includes\\db\\DatabaseMysql.php on line 12
  • PHP Fatal error: Maximum execution time of 30 seconds exceeded in C:\\Users\\Man\\Website\\mediawiki_svn\\extensions\\NewestPages\\NewestPages.i18n.php on line 10
  • PHP Fatal error: Maximum execution time of 30 seconds exceeded in C:\\Users\\Man\\Website\\mediawiki_svn\\extensions\\AbuseFilter\\AbuseFilter.i18n.php on line 8
  • PHP Fatal error: Maximum execution time of 30 seconds exceeded in C:\\Users\\Man\\Website\\mediawiki_svn\\includes\\db\\DatabaseMysql.php on line 12
  • PHP Fatal error: Maximum execution time of 30 seconds exceeded in C:\\Users\\Man\\Website\\mediawiki_svn\\includes\\db\\DatabaseMysql.php on line 69
  • PHP Fatal error: Maximum execution time of 30 seconds exceeded in C:\\Users\\Man\\Website\\mediawiki_svn\\includes\\LocalisationCache.php on line 720
#Comment by Tim Starling (talk | contribs)   06:42, 2 July 2009

Works for me, with a recache time of ~0.8 seconds. Is your database very slow?

#Comment by Jidanni (talk | contribs)   11:01, 2 July 2009

Please see Buzilla:19447. If one's $wgLanguageCode!='en' one's wiki will get stuck, unable to serve HTTP, and unable to run update.php. ~~~~

#Comment by Jidanni (talk | contribs)   11:02, 2 July 2009

Please see Bugzilla:19447. If one's $wgLanguageCode!='en' one's wiki will get stuck, unable to serve HTTP, and unable to run update.php.

#Comment by Tim Starling (talk | contribs)   08:05, 3 July 2009

More related changes in r52732.

#Comment by Jidanni (talk | contribs)   03:45, 6 July 2009

We read in config/index.php

## Set \$wgCacheDirectory to a writable directory on the web server
## to make your wiki go slightly faster. The directory should not
## be publically accessible from the web.
#\$wgCacheDirectory = \"\$IP/cache\";

I assume "slightly" and that it is commented out mean: don't bother. OK.

#Comment by Tim Starling (talk | contribs)   04:51, 6 July 2009

It's commented out because it will completely break the wiki if the cache directory is not writable. The amount of speed increase is dependent on hardware. I can assure you that Wikimedia most certainly will be bothering.

#Comment by Jidanni (talk | contribs)   05:02, 6 July 2009

OK. I assume my 'one recent change a day' wikis won't benefit, but it would be neat if some guide figures were mentioned for when it would be worth it. Glad it looks flexible enough for Manual:Wiki_family users.

#Comment by Jidanni (talk | contribs)   19:34, 6 July 2009

And: Ah, so the bigger the wiki, the more it is useful. please mention that in the comment. else the user could have just as well guessed the opposite.

#Comment by Mr.Z-man (talk | contribs)   03:40, 20 July 2009

The update to the LocalisationUpdate extension seems to have not been done yet.

#Comment by Catrope (talk | contribs)   21:06, 18 August 2009

Done now.

Status & tagging log