Remove explicit DB access

* Uses BagOfStuff caching instead of custom-made DB cache
* By configuring the BagOfStuff cache in a way that
  it writes to the database no performance implications are expected
* For WMF-use this should have no effect since restbase
  is used to cache stuff
* Replaces Ib2c216f54e6817ee2c3be0355ba72bd4769ba6ea

Bug: T349442
Change-Id: I1ce8ad9cf4c1a9ae71f447e4e067b39ee2601640
这个提交包含在:
Moritz Schubotz (physikerwelt) 2023-11-19 20:23:09 +01:00 提交者 Physikerwelt
父节点 7f8a359a84
当前提交 9236575a1a
共有 23 个文件被更改,包括 192 次插入592 次删除

查看文件

@ -38,7 +38,8 @@ return [
),
Math::getMathConfig( $services ),
$services->getUserOptionsLookup(),
LoggerFactory::getInstance( 'Math' )
LoggerFactory::getInstance( 'Math' ),
$services->getMainWANObjectCache()
);
},
'Math.WikibaseConnector' => static function ( MediaWikiServices $services ): MathWikibaseConnector {

查看文件

@ -1,59 +0,0 @@
[
{
"name": "mathlatexml",
"columns": [
{
"name": "math_inputhash",
"type": "binary",
"options": {
"notnull": true,
"length": 16,
"fixed": false
}
},
{
"name": "math_inputtex",
"type": "text",
"options": {
"notnull": true,
"length": 65535
}
},
{
"name": "math_tex",
"type": "text",
"options": {
"notnull": false,
"length": 65535
}
},
{
"name": "math_mathml",
"type": "text",
"options": {
"notnull": false,
"length": 16777215
}
},
{
"name": "math_svg",
"type": "text",
"options": {
"notnull": false,
"length": 65535
}
},
{
"name": "math_style",
"type": "mwtinyint",
"options": {
"notnull": false
}
}
],
"indexes": [],
"pk": [
"math_inputhash"
]
}
]

查看文件

@ -1,74 +0,0 @@
[
{
"name": "mathoid",
"columns": [
{
"name": "math_inputhash",
"type": "binary",
"options": {
"notnull": true,
"length": 16,
"fixed": false
}
},
{
"name": "math_input",
"type": "text",
"options": {
"notnull": true,
"length": 65535
}
},
{
"name": "math_tex",
"type": "text",
"options": {
"notnull": false,
"length": 65535
}
},
{
"name": "math_mathml",
"type": "text",
"options": {
"notnull": false,
"length": 65535
}
},
{
"name": "math_svg",
"type": "text",
"options": {
"notnull": false,
"length": 65535
}
},
{
"name": "math_style",
"type": "mwtinyint",
"options": {
"notnull": false
}
},
{
"name": "math_input_type",
"type": "mwtinyint",
"options": {
"notnull": false
}
},
{
"name": "math_png",
"type": "blob",
"options": {
"notnull": false,
"length": 16777215
}
}
],
"indexes": [],
"pk": [
"math_inputhash"
]
}
]

查看文件

@ -1,13 +0,0 @@
-- This file is automatically generated using maintenance/generateSchemaSql.php.
-- Source: Math/sql/mathlatexml.json
-- Do not modify this file directly.
-- See https://www.mediawiki.org/wiki/Manual:Schema_changes
CREATE TABLE /*_*/mathlatexml (
math_inputhash VARBINARY(16) NOT NULL,
math_inputtex TEXT NOT NULL,
math_tex TEXT DEFAULT NULL,
math_mathml MEDIUMTEXT DEFAULT NULL,
math_svg TEXT DEFAULT NULL,
math_style TINYINT DEFAULT NULL,
PRIMARY KEY(math_inputhash)
) /*$wgDBTableOptions*/;

查看文件

@ -1,15 +0,0 @@
-- This file is automatically generated using maintenance/generateSchemaSql.php.
-- Source: Math/sql/mathoid.json
-- Do not modify this file directly.
-- See https://www.mediawiki.org/wiki/Manual:Schema_changes
CREATE TABLE /*_*/mathoid (
math_inputhash VARBINARY(16) NOT NULL,
math_input TEXT NOT NULL,
math_tex TEXT DEFAULT NULL,
math_mathml TEXT DEFAULT NULL,
math_svg TEXT DEFAULT NULL,
math_style TINYINT DEFAULT NULL,
math_input_type TINYINT DEFAULT NULL,
math_png MEDIUMBLOB DEFAULT NULL,
PRIMARY KEY(math_inputhash)
) /*$wgDBTableOptions*/;

查看文件

@ -1 +0,0 @@
ALTER TABLE /*_*/mathoid ADD math_png mediumblob;

查看文件

@ -1,13 +0,0 @@
-- This file is automatically generated using maintenance/generateSchemaSql.php.
-- Source: Math/sql/mathlatexml.json
-- Do not modify this file directly.
-- See https://www.mediawiki.org/wiki/Manual:Schema_changes
CREATE TABLE mathlatexml (
math_inputhash TEXT NOT NULL,
math_inputtex TEXT NOT NULL,
math_tex TEXT DEFAULT NULL,
math_mathml TEXT DEFAULT NULL,
math_svg TEXT DEFAULT NULL,
math_style SMALLINT DEFAULT NULL,
PRIMARY KEY(math_inputhash)
);

查看文件

@ -1,15 +0,0 @@
-- This file is automatically generated using maintenance/generateSchemaSql.php.
-- Source: Math/sql/mathoid.json
-- Do not modify this file directly.
-- See https://www.mediawiki.org/wiki/Manual:Schema_changes
CREATE TABLE mathoid (
math_inputhash TEXT NOT NULL,
math_input TEXT NOT NULL,
math_tex TEXT DEFAULT NULL,
math_mathml TEXT DEFAULT NULL,
math_svg TEXT DEFAULT NULL,
math_style SMALLINT DEFAULT NULL,
math_input_type SMALLINT DEFAULT NULL,
math_png TEXT DEFAULT NULL,
PRIMARY KEY(math_inputhash)
);

查看文件

@ -1,13 +0,0 @@
-- This file is automatically generated using maintenance/generateSchemaSql.php.
-- Source: Math/sql/mathlatexml.json
-- Do not modify this file directly.
-- See https://www.mediawiki.org/wiki/Manual:Schema_changes
CREATE TABLE /*_*/mathlatexml (
math_inputhash BLOB NOT NULL,
math_inputtex CLOB NOT NULL,
math_tex CLOB DEFAULT NULL,
math_mathml CLOB DEFAULT NULL,
math_svg CLOB DEFAULT NULL,
math_style SMALLINT DEFAULT NULL,
PRIMARY KEY(math_inputhash)
);

查看文件

@ -1,15 +0,0 @@
-- This file is automatically generated using maintenance/generateSchemaSql.php.
-- Source: Math/sql/mathoid.json
-- Do not modify this file directly.
-- See https://www.mediawiki.org/wiki/Manual:Schema_changes
CREATE TABLE /*_*/mathoid (
math_inputhash BLOB NOT NULL,
math_input CLOB NOT NULL,
math_tex CLOB DEFAULT NULL,
math_mathml CLOB DEFAULT NULL,
math_svg CLOB DEFAULT NULL,
math_style SMALLINT DEFAULT NULL,
math_input_type SMALLINT DEFAULT NULL,
math_png BLOB DEFAULT NULL,
PRIMARY KEY(math_inputhash)
);

查看文件

@ -3,7 +3,6 @@
namespace MediaWiki\Extension\Math\HookHandlers;
use DatabaseUpdater;
use LogicException;
use MediaWiki\Installer\Hook\LoadExtensionSchemaUpdatesHook;
/**
@ -19,22 +18,11 @@ class SchemaHooksHandler implements LoadExtensionSchemaUpdatesHook {
public function onLoadExtensionSchemaUpdates( $updater ) {
$type = $updater->getDB()->getType();
if ( !in_array( $type, [ 'mysql', 'sqlite', 'postgres' ], true ) ) {
throw new LogicException( "Math extension does not currently support $type database." );
return;
}
foreach ( [ 'mathoid', 'mathlatexml' ] as $mode ) {
$updater->addExtensionTable(
$mode,
__DIR__ . "/../../sql/$type/$mode.sql"
);
}
if ( $type === 'mysql' ) {
$updater->addExtensionField(
'mathoid',
'math_png',
__DIR__ . '/../../sql/' . $type . '/patch-mathoid.add_png.sql'
);
$updater->dropExtensionTable( $mode );
}
}
}

查看文件

@ -21,9 +21,9 @@ class MathLaTeXML extends MathMathML {
/** @var string settings for LaTeXML daemon */
private $LaTeXMLSettings = '';
public function __construct( $tex = '', $params = [] ) {
public function __construct( $tex = '', $params = [], $cache = null ) {
global $wgMathLaTeXMLUrl;
parent::__construct( $tex, $params );
parent::__construct( $tex, $params, $cache );
$this->host = $wgMathLaTeXMLUrl;
$this->setMode( MathConfig::MODE_LATEXML );
}

查看文件

@ -51,9 +51,9 @@ class MathMathML extends MathRenderer {
/** @var string|null */
private $mathoidStyle;
public function __construct( string $tex = '', array $params = [] ) {
public function __construct( string $tex = '', array $params = [], $cache = null ) {
global $wgMathMathMLUrl;
parent::__construct( $tex, $params );
parent::__construct( $tex, $params, $cache );
$this->setMode( MathConfig::MODE_MATHML );
$this->host = $wgMathMathMLUrl;
if ( isset( $params['type'] ) ) {
@ -354,7 +354,7 @@ class MathMathML extends MathRenderer {
return $this->svgPath;
}
return SpecialPage::getTitleFor( 'MathShowImage' )->getLocalURL( [
'hash' => $this->getMd5(),
'hash' => $this->getInputHash(),
'mode' => $this->getMode(),
'noRender' => $noRender
]
@ -493,7 +493,7 @@ class MathMathML extends MathRenderer {
protected function dbOutArray() {
$out = parent::dbOutArray();
if ( $this->getMathTableName() == 'mathoid' ) {
if ( $this->getMathTableName() === 'mathoid' ) {
$out['math_input'] = $out['math_inputtex'];
unset( $out['math_inputtex'] );
}
@ -502,20 +502,20 @@ class MathMathML extends MathRenderer {
protected function dbInArray() {
$out = parent::dbInArray();
if ( $this->getMathTableName() == 'mathoid' ) {
if ( $this->getMathTableName() === 'mathoid' ) {
$out = array_diff( $out, [ 'math_inputtex' ] );
$out[] = 'math_input';
}
return $out;
}
protected function initializeFromDatabaseRow( $rpage ) {
public function initializeFromCache( $rpage ) {
// mathoid allows different input formats
// therefore the column name math_inputtex was changed to math_input
if ( $this->getMathTableName() == 'mathoid' && !empty( $rpage->math_input ) ) {
$this->userInputTex = $rpage->math_input;
if ( $this->getMathTableName() === 'mathoid' && isset( $rpage['math_input'] ) ) {
$this->userInputTex = $rpage['math_input'];
}
parent::initializeFromDatabaseRow( $rpage );
parent::initializeFromCache( $rpage );
}
/**

查看文件

@ -48,7 +48,7 @@ class MathMathMLCli extends MathMathML {
*/
private function initializeFromCliResponse( $res ) {
global $wgMathoidCli;
if ( !property_exists( $res, $this->getMd5() ) ) {
if ( !property_exists( $res, $this->getInputHash() ) ) {
$this->lastError =
$this->getError( 'math_mathoid_error', 'cli',
var_export( get_object_vars( $res ), true ) );
@ -58,7 +58,7 @@ class MathMathMLCli extends MathMathML {
$this->lastError = $this->getError( 'math_empty_tex' );
return false;
}
$response = $res->{$this->getMd5()};
$response = $res->{$this->getInputHash()};
if ( !$response->success ) {
$this->lastError = $this->renderError( $response );
return false;
@ -103,7 +103,7 @@ class MathMathMLCli extends MathMathML {
'query' => [
'q' => $this->getTex(),
'type' => $this->getInputType(),
'hash' => $this->getMd5(),
'hash' => $this->getInputHash(),
],
];
}

查看文件

@ -21,8 +21,8 @@ use StatusValue;
class MathNativeMML extends MathMathML {
private LocalChecker $checker;
public function __construct( $tex = '', $params = [] ) {
parent::__construct( $tex, $params );
public function __construct( $tex = '', $params = [], $cache = null ) {
parent::__construct( $tex, $params, $cache );
$this->setMode( MathConfig::MODE_NATIVE_MML );
}
@ -65,7 +65,7 @@ class MathNativeMML extends MathMathML {
return $this->getMathml();
}
public function readFromDatabase() {
public function readFromCache(): bool {
return false;
}

查看文件

@ -11,7 +11,6 @@
namespace MediaWiki\Extension\Math;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Extension\Math\InputCheck\BaseChecker;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
@ -20,14 +19,14 @@ use Message;
use Parser;
use Psr\Log\LoggerInterface;
use RequestContext;
use stdClass;
use StringUtils;
use WANObjectCache;
/**
* Abstract base class with static methods for rendering the <math> tags using
* different technologies. These static methods create a new instance of the
* extending classes and render the math tags based on the mode setting of the user.
* Furthermore this class handles the caching of the rendered output.
* Furthermore, this class handles the caching of the rendered output.
*
* @author Tomasz Wegrzanowski
* @author Brion Vibber
@ -58,13 +57,11 @@ abstract class MathRenderer {
/** @var bool has the mathematical content changed */
protected $changed = false;
/** @var bool is there a database entry for the mathematical content */
protected $storedInDatabase = null;
protected $storedInCache = null;
/** @var bool is there a request to purge the existing mathematical content */
protected $purge = false;
/** @var string with last occurred error */
protected $lastError = '';
/** @var string md5 value from userInputTex */
protected $md5 = '';
/** @var string binary packed inputhash */
protected $inputHash = '';
/** @var string rendering mode */
@ -78,13 +75,17 @@ abstract class MathRenderer {
/** @var LoggerInterface */
private $logger;
private WANObjectCache $cache;
/**
* Constructs a base MathRenderer
*
* @param string $tex (optional) LaTeX markup
* @param array $params (optional) HTML attributes
* @param WANObjectCache|null $cache (optional)
*/
public function __construct( $tex = '', $params = [] ) {
public function __construct( string $tex = '', $params = [], $cache = null ) {
$this->cache = $cache ?? MediaWikiServices::getInstance()->getMainWANObjectCache();
$this->params = $params;
if ( isset( $params['id'] ) ) {
$this->id = $params['id'];
@ -119,18 +120,6 @@ abstract class MathRenderer {
$this->logger = LoggerFactory::getInstance( 'Math' );
}
/**
* @param string $md5
* @return self the MathRenderer generated from md5
*/
public static function newFromMd5( $md5 ) {
// @phan-suppress-next-line PhanTypeInstantiateAbstractStatic
$instance = new static();
$instance->setMd5( $md5 );
$instance->readFromDatabase();
return $instance;
}
/**
* Static factory method for getting a renderer based on mode
*
@ -181,64 +170,31 @@ abstract class MathRenderer {
*
* @return string hash
*/
public function getMd5() {
if ( !$this->md5 ) {
$this->md5 = md5( $this->userInputTex );
}
return $this->md5;
}
/**
* Set the input hash (if user input tex is not available)
* @param string $md5
*/
public function setMd5( $md5 ) {
$this->md5 = $md5;
}
/**
* Return hash of input
*
* @return string hash
*/
public function getInputHash() {
// TODO: What happens if $tex is empty?
public function getInputHash(): string {
if ( !$this->inputHash ) {
$dbr = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->getReplicaDatabase();
return $dbr->encodeBlob( pack( "H32", $this->getMd5() ) ); # Binary packed, not hex
$this->inputHash = hash( 'md5', // xxh128 might be better when dropping php 7 support
$this->mode .
$this->userInputTex .
implode( $this->params )
);
}
return $this->inputHash;
}
/**
* Decode binary packed hash from the database to md5 of input_tex
* @param string $hash (binary)
* @return string md5
*/
private static function dbHash2md5( $hash ) {
$dbr = wfGetDB( DB_REPLICA );
$xhash = unpack( 'H32md5', $dbr->decodeBlob( $hash ) . " " );
return $xhash['md5'];
}
/**
* Reads rendering data from database
*
* @return bool true if read successfully, false otherwise
*/
public function readFromDatabase() {
$dbr = MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->getReplicaDatabase();
$rpage = $dbr->selectRow( $this->getMathTableName(),
$this->dbInArray(),
[ 'math_inputhash' => $this->getInputHash() ],
__METHOD__ );
public function readFromCache(): bool {
$rpage = $this->cache->get( $this->getCacheKey() );
if ( $rpage !== false ) {
$this->initializeFromDatabaseRow( $rpage );
$this->storedInDatabase = true;
$this->initializeFromCache( $rpage );
$this->storedInCache = true;
return true;
} else {
# Missing from the database and/or the render cache
$this->storedInDatabase = false;
$this->storedInCache = false;
return false;
}
}
@ -258,82 +214,38 @@ abstract class MathRenderer {
/**
* Reads the values from the database but does not overwrite set values with empty values
* @param stdClass $rpage (a database row)
* @param array $rpage (a database row)
*/
protected function initializeFromDatabaseRow( $rpage ) {
$this->inputHash = $rpage->math_inputhash; // MUST NOT BE NULL
$this->md5 = self::dbHash2md5( $this->inputHash );
if ( !empty( $rpage->math_mathml ) ) {
$this->mathml = $rpage->math_mathml;
public function initializeFromCache( $rpage ) {
$this->inputHash = $rpage['math_inputhash']; // MUST NOT BE NULL
if ( isset( $rpage['math_mathml'] ) ) {
$this->mathml = $rpage['math_mathml'];
}
if ( !empty( $rpage->math_inputtex ) ) {
// in the current database the field is probably not set.
$this->userInputTex = $rpage->math_inputtex;
if ( isset( $rpage['math_inputtex'] ) ) {
$this->userInputTex = $rpage['math_inputtex'];
}
if ( !empty( $rpage->math_tex ) ) {
$this->tex = $rpage->math_tex;
if ( isset( $rpage['math_tex'] ) ) {
$this->tex = $rpage['math_tex'];
}
if ( !empty( $rpage->math_svg ) ) {
$this->svg = $rpage->math_svg;
if ( isset( $rpage['math_svg'] ) ) {
$this->svg = $rpage['math_svg'];
}
$this->changed = false;
}
/**
* Writes rendering entry to database.
* Writes rendering entry to cache.
*
* WARNING: Use writeCache() instead of this method to be sure that all
* renderer specific (such as squid caching) are taken into account.
* This function stores the values that are currently present in the class
* to the database even if they are empty.
* to the cache even if they are empty.
*
* This function can be seen as protected function.
* @param \Wikimedia\Rdbms\IDatabase|null $dbw
*/
public function writeToDatabase( $dbw = null ) {
# Now save it back to the DB:
if ( MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) {
return;
}
public function writeToCache() {
$outArray = $this->dbOutArray();
$mathTableName = $this->getMathTableName();
$fname = __METHOD__;
if ( $this->isInDatabase() ) {
$this->debug( 'Update database entry' );
$inputHash = $this->getInputHash();
DeferredUpdates::addCallableUpdate( function () use (
$dbw, $outArray, $inputHash, $mathTableName, $fname
) {
$dbw = $dbw ?: wfGetDB( DB_PRIMARY );
$dbw->update( $mathTableName, $outArray,
[ 'math_inputhash' => $inputHash ], $fname );
$this->logger->debug(
'Row updated after db transaction was idle: ' .
var_export( $outArray, true ) . " to database" );
} );
} else {
$this->storedInDatabase = true;
$this->debug( 'Store new entry in database' );
DeferredUpdates::addCallableUpdate( function () use (
$dbw, $outArray, $mathTableName, $fname
) {
$dbw = $dbw ?: wfGetDB( DB_PRIMARY );
$dbw->insert( $mathTableName, $outArray, $fname, [ 'IGNORE' ] );
LoggerFactory::getInstance( 'Math' )->debug(
'Row inserted after db transaction was idle {out}.',
[
'out' => var_export( $outArray, true ),
]
);
if ( $dbw->affectedRows() == 0 ) {
// That's the price for the delayed update.
$this->logger->warning(
'Entry could not be written. Might be changed in between.' );
}
} );
}
$this->cache->set( $this->getCacheKey(), $outArray );
}
/**
@ -346,7 +258,8 @@ abstract class MathRenderer {
'math_mathml' => $this->mathml,
'math_inputtex' => $this->userInputTex,
'math_tex' => $this->tex,
'math_svg' => $this->svg
'math_svg' => $this->svg,
'math_mode' => $this->mode
];
return $out;
}
@ -409,7 +322,7 @@ abstract class MathRenderer {
$this->debug( 'Writing of cache requested' );
if ( $this->isChanged() ) {
$this->debug( 'Change detected. Perform writing' );
$this->writeToDatabase();
$this->writeToCache();
return true;
} else {
$this->debug( "Nothing was changed. Don't write to database" );
@ -591,7 +504,7 @@ abstract class MathRenderer {
return true;
} else {
if ( $texCheckDisabled === MathConfig::NEW && $this->mode != MathConfig::MODE_SOURCE ) {
if ( $this->readFromDatabase() ) {
if ( $this->readFromCache() ) {
$this->debug( 'Skip TeX check' );
$this->texSecure = true;
return true;
@ -603,10 +516,10 @@ abstract class MathRenderer {
}
public function isInDatabase() {
if ( $this->storedInDatabase === null ) {
$this->readFromDatabase();
if ( $this->storedInCache === null ) {
$this->readFromCache();
}
return $this->storedInDatabase;
return $this->storedInCache;
}
/**
@ -697,4 +610,11 @@ abstract class MathRenderer {
protected function debug( $msg ) {
$this->logger->debug( "$msg for \"{tex}\".", [ 'tex' => $this->userInputTex ] );
}
private function getCacheKey() {
return $this->cache->makeGlobalKey(
self::class,
$this->getInputHash()
);
}
}

查看文件

@ -2,6 +2,7 @@
namespace MediaWiki\Extension\Math\Render;
use InvalidArgumentException;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Extension\Math\MathConfig;
use MediaWiki\Extension\Math\MathLaTeXML;
@ -12,6 +13,7 @@ use MediaWiki\Extension\Math\MathRenderer;
use MediaWiki\Extension\Math\MathSource;
use MediaWiki\User\Options\UserOptionsLookup;
use Psr\Log\LoggerInterface;
use WANObjectCache;
class RendererFactory {
@ -34,23 +36,28 @@ class RendererFactory {
/** @var LoggerInterface */
private $logger;
private WANObjectCache $cache;
/**
* @param ServiceOptions $serviceOptions
* @param MathConfig $mathConfig
* @param UserOptionsLookup $userOptionsLookup
* @param LoggerInterface $logger
* @param WANObjectCache $cache
*/
public function __construct(
ServiceOptions $serviceOptions,
MathConfig $mathConfig,
UserOptionsLookup $userOptionsLookup,
LoggerInterface $logger
LoggerInterface $logger,
WANObjectCache $cache
) {
$serviceOptions->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
$this->options = $serviceOptions;
$this->mathConfig = $mathConfig;
$this->userOptionsLookup = $userOptionsLookup;
$this->logger = $logger;
$this->cache = $cache;
}
/**
@ -91,17 +98,17 @@ class RendererFactory {
$renderer = new MathSource( $tex, $params );
break;
case MathConfig::MODE_NATIVE_MML:
$renderer = new MathNativeMML( $tex, $params );
$renderer = new MathNativeMML( $tex, $params, $this->cache );
break;
case MathConfig::MODE_LATEXML:
$renderer = new MathLaTeXML( $tex, $params );
$renderer = new MathLaTeXML( $tex, $params, $this->cache );
break;
case MathConfig::MODE_MATHML:
default:
if ( $this->options->get( 'MathoidCli' ) ) {
$renderer = new MathMathMLCli( $tex, $params );
$renderer = new MathMathMLCli( $tex, $params, $this->cache );
} else {
$renderer = new MathMathML( $tex, $params );
$renderer = new MathMathML( $tex, $params, $this->cache );
}
}
$this->logger->debug(
@ -113,4 +120,15 @@ class RendererFactory {
);
return $renderer;
}
public function getFromHash( $hash ) {
$rpage = $this->cache->get( $hash );
if ( $rpage === false ) {
throw new InvalidArgumentException( 'Cache key is invalid' );
}
$mode = $rpage['math_mode'];
$renderer = $this->getRenderer( '', [], $mode );
$renderer->initializeFromCache( $rpage );
return $renderer;
}
}

查看文件

@ -81,10 +81,9 @@ class SpecialMathShowImage extends SpecialPage {
echo $this->printSvgError( 'No Inputhash specified' );
} else {
if ( $tex === '' && $asciimath === '' ) {
$this->renderer = $this->rendererFactory->getRenderer( '', [], $this->mode );
$this->renderer->setMd5( $hash );
$this->renderer = $this->rendererFactory->getFromHash( $hash );
$this->noRender = $request->getBool( 'noRender', false );
$isInDatabase = $this->renderer->readFromDatabase();
$isInDatabase = $this->renderer->readFromCache();
if ( $isInDatabase || $this->noRender ) {
$success = $isInDatabase;
} else {

查看文件

@ -0,0 +1,79 @@
<?php
use MediaWiki\Extension\Math\MathMathML;
use MediaWiki\Extension\Math\MathRenderer;
/**
* Test the database access and core functionality of MathRenderer.
*
* @covers \MediaWiki\Extension\Math\MathRenderer
*
* @group Math
*
* @license GPL-2.0-or-later
*/
class MathCacheTest extends MediaWikiIntegrationTestCase {
/**
* @var MathRenderer
*/
private $renderer;
private const SOME_TEX = "a+b";
private const SOME_MATHML = "iℏ∂_tΨ=H^Ψ<mrow><\ci>";
protected function setUp(): void {
parent::setUp();
$this->renderer = new MathMathML( self::SOME_TEX );
}
/**
* Checks the tex and hash functions
* @covers \MediaWiki\Extension\Math\MathRenderer::getInputHash
*/
public function testInputHash() {
$this->assertEquals( 'beb7506b16f7c36aa0f9c8c9ef41b40b', $this->renderer->getInputHash() );
}
/**
* Helper function to set the current state of the sample renderer instance to the test values
*/
public function setValues() {
// set some values
$this->renderer->setTex( self::SOME_TEX );
$this->renderer->setMathml( self::SOME_MATHML );
}
/**
* Checks database access. Writes an entry and reads it back.
* @covers \MediaWiki\Extension\Math\MathRenderer::writeToCache
* @covers \MediaWiki\Extension\Math\MathRenderer::readFromCache
*/
public function testDBBasics() {
$this->setValues();
$this->renderer->writeToCache();
$renderer2 = new MathMathML( self::SOME_TEX, [ 'display' => '' ] );
$this->assertTrue( $renderer2->readFromCache(), 'Reading from database failed' );
// comparing the class object does now work due to null values etc.
$this->assertEquals(
$this->renderer->getTex(), $renderer2->getTex(), "test if tex is the same"
);
$this->assertEquals(
$this->renderer->getMathml(), $renderer2->getMathml(), "Check MathML encoding"
);
$this->assertEquals(
$this->renderer->getHtmlOutput(), $renderer2->getHtmlOutput(), 'test if HTML is the same'
);
}
/**
* This test checks if no additional write operation
* is performed, if the entry already existed in the database.
*/
public function testNoWrite() {
$this->setValues();
$inputHash = $this->renderer->getInputHash();
$this->assertTrue( $this->renderer->isChanged() );
$this->assertTrue( $this->renderer->writeCache(), "Write new entry" );
$this->assertTrue( $this->renderer->readFromCache(), "Read entry from database" );
$this->assertFalse( $this->renderer->isChanged() );
}
}

查看文件

@ -1,126 +0,0 @@
<?php
use MediaWiki\Extension\Math\MathConfig;
use MediaWiki\Extension\Math\MathMathML;
use MediaWiki\Extension\Math\MathRenderer;
/**
* Test the database access and core functionality of MathRenderer.
*
* @covers \MediaWiki\Extension\Math\MathRenderer
*
* @group Math
* @group Database
*
* @license GPL-2.0-or-later
*/
class MathDatabaseTest extends MediaWikiIntegrationTestCase {
/**
* @var MathRenderer
*/
private $renderer;
private const SOME_TEX = "a+b";
private const SOME_MATHML = "iℏ∂_tΨ=H^Ψ<mrow><\ci>";
/**
* creates a new database connection and a new math renderer
* TODO: Check if there is a way to get database access without creating
* the connection to the database explicitly
* function addDBData() {
* $this->tablesUsed[] = 'math';
* }
* was not sufficient.
* @throws Exception
*/
protected function setUp(): void {
parent::setUp();
// TODO: figure out why this is necessary
$this->db = $this->getServiceContainer()
->getDBLoadBalancer()
->getConnection( DB_PRIMARY );
$this->renderer = new MathMathML( self::SOME_TEX );
$this->tablesUsed[] = 'mathoid';
}
/**
* Checks the tex and hash functions
* @covers \MediaWiki\Extension\Math\MathRenderer::getInputHash
*/
public function testInputHash() {
$expectedhash = $this->db->encodeBlob( pack( "H32", md5( self::SOME_TEX ) ) );
$this->assertEquals( $expectedhash, $this->renderer->getInputHash() );
}
/**
* Helper function to set the current state of the sample renderer instance to the test values
*/
public function setValues() {
// set some values
$this->renderer->setTex( self::SOME_TEX );
$this->renderer->setMathml( self::SOME_MATHML );
}
/**
* Checks database access. Writes an entry and reads it back.
* @covers \MediaWiki\Extension\Math\MathRenderer::writeToDatabase
* @covers \MediaWiki\Extension\Math\MathRenderer::readFromDatabase
*/
public function testDBBasics() {
$this->setValues();
$this->renderer->writeToDatabase( $this->db );
$renderer2 = new MathMathML( self::SOME_TEX );
$this->assertTrue( $renderer2->readFromDatabase(), 'Reading from database failed' );
// comparing the class object does now work due to null values etc.
$this->assertEquals(
$this->renderer->getTex(), $renderer2->getTex(), "test if tex is the same"
);
$this->assertEquals(
$this->renderer->getMathml(), $renderer2->getMathml(), "Check MathML encoding"
);
$this->assertEquals(
$this->renderer->getHtmlOutput(), $renderer2->getHtmlOutput(), 'test if HTML is the same'
);
}
/**
* Checks the creation of the math table.
* @covers \MediaWiki\Extension\Math\HookHandlers\SchemaHooksHandler::onLoadExtensionSchemaUpdates
*/
public function testCreateTable() {
$this->markTestSkippedIfDbType( 'postgres' );
$this->markTestSkippedIfDbType( 'sqlite' );
$this->setMwGlobals( 'wgMathValidModes', [ MathConfig::MODE_MATHML ] );
$this->db->dropTable( "mathoid", __METHOD__ );
$dbu = DatabaseUpdater::newForDB( $this->db );
$dbu->doUpdates( [ "extensions" ] );
$this->expectOutputRegex( '/(.*)Creating mathoid table(.*)/' );
$this->setValues();
$this->renderer->writeToDatabase();
$res = $this->db->select( "mathoid", "*" );
$row = $res->fetchRow();
$this->assertCount( 16, $row );
}
/**
* This test checks if no additional write operation
* is performed, if the entry already existed in the database.
*/
public function testNoWrite() {
$this->setValues();
$inputHash = $this->renderer->getInputHash();
$this->assertTrue( $this->renderer->isChanged() );
$this->assertTrue( $this->renderer->writeCache(), "Write new entry" );
$res = $this->db->selectField( "mathoid", "math_inputhash",
[ "math_inputhash" => $inputHash ] );
$this->assertTrue( $res !== false, "Check database entry" );
$this->assertTrue( $this->renderer->readFromDatabase(), "Read entry from database" );
$this->assertFalse( $this->renderer->isChanged() );
// modify the database entry manually
$this->db->delete( "mathoid", [ "math_inputhash" => $inputHash ] );
// the renderer should not be aware of the modification and should not recreate the entry
$this->assertFalse( $this->renderer->writeCache() );
// as a result no entry can be found in the database.
$this->assertFalse( $this->renderer->readFromDatabase() );
}
}

查看文件

@ -1,22 +0,0 @@
<?php
use MediaWiki\Extension\Math\InputCheck\BaseChecker;
/**
* @covers \MediaWiki\Extension\Math\InputCheck\BaseChecker
*
* @group Math
*
* @license GPL-2.0-or-later
*/
class MathInputCheckTest extends MediaWikiIntegrationTestCase {
public function testAbstractClass() {
$InputCheck = $this->getMockForAbstractClass( BaseChecker::class );
/** @var BaseChecker $InputCheck */
$this->assertFalse( $InputCheck->IsValid() );
$this->assertNull( $InputCheck->getError() );
$this->assertNull( $InputCheck->getValidTex() );
}
}

查看文件

@ -1,24 +1,18 @@
<?php
use MediaWiki\Extension\Math\MathConfig;
use MediaWiki\Extension\Math\MathLaTeXML;
/**
* @covers \MediaWiki\Extension\Math\MathLaTeXML
*
* @group Math
* @group Database
*
* @license GPL-2.0-or-later
*/
class MathLaTeXMLDatabaseTest extends MediaWikiIntegrationTestCase {
class MathLaTeXMLCacheTest extends MediaWikiIntegrationTestCase {
public $renderer;
private const SOME_TEX = "a+b";
private const SOME_HTML = "a<sub>b</sub>";
private const SOME_MATHML = "iℏ∂_tΨ=H^Ψ<mrow><\ci>";
private const SOME_LOG = "Sample Log Text.";
private const SOME_TIMESTAMP = 1272509157;
private const SOME_SVG = "<?xml </svg >>%%LIKE;'\" DROP TABLE math;";
/**
* Helper function to test protected/private Methods
@ -32,21 +26,9 @@ class MathLaTeXMLDatabaseTest extends MediaWikiIntegrationTestCase {
return $method;
}
/**
* creates a new database connection and a new math renderer
* TODO: Check if there is a way to get database access without creating
* the connection to the database explicitly
* function addDBData() {
* $this->tablesUsed[] = 'math';
* }
* was not sufficient.
*/
protected function setUp(): void {
parent::setUp();
// TODO: figure out why this is necessary
$this->db = wfGetDB( DB_PRIMARY );
$this->renderer = new MathLaTeXML( self::SOME_TEX );
self::setupTestDB( $this->db, "mathtest" );
}
/**
@ -54,8 +36,8 @@ class MathLaTeXMLDatabaseTest extends MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Extension\Math\MathRenderer::getInputHash
*/
public function testInputHash() {
$expectedhash = $this->db->encodeBlob( pack( "H32", md5( self::SOME_TEX ) ) );
$this->assertEquals( $expectedhash, $this->renderer->getInputHash() );
$this->assertIsString( $this->renderer->getInputHash() );
$this->assertStringMatchesFormat( '%x', $this->renderer->getInputHash() );
}
/**
@ -77,38 +59,17 @@ class MathLaTeXMLDatabaseTest extends MediaWikiIntegrationTestCase {
$this->assertEquals( "mathlatexml", $tableName, "Wrong latexml table name" );
}
/**
* Checks the creation of the math table.
* @covers \MediaWiki\Extension\Math\HookHandlers\SchemaHooksHandler::onLoadExtensionSchemaUpdates
*/
public function testCreateTable() {
$this->markTestSkippedIfDbType( 'postgres' );
$this->markTestSkippedIfDbType( 'sqlite' );
$this->setMwGlobals( 'wgMathValidModes', [ MathConfig::MODE_LATEXML ] );
$this->db->dropTable( "mathlatexml", __METHOD__ );
$dbu = DatabaseUpdater::newForDB( $this->db );
$dbu->doUpdates( [ "extensions" ] );
$this->expectOutputRegex( '/(.*)Creating mathlatexml table(.*)/' );
$this->setValues();
$this->renderer->writeToDatabase();
$res = $this->db->select( "mathlatexml", "*" );
$row = $res->fetchRow();
$this->assertCount( 12, $row );
}
/**
* Checks database access. Writes an entry and reads it back.
* @depends testCreateTable
* @covers \MediaWiki\Extension\Math\MathRenderer::writeToDatabase
* @covers \MediaWiki\Extension\Math\MathRenderer::readFromDatabase
* @covers \MediaWiki\Extension\Math\MathRenderer::writeToCache
* @covers \MediaWiki\Extension\Math\MathRenderer::readFromCache
*/
public function testDBBasics() {
$this->setValues();
$this->renderer->writeToDatabase();
$this->renderer->writeToCache();
$renderer2 = $this->renderer = new MathLaTeXML( self::SOME_TEX );
$renderer2->readFromDatabase();
$renderer2->readFromCache();
// comparing the class object does now work due to null values etc.
$this->assertEquals(
$this->renderer->getTex(), $renderer2->getTex(), "test if tex is the same"

查看文件

@ -39,12 +39,12 @@ class MathRendererTest extends MediaWikiIntegrationTestCase {
public function testWriteCacheSkip() {
$renderer =
$this->getMockBuilder( MathRenderer::class )->onlyMethods( [
'writeToDatabase',
'writeToCache',
'render',
'getMathTableName',
'getHtmlOutput'
] )->getMock();
$renderer->expects( $this->never() )->method( 'writeToDatabase' );
$renderer->expects( $this->never() )->method( 'writeToCache' );
/** @var MathRenderer $renderer */
$renderer->writeCache();
}
@ -56,12 +56,12 @@ class MathRendererTest extends MediaWikiIntegrationTestCase {
public function testWriteCache() {
$renderer =
$this->getMockBuilder( MathRenderer::class )->onlyMethods( [
'writeToDatabase',
'writeToCache',
'render',
'getMathTableName',
'getHtmlOutput'
] )->getMock();
$renderer->expects( $this->never() )->method( 'writeToDatabase' );
$renderer->expects( $this->never() )->method( 'writeToCache' );
/** @var MathRenderer $renderer */
$renderer->writeCache();
}
@ -87,10 +87,10 @@ class MathRendererTest extends MediaWikiIntegrationTestCase {
'render',
'getMathTableName',
'getHtmlOutput',
'readFromDatabase',
'readFromCache',
'setTex'
] )->setConstructorArgs( [ self::TEXVCCHECK_INPUT ] )->getMock();
$renderer->expects( $this->never() )->method( 'readFromDatabase' );
$renderer->expects( $this->never() )->method( 'readFromCache' );
$renderer->expects( $this->once() )->method( 'setTex' )->with( self::TEXVCCHECK_OUTPUT );
/** @var MathRenderer $renderer */
@ -106,10 +106,10 @@ class MathRendererTest extends MediaWikiIntegrationTestCase {
'render',
'getMathTableName',
'getHtmlOutput',
'readFromDatabase',
'readFromCache',
'setTex'
] )->setConstructorArgs( [ self::TEXVCCHECK_INPUT ] )->getMock();
$renderer->expects( $this->never() )->method( 'readFromDatabase' );
$renderer->expects( $this->never() )->method( 'readFromCache' );
$renderer->expects( $this->never() )->method( 'setTex' );
/** @var MathRenderer $renderer */
@ -125,10 +125,10 @@ class MathRendererTest extends MediaWikiIntegrationTestCase {
'render',
'getMathTableName',
'getHtmlOutput',
'readFromDatabase',
'readFromCache',
'setTex'
] )->setConstructorArgs( [ self::TEXVCCHECK_INPUT ] )->getMock();
$renderer->expects( $this->once() )->method( 'readFromDatabase' )
$renderer->expects( $this->once() )->method( 'readFromCache' )
->willReturn( false );
$renderer->expects( $this->once() )->method( 'setTex' )->with( self::TEXVCCHECK_OUTPUT );
@ -147,10 +147,10 @@ class MathRendererTest extends MediaWikiIntegrationTestCase {
'render',
'getMathTableName',
'getHtmlOutput',
'readFromDatabase',
'readFromCache',
'setTex'
] )->setConstructorArgs( [ self::TEXVCCHECK_INPUT ] )->getMock();
$renderer->expects( $this->once() )->method( 'readFromDatabase' )
$renderer->expects( $this->once() )->method( 'readFromCache' )
->willReturn( true );
$renderer->expects( $this->never() )->method( 'setTex' );