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(): bool { // 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}"; } }