Split LocalisationCache.php classes into own files

Change-Id: I1b88081e6b082fcad73990550a3ffbf58e11d97e
This commit is contained in:
addshore 2016-03-21 11:51:16 +00:00
parent 848cf81714
commit b8ca311a1c
8 changed files with 495 additions and 389 deletions

View file

@ -634,11 +634,11 @@ $wgAutoloadLocalClasses = [
'LBFactoryMulti' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMulti.php', 'LBFactoryMulti' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMulti.php',
'LBFactorySimple' => __DIR__ . '/includes/db/loadbalancer/LBFactorySimple.php', 'LBFactorySimple' => __DIR__ . '/includes/db/loadbalancer/LBFactorySimple.php',
'LBFactorySingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php', 'LBFactorySingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php',
'LCStore' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LCStore' => __DIR__ . '/includes/cache/localisation/LCStore.php',
'LCStoreCDB' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LCStoreCDB' => __DIR__ . '/includes/cache/localisation/LCStoreCDB.php',
'LCStoreDB' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LCStoreDB' => __DIR__ . '/includes/cache/localisation/LCStoreDB.php',
'LCStoreNull' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LCStoreNull' => __DIR__ . '/includes/cache/localisation/LCStoreNull.php',
'LCStoreStaticArray' => __DIR__ . '/includes/cache/LCStoreStaticArray.php', 'LCStoreStaticArray' => __DIR__ . '/includes/cache/localisation/LCStoreStaticArray.php',
'LangMemUsage' => __DIR__ . '/maintenance/language/langmemusage.php', 'LangMemUsage' => __DIR__ . '/maintenance/language/langmemusage.php',
'Language' => __DIR__ . '/languages/Language.php', 'Language' => __DIR__ . '/languages/Language.php',
'LanguageAr' => __DIR__ . '/languages/classes/LanguageAr.php', 'LanguageAr' => __DIR__ . '/languages/classes/LanguageAr.php',
@ -718,8 +718,9 @@ $wgAutoloadLocalClasses = [
'LocalIdLookup' => __DIR__ . '/includes/user/LocalIdLookup.php', 'LocalIdLookup' => __DIR__ . '/includes/user/LocalIdLookup.php',
'LocalRepo' => __DIR__ . '/includes/filerepo/LocalRepo.php', 'LocalRepo' => __DIR__ . '/includes/filerepo/LocalRepo.php',
'LocalSettingsGenerator' => __DIR__ . '/includes/installer/LocalSettingsGenerator.php', 'LocalSettingsGenerator' => __DIR__ . '/includes/installer/LocalSettingsGenerator.php',
'LocalisationCache' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LocalisationCache' => __DIR__ . '/includes/cache/localisation/LocalisationCache.php',
'LocalisationCacheBulkLoad' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LocalisationCacheBulkLoad' => __DIR__ .
'/includes/cache/localisation/LocalisationCacheBulkLoad.php',
'LockManager' => __DIR__ . '/includes/filebackend/lockmanager/LockManager.php', 'LockManager' => __DIR__ . '/includes/filebackend/lockmanager/LockManager.php',
'LockManagerGroup' => __DIR__ . '/includes/filebackend/lockmanager/LockManagerGroup.php', 'LockManagerGroup' => __DIR__ . '/includes/filebackend/lockmanager/LockManagerGroup.php',
'LogEntry' => __DIR__ . '/includes/logging/LogEntry.php', 'LogEntry' => __DIR__ . '/includes/logging/LogEntry.php',

66
includes/cache/localisation/LCStore.php vendored Normal file
View file

@ -0,0 +1,66 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
/**
* Interface for the persistence layer of LocalisationCache.
*
* 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
* 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
* expected to delete all data previously stored for that language.
*
* The values stored are PHP variables suitable for serialize(). Implementations
* of LCStore are responsible for serializing and unserializing.
*/
interface LCStore {
/**
* Get a value.
* @param string $code Language code
* @param string $key Cache key
*/
function get( $code, $key );
/**
* Start a write transaction.
* @param string $code Language code
*/
function startWrite( $code );
/**
* Finish a write transaction.
*/
function finishWrite();
/**
* Set a key to a given value. startWrite() must be called before this
* is called, and finishWrite() must be called afterwards.
* @param string $key
* @param mixed $value
*/
function set( $key, $value );
}

View file

@ -0,0 +1,141 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
/**
* LCStore implementation which stores data as a collection of CDB files in the
* 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
* available than it is with the PHP port.
*
* See Cdb.php and http://cr.yp.to/cdb.html
*/
class LCStoreCDB implements LCStore {
/** @var CdbReader[] */
private $readers;
/** @var CdbWriter */
private $writer;
/** @var string Current language code */
private $currentLang;
/** @var bool|string Cache directory. False if not set */
private $directory;
function __construct( $conf = [] ) {
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 );
$this->readers[$code] = false;
if ( file_exists( $fileName ) ) {
try {
$this->readers[$code] = CdbReader::open( $fileName );
} catch ( CdbException $e ) {
wfDebug( __METHOD__ . ": unable to open cdb file for reading\n" );
}
}
}
if ( !$this->readers[$code] ) {
return null;
} else {
$value = false;
try {
$value = $this->readers[$code]->get( $key );
} catch ( CdbException $e ) {
wfDebug( __METHOD__ . ": CdbException caught, error message was "
. $e->getMessage() . "\n" );
}
if ( $value === false ) {
return null;
}
return unserialize( $value );
}
}
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();
}
try {
$this->writer = CdbWriter::open( $this->getFileName( $code ) );
} catch ( CdbException $e ) {
throw new MWException( $e->getMessage() );
}
$this->currentLang = $code;
}
public function finishWrite() {
// Close the writer
try {
$this->writer->close();
} catch ( CdbException $e ) {
throw new MWException( $e->getMessage() );
}
$this->writer = null;
unset( $this->readers[$this->currentLang] );
$this->currentLang = null;
}
public function set( $key, $value ) {
if ( is_null( $this->writer ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before calling set()' );
}
try {
$this->writer->set( $key, serialize( $value ) );
} catch ( CdbException $e ) {
throw new MWException( $e->getMessage() );
}
}
protected function getFileName( $code ) {
if ( strval( $code ) === '' || strpos( $code, '/' ) !== false ) {
throw new MWException( __METHOD__ . ": Invalid language \"$code\"" );
}
return "{$this->directory}/l10n_cache-$code.cdb";
}
}

View file

@ -0,0 +1,114 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
/**
* LCStore implementation which uses the standard DB functions to store data.
* This will work on any MediaWiki installation.
*/
class LCStoreDB implements LCStore {
/** @var string */
private $currentLang;
/** @var bool */
private $writesDone = false;
/** @var IDatabase */
private $dbw;
/** @var array */
private $batch = [];
/** @var bool */
private $readOnly = false;
public function get( $code, $key ) {
if ( $this->writesDone && $this->dbw ) {
$db = $this->dbw; // see the changes in finishWrite()
} else {
$db = wfGetDB( DB_SLAVE );
}
$value = $db->selectField(
'l10n_cache',
'lc_value',
[ 'lc_lang' => $code, 'lc_key' => $key ],
__METHOD__
);
return ( $value !== false ) ? unserialize( $db->decodeBlob( $value ) ) : null;
}
public function startWrite( $code ) {
if ( $this->readOnly ) {
return;
} elseif ( !$code ) {
throw new MWException( __METHOD__ . ": Invalid language \"$code\"" );
}
$this->dbw = wfGetDB( DB_MASTER );
$this->readOnly = $this->dbw->isReadOnly();
$this->currentLang = $code;
$this->batch = [];
}
public function finishWrite() {
if ( $this->readOnly ) {
return;
} elseif ( is_null( $this->currentLang ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before finishWrite()' );
}
$this->dbw->startAtomic( __METHOD__ );
try {
$this->dbw->delete(
'l10n_cache',
[ 'lc_lang' => $this->currentLang ],
__METHOD__
);
foreach ( array_chunk( $this->batch, 500 ) as $rows ) {
$this->dbw->insert( 'l10n_cache', $rows, __METHOD__ );
}
$this->writesDone = true;
} catch ( DBQueryError $e ) {
if ( $this->dbw->wasReadOnlyError() ) {
$this->readOnly = true; // just avoid site down time
} else {
throw $e;
}
}
$this->dbw->endAtomic( __METHOD__ );
$this->currentLang = null;
$this->batch = [];
}
public function set( $key, $value ) {
if ( $this->readOnly ) {
return;
} elseif ( is_null( $this->currentLang ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before set()' );
}
$this->batch[] = [
'lc_lang' => $this->currentLang,
'lc_key' => $key,
'lc_value' => $this->dbw->encodeBlob( serialize( $value ) )
];
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
/**
* Null store backend, used to avoid DB errors during install
*/
class LCStoreNull implements LCStore {
public function get( $code, $key ) {
return null;
}
public function startWrite( $code ) {
}
public function finishWrite() {
}
public function set( $key, $value ) {
}
}

View file

@ -20,7 +20,6 @@
* @file * @file
*/ */
use Cdb\Exception as CdbException;
use Cdb\Reader as CdbReader; use Cdb\Reader as CdbReader;
use Cdb\Writer as CdbWriter; use Cdb\Writer as CdbWriter;
use CLDRPluralRuleParser\Evaluator; use CLDRPluralRuleParser\Evaluator;
@ -1095,385 +1094,5 @@ class LocalisationCache {
$this->store = new LCStoreNull; $this->store = new LCStoreNull;
$this->manualRecache = false; $this->manualRecache = false;
} }
}
/**
* Interface for the persistence layer of LocalisationCache.
*
* 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
* 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
* expected to delete all data previously stored for that language.
*
* The values stored are PHP variables suitable for serialize(). Implementations
* of LCStore are responsible for serializing and unserializing.
*/
interface LCStore {
/**
* Get a value.
* @param string $code Language code
* @param string $key Cache key
*/
function get( $code, $key );
/**
* Start a write transaction.
* @param string $code Language code
*/
function startWrite( $code );
/**
* Finish a write transaction.
*/
function finishWrite();
/**
* Set a key to a given value. startWrite() must be called before this
* is called, and finishWrite() must be called afterwards.
* @param string $key
* @param mixed $value
*/
function set( $key, $value );
}
/**
* LCStore implementation which uses the standard DB functions to store data.
* This will work on any MediaWiki installation.
*/
class LCStoreDB implements LCStore {
/** @var string */
private $currentLang;
/** @var bool */
private $writesDone = false;
/** @var IDatabase */
private $dbw;
/** @var array */
private $batch = [];
/** @var bool */
private $readOnly = false;
public function get( $code, $key ) {
if ( $this->writesDone && $this->dbw ) {
$db = $this->dbw; // see the changes in finishWrite()
} else {
$db = wfGetDB( DB_SLAVE );
}
$value = $db->selectField(
'l10n_cache',
'lc_value',
[ 'lc_lang' => $code, 'lc_key' => $key ],
__METHOD__
);
return ( $value !== false ) ? unserialize( $db->decodeBlob( $value ) ) : null;
}
public function startWrite( $code ) {
if ( $this->readOnly ) {
return;
} elseif ( !$code ) {
throw new MWException( __METHOD__ . ": Invalid language \"$code\"" );
}
$this->dbw = wfGetDB( DB_MASTER );
$this->readOnly = $this->dbw->isReadOnly();
$this->currentLang = $code;
$this->batch = [];
}
public function finishWrite() {
if ( $this->readOnly ) {
return;
} elseif ( is_null( $this->currentLang ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before finishWrite()' );
}
$this->dbw->startAtomic( __METHOD__ );
try {
$this->dbw->delete(
'l10n_cache',
[ 'lc_lang' => $this->currentLang ],
__METHOD__
);
foreach ( array_chunk( $this->batch, 500 ) as $rows ) {
$this->dbw->insert( 'l10n_cache', $rows, __METHOD__ );
}
$this->writesDone = true;
} catch ( DBQueryError $e ) {
if ( $this->dbw->wasReadOnlyError() ) {
$this->readOnly = true; // just avoid site down time
} else {
throw $e;
}
}
$this->dbw->endAtomic( __METHOD__ );
$this->currentLang = null;
$this->batch = [];
}
public function set( $key, $value ) {
if ( $this->readOnly ) {
return;
} elseif ( is_null( $this->currentLang ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before set()' );
}
$this->batch[] = [
'lc_lang' => $this->currentLang,
'lc_key' => $key,
'lc_value' => $this->dbw->encodeBlob( serialize( $value ) )
];
}
}
/**
* LCStore implementation which stores data as a collection of CDB files in the
* 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
* available than it is with the PHP port.
*
* See Cdb.php and http://cr.yp.to/cdb.html
*/
class LCStoreCDB implements LCStore {
/** @var CdbReader[] */
private $readers;
/** @var CdbWriter */
private $writer;
/** @var string Current language code */
private $currentLang;
/** @var bool|string Cache directory. False if not set */
private $directory;
function __construct( $conf = [] ) {
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 );
$this->readers[$code] = false;
if ( file_exists( $fileName ) ) {
try {
$this->readers[$code] = CdbReader::open( $fileName );
} catch ( CdbException $e ) {
wfDebug( __METHOD__ . ": unable to open cdb file for reading\n" );
}
}
}
if ( !$this->readers[$code] ) {
return null;
} else {
$value = false;
try {
$value = $this->readers[$code]->get( $key );
} catch ( CdbException $e ) {
wfDebug( __METHOD__ . ": CdbException caught, error message was "
. $e->getMessage() . "\n" );
}
if ( $value === false ) {
return null;
}
return unserialize( $value );
}
}
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();
}
try {
$this->writer = CdbWriter::open( $this->getFileName( $code ) );
} catch ( CdbException $e ) {
throw new MWException( $e->getMessage() );
}
$this->currentLang = $code;
}
public function finishWrite() {
// Close the writer
try {
$this->writer->close();
} catch ( CdbException $e ) {
throw new MWException( $e->getMessage() );
}
$this->writer = null;
unset( $this->readers[$this->currentLang] );
$this->currentLang = null;
}
public function set( $key, $value ) {
if ( is_null( $this->writer ) ) {
throw new MWException( __CLASS__ . ': must call startWrite() before calling set()' );
}
try {
$this->writer->set( $key, serialize( $value ) );
} catch ( CdbException $e ) {
throw new MWException( $e->getMessage() );
}
}
protected function getFileName( $code ) {
if ( strval( $code ) === '' || strpos( $code, '/' ) !== false ) {
throw new MWException( __METHOD__ . ": Invalid language \"$code\"" );
}
return "{$this->directory}/l10n_cache-$code.cdb";
}
}
/**
* Null store backend, used to avoid DB errors during install
*/
class LCStoreNull 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 LocalisationCacheBulkLoad extends LocalisationCache {
/**
* A cache of the contents of data files.
* Core files are serialized to avoid using ~1GB of RAM during a recache.
*/
private $fileCache = [];
/**
* 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
* the language codes that are ready to be deleted at the beginning.
*/
private $mruLangs = [];
/**
* Maximum number of languages that may be loaded into $this->data
*/
private $maxLoadedLangs = 10;
/**
* @param string $fileName
* @param string $fileType
* @return array|mixed
*/
protected function readPHPFile( $fileName, $fileType ) {
$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] );
} else {
return $this->fileCache[$fileName][$fileType];
}
}
/**
* @param string $code
* @param string $key
* @return mixed
*/
public function getItem( $code, $key ) {
unset( $this->mruLangs[$code] );
$this->mruLangs[$code] = true;
return parent::getItem( $code, $key );
}
/**
* @param string $code
* @param string $key
* @param string $subkey
* @return mixed
*/
public function getSubitem( $code, $key, $subkey ) {
unset( $this->mruLangs[$code] );
$this->mruLangs[$code] = true;
return parent::getSubitem( $code, $key, $subkey );
}
/**
* @param string $code
*/
public function recache( $code ) {
parent::recache( $code );
unset( $this->mruLangs[$code] );
$this->mruLangs[$code] = true;
$this->trimCache();
}
/**
* @param string $code
*/
public function unload( $code ) {
unset( $this->mruLangs[$code] );
parent::unload( $code );
}
/**
* Unload cached languages until there are less than $this->maxLoadedLangs
*/
protected function trimCache() {
while ( count( $this->data ) > $this->maxLoadedLangs && count( $this->mruLangs ) ) {
reset( $this->mruLangs );
$code = key( $this->mruLangs );
wfDebug( __METHOD__ . ": unloading $code\n" );
$this->unload( $code );
}
}
} }

View file

@ -0,0 +1,126 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
/**
* A localisation cache optimised for loading large amounts of data for many
* languages. Used by rebuildLocalisationCache.php.
*/
class LocalisationCacheBulkLoad extends LocalisationCache {
/**
* A cache of the contents of data files.
* Core files are serialized to avoid using ~1GB of RAM during a recache.
*/
private $fileCache = [];
/**
* 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
* the language codes that are ready to be deleted at the beginning.
*/
private $mruLangs = [];
/**
* Maximum number of languages that may be loaded into $this->data
*/
private $maxLoadedLangs = 10;
/**
* @param string $fileName
* @param string $fileType
* @return array|mixed
*/
protected function readPHPFile( $fileName, $fileType ) {
$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] );
} else {
return $this->fileCache[$fileName][$fileType];
}
}
/**
* @param string $code
* @param string $key
* @return mixed
*/
public function getItem( $code, $key ) {
unset( $this->mruLangs[$code] );
$this->mruLangs[$code] = true;
return parent::getItem( $code, $key );
}
/**
* @param string $code
* @param string $key
* @param string $subkey
* @return mixed
*/
public function getSubitem( $code, $key, $subkey ) {
unset( $this->mruLangs[$code] );
$this->mruLangs[$code] = true;
return parent::getSubitem( $code, $key, $subkey );
}
/**
* @param string $code
*/
public function recache( $code ) {
parent::recache( $code );
unset( $this->mruLangs[$code] );
$this->mruLangs[$code] = true;
$this->trimCache();
}
/**
* @param string $code
*/
public function unload( $code ) {
unset( $this->mruLangs[$code] );
parent::unload( $code );
}
/**
* Unload cached languages until there are less than $this->maxLoadedLangs
*/
protected function trimCache() {
while ( count( $this->data ) > $this->maxLoadedLangs && count( $this->mruLangs ) ) {
reset( $this->mruLangs );
$code = key( $this->mruLangs );
wfDebug( __METHOD__ . ": unloading $code\n" );
$this->unload( $code );
}
}
}