Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
67.35% covered (warning)
67.35%
33 / 49
36.36% covered (danger)
36.36%
8 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
ScribuntoEngineBase
67.35% covered (warning)
67.35%
33 / 49
36.36% covered (danger)
36.36%
8 / 22
55.30
0.00% covered (danger)
0.00%
0 / 1
 newModule
n/a
0 / 0
n/a
0 / 0
0
 runConsole
n/a
0 / 0
n/a
0 / 0
0
 getSoftwareInfo
n/a
0 / 0
n/a
0 / 0
0
 __construct
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 __destruct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 destroy
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newException
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultExceptionParams
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 fetchModuleFromParser
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
4.10
 validate
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getResourceUsage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGeSHiLanguage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCodeEditorLanguage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getParser
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLibraries
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getLibraryPaths
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 reportLimitData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 formatLimitData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 updateRedirect
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRedirectTarget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeRedirectContent
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 supportsRedirects
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Wikitext scripting infrastructure for MediaWiki: base classes.
4 * Copyright (C) 2012 Victor Vasiliev <vasilvv@gmail.com> et al
5 * https://www.mediawiki.org/
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * http://www.gnu.org/copyleft/gpl.html
21 */
22
23namespace MediaWiki\Extension\Scribunto;
24
25use MediaWiki\Extension\Scribunto\Hooks\HookRunner;
26use MediaWiki\MediaWikiServices;
27use MediaWiki\Parser\ParserOutput;
28use MediaWiki\Status\Status;
29use MediaWiki\Title\Title;
30use Parser;
31
32/**
33 * Base class for all script engines. Includes all code
34 * not related to particular modules, like tracking links between
35 * modules or loading module texts.
36 */
37abstract class ScribuntoEngineBase {
38
39    // Flags for ScribuntoEngineBase::getResourceUsage()
40    public const CPU_SECONDS = 1;
41    public const MEM_PEAK_BYTES = 2;
42
43    /**
44     * @var Title|null
45     */
46    protected $title;
47
48    /**
49     * @var array
50     */
51    protected $options;
52
53    /**
54     * @var (ScribuntoModuleBase|null)[]
55     */
56    protected $modules = [];
57
58    /**
59     * @var Parser|null
60     */
61    protected $parser;
62
63    /**
64     * Creates a new module object within this engine
65     *
66     * @param string $text
67     * @param string|bool $chunkName
68     * @return ScribuntoModuleBase
69     */
70    abstract protected function newModule( $text, $chunkName );
71
72    /**
73     * Run an interactive console request
74     *
75     * @param array $params Associative array. Options are:
76     *    - title: The title object for the module being debugged
77     *    - content: The text content of the module
78     *    - prevQuestions: An array of previous "questions" used to establish the state
79     *    - question: The current "question", a string script
80     *
81     * @return array containing:
82     *    - print: The resulting print buffer
83     *    - return: The resulting return value
84     */
85    abstract public function runConsole( array $params );
86
87    /**
88     * Get software information for Special:Version
89     * @param array &$software
90     */
91    abstract public function getSoftwareInfo( array &$software );
92
93    /**
94     * @param array $options Associative array of options:
95     *    - parser:            A Parser object
96     */
97    public function __construct( array $options ) {
98        $this->options = $options;
99        if ( isset( $options['parser'] ) ) {
100            $this->parser = $options['parser'];
101        }
102        if ( isset( $options['title'] ) ) {
103            $this->title = $options['title'];
104        }
105    }
106
107    public function __destruct() {
108        $this->destroy();
109    }
110
111    public function destroy() {
112        // Break reference cycles
113        $this->parser = null;
114        $this->title = null;
115        $this->modules = [];
116    }
117
118    /**
119     * @param Title $title
120     */
121    public function setTitle( $title ) {
122        $this->title = $title;
123    }
124
125    /**
126     * @return Title
127     */
128    public function getTitle() {
129        return $this->title;
130    }
131
132    /**
133     * Get an element from the configuration array
134     *
135     * @param string $optionName
136     * @return mixed
137     */
138    public function getOption( $optionName ) {
139        return $this->options[$optionName] ?? null;
140    }
141
142    /**
143     * @param string $message
144     * @param array $params
145     * @return ScribuntoException
146     */
147    public function newException( $message, array $params = [] ) {
148        return new ScribuntoException( $message, $this->getDefaultExceptionParams() + $params );
149    }
150
151    /**
152     * @return array
153     */
154    public function getDefaultExceptionParams() {
155        $params = [];
156        if ( $this->title ) {
157            $params['title'] = $this->title;
158        } else {
159            wfDeprecated( __METHOD__ . ' without valid title for engine', '1.42' );
160        }
161        return $params;
162    }
163
164    /**
165     * Load a module from some parser-defined template loading mechanism and
166     * register a parser output dependency.
167     *
168     * Does not initialize the module, i.e. do not expect it to complain if the module
169     * text is garbage or has syntax error. Returns a module or null if it doesn't exist.
170     *
171     * @param Title $title The title of the module
172     * @return ScribuntoModuleBase|null
173     */
174    public function fetchModuleFromParser( Title $title ) {
175        $key = $title->getPrefixedDBkey();
176        if ( !array_key_exists( $key, $this->modules ) ) {
177            [ $text, $finalTitle ] = $this->parser->fetchTemplateAndTitle( $title );
178            if ( $text === false ) {
179                $this->modules[$key] = null;
180                return null;
181            }
182
183            $finalKey = $finalTitle->getPrefixedDBkey();
184            if ( !isset( $this->modules[$finalKey] ) ) {
185                $this->modules[$finalKey] = $this->newModule( $text, $finalKey );
186            }
187            // $finalKey may be different from $key in the case of redirects;
188            // store the module in both places.
189            // @phan-suppress-next-line PhanTypeMismatchProperty
190            $this->modules[$key] = $this->modules[$finalKey];
191        }
192        return $this->modules[$key];
193    }
194
195    /**
196     * Validates the script and returns a Status object containing the syntax
197     * errors for the given code.
198     *
199     * @param string $text
200     * @param string|bool $chunkName
201     * @return Status
202     */
203    public function validate( $text, $chunkName = false ) {
204        $module = $this->newModule( $text, $chunkName );
205        return $module->validate();
206    }
207
208    /**
209     * Get CPU and memory usage information, if the script engine
210     * provides it.
211     *
212     * If the script engine is capable of reporting CPU and memory usage
213     * data, it should override this implementation.
214     *
215     * @param int $resource One of ScribuntoEngineBase::CPU_SECONDS
216     *  or ScribuntoEngineBase::MEM_PEAK_BYTES.
217     * @return float|int|false Resource usage for the specified resource
218     *  or false if not available.
219     */
220    public function getResourceUsage( $resource ) {
221        return false;
222    }
223
224    /**
225     * Get the language for GeSHi syntax highlighter.
226     * @return string|false
227     */
228    public function getGeSHiLanguage() {
229        return false;
230    }
231
232    /**
233     * Get the language for Ace code editor.
234     * @return string|false
235     */
236    public function getCodeEditorLanguage() {
237        return false;
238    }
239
240    /**
241     * @return Parser
242     */
243    public function getParser() {
244        return $this->parser;
245    }
246
247    /**
248     * Load a list of all libraries supported by this engine
249     *
250     * The return value is an array with keys being the library name seen by
251     * the module and values being either a PHP class name or an array with the
252     * following elements:
253     *  - class: (string) Class to load (required)
254     *  - deferLoad: (bool) Library should not be loaded at startup; modules
255     *      needing the library must request it (e.g. via 'require' in Lua)
256     *
257     * @param string $engine script engine we're using (eg: lua)
258     * @param array $coreLibraries Array of core libraries we support
259     * @return array
260     */
261    protected function getLibraries( $engine, array $coreLibraries = [] ) {
262        $extraLibraries = [];
263        ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
264            ->onScribuntoExternalLibraries( $engine, $extraLibraries );
265        return $coreLibraries + $extraLibraries;
266    }
267
268    /**
269     * Load a list of all paths libraries can be in for this engine
270     *
271     * @param string $engine script engine we're using (eg: lua)
272     * @param array $coreLibraryPaths Array of library paths to use by default
273     * @return array
274     */
275    protected function getLibraryPaths( $engine, array $coreLibraryPaths = [] ) {
276        $extraLibraryPaths = [];
277        ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
278            ->onScribuntoExternalLibraryPaths( $engine, $extraLibraryPaths );
279        return array_merge( $coreLibraryPaths, $extraLibraryPaths );
280    }
281
282    /**
283     * Add limit report data to a ParserOutput object
284     *
285     * @param ParserOutput $output ParserOutput object in which to add limit data
286     */
287    public function reportLimitData( ParserOutput $output ) {
288    }
289
290    /**
291     * Format limit report data
292     *
293     * @param string $key
294     * @param mixed &$value
295     * @param string &$report
296     * @param bool $isHTML
297     * @param bool $localize
298     * @return bool
299     */
300    public function formatLimitData( $key, &$value, &$report, $isHTML, $localize ) {
301        return true;
302    }
303
304    /**
305     * @see Content::updateRedirect
306     *
307     * @param ScribuntoContent $content
308     * @param Title $target
309     * @return ScribuntoContent
310     */
311    public function updateRedirect( ScribuntoContent $content, Title $target ): ScribuntoContent {
312        return $content;
313    }
314
315    /**
316     * @see Content::getRedirectTarget
317     *
318     * @param ScribuntoContent $content
319     * @return Title|null
320     */
321    public function getRedirectTarget( ScribuntoContent $content ) {
322        return null;
323    }
324
325    /**
326     * @see ContentHandler::makeRedirectContent
327     * @param Title $destination
328     * @param string $text
329     * @return ScribuntoContent|null
330     */
331    public function makeRedirectContent( Title $destination, $text = '' ) {
332        return null;
333    }
334
335    /**
336     * @see ContentHandler::supportsRedirects
337     * @return bool false
338     */
339    public function supportsRedirects(): bool {
340        return false;
341    }
342}