| 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 |
| 1 | 10 | + native |
| Index: trunk/phase3/maintenance/updaters.inc |
| — | — | @@ -161,6 +161,7 @@ |
| 162 | 162 | array( 'add_table', 'log_search', 'patch-log_search.sql' ), |
| 163 | 163 | array( 'do_log_search_population' ), |
| 164 | 164 | array( 'add_field', 'logging', 'log_user_text', 'patch-log_user_text.sql' ), |
| | 165 | + array( 'add_table', 'l10n_cache', 'patch-l10n_cache.sql' ), |
| 165 | 166 | ), |
| 166 | 167 | |
| 167 | 168 | 'sqlite' => array( |
| — | — | @@ -180,6 +181,7 @@ |
| 181 | 182 | array( 'add_table', 'log_search', 'patch-log_search.sql' ), |
| 182 | 183 | array( 'do_log_search_population' ), |
| 183 | 184 | array( 'add_field', 'redirect', 'rd_interwiki', 'patch-rd_interwiki.sql' ), |
| | 185 | + array( 'add_table', 'l10n_cache', 'patch-l10n_cache.sql' ), |
| 184 | 186 | ), |
| 185 | 187 | ); |
| 186 | 188 | |
| 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 |
| 1 | 43 | + native |
| Index: trunk/phase3/maintenance/tables.sql |
| — | — | @@ -1310,4 +1310,15 @@ |
| 1311 | 1311 | vt_tag varchar(255) NOT NULL PRIMARY KEY |
| 1312 | 1312 | ) /*$wgDBTableOptions*/; |
| 1313 | 1313 | |
| | 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 | + |
| 1314 | 1325 | -- vim: sw=2 sts=2 et |
| Index: trunk/phase3/docs/hooks.txt |
| — | — | @@ -833,13 +833,15 @@ |
| 834 | 834 | &$result: Set this to either true (passes) or the key for a message error |
| 835 | 835 | $user: User the password is being validated for |
| 836 | 836 | |
| 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 |
| 839 | 840 | $magicExtensions: associative array of magic words synonyms |
| 840 | 841 | $lang: laguage code (string) |
| 841 | 842 | |
| 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 |
| 844 | 846 | $specialPageAliases: associative array of magic words synonyms |
| 845 | 847 | $lang: laguage code (string) |
| 846 | 848 | |
| — | — | @@ -900,10 +902,6 @@ |
| 901 | 903 | 'ListDefinedTags': When trying to find all defined tags. |
| 902 | 904 | &$tags: The list of tags. |
| 903 | 905 | |
| 904 | | -'LoadAllMessages': called by MessageCache::loadAllMessages() to load extensions |
| 905 | | -messages |
| 906 | | -&$messageCache: The MessageCache object |
| 907 | | - |
| 908 | 906 | 'LoadExtensionSchemaUpdates': called by maintenance/updaters.inc when upgrading |
| 909 | 907 | database schema |
| 910 | 908 | |
| — | — | @@ -1000,13 +998,6 @@ |
| 1001 | 999 | $title: name of the page changed. |
| 1002 | 1000 | $text: new contents of the page. |
| 1003 | 1001 | |
| 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 | | - |
| 1011 | 1002 | 'MonoBookTemplateToolboxEnd': Called by Monobook skin after toolbox links have |
| 1012 | 1003 | been rendered (useful for adding more) |
| 1013 | 1004 | 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 @@ |
| 2933 | 2933 | |
| 2934 | 2934 | /** |
| 2935 | 2935 | * 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 |
| 2942 | 2937 | */ |
| 2943 | 2938 | 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 | | - } |
| 2972 | 2939 | } |
| 2973 | 2940 | |
| 2974 | 2941 | /** |
| 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 |
| 1 | 882 | + native |
| Index: trunk/phase3/includes/MessageCache.php |
| — | — | @@ -23,9 +23,6 @@ |
| 24 | 24 | |
| 25 | 25 | var $mUseCache, $mDisable, $mExpiry; |
| 26 | 26 | var $mKeys, $mParserOptions, $mParser; |
| 27 | | - var $mExtensionMessages = array(); |
| 28 | | - var $mInitialised = false; |
| 29 | | - var $mAllMessagesLoaded = array(); // Extension messages |
| 30 | 27 | |
| 31 | 28 | // Variable for tracking which variables are loaded |
| 32 | 29 | var $mLoadedLanguages = array(); |
| — | — | @@ -37,7 +34,6 @@ |
| 38 | 35 | $this->mExpiry = $expiry; |
| 39 | 36 | $this->mDisableTransform = false; |
| 40 | 37 | $this->mKeys = false; # initialised on demand |
| 41 | | - $this->mInitialised = true; |
| 42 | 38 | $this->mParser = null; |
| 43 | 39 | } |
| 44 | 40 | |
| — | — | @@ -62,9 +58,9 @@ |
| 63 | 59 | * @return false on failure. |
| 64 | 60 | */ |
| 65 | 61 | function loadFromLocal( $hash, $code ) { |
| 66 | | - global $wgLocalMessageCache, $wgLocalMessageCacheSerialized; |
| | 62 | + global $wgCacheDirectory, $wgLocalMessageCacheSerialized; |
| 67 | 63 | |
| 68 | | - $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code"; |
| | 64 | + $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; |
| 69 | 65 | |
| 70 | 66 | # Check file existence |
| 71 | 67 | wfSuppressWarnings(); |
| — | — | @@ -106,10 +102,10 @@ |
| 107 | 103 | * Save the cache to a local file. |
| 108 | 104 | */ |
| 109 | 105 | function saveToLocal( $serialized, $hash, $code ) { |
| 110 | | - global $wgLocalMessageCache; |
| | 106 | + global $wgCacheDirectory; |
| 111 | 107 | |
| 112 | | - $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code"; |
| 113 | | - wfMkdirParents( $wgLocalMessageCache ); // might fail |
| | 108 | + $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; |
| | 109 | + wfMkdirParents( $wgCacheDirectory ); // might fail |
| 114 | 110 | |
| 115 | 111 | wfSuppressWarnings(); |
| 116 | 112 | $file = fopen( $filename, 'w' ); |
| — | — | @@ -126,11 +122,11 @@ |
| 127 | 123 | } |
| 128 | 124 | |
| 129 | 125 | function saveToScript( $array, $hash, $code ) { |
| 130 | | - global $wgLocalMessageCache; |
| | 126 | + global $wgCacheDirectory; |
| 131 | 127 | |
| 132 | | - $filename = "$wgLocalMessageCache/messages-" . wfWikiID() . "-$code"; |
| | 128 | + $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; |
| 133 | 129 | $tempFilename = $filename . '.tmp'; |
| 134 | | - wfMkdirParents( $wgLocalMessageCache ); // might fail |
| | 130 | + wfMkdirParents( $wgCacheDirectory ); // might fail |
| 135 | 131 | |
| 136 | 132 | wfSuppressWarnings(); |
| 137 | 133 | $file = fopen( $tempFilename, 'w'); |
| — | — | @@ -174,7 +170,7 @@ |
| 175 | 171 | |
| 176 | 172 | /** |
| 177 | 173 | * 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) |
| 179 | 175 | * (2) memcached |
| 180 | 176 | * (3) from the database. |
| 181 | 177 | * |
| — | — | @@ -191,7 +187,7 @@ |
| 192 | 188 | * @param $code String: language to which load messages |
| 193 | 189 | */ |
| 194 | 190 | function load( $code = false ) { |
| 195 | | - global $wgLocalMessageCache; |
| | 191 | + global $wgUseLocalMessageCache; |
| 196 | 192 | |
| 197 | 193 | if ( !$this->mUseCache ) { |
| 198 | 194 | return true; |
| — | — | @@ -227,7 +223,7 @@ |
| 228 | 224 | # (1) local cache |
| 229 | 225 | # Hash of the contents is stored in memcache, to detect if local cache goes |
| 230 | 226 | # out of date (due to update in other thread?) |
| 231 | | - if ( $wgLocalMessageCache !== false ) { |
| | 227 | + if ( $wgUseLocalMessageCache ) { |
| 232 | 228 | wfProfileIn( __METHOD__ . '-fromlocal' ); |
| 233 | 229 | |
| 234 | 230 | $hash = $this->mMemc->get( wfMemcKey( 'messages', $code, 'hash' ) ); |
| — | — | @@ -423,7 +419,7 @@ |
| 424 | 420 | */ |
| 425 | 421 | protected function saveToCaches( $cache, $memc = true, $code = false ) { |
| 426 | 422 | wfProfileIn( __METHOD__ ); |
| 427 | | - global $wgLocalMessageCache, $wgLocalMessageCacheSerialized; |
| | 423 | + global $wgUseLocalMessageCache, $wgLocalMessageCacheSerialized; |
| 428 | 424 | |
| 429 | 425 | $cacheKey = wfMemcKey( 'messages', $code ); |
| 430 | 426 | |
| — | — | @@ -440,7 +436,7 @@ |
| 441 | 437 | } |
| 442 | 438 | |
| 443 | 439 | # Save to local cache |
| 444 | | - if ( $wgLocalMessageCache !== false ) { |
| | 440 | + if ( $wgUseLocalMessageCache ) { |
| 445 | 441 | $serialized = serialize( $cache ); |
| 446 | 442 | $hash = md5( $serialized ); |
| 447 | 443 | $this->mMemc->set( wfMemcKey( 'messages', $code, 'hash' ), $hash, $this->mExpiry ); |
| — | — | @@ -508,36 +504,22 @@ |
| 509 | 505 | $lang = wfGetLangObj( $langcode ); |
| 510 | 506 | $langcode = $lang->getCode(); |
| 511 | 507 | |
| 512 | | - # If uninitialised, someone is trying to call this halfway through Setup.php |
| 513 | | - if( !$this->mInitialised ) { |
| 514 | | - return '<' . htmlspecialchars($key) . '>'; |
| 515 | | - } |
| 516 | | - |
| 517 | 508 | $message = false; |
| 518 | 509 | |
| 519 | 510 | # 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 ); |
| 522 | 514 | |
| 523 | 515 | # Try the MediaWiki namespace |
| 524 | 516 | if( !$this->mDisable && $useDB ) { |
| 525 | | - $title = $wgContLang->ucfirst( $lckey ); |
| | 517 | + $title = $uckey; |
| 526 | 518 | if(!$isFullKey && ( $langcode != $wgContLanguageCode ) ) { |
| 527 | 519 | $title .= '/' . $langcode; |
| 528 | 520 | } |
| 529 | 521 | $message = $this->getMsgFromNamespace( $title, $langcode ); |
| 530 | 522 | } |
| 531 | | - if( $message === false ) |
| 532 | | - wfRunHooks( 'MessageNotInMwNs', array( &$message, $lckey, $langcode, $isFullKey ) ); |
| 533 | 523 | |
| 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 | | - |
| 542 | 524 | # Try the array in the language object |
| 543 | 525 | if ( $message === false ) { |
| 544 | 526 | $message = $lang->getMessage( $lckey ); |
| — | — | @@ -547,19 +529,15 @@ |
| 548 | 530 | } |
| 549 | 531 | |
| 550 | 532 | # 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; |
| 564 | 542 | } |
| 565 | 543 | } |
| 566 | 544 | } |
| — | — | @@ -568,7 +546,7 @@ |
| 569 | 547 | if( ($message === false || $message === '-' ) && |
| 570 | 548 | !$this->mDisable && $useDB && |
| 571 | 549 | !$isFullKey && ($langcode != $wgContLanguageCode) ) { |
| 572 | | - $message = $this->getMsgFromNamespace( $wgContLang->ucfirst( $lckey ), $wgContLanguageCode ); |
| | 550 | + $message = $this->getMsgFromNamespace( $uckey, $wgContLanguageCode ); |
| 573 | 551 | } |
| 574 | 552 | |
| 575 | 553 | # Final fallback |
| — | — | @@ -662,7 +640,7 @@ |
| 663 | 641 | } |
| 664 | 642 | |
| 665 | 643 | function transform( $message, $interface = false, $language = null ) { |
| 666 | | - // Avoid creating parser if nothing to transfrom |
| | 644 | + // Avoid creating parser if nothing to transform |
| 667 | 645 | if( strpos( $message, '{{' ) === false ) { |
| 668 | 646 | return $message; |
| 669 | 647 | } |
| — | — | @@ -709,71 +687,6 @@ |
| 710 | 688 | } |
| 711 | 689 | |
| 712 | 690 | /** |
| 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 | | - /** |
| 778 | 691 | * Clear all stored messages. Mainly used after a mass rebuild. |
| 779 | 692 | */ |
| 780 | 693 | function clear() { |
| — | — | @@ -788,83 +701,18 @@ |
| 789 | 702 | } |
| 790 | 703 | } |
| 791 | 704 | |
| | 705 | + /** |
| | 706 | + * @deprecated |
| | 707 | + */ |
| 792 | 708 | 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! |
| 809 | 709 | } |
| 810 | 710 | |
| 811 | 711 | /** |
| 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 |
| 818 | 713 | */ |
| 819 | 714 | 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__ ); |
| 845 | 715 | } |
| 846 | 716 | |
| 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 | | - |
| 869 | 717 | public function figureMessage( $key ) { |
| 870 | 718 | global $wgContLanguageCode; |
| 871 | 719 | $pieces = explode( '/', $key ); |
| Index: trunk/phase3/includes/CacheDependency.php |
| — | — | @@ -134,6 +134,11 @@ |
| 135 | 135 | $this->timestamp = $timestamp; |
| 136 | 136 | } |
| 137 | 137 | |
| | 138 | + function __sleep() { |
| | 139 | + $this->loadDependencyValues(); |
| | 140 | + return array( 'filename', 'timestamp' ); |
| | 141 | + } |
| | 142 | + |
| 138 | 143 | function loadDependencyValues() { |
| 139 | 144 | if ( is_null( $this->timestamp ) ) { |
| 140 | 145 | if ( !file_exists( $this->filename ) ) { |
| Index: trunk/phase3/includes/api/ApiQuerySiteinfo.php |
| — | — | @@ -185,8 +185,7 @@ |
| 186 | 186 | |
| 187 | 187 | protected function appendNamespaceAliases( $property ) { |
| 188 | 188 | global $wgNamespaceAliases, $wgContLang; |
| 189 | | - $wgContLang->load(); |
| 190 | | - $aliases = array_merge( $wgNamespaceAliases, $wgContLang->namespaceAliases ); |
| | 189 | + $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() ); |
| 191 | 190 | $namespaces = $wgContLang->getNamespaces(); |
| 192 | 191 | $data = array(); |
| 193 | 192 | foreach( $aliases as $title => $ns ) { |
| Index: trunk/phase3/includes/Hooks.php |
| — | — | @@ -32,15 +32,16 @@ |
| 33 | 33 | |
| 34 | 34 | global $wgHooks; |
| 35 | 35 | |
| | 36 | + // Return quickly in the most common case |
| | 37 | + if ( !isset( $wgHooks[$event] ) ) { |
| | 38 | + return true; |
| | 39 | + } |
| | 40 | + |
| 36 | 41 | if (!is_array($wgHooks)) { |
| 37 | 42 | throw new MWException("Global hooks array is not an array!\n"); |
| 38 | 43 | return false; |
| 39 | 44 | } |
| 40 | 45 | |
| 41 | | - if (!array_key_exists($event, $wgHooks)) { |
| 42 | | - return true; |
| 43 | | - } |
| 44 | | - |
| 45 | 46 | if (!is_array($wgHooks[$event])) { |
| 46 | 47 | throw new MWException("Hooks array for event '$event' is not an array!\n"); |
| 47 | 48 | return false; |
| Index: trunk/phase3/includes/Cdb.php |
| — | — | @@ -13,7 +13,7 @@ |
| 14 | 14 | if ( self::haveExtension() ) { |
| 15 | 15 | return new CdbReader_DBA( $fileName ); |
| 16 | 16 | } else { |
| 17 | | - wfDebug( 'Warning: no dba extension found, using emulation.' ); |
| | 17 | + wfDebug( "Warning: no dba extension found, using emulation.\n" ); |
| 18 | 18 | return new CdbReader_PHP( $fileName ); |
| 19 | 19 | } |
| 20 | 20 | } |
| — | — | @@ -61,7 +61,7 @@ |
| 62 | 62 | if ( CdbReader::haveExtension() ) { |
| 63 | 63 | return new CdbWriter_DBA( $fileName ); |
| 64 | 64 | } else { |
| 65 | | - wfDebug( 'Warning: no dba extension found, using emulation.' ); |
| | 65 | + wfDebug( "Warning: no dba extension found, using emulation.\n" ); |
| 66 | 66 | return new CdbWriter_PHP( $fileName ); |
| 67 | 67 | } |
| 68 | 68 | } |
| Index: trunk/phase3/includes/AutoLoader.php |
| — | — | @@ -115,6 +115,8 @@ |
| 116 | 116 | 'Interwiki' => 'includes/Interwiki.php', |
| 117 | 117 | 'IP' => 'includes/IP.php', |
| 118 | 118 | 'Job' => 'includes/JobQueue.php', |
| | 119 | + 'LCStore_DB' => 'includes/LocalisationCache.php', |
| | 120 | + 'LCStore_CDB' => 'includes/LocalisationCache.php', |
| 119 | 121 | 'License' => 'includes/Licenses.php', |
| 120 | 122 | 'Licenses' => 'includes/Licenses.php', |
| 121 | 123 | 'LinkBatch' => 'includes/LinkBatch.php', |
| — | — | @@ -122,6 +124,8 @@ |
| 123 | 125 | 'Linker' => 'includes/Linker.php', |
| 124 | 126 | 'LinkFilter' => 'includes/LinkFilter.php', |
| 125 | 127 | 'LinksUpdate' => 'includes/LinksUpdate.php', |
| | 128 | + 'LocalisationCache' => 'includes/LocalisationCache.php', |
| | 129 | + 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php', |
| 126 | 130 | 'LogPage' => 'includes/LogPage.php', |
| 127 | 131 | 'LogPager' => 'includes/LogEventsList.php', |
| 128 | 132 | 'LogEventsList' => 'includes/LogEventsList.php', |
| Index: trunk/phase3/includes/MagicWord.php |
| — | — | @@ -185,7 +185,7 @@ |
| 186 | 186 | */ |
| 187 | 187 | static function &get( $id ) { |
| 188 | 188 | wfProfileIn( __METHOD__ ); |
| 189 | | - if (!array_key_exists( $id, self::$mObjects ) ) { |
| | 189 | + if ( !isset( self::$mObjects[$id] ) ) { |
| 190 | 190 | $mw = new MagicWord(); |
| 191 | 191 | $mw->load( $id ); |
| 192 | 192 | self::$mObjects[$id] = $mw; |
| Index: trunk/phase3/includes/HTMLFileCache.php |
| — | — | @@ -14,6 +14,7 @@ |
| 15 | 15 | * - $wgCachePages |
| 16 | 16 | * - $wgCacheEpoch |
| 17 | 17 | * - $wgUseFileCache |
| | 18 | + * - $wgCacheDirectory |
| 18 | 19 | * - $wgFileCacheDirectory |
| 19 | 20 | * - $wgUseGzip |
| 20 | 21 | * |
| — | — | @@ -30,7 +31,16 @@ |
| 31 | 32 | |
| 32 | 33 | public function fileCacheName() { |
| 33 | 34 | 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 | + |
| 35 | 45 | # Store raw pages (like CSS hits) elsewhere |
| 36 | 46 | $subdir = ($this->mType === 'raw') ? 'raw/' : ''; |
| 37 | 47 | $key = $this->mTitle->getPrefixedDbkey(); |
| Index: trunk/phase3/includes/DefaultSettings.php |
| — | — | @@ -165,6 +165,12 @@ |
| 166 | 166 | /**@}*/ |
| 167 | 167 | |
| 168 | 168 | /** |
| | 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 | +/** |
| 169 | 175 | * Default value for chmoding of new directories. |
| 170 | 176 | */ |
| 171 | 177 | $wgDirectoryMode = 0777; |
| — | — | @@ -755,16 +761,36 @@ |
| 756 | 762 | /**@}*/ |
| 757 | 763 | |
| 758 | 764 | /** |
| 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. |
| 760 | 767 | */ |
| 761 | | -$wgLocalMessageCache = false; |
| | 768 | +$wgUseLocalMessageCache = false; |
| | 769 | + |
| 762 | 770 | /** |
| 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. |
| 766 | 786 | */ |
| 767 | | -$wgLocalMessageCacheSerialized = true; |
| | 787 | +$wgLocalisationCacheConf = array( |
| | 788 | + 'class' => 'LocalisationCache', |
| | 789 | + 'store' => 'detect', |
| | 790 | + 'storeClass' => false, |
| | 791 | + 'manualRecache' => false, |
| | 792 | +); |
| 768 | 793 | |
| | 794 | + |
| 769 | 795 | # Language settings |
| 770 | 796 | # |
| 771 | 797 | /** Site language code, should be one of ./languages/Language(.*).php */ |
| — | — | @@ -872,20 +898,6 @@ |
| 873 | 899 | */ |
| 874 | 900 | $wgMaxMsgCacheEntrySize = 10000; |
| 875 | 901 | |
| 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 | | - |
| 890 | 902 | /** Whether to enable language variant conversion. */ |
| 891 | 903 | $wgDisableLangConversion = false; |
| 892 | 904 | |
| — | — | @@ -1509,7 +1521,7 @@ |
| 1510 | 1522 | $wgUseFileCache = false; |
| 1511 | 1523 | |
| 1512 | 1524 | /** Directory where the cached page will be saved */ |
| 1513 | | -$wgFileCacheDirectory = false; ///< defaults to "{$wgUploadDirectory}/cache"; |
| | 1525 | +$wgFileCacheDirectory = false; ///< defaults to "$wgCacheDirectory/html"; |
| 1514 | 1526 | |
| 1515 | 1527 | /** |
| 1516 | 1528 | * When using the file cache, we can store the cached HTML gzipped to save disk |
| — | — | @@ -2550,11 +2562,16 @@ |
| 2551 | 2563 | $wgSkinExtensionFunctions = array(); |
| 2552 | 2564 | |
| 2553 | 2565 | /** |
| 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. |
| 2558 | 2567 | * |
| | 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 | + * |
| 2559 | 2576 | * Example: |
| 2560 | 2577 | * $wgExtensionMessagesFiles['ConfirmEdit'] = dirname(__FILE__).'/ConfirmEdit.i18n.php'; |
| 2561 | 2578 | * |
| — | — | @@ -2563,13 +2580,7 @@ |
| 2564 | 2581 | |
| 2565 | 2582 | /** |
| 2566 | 2583 | * 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 |
| 2574 | 2585 | */ |
| 2575 | 2586 | $wgExtensionAliasesFiles = array(); |
| 2576 | 2587 | |
| Index: trunk/phase3/includes/specials/SpecialAllmessages.php |
| — | — | @@ -29,8 +29,7 @@ |
| 30 | 30 | |
| 31 | 31 | $wgMessageCache->loadAllMessages(); |
| 32 | 32 | |
| 33 | | - $sortedArray = array_merge( Language::getMessagesFor( 'en' ), |
| 34 | | - $wgMessageCache->getExtensionMessagesFor( 'en' ) ); |
| | 33 | + $sortedArray = Language::getMessagesFor( 'en' ); |
| 35 | 34 | ksort( $sortedArray ); |
| 36 | 35 | |
| 37 | 36 | $messages = array(); |
| Index: trunk/phase3/includes/Exception.php |
| — | — | @@ -8,13 +8,13 @@ |
| 9 | 9 | * @ingroup Exception |
| 10 | 10 | */ |
| 11 | 11 | class MWException extends Exception { |
| 12 | | - |
| 13 | 12 | /** |
| 14 | 13 | * Should the exception use $wgOut to output the error ? |
| 15 | 14 | * @return bool |
| 16 | 15 | */ |
| 17 | 16 | function useOutputPage() { |
| 18 | | - return !empty( $GLOBALS['wgFullyInitialised'] ) && |
| | 17 | + return $this->useMessageCache() && |
| | 18 | + !empty( $GLOBALS['wgFullyInitialised'] ) && |
| 19 | 19 | ( !empty( $GLOBALS['wgArticle'] ) || ( !empty( $GLOBALS['wgOut'] ) && !$GLOBALS['wgOut']->isArticle() ) ) && |
| 20 | 20 | !empty( $GLOBALS['wgTitle'] ); |
| 21 | 21 | } |
| — | — | @@ -25,6 +25,11 @@ |
| 26 | 26 | */ |
| 27 | 27 | function useMessageCache() { |
| 28 | 28 | global $wgLang; |
| | 29 | + foreach ( $this->getTrace() as $frame ) { |
| | 30 | + if ( $frame['class'] == 'LocalisationCache' ) { |
| | 31 | + return false; |
| | 32 | + } |
| | 33 | + } |
| 29 | 34 | return is_object( $wgLang ); |
| 30 | 35 | } |
| 31 | 36 | |
| 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 @@ |
| 2 | 2 | |
| 3 | | -MESSAGE_SOURCES=$(wildcard ../languages/messages/Messages*.php) |
| 4 | | -MESSAGE_TARGETS=$(patsubst ../languages/messages/Messages%.php, Messages%.ser, $(MESSAGE_SOURCES)) |
| 5 | 3 | 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) |
| 15 | 6 | |
| 16 | 7 | .PHONY: all dist clean |
| 17 | 8 | |
| 18 | 9 | all: $(ALL_TARGETS) |
| | 10 | + @echo 'Warning: messages are no longer serialized by this makefile.' |
| 19 | 11 | |
| 20 | 12 | dist: $(DIST_TARGETS) |
| 21 | 13 | |
| — | — | @@ -24,5 +16,3 @@ |
| 25 | 17 | Utf8Case.ser : ../includes/normal/Utf8Case.php |
| 26 | 18 | php serialize.php -o $@ $< |
| 27 | 19 | |
| 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 @@ |
| 1925 | 1925 | ## you can enable inline LaTeX equations: |
| 1926 | 1926 | \$wgUseTeX = false; |
| 1927 | 1927 | |
| | 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 | + |
| 1928 | 1933 | \$wgLocalInterwiki = strtolower( \$wgSitename ); |
| 1929 | 1934 | |
| 1930 | 1935 | \$wgLanguageCode = \"{$slconf['LanguageCode']}\"; |
| Index: trunk/phase3/languages/messages/MessagesEn.php |
| — | — | @@ -474,6 +474,109 @@ |
| 475 | 475 | 'button-hr' => 'button_hr.png', |
| 476 | 476 | ); |
| 477 | 477 | |
| | 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 | + |
| 478 | 581 | #------------------------------------------------------------------- |
| 479 | 582 | # Default messages |
| 480 | 583 | #------------------------------------------------------------------- |
| Index: trunk/phase3/languages/Language.php |
| — | — | @@ -57,24 +57,12 @@ |
| 58 | 58 | var $mConverter, $mVariants, $mCode, $mLoaded = false; |
| 59 | 59 | var $mMagicExtensions = array(), $mMagicHookDone = false; |
| 60 | 60 | |
| 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; |
| 70 | 65 | |
| 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; |
| 79 | 67 | static public $mLangObjCache = array(); |
| 80 | 68 | |
| 81 | 69 | static public $mWeekdayMsgs = array( |
| — | — | @@ -180,6 +168,15 @@ |
| 181 | 169 | return $lang; |
| 182 | 170 | } |
| 183 | 171 | |
| | 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 | + |
| 184 | 181 | function __construct() { |
| 185 | 182 | $this->mConverter = new FakeConverter($this); |
| 186 | 183 | // Set the code to the name of the descendant |
| — | — | @@ -188,6 +185,7 @@ |
| 189 | 186 | } else { |
| 190 | 187 | $this->mCode = str_replace( '_', '-', strtolower( substr( get_class( $this ), 8 ) ) ); |
| 191 | 188 | } |
| | 189 | + self::getLocalisationCache(); |
| 192 | 190 | } |
| 193 | 191 | |
| 194 | 192 | /** |
| — | — | @@ -215,7 +213,11 @@ |
| 216 | 214 | } |
| 217 | 215 | |
| 218 | 216 | 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 | + } |
| 220 | 222 | } |
| 221 | 223 | |
| 222 | 224 | /** |
| — | — | @@ -223,15 +225,34 @@ |
| 224 | 226 | * @return array |
| 225 | 227 | */ |
| 226 | 228 | function getBookstoreList() { |
| 227 | | - $this->load(); |
| 228 | | - return $this->bookstoreList; |
| | 229 | + return self::$dataCache->getItem( $this->mCode, 'bookstoreList' ); |
| 229 | 230 | } |
| 230 | 231 | |
| 231 | 232 | /** |
| 232 | 233 | * @return array |
| 233 | 234 | */ |
| 234 | 235 | 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 | + } |
| 236 | 257 | return $this->namespaceNames; |
| 237 | 258 | } |
| 238 | 259 | |
| — | — | @@ -287,11 +308,54 @@ |
| 288 | 309 | * @return mixed An integer if $text is a valid value otherwise false |
| 289 | 310 | */ |
| 290 | 311 | function getLocalNsIndex( $text ) { |
| 291 | | - $this->load(); |
| 292 | 312 | $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; |
| 294 | 315 | } |
| 295 | 316 | |
| | 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 | + |
| 296 | 360 | /** |
| 297 | 361 | * Get a namespace key by value, case insensitive. Canonical namespace |
| 298 | 362 | * names override custom ones defined for the current language. |
| — | — | @@ -300,10 +364,12 @@ |
| 301 | 365 | * @return mixed An integer if $text is a valid value otherwise false |
| 302 | 366 | */ |
| 303 | 367 | function getNsIndex( $text ) { |
| 304 | | - $this->load(); |
| 305 | 368 | $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; |
| 308 | 374 | } |
| 309 | 375 | |
| 310 | 376 | /** |
| — | — | @@ -335,48 +401,41 @@ |
| 336 | 402 | } |
| 337 | 403 | |
| 338 | 404 | function getMathNames() { |
| 339 | | - $this->load(); |
| 340 | | - return $this->mathNames; |
| | 405 | + return self::$dataCache->getItem( $this->mCode, 'mathNames' ); |
| 341 | 406 | } |
| 342 | 407 | |
| 343 | 408 | function getDatePreferences() { |
| 344 | | - $this->load(); |
| 345 | | - return $this->datePreferences; |
| | 409 | + return self::$dataCache->getItem( $this->mCode, 'datePreferences' ); |
| 346 | 410 | } |
| 347 | 411 | |
| 348 | 412 | function getDateFormats() { |
| 349 | | - $this->load(); |
| 350 | | - return $this->dateFormats; |
| | 413 | + return self::$dataCache->getItem( $this->mCode, 'dateFormats' ); |
| 351 | 414 | } |
| 352 | 415 | |
| 353 | 416 | 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 | + } |
| 356 | 424 | } |
| 357 | 425 | |
| 358 | 426 | function getDatePreferenceMigrationMap() { |
| 359 | | - $this->load(); |
| 360 | | - return $this->datePreferenceMigrationMap; |
| | 427 | + return self::$dataCache->getItem( $this->mCode, 'datePreferenceMigrationMap' ); |
| 361 | 428 | } |
| 362 | 429 | |
| 363 | 430 | function getImageFile( $image ) { |
| 364 | | - $this->load(); |
| 365 | | - return $this->imageFiles[$image]; |
| | 431 | + return self::$dataCache->getSubitem( $this->mCode, 'imageFiles', $image ); |
| 366 | 432 | } |
| 367 | 433 | |
| 368 | 434 | 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' ); |
| 376 | 436 | } |
| 377 | 437 | |
| 378 | 438 | function getExtraUserToggles() { |
| 379 | | - $this->load(); |
| 380 | | - return $this->extraUserToggles; |
| | 439 | + return self::$dataCache->getItem( $this->mCode, 'extraUserToggles' ); |
| 381 | 440 | } |
| 382 | 441 | |
| 383 | 442 | function getUserToggle( $tog ) { |
| — | — | @@ -1319,6 +1378,28 @@ |
| 1320 | 1379 | } |
| 1321 | 1380 | |
| 1322 | 1381 | /** |
| | 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 | + /** |
| 1323 | 1404 | * @param $ts Mixed: the time format which needs to be turned into a |
| 1324 | 1405 | * date('YmdHis') format with wfTimestamp(TS_MW,$ts) |
| 1325 | 1406 | * @param $adj Bool: whether to adjust the time output according to the |
| — | — | @@ -1329,16 +1410,11 @@ |
| 1330 | 1411 | * @return string |
| 1331 | 1412 | */ |
| 1332 | 1413 | function date( $ts, $adj = false, $format = true, $timecorrection = false ) { |
| 1333 | | - $this->load(); |
| 1334 | 1414 | if ( $adj ) { |
| 1335 | 1415 | $ts = $this->userAdjust( $ts, $timecorrection ); |
| 1336 | 1416 | } |
| 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 ); |
| 1343 | 1419 | } |
| 1344 | 1420 | |
| 1345 | 1421 | /** |
| — | — | @@ -1352,16 +1428,11 @@ |
| 1353 | 1429 | * @return string |
| 1354 | 1430 | */ |
| 1355 | 1431 | function time( $ts, $adj = false, $format = true, $timecorrection = false ) { |
| 1356 | | - $this->load(); |
| 1357 | 1432 | if ( $adj ) { |
| 1358 | 1433 | $ts = $this->userAdjust( $ts, $timecorrection ); |
| 1359 | 1434 | } |
| 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 ); |
| 1366 | 1437 | } |
| 1367 | 1438 | |
| 1368 | 1439 | /** |
| — | — | @@ -1376,30 +1447,20 @@ |
| 1377 | 1448 | * @return string |
| 1378 | 1449 | */ |
| 1379 | 1450 | function timeanddate( $ts, $adj = false, $format = true, $timecorrection = false) { |
| 1380 | | - $this->load(); |
| 1381 | | - |
| 1382 | 1451 | $ts = wfTimestamp( TS_MW, $ts ); |
| 1383 | | - |
| 1384 | 1452 | if ( $adj ) { |
| 1385 | 1453 | $ts = $this->userAdjust( $ts, $timecorrection ); |
| 1386 | 1454 | } |
| 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 ); |
| 1394 | 1457 | } |
| 1395 | 1458 | |
| 1396 | 1459 | 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 ); |
| 1399 | 1461 | } |
| 1400 | 1462 | |
| 1401 | 1463 | function getAllMessages() { |
| 1402 | | - $this->load(); |
| 1403 | | - return $this->messages; |
| | 1464 | + return self::$dataCache->getItem( $this->mCode, 'messages' ); |
| 1404 | 1465 | } |
| 1405 | 1466 | |
| 1406 | 1467 | function iconv( $in, $out, $string ) { |
| — | — | @@ -1590,8 +1651,7 @@ |
| 1591 | 1652 | } |
| 1592 | 1653 | |
| 1593 | 1654 | function fallback8bitEncoding() { |
| 1594 | | - $this->load(); |
| 1595 | | - return $this->fallback8bitEncoding; |
| | 1655 | + return self::$dataCache->getItem( $this->mCode, 'fallback8bitEncoding' ); |
| 1596 | 1656 | } |
| 1597 | 1657 | |
| 1598 | 1658 | /** |
| — | — | @@ -1669,7 +1729,7 @@ |
| 1670 | 1730 | * if we need to pad short words... |
| 1671 | 1731 | */ |
| 1672 | 1732 | protected function minSearchLength() { |
| 1673 | | - if( !isset( $this->minSearchLength ) ) { |
| | 1733 | + if( is_null( $this->minSearchLength ) ) { |
| 1674 | 1734 | $sql = "show global variables like 'ft\\_min\\_word\\_len'"; |
| 1675 | 1735 | $dbr = wfGetDB( DB_SLAVE ); |
| 1676 | 1736 | $result = $dbr->query( $sql ); |
| — | — | @@ -1789,8 +1849,7 @@ |
| 1790 | 1850 | * @return bool |
| 1791 | 1851 | */ |
| 1792 | 1852 | function isRTL() { |
| 1793 | | - $this->load(); |
| 1794 | | - return $this->rtl; |
| | 1853 | + return self::$dataCache->getItem( $this->mCode, 'rtl' ); |
| 1795 | 1854 | } |
| 1796 | 1855 | |
| 1797 | 1856 | /** |
| — | — | @@ -1803,8 +1862,7 @@ |
| 1804 | 1863 | } |
| 1805 | 1864 | |
| 1806 | 1865 | function capitalizeAllNouns() { |
| 1807 | | - $this->load(); |
| 1808 | | - return $this->capitalizeAllNouns; |
| | 1866 | + return self::$dataCache->getItem( $this->mCode, 'capitalizeAllNouns' ); |
| 1809 | 1867 | } |
| 1810 | 1868 | |
| 1811 | 1869 | /** |
| — | — | @@ -1822,13 +1880,11 @@ |
| 1823 | 1881 | * @return bool |
| 1824 | 1882 | */ |
| 1825 | 1883 | function linkPrefixExtension() { |
| 1826 | | - $this->load(); |
| 1827 | | - return $this->linkPrefixExtension; |
| | 1884 | + return self::$dataCache->getItem( $this->mCode, 'linkPrefixExtension' ); |
| 1828 | 1885 | } |
| 1829 | 1886 | |
| 1830 | | - function &getMagicWords() { |
| 1831 | | - $this->load(); |
| 1832 | | - return $this->magicWords; |
| | 1887 | + function getMagicWords() { |
| | 1888 | + return self::$dataCache->getItem( $this->mCode, 'magicWords' ); |
| 1833 | 1889 | } |
| 1834 | 1890 | |
| 1835 | 1891 | # Fill a MagicWord object with data from here |
| — | — | @@ -1840,16 +1896,11 @@ |
| 1841 | 1897 | if ( isset( $this->mMagicExtensions[$mw->mId] ) ) { |
| 1842 | 1898 | $rawEntry = $this->mMagicExtensions[$mw->mId]; |
| 1843 | 1899 | } else { |
| 1844 | | - $magicWords =& $this->getMagicWords(); |
| | 1900 | + $magicWords = $this->getMagicWords(); |
| 1845 | 1901 | if ( isset( $magicWords[$mw->mId] ) ) { |
| 1846 | 1902 | $rawEntry = $magicWords[$mw->mId]; |
| 1847 | 1903 | } 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; |
| 1854 | 1905 | } |
| 1855 | 1906 | } |
| 1856 | 1907 | |
| — | — | @@ -1887,43 +1938,11 @@ |
| 1888 | 1939 | * case folded alias => real name |
| 1889 | 1940 | */ |
| 1890 | 1941 | function getSpecialPageAliases() { |
| 1891 | | - $this->load(); |
| 1892 | | - |
| 1893 | 1942 | // Cache aliases because it may be slow to load them |
| 1894 | | - if ( !isset( $this->mExtendedSpecialPageAliases ) ) { |
| 1895 | | - |
| | 1943 | + if ( is_null( $this->mExtendedSpecialPageAliases ) ) { |
| 1896 | 1944 | // 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' ); |
| 1928 | 1947 | wfRunHooks( 'LanguageGetSpecialPageAliases', |
| 1929 | 1948 | array( &$this->mExtendedSpecialPageAliases, $this->getCode() ) ); |
| 1930 | 1949 | } |
| — | — | @@ -1932,20 +1951,6 @@ |
| 1933 | 1952 | } |
| 1934 | 1953 | |
| 1935 | 1954 | /** |
| 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 | | - /** |
| 1950 | 1955 | * Italic is unsuitable for some languages |
| 1951 | 1956 | * |
| 1952 | 1957 | * @param $text String: the text to be emphasized. |
| — | — | @@ -2017,13 +2022,11 @@ |
| 2018 | 2023 | } |
| 2019 | 2024 | |
| 2020 | 2025 | function digitTransformTable() { |
| 2021 | | - $this->load(); |
| 2022 | | - return $this->digitTransformTable; |
| | 2026 | + return self::$dataCache->getItem( $this->mCode, 'digitTransformTable' ); |
| 2023 | 2027 | } |
| 2024 | 2028 | |
| 2025 | 2029 | function separatorTransformTable() { |
| 2026 | | - $this->load(); |
| 2027 | | - return $this->separatorTransformTable; |
| | 2030 | + return self::$dataCache->getItem( $this->mCode, 'separatorTransformTable' ); |
| 2028 | 2031 | } |
| 2029 | 2032 | |
| 2030 | 2033 | |
| — | — | @@ -2380,8 +2383,7 @@ |
| 2381 | 2384 | * @return string |
| 2382 | 2385 | */ |
| 2383 | 2386 | function linkTrail() { |
| 2384 | | - $this->load(); |
| 2385 | | - return $this->linkTrail; |
| | 2387 | + return self::$dataCache->getItem( $this->mCode, 'linkTrail' ); |
| 2386 | 2388 | } |
| 2387 | 2389 | |
| 2388 | 2390 | function getLangObj() { |
| — | — | @@ -2413,308 +2415,33 @@ |
| 2414 | 2416 | return self::getFileName( "$IP/languages/classes/Language", $code, '.php' ); |
| 2415 | 2417 | } |
| 2416 | 2418 | |
| 2417 | | - static function getLocalisationArray( $code, $disableCache = false ) { |
| 2418 | | - self::loadLocalisation( $code, $disableCache ); |
| 2419 | | - return self::$mLocalisationCache[$code]; |
| 2420 | | - } |
| 2421 | | - |
| 2422 | 2419 | /** |
| 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 | | - /** |
| 2598 | 2420 | * Get the fallback for a given language |
| 2599 | 2421 | */ |
| 2600 | 2422 | 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' ); |
| 2621 | 2428 | } |
| 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; |
| 2631 | 2429 | } |
| 2632 | 2430 | |
| 2633 | 2431 | /** |
| 2634 | 2432 | * Get all messages for a given language |
| | 2433 | + * WARNING: this may take a long time |
| 2635 | 2434 | */ |
| 2636 | 2435 | static function getMessagesFor( $code ) { |
| 2637 | | - self::loadLocalisation( $code ); |
| 2638 | | - return self::$mLocalisationCache[$code]['messages']; |
| | 2436 | + return self::getLocalisationCache()->getItem( $code, 'messages' ); |
| 2639 | 2437 | } |
| 2640 | 2438 | |
| 2641 | 2439 | /** |
| 2642 | 2440 | * Get a message for a given language |
| 2643 | 2441 | */ |
| 2644 | 2442 | 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 ); |
| 2647 | 2444 | } |
| 2648 | 2445 | |
| 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 | | - |
| 2719 | 2446 | function fixVariableInNamespace( $talk ) { |
| 2720 | 2447 | if ( strpos( $talk, '$1' ) === false ) return $talk; |
| 2721 | 2448 | |
| Index: trunk/phase3/RELEASE-NOTES |
| — | — | @@ -41,6 +41,15 @@ |
| 42 | 42 | appropriate privileges. Creating this user with web-install page requires |
| 43 | 43 | oci8.privileged_connect set to On in php.ini. |
| 44 | 44 | * 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. |
| 45 | 54 | |
| 46 | 55 | === New features in 1.16 === |
| 47 | 56 | |
| — | — | @@ -93,6 +102,12 @@ |
| 94 | 103 | the DBA extension is not available. |
| 95 | 104 | * (bug 14611) Added support showing the version of the image thumbnailing |
| 96 | 105 | 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. |
| 97 | 112 | |
| 98 | 113 | === Bug fixes in 1.16 === |
| 99 | 114 | |