mediawiki-extensions-Math/MathMathMLCli.php
Moritz Schubotz d8822169ab
Mathoid CLI interface
Renders formulae via mathoid without to run mathoid as a service.
Mathoid 0.7.1 or later must be installed locally and configured to be
accessed directly from the math extension. It has been tested with the
config.dev.yaml of version 0.7.1. If mathoid is installed in
'/srv/mathoid' the following line might be added to LocalSettings.php
$wgMathoidCli = ['/srv/mathoid/cli.js', '-c', '/srv/mathoid/config.dev.yaml'];
i.e., make sure to specify the -c parameter with an absolute path.
In addition mathoid uses more memory than the the default. With
the config.dev.yaml a value of
$wgMaxShellMemory = 2097152;
has been tested to work well.

Change-Id: I0600f056d21927963267cf979d342e313419e9fa
2018-02-21 20:29:20 +01:00

195 lines
4.7 KiB
PHP

<?php
use MediaWiki\Logger\LoggerFactory;
use \MediaWiki\MediaWikiServices;
/**
* Created by PhpStorm.
* User: Moritz
* Date: 15.08.2017
* Time: 09:33
*/
class MathMathMLCli extends MathMathML {
/**
* @param array &$tags math tags
* @return bool
* @throws MWException
*/
public static function batchEvaluate( &$tags ) {
$req = [];
foreach ( $tags as $key => $tag ) {
/** @var MathMathMLCli $renderer */
$renderer = $tag[0];
// checking if the rendering is in the database is no security issue since only the md5
// hash of the user input string will be sent to the database
if ( !$renderer->isInDatabase() ) {
$req[] = $renderer->getMathoidCliQuery();
}
}
if ( count( $req ) === 0 ) {
return true;
}
$exitCode = 1;
$res = self::evaluateWithCli( $req, $exitCode );
foreach ( $tags as $key => $tag ) {
/** @var MathMathMLCli $renderer */
$renderer = $tag[0];
if ( !$renderer->isInDatabase() ) {
$renderer->initializeFromCliResponse( $res );
}
}
return true;
}
/**
* @param Object $res
* @return bool
*/
private function initializeFromCliResponse( $res ) {
global $wgMathoidCli;
if ( !property_exists( $res, $this->getMd5() ) ) {
$this->lastError =
$this->getError( 'math_mathoid_error', 'cli',
var_export( get_object_vars( $res ) ) );
return false;
}
if ( $this->isEmpty() ) {
return false;
}
$response = $res->{$this->getMd5()};
if ( !$response->success ) {
$this->lastError = $this->renderError( $response );
return false;
}
$this->texSecure = true;
$this->tex = $response->sanetex;
// The host name is only relevant for the debugging. So using file:// to indicate that the
// cli interface seems to be OK.
$this->processJsonResult( $response, 'file://' . $wgMathoidCli[0] );
$this->mathStyle = $response->mathoidStyle;
$this->png = implode( array_map( "chr", $response->png->data ) );
$this->changed = true;
}
public function renderError( $response ) {
$msg = $response->error;
try {
switch ( $response->detail->status ) {
case "F":
$msg .= "\n Found {$response->detail->details}" .
$this->appendLocationInfo( $response );
break;
case 'S':
case "C":
$msg .= $this->appendLocationInfo( $response );
break;
case '-':
// we do not know any cases that triggers this error
}
}
catch ( Exception $e ) {
// use default error message
}
return $this->getError( 'math_mathoid_error', 'cli', $msg );
}
/**
* @return array
*/
public function getMathoidCliQuery() {
return [
'query' => [
'q' => $this->getTex(),
'type' => $this->getInputType(),
'hash' => $this->getMd5(),
],
];
}
/**
* @param string $req request
* @param int &$exitCode exit code
* @return mixed
* @throws MWException
*/
public static function evaluateWithCli( $req, &$exitCode = null ) {
global $wgMathoidCli;
$json_req = json_encode( $req );
$cmd = MediaWikiServices::getInstance()->getShellCommandFactory()->create();
$cmd->params( $wgMathoidCli );
$cmd->input( $json_req );
$result = $cmd->execute();
if ( $result->getExitCode() != 0 ) {
LoggerFactory::getInstance( 'Math' )->error( 'Can not process {req} with config
{conf} returns {res}', [
'req' => $req,
'conf' => var_export( $wgMathoidCli, true ),
'res' => var_export( $result, true ),
] );
throw new MWException( "Mathoid cli '$wgMathoidCli[0]' is not executable." );
}
$res = json_decode( $result->getStdout() );
if ( !$res ) {
throw new MWException( "Mathoid cli response '$res' is no valid JSON file." );
}
return $res;
}
public function render( $forceReRendering = false ) {
if ( $this->getLastError() ) {
return false;
}
return true;
}
protected function doCheck() {
// avoid that restbase is called if check is set to always
return $this->texSecure;
}
protected function initializeFromDatabaseRow( $rpage ) {
if ( !empty( $rpage->math_svg ) ) {
$this->png = $rpage->math_png;
}
parent::initializeFromDatabaseRow( $rpage ); // TODO: Change the autogenerated stub
}
protected function dbOutArray() {
$out = parent::dbOutArray();
$out['math_png'] = $this->png;
return $out;
}
protected function dbInArray() {
$out = parent::dbInArray();
$out[] = 'math_png';
return $out;
}
public function getPng() {
if ( !$this->png ) {
$this->initializeFromCliResponse( self::evaluateWithCli( [
$this->getMathoidCliQuery(),
] ) );
}
return parent::getPng();
}
/**
* @param $response object from cli
* @return string containing the location information
*/
private function appendLocationInfo( $response ) {
return "in {$response->detail->line}:{$response->detail->column}";
}
}