Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 77 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
MathMathMLCli | |
0.00% |
0 / 77 |
|
0.00% |
0 / 8 |
600 | |
0.00% |
0 / 1 |
batchEvaluate | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
42 | |||
initializeFromCliResponse | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
20 | |||
renderError | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
42 | |||
getMathoidCliQuery | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
evaluateWithCli | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
12 | |||
render | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
doCheck | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
appendLocationInfo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Math; |
4 | |
5 | use Exception; |
6 | use MediaWiki\Logger\LoggerFactory; |
7 | use MediaWiki\MediaWikiServices; |
8 | use RuntimeException; |
9 | use stdClass; |
10 | |
11 | /** |
12 | * @author Moritz Schubotz |
13 | */ |
14 | class 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( stdClass $response ): string { |
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 | /** @inheritDoc */ |
142 | public function render() { |
143 | if ( $this->getLastError() ) { |
144 | return false; |
145 | } |
146 | |
147 | return true; |
148 | } |
149 | |
150 | protected function doCheck(): bool { |
151 | // avoid that restbase is called if check is set to always |
152 | return $this->texSecure; |
153 | } |
154 | |
155 | /** |
156 | * @param stdClass $response object from cli |
157 | * @return string containing the location information |
158 | */ |
159 | private function appendLocationInfo( $response ) { |
160 | return "in {$response->detail->line}:{$response->detail->column}"; |
161 | } |
162 | } |