<?php
-define( 'MW_LC_VERSION', 1 );
+define( 'MW_LC_VERSION', 2 );
/**
* Class for caching the contents of localisation files, Messages*.php
*
* An instance of this class is available using Language::getLocalisationCache().
*
- * The values retrieved from here are merged, containing items from extension
- * files, core messages files and the language fallback sequence (e.g. zh-cn ->
+ * The values retrieved from here are merged, containing items from extension
+ * files, core messages files and the language fallback sequence (e.g. zh-cn ->
* zh-hans -> en ). Some common errors are corrected, for example namespace
* names with spaces instead of underscores, but heavyweight processing, such
* as grammatical transformation, is done by the caller.
* All item keys
*/
static public $allKeys = array(
- 'fallback', 'namespaceNames', 'mathNames', 'bookstoreList',
+ 'fallback', 'namespaceNames', 'bookstoreList',
'magicWords', 'messages', 'rtl', 'capitalizeAllNouns', 'digitTransformTable',
'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension',
- 'defaultUserOptionOverrides', 'linkTrail', 'namespaceAliases',
+ 'linkTrail', 'namespaceAliases',
'dateFormats', 'datePreferences', 'datePreferenceMigrationMap',
'defaultDateFormat', 'extraUserToggles', 'specialPageAliases',
- 'imageFiles', 'preloadedMessages',
+ 'imageFiles', 'preloadedMessages', 'namespaceGenderAliases',
+ 'digitGroupingPattern'
);
/**
* Keys for items which consist of associative arrays, which may be merged
* by a fallback sequence.
*/
- static public $mergeableMapKeys = array( 'messages', 'namespaceNames', 'mathNames',
- 'dateFormats', 'defaultUserOptionOverrides', 'magicWords', 'imageFiles',
- 'preloadedMessages',
+ static public $mergeableMapKeys = array( 'messages', 'namespaceNames',
+ 'dateFormats', 'imageFiles', 'preloadedMessages',
);
/**
*/
static public $optionalMergeKeys = array( 'bookstoreList' );
+ /**
+ * Keys for items that are formatted like $magicWords
+ */
+ static public $magicWordKeys = array( 'magicWords' );
+
/**
* Keys for items where the subitems are stored in the backend separately.
*/
/**
* Keys which are loaded automatically by initLanguage()
*/
- static public $preloadedKeys = array( 'dateFormats', 'namespaceNames',
- 'defaultUserOptionOverrides' );
+ static public $preloadedKeys = array( 'dateFormats', 'namespaceNames' );
/**
* Constructor.
- * For constructor parameters, see the documentation in DefaultSettings.php
+ * For constructor parameters, see the documentation in DefaultSettings.php
* for $wgLocalisationCacheConf.
+ *
+ * @param $conf Array
*/
function __construct( $conf ) {
global $wgCacheDirectory;
$this->conf = $conf;
- $this->data = array();
- $this->loadedItems = array();
- $this->loadedSubitems = array();
- $this->initialisedLangs = array();
+ $storeConf = array();
if ( !empty( $conf['storeClass'] ) ) {
$storeClass = $conf['storeClass'];
} else {
$storeClass = $wgCacheDirectory ? 'LCStore_CDB' : 'LCStore_DB';
break;
default:
- throw new MWException(
- 'Please set $wgLocalisationConf[\'store\'] to something sensible.' );
+ throw new MWException(
+ 'Please set $wgLocalisationCacheConf[\'store\'] to something sensible.' );
}
}
wfDebug( get_class( $this ) . ": using store $storeClass\n" );
- $this->store = new $storeClass;
+ if ( !empty( $conf['storeDirectory'] ) ) {
+ $storeConf['directory'] = $conf['storeDirectory'];
+ }
+
+ $this->store = new $storeClass( $storeConf );
foreach ( array( 'manualRecache', 'forceRecache' ) as $var ) {
if ( isset( $conf[$var] ) ) {
$this->$var = $conf[$var];
self::$mergeableMapKeys,
self::$mergeableListKeys,
self::$mergeableAliasListKeys,
- self::$optionalMergeKeys
+ self::$optionalMergeKeys,
+ self::$magicWordKeys
) );
}
return isset( $this->mergeableKeys[$key] );
$this->loadItem( $code, $key );
wfProfileOut( __METHOD__.'-load' );
}
+
if ( $key === 'fallback' && isset( $this->shallowFallbacks[$code] ) ) {
return $this->shallowFallbacks[$code];
}
+
return $this->data[$code][$key];
}
* Get a subitem, for instance a single message for a given language.
*/
public function getSubitem( $code, $key, $subkey ) {
- if ( !isset( $this->loadedSubitems[$code][$key][$subkey] ) ) {
- if ( isset( $this->loadedItems[$code][$key] ) ) {
- if ( isset( $this->data[$code][$key][$subkey] ) ) {
- return $this->data[$code][$key][$subkey];
- } else {
- return null;
- }
+ if ( !isset( $this->loadedSubitems[$code][$key][$subkey] )
+ && !isset( $this->loadedItems[$code][$key] ) )
+ {
+ wfProfileIn( __METHOD__.'-load' );
+ $this->loadSubitem( $code, $key, $subkey );
+ wfProfileOut( __METHOD__.'-load' );
+ }
+
+ if ( isset( $this->data[$code][$key][$subkey] ) ) {
+ return $this->data[$code][$key][$subkey];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the list of subitem keys for a given item.
+ *
+ * This is faster than array_keys($lc->getItem(...)) for the items listed in
+ * self::$splitKeys.
+ *
+ * Will return null if the item is not found, or false if the item is not an
+ * array.
+ */
+ public function getSubitemList( $code, $key ) {
+ if ( in_array( $key, self::$splitKeys ) ) {
+ return $this->getSubitem( $code, 'list', $key );
+ } else {
+ $item = $this->getItem( $code, $key );
+ if ( is_array( $item ) ) {
+ return array_keys( $item );
} else {
- wfProfileIn( __METHOD__.'-load' );
- $this->loadSubitem( $code, $key, $subkey );
- wfProfileOut( __METHOD__.'-load' );
+ return false;
}
}
- return $this->data[$code][$key][$subkey];
}
/**
if ( !isset( $this->initialisedLangs[$code] ) ) {
$this->initLanguage( $code );
}
+
// Check to see if initLanguage() loaded it for us
if ( isset( $this->loadedItems[$code][$key] ) ) {
return;
}
+
if ( isset( $this->shallowFallbacks[$code] ) ) {
$this->loadItem( $this->shallowFallbacks[$code], $key );
return;
}
+
if ( in_array( $key, self::$splitKeys ) ) {
$subkeyList = $this->getSubitem( $code, 'list', $key );
foreach ( $subkeyList as $subkey ) {
} else {
$this->data[$code][$key] = $this->store->get( $code, $key );
}
+
$this->loadedItems[$code][$key] = true;
}
$this->loadItem( $code, $key );
return;
}
+
if ( !isset( $this->initialisedLangs[$code] ) ) {
$this->initLanguage( $code );
}
+
// Check to see if initLanguage() loaded it for us
- if ( isset( $this->loadedSubitems[$code][$key][$subkey] ) ) {
+ if ( isset( $this->loadedItems[$code][$key] )
+ || isset( $this->loadedSubitems[$code][$key][$subkey] ) )
+ {
return;
}
+
if ( isset( $this->shallowFallbacks[$code] ) ) {
$this->loadSubitem( $this->shallowFallbacks[$code], $key, $subkey );
return;
}
+
$value = $this->store->get( $code, "$key:$subkey" );
$this->data[$code][$key][$subkey] = $value;
$this->loadedSubitems[$code][$key][$subkey] = true;
wfDebug( __METHOD__."($code): cache missing, need to make one\n" );
return true;
}
+
foreach ( $deps as $dep ) {
- if ( $dep->isExpired() ) {
- wfDebug( __METHOD__."($code): cache for $code expired due to " .
+ // Because we're unserializing stuff from cache, we
+ // could receive objects of classes that don't exist
+ // anymore (e.g. uninstalled extensions)
+ // When this happens, always expire the cache
+ if ( !$dep instanceof CacheDependency || $dep->isExpired() ) {
+ wfDebug( __METHOD__."($code): cache for $code expired due to " .
get_class( $dep ) . "\n" );
return true;
}
}
+
return false;
}
if ( isset( $this->initialisedLangs[$code] ) ) {
return;
}
+
$this->initialisedLangs[$code] = true;
+ # If the code is of the wrong form for a Messages*.php file, do a shallow fallback
+ if ( !Language::isValidBuiltInCode( $code ) ) {
+ $this->initShallowFallback( $code, 'en' );
+ return;
+ }
+
# Recache the data if necessary
if ( !$this->manualRecache && $this->isExpired( $code ) ) {
if ( file_exists( Language::getMessagesFileName( $code ) ) ) {
if ( $this->manualRecache ) {
// No Messages*.php file. Do shallow fallback to en.
if ( $code === 'en' ) {
- throw new MWException( 'No localisation cache found for English. ' .
+ throw new MWException( 'No localisation cache found for English. ' .
'Please run maintenance/rebuildLocalisationCache.php.' );
}
$this->initShallowFallback( $code, 'en' );
}
/**
- * Create a fallback from one language to another, without creating a
+ * Create a fallback from one language to another, without creating a
* complete persistent cache.
*/
public function initShallowFallback( $primaryCode, $fallbackCode ) {
*/
protected function readPHPFile( $_fileName, $_fileType ) {
// Disable APC caching
- $_apcEnabled = ini_set( 'apc.enabled', '0' );
+ $_apcEnabled = ini_set( 'apc.cache_by_default', '0' );
include( $_fileName );
- ini_set( 'apc.enabled', $_apcEnabled );
+ ini_set( 'apc.cache_by_default', $_apcEnabled );
if ( $_fileType == 'core' || $_fileType == 'extension' ) {
$data = compact( self::$allKeys );
} else {
throw new MWException( __METHOD__.": Invalid file type: $_fileType" );
}
+
return $data;
}
/**
- * Merge two localisation values, a primary and a fallback, overwriting the
+ * Merge two localisation values, a primary and a fallback, overwriting the
* primary value in place.
*/
protected function mergeItem( $key, &$value, $fallbackValue ) {
if ( !empty( $value['inherit'] ) ) {
$value = array_merge( $fallbackValue, $value );
}
+
if ( isset( $value['inherit'] ) ) {
unset( $value['inherit'] );
}
+ } elseif ( in_array( $key, self::$magicWordKeys ) ) {
+ $this->mergeMagicWords( $value, $fallbackValue );
}
}
} else {
}
}
+ protected function mergeMagicWords( &$value, $fallbackValue ) {
+ foreach ( $fallbackValue as $magicName => $fallbackInfo ) {
+ if ( !isset( $value[$magicName] ) ) {
+ $value[$magicName] = $fallbackInfo;
+ } else {
+ $oldSynonyms = array_slice( $fallbackInfo, 1 );
+ $newSynonyms = array_slice( $value[$magicName], 1 );
+ $synonyms = array_values( array_unique( array_merge(
+ $newSynonyms, $oldSynonyms ) ) );
+ $value[$magicName] = array_merge( array( $fallbackInfo[0] ), $synonyms );
+ }
+ }
+ }
+
/**
* Given an array mapping language code to localisation value, such as is
* found in extension *.i18n.php files, iterate through a fallback sequence
* to merge the given data with an existing primary value.
*
- * Returns true if any data from the extension array was used, false
+ * Returns true if any data from the extension array was used, false
* otherwise.
*/
protected function mergeExtensionItem( $codeSequence, $key, &$value, $fallbackValue ) {
$used = true;
}
}
+
return $used;
}
* and save it to the persistent cache store and the process cache
*/
public function recache( $code ) {
- static $recursionGuard = array();
global $wgExtensionMessagesFiles, $wgExtensionAliasesFiles;
wfProfileIn( __METHOD__ );
# Initial values
$initialData = array_combine(
- self::$allKeys,
+ self::$allKeys,
array_fill( 0, count( self::$allKeys ), null ) );
$coreData = $initialData;
$deps = array();
foreach ( $data as $key => $value ) {
$this->mergeItem( $key, $coreData[$key], $value );
}
+
}
# Fill in the fallback if it's not there already
$coreData['fallback'] = $code === 'en' ? false : 'en';
}
- if ( $coreData['fallback'] !== false ) {
- # Guard against circular references
- if ( isset( $recursionGuard[$code] ) ) {
- throw new MWException( "Error: Circular fallback reference in language code $code" );
+ if ( $coreData['fallback'] === false ) {
+ $coreData['fallbackSequence'] = array();
+ } else {
+ $coreData['fallbackSequence'] = array_map( 'trim', explode( ',', $coreData['fallback'] ) );
+ $len = count( $coreData['fallbackSequence'] );
+
+ # Ensure that the sequence ends at en
+ if ( $coreData['fallbackSequence'][$len - 1] !== 'en' ) {
+ $coreData['fallbackSequence'][] = 'en';
}
- $recursionGuard[$code] = true;
# Load the fallback localisation item by item and merge it
- $deps = array_merge( $deps, $this->getItem( $coreData['fallback'], 'deps' ) );
- foreach ( self::$allKeys as $key ) {
- if ( is_null( $coreData[$key] ) || $this->isMergeableKey( $key ) ) {
- $fallbackValue = $this->getItem( $coreData['fallback'], $key );
- $this->mergeItem( $key, $coreData[$key], $fallbackValue );
+ foreach ( $coreData['fallbackSequence'] as $fbCode ) {
+ # Load the secondary localisation from the source file to
+ # avoid infinite cycles on cyclic fallbacks
+ $fbFilename = Language::getMessagesFileName( $fbCode );
+
+ if ( !file_exists( $fbFilename ) ) {
+ continue;
+ }
+
+ $deps[] = new FileDependency( $fbFilename );
+ $fbData = $this->readPHPFile( $fbFilename, 'core' );
+
+ foreach ( self::$allKeys as $key ) {
+ if ( !isset( $fbData[$key] ) ) {
+ continue;
+ }
+
+ if ( is_null( $coreData[$key] ) || $this->isMergeableKey( $key ) ) {
+ $this->mergeItem( $key, $coreData[$key], $fbData[$key] );
+ }
}
}
- $fallbackSequence = $this->getItem( $coreData['fallback'], 'fallbackSequence' );
- array_unshift( $fallbackSequence, $coreData['fallback'] );
- $coreData['fallbackSequence'] = $fallbackSequence;
- unset( $recursionGuard[$code] );
- } else {
- $coreData['fallbackSequence'] = array();
}
+
$codeSequence = array_merge( array( $code ), $coreData['fallbackSequence'] );
# Load the extension localisations
# This is done after the core because we know the fallback sequence now.
- # But it has a higher precedence for merging so that we can support things
+ # But it has a higher precedence for merging so that we can support things
# like site-specific message overrides.
$allData = $initialData;
foreach ( $wgExtensionMessagesFiles as $fileName ) {
$data = $this->readPHPFile( $fileName, 'extension' );
$used = false;
+
foreach ( $data as $key => $item ) {
- $used = $used ||
- $this->mergeExtensionItem( $codeSequence, $key, $allData[$key], $item );
+ if( $this->mergeExtensionItem( $codeSequence, $key, $allData[$key], $item ) ) {
+ $used = true;
+ }
}
+
if ( $used ) {
$deps[] = new FileDependency( $fileName );
}
# Load deprecated $wgExtensionAliasesFiles
foreach ( $wgExtensionAliasesFiles as $fileName ) {
$data = $this->readPHPFile( $fileName, 'aliases' );
+
if ( !isset( $data['aliases'] ) ) {
continue;
}
+
$used = $this->mergeExtensionItem( $codeSequence, 'specialPageAliases',
$allData['specialPageAliases'], $data['aliases'] );
+
if ( $used ) {
$deps[] = new FileDependency( $fileName );
}
}
# Decouple the reference to prevent accidental damage
unset($page);
-
- # Fix broken defaultUserOptionOverrides
- if ( !is_array( $allData['defaultUserOptionOverrides'] ) ) {
- $allData['defaultUserOptionOverrides'] = array();
- }
-
- # Set the preload key
- $allData['preload'] = $this->buildPreload( $allData );
# Set the list keys
$allData['list'] = array();
# Run hooks
wfRunHooks( 'LocalisationCacheRecache', array( $this, $code, &$allData ) );
- if ( is_null( $allData['defaultUserOptionOverrides'] ) ) {
- throw new MWException( __METHOD__.': Localisation data failed sanity check! ' .
+ if ( is_null( $allData['namespaceNames'] ) ) {
+ throw new MWException( __METHOD__.': Localisation data failed sanity check! ' .
'Check that your languages/messages/MessagesEn.php file is intact.' );
}
+ # Set the preload key
+ $allData['preload'] = $this->buildPreload( $allData );
+
# Save to the process cache and register the items loaded
$this->data[$code] = $allData;
foreach ( $allData as $key => $item ) {
}
$this->store->finishWrite();
+ # Clear out the MessageBlobStore
+ # HACK: If using a null (i.e. disabled) storage backend, we
+ # can't write to the MessageBlobStore either
+ if ( !$this->store instanceof LCStore_Null ) {
+ MessageBlobStore::clear();
+ }
+
wfProfileOut( __METHOD__ );
}
foreach ( self::$preloadedKeys as $key ) {
$preload[$key] = $data[$key];
}
+
foreach ( $data['preloadedMessages'] as $subkey ) {
if ( isset( $data['messages'][$subkey] ) ) {
$subitem = $data['messages'][$subkey];
}
$preload['messages'][$subkey] = $subitem;
}
+
return $preload;
}
/**
- * Unload the data for a given language from the object cache.
+ * Unload the data for a given language from the object cache.
* Reduces memory usage.
*/
public function unload( $code ) {
unset( $this->loadedItems[$code] );
unset( $this->loadedSubitems[$code] );
unset( $this->initialisedLangs[$code] );
+
foreach ( $this->shallowFallbacks as $shallowCode => $fbCode ) {
if ( $fbCode === $code ) {
$this->unload( $shallowCode );
}
}
}
+
+ /**
+ * Unload all data
+ */
+ public function unloadAll() {
+ foreach ( $this->initialisedLangs as $lang => $unused ) {
+ $this->unload( $lang );
+ }
+ }
+
+ /**
+ * Disable the storage backend
+ */
+ public function disableBackend() {
+ $this->store = new LCStore_Null;
+ $this->manualRecache = false;
+ }
}
/**
* The persistence layer is two-level hierarchical cache. The first level
* is the language, the second level is the item or subitem.
*
- * Since the data for a whole language is rebuilt in one operation, it needs
- * to have a fast and atomic method for deleting or replacing all of the
+ * Since the data for a whole language is rebuilt in one operation, it needs
+ * to have a fast and atomic method for deleting or replacing all of the
* current data for a given language. The interface reflects this bulk update
- * operation. Callers writing to the cache must first call startWrite(), then
- * will call set() a couple of thousand times, then will call finishWrite()
- * to commit the operation. When finishWrite() is called, the cache is
+ * operation. Callers writing to the cache must first call startWrite(), then
+ * will call set() a couple of thousand times, then will call finishWrite()
+ * to commit the operation. When finishWrite() is called, the cache is
* expected to delete all data previously stored for that language.
*
- * The values stored are PHP variables suitable for serialize(). Implementations
+ * The values stored are PHP variables suitable for serialize(). Implementations
* of LCStore are responsible for serializing and unserializing.
*/
interface LCStore {
* @param $code Language code
* @param $key Cache key
*/
- public function get( $code, $key );
+ function get( $code, $key );
/**
* Start a write transaction.
* @param $code Language code
*/
- public function startWrite( $code );
+ function startWrite( $code );
/**
* Finish a write transaction.
*/
- public function finishWrite();
+ function finishWrite();
/**
* Set a key to a given value. startWrite() must be called before this
* is called, and finishWrite() must be called afterwards.
*/
- public function set( $key, $value );
-
+ function set( $key, $value );
}
/**
- * LCStore implementation which uses the standard DB functions to store data.
+ * LCStore implementation which uses the standard DB functions to store data.
* This will work on any MediaWiki installation.
*/
class LCStore_DB implements LCStore {
var $currentLang;
var $writesDone = false;
var $dbw, $batch;
+ var $readOnly = false;
public function get( $code, $key ) {
if ( $this->writesDone ) {
}
public function startWrite( $code ) {
+ if ( $this->readOnly ) {
+ return;
+ }
+
if ( !$code ) {
throw new MWException( __METHOD__.": Invalid language \"$code\"" );
}
+
$this->dbw = wfGetDB( DB_MASTER );
- $this->dbw->begin();
- $this->dbw->delete( 'l10n_cache', array( 'lc_lang' => $code ), __METHOD__ );
+ try {
+ $this->dbw->begin();
+ $this->dbw->delete( 'l10n_cache', array( 'lc_lang' => $code ), __METHOD__ );
+ } catch ( DBQueryError $e ) {
+ if ( $this->dbw->wasReadOnlyError() ) {
+ $this->readOnly = true;
+ $this->dbw->rollback();
+ $this->dbw->ignoreErrors( false );
+ return;
+ } else {
+ throw $e;
+ }
+ }
+
$this->currentLang = $code;
$this->batch = array();
}
public function finishWrite() {
+ if ( $this->readOnly ) {
+ return;
+ }
+
if ( $this->batch ) {
$this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ );
}
+
$this->dbw->commit();
$this->currentLang = null;
$this->dbw = null;
}
public function set( $key, $value ) {
+ if ( $this->readOnly ) {
+ return;
+ }
+
if ( is_null( $this->currentLang ) ) {
throw new MWException( __CLASS__.': must call startWrite() before calling set()' );
}
+
$this->batch[] = array(
'lc_lang' => $this->currentLang,
'lc_key' => $key,
'lc_value' => serialize( $value ) );
+
if ( count( $this->batch ) >= 100 ) {
$this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ );
$this->batch = array();
* directory given by $wgCacheDirectory. If $wgCacheDirectory is not set, this
* will throw an exception.
*
- * Profiling indicates that on Linux, this implementation outperforms MySQL if
- * the directory is on a local filesystem and there is ample kernel cache
- * space. The performance advantage is greater when the DBA extension is
+ * Profiling indicates that on Linux, this implementation outperforms MySQL if
+ * the directory is on a local filesystem and there is ample kernel cache
+ * space. The performance advantage is greater when the DBA extension is
* available than it is with the PHP port.
*
* See Cdb.php and http://cr.yp.to/cdb.html
*/
class LCStore_CDB implements LCStore {
- var $readers, $writer, $currentLang;
-
+ var $readers, $writer, $currentLang, $directory;
+
+ function __construct( $conf = array() ) {
+ global $wgCacheDirectory;
+
+ if ( isset( $conf['directory'] ) ) {
+ $this->directory = $conf['directory'];
+ } else {
+ $this->directory = $wgCacheDirectory;
+ }
+ }
+
public function get( $code, $key ) {
if ( !isset( $this->readers[$code] ) ) {
$fileName = $this->getFileName( $code );
+
if ( !file_exists( $fileName ) ) {
$this->readers[$code] = false;
} else {
$this->readers[$code] = CdbReader::open( $fileName );
}
}
+
if ( !$this->readers[$code] ) {
return null;
} else {
$value = $this->readers[$code]->get( $key );
+
if ( $value === false ) {
return null;
}
}
public function startWrite( $code ) {
+ if ( !file_exists( $this->directory ) ) {
+ if ( !wfMkdirParents( $this->directory, null, __METHOD__ ) ) {
+ throw new MWException( "Unable to create the localisation store " .
+ "directory \"{$this->directory}\"" );
+ }
+ }
+
+ // Close reader to stop permission errors on write
+ if( !empty($this->readers[$code]) ) {
+ $this->readers[$code]->close();
+ }
+
$this->writer = CdbWriter::open( $this->getFileName( $code ) );
$this->currentLang = $code;
}
// Close the writer
$this->writer->close();
$this->writer = null;
-
- // Reopen the reader
- if ( !empty( $this->readers[$this->currentLang] ) ) {
- $this->readers[$this->currentLang]->close();
- }
unset( $this->readers[$this->currentLang] );
$this->currentLang = null;
}
}
protected function getFileName( $code ) {
- global $wgCacheDirectory;
if ( !$code || strpos( $code, '/' ) !== false ) {
throw new MWException( __METHOD__.": Invalid language \"$code\"" );
}
- return "$wgCacheDirectory/l10n_cache-$code.cdb";
+ return "{$this->directory}/l10n_cache-$code.cdb";
}
}
/**
- * A localisation cache optimised for loading large amounts of data for many
+ * Null store backend, used to avoid DB errors during install
+ */
+class LCStore_Null implements LCStore {
+ public function get( $code, $key ) {
+ return null;
+ }
+
+ public function startWrite( $code ) {}
+ public function finishWrite() {}
+ public function set( $key, $value ) {}
+}
+
+/**
+ * A localisation cache optimised for loading large amounts of data for many
* languages. Used by rebuildLocalisationCache.php.
*/
class LocalisationCache_BulkLoad extends LocalisationCache {
/**
* Most recently used languages. Uses the linked-list aspect of PHP hashtables
- * to keep the most recently used language codes at the end of the array, and
+ * to keep the most recently used language codes at the end of the array, and
* the language codes that are ready to be deleted at the beginning.
*/
var $mruLangs = array();
$serialize = $fileType === 'core';
if ( !isset( $this->fileCache[$fileName][$fileType] ) ) {
$data = parent::readPHPFile( $fileName, $fileType );
+
if ( $serialize ) {
$encData = serialize( $data );
} else {
$encData = $data;
}
+
$this->fileCache[$fileName][$fileType] = $encData;
+
return $data;
} elseif ( $serialize ) {
return unserialize( $this->fileCache[$fileName][$fileType] );