Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
MathMathMLCli
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 8
600
0.00% covered (danger)
0.00%
0 / 1
 batchEvaluate
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 initializeFromCliResponse
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 renderError
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
42
 getMathoidCliQuery
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 evaluateWithCli
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
12
 render
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 doCheck
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 appendLocationInfo
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\Math;
4
5use Exception;
6use MediaWiki\Logger\LoggerFactory;
7use MediaWiki\MediaWikiServices;
8use RuntimeException;
9use stdClass;
10
11/**
12 * @author Moritz Schubotz
13 */
14class MathMathMLCli extends MathMathML {
15
16    /**
17     * @param MathRenderer[] $renderers
18     * @return bool
19     */
20    public static function batchEvaluate( array $renderers ) {
21        $req = [];
22        foreach ( $renderers as $renderer ) {
23            '@phan-var MathMathMLCli $renderer';
24            // checking if the rendering is in the database is no security issue since only the md5
25            // hash of the user input string will be sent to the database
26            if ( !$renderer->isInDatabase() ) {
27                $req[] = $renderer->getMathoidCliQuery();
28            }
29        }
30        if ( count( $req ) === 0 ) {
31            return true;
32        }
33        $exitCode = 1;
34        $res = self::evaluateWithCli( $req, $exitCode );
35        foreach ( $renderers as $renderer ) {
36            '@phan-var MathMathMLCli $renderer';
37            if ( !$renderer->isInDatabase() ) {
38                $renderer->initializeFromCliResponse( $res );
39            }
40        }
41
42        return true;
43    }
44
45    /**
46     * @param stdClass $res
47     * @return bool
48     */
49    private function initializeFromCliResponse( $res ) {
50        global $wgMathoidCli;
51        if ( !property_exists( $res, $this->getInputHash() ) ) {
52            $this->lastError =
53                $this->getError( 'math_mathoid_error', 'cli',
54                    var_export( get_object_vars( $res ), true ) );
55            return false;
56        }
57        if ( $this->isEmpty() ) {
58            $this->lastError = $this->getError( 'math_empty_tex' );
59            return false;
60        }
61        $response = $res->{$this->getInputHash()};
62        if ( !$response->success ) {
63            $this->lastError = $this->renderError( $response );
64            return false;
65        }
66        $this->texSecure = true;
67        $this->tex = $response->sanetex;
68        // The host name is only relevant for the debugging. So using file:// to indicate that the
69        // cli interface seems to be OK.
70        $this->processJsonResult( $response, 'file://' . $wgMathoidCli[0] );
71        $this->mathStyle = $response->mathoidStyle;
72        $this->changed = true;
73        return true;
74    }
75
76    public function renderError( $response ) {
77        $msg = $response->error;
78        try {
79            switch ( $response->detail->status ) {
80                case "F":
81                    $msg .= "\n Found {$response->detail->details}" .
82                            $this->appendLocationInfo( $response );
83                    break;
84                case 'S':
85                case "C":
86                    $msg .= $this->appendLocationInfo( $response );
87                    break;
88                case '-':
89                    // we do not know any cases that triggers this error
90            }
91        } catch ( Exception $e ) {
92            // use default error message
93        }
94
95        return $this->getError( 'math_mathoid_error', 'cli', $msg );
96    }
97
98    /**
99     * @return array
100     */
101    public function getMathoidCliQuery() {
102        return [
103            'query' => [
104                'q' => $this->getTex(),
105                'type' => $this->getInputType(),
106                'hash' => $this->getInputHash(),
107            ],
108        ];
109    }
110
111    /**
112     * @param mixed $req request
113     * @param int|null &$exitCode
114     * @return mixed
115     */
116    private static function evaluateWithCli( $req, &$exitCode = null ) {
117        global $wgMathoidCli;
118        $json_req = json_encode( $req );
119        $cmd = MediaWikiServices::getInstance()->getShellCommandFactory()->create();
120        $cmd->params( $wgMathoidCli );
121        $cmd->input( $json_req );
122        $result = $cmd->execute();
123        if ( $result->getExitCode() != 0 ) {
124            $errorMsg = $result->getStderr();
125            LoggerFactory::getInstance( 'Math' )->error( 'Can not process {req} with config
126             {conf} returns {res}', [
127                'req' => $req,
128                'conf' => var_export( $wgMathoidCli, true ),
129                'res' => var_export( $result, true ),
130            ] );
131            throw new RuntimeException( "Failed to execute Mathoid cli '$wgMathoidCli[0]', reason: $errorMsg" );
132        }
133        $res = json_decode( $result->getStdout() );
134        if ( !$res ) {
135            throw new RuntimeException( "Mathoid cli response '$res' is no valid JSON file." );
136        }
137
138        return $res;
139    }
140
141    public function render() {
142        if ( $this->getLastError() ) {
143            return false;
144        }
145
146        return true;
147    }
148
149    protected function doCheck(): bool {
150        // avoid that restbase is called if check is set to always
151        return $this->texSecure;
152    }
153
154    /**
155     * @param stdClass $response object from cli
156     * @return string containing the location information
157     */
158    private function appendLocationInfo( $response ) {
159        return "in {$response->detail->line}:{$response->detail->column}";
160    }
161}