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