Split LocalisationCache.php classes into own files
Change-Id: I1b88081e6b082fcad73990550a3ffbf58e11d97e
This commit is contained in:
parent
848cf81714
commit
b8ca311a1c
8 changed files with 495 additions and 389 deletions
15
autoload.php
15
autoload.php
|
|
@ -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
66
includes/cache/localisation/LCStore.php
vendored
Normal 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 );
|
||||||
|
|
||||||
|
}
|
||||||
141
includes/cache/localisation/LCStoreCDB.php
vendored
Normal file
141
includes/cache/localisation/LCStoreCDB.php
vendored
Normal 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
114
includes/cache/localisation/LCStoreDB.php
vendored
Normal file
114
includes/cache/localisation/LCStoreDB.php
vendored
Normal 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 ) )
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
includes/cache/localisation/LCStoreNull.php
vendored
Normal file
39
includes/cache/localisation/LCStoreNull.php
vendored
Normal 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 ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
126
includes/cache/localisation/LocalisationCacheBulkLoad.php
vendored
Normal file
126
includes/cache/localisation/LocalisationCacheBulkLoad.php
vendored
Normal 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue