mediawiki-extensions-Math/src/MathMathMLCli.php

201 lines
5 KiB
PHP
Raw Normal View History

<?php
namespace MediaWiki\Extension\Math;
use Exception;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use MWException;
use stdClass;
/**
* @author Moritz Schubotz
*/
class MathMathMLCli extends MathMathML {
/**
* @param MathRenderer[] $renderers
* @return bool
* @throws MWException
*/
public static function batchEvaluate( array $renderers ) {
$req = [];
foreach ( $renderers as $renderer ) {
'@phan-var MathMathMLCli $renderer';
// 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 ( $renderers as $renderer ) {
'@phan-var MathMathMLCli $renderer';
if ( !$renderer->isInDatabase() ) {
$renderer->initializeFromCliResponse( $res );
}
}
return true;
}
/**
* @param stdClass $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 ), true ) );
return false;
}
if ( $this->isEmpty() ) {
$this->lastError = $this->getError( 'math_empty_tex' );
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;
if ( property_exists( $response, 'png' ) ) {
$this->png = implode( array_map( "chr", $response->png->data ) );
} else {
LoggerFactory::getInstance( 'Math' )->error( 'Mathoid did not return a PNG image.' .
' Check your librsvg installation https://github.com/wikimedia/mathoid/.' );
}
$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 mixed $req request
* @param int|null &$exitCode
* @return mixed
* @throws MWException
*/
private 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 ) {
$errorMsg = $result->getStderr();
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( "Failed to execute Mathoid cli '$wgMathoidCli[0]', reason: $errorMsg" );
}
$res = json_decode( $result->getStdout() );
if ( !$res ) {
throw new MWException( "Mathoid cli response '$res' is no valid JSON file." );
}
return $res;
}
public function render() {
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 stdClass $response object from cli
* @return string containing the location information
*/
private function appendLocationInfo( $response ) {
return "in {$response->detail->line}:{$response->detail->column}";
}
}