global $wgCacheDirectory;
$this->conf = $conf;
+ $storeConf = array();
if ( !empty( $conf['storeClass'] ) ) {
$storeClass = $conf['storeClass'];
} else {
break;
default:
throw new MWException(
- 'Please set $wgLocalisationConf[\'store\'] to something sensible.' );
+ '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];
if ( isset( $this->legacyData[$code][$key][$subkey] ) ) {
return $this->legacyData[$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];
}
/**
$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] ) ) {
return true;
}
foreach ( $deps as $dep ) {
- if ( $dep->isExpired() ) {
+ // 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;
*/
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 );
$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 );
$allData['defaultUserOptionOverrides'] = array();
}
- # Set the preload key
- $allData['preload'] = $this->buildPreload( $allData );
-
# Set the list keys
$allData['list'] = array();
foreach ( self::$splitKeys as $key ) {
'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__ );
}
}
}
+ /**
+ * Unload all data
+ */
+ public function unloadAll() {
+ foreach ( $this->initialisedLangs as $lang => $unused ) {
+ $this->unload( $lang );
+ }
+ }
+
/**
* Add messages to the cache, from an extension that has not yet been
* migrated to $wgExtensionMessages or the LocalisationCacheRecache hook.
}
}
}
+
+ /**
+ * Disable the storage backend
+ */
+ public function disableBackend() {
+ $this->store = new LCStore_Null;
+ $this->manualRecache = false;
+ }
}
/**
* @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 );
}
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__ );
}
}
public function set( $key, $value ) {
+ if ( $this->readOnly ) {
+ return;
+ }
if ( is_null( $this->currentLang ) ) {
throw new MWException( __CLASS__.': must call startWrite() before calling set()' );
}
* 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 );
}
public function startWrite( $code ) {
+ if ( !file_exists( $this->directory ) ) {
+ if ( !wfMkdirParents( $this->directory ) ) {
+ 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";
+ }
+}
+
+/**
+ * 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 ) {}
}
/**