ScribuntoLuaLibrary   A
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 356
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 98.17%

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 5
dl 0
loc 356
ccs 107
cts 109
cp 0.9817
rs 9.44
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getPropertyType() 0 17 3
A isAQueryResult() 0 3 1
B processLuaArguments() 0 26 7
A register() 0 13 1
A ask() 0 18 2
A getQueryResult() 0 20 3
A info() 0 28 5
A set() 0 25 2
A subobject() 0 34 4
A convertArrayToLuaTable() 0 11 3
A doPostProcessParserFunctionCallResult() 0 17 4
A getLibraryFactory() 0 12 2
1
<?php
2
3
namespace SMW\Scribunto;
4
5
use Scribunto_LuaLibraryBase;
6
use SMW\DIProperty;
7
use SMW\ApplicationFactory;
8
use SMWQueryResult as QueryResult;
9
use SMWOutputs;
10
11
/**
12
 * @license GNU GPL v2+
13
 * @since 1.0
14
 *
15
 * @author mwjames
16
 */
17
class ScribuntoLuaLibrary extends Scribunto_LuaLibraryBase {
18
19
	/**
20
	 * @var LibraryFactory
21
	 */
22
	private $libraryFactory;
23
24
	/**
25
	 * This is the name of the key for error messages
26
	 *
27
	 * @var string
28
	 * @since 1.0
29
	 */
30
	const SMW_ERROR_FIELD = 'error';
31
32
	/**
33
	 * @since 1.0
34
	 */
35 51
	public function register() {
36
37
		$lib = [
38 51
			'ask'             => [ $this, 'ask' ],
39 51
			'getPropertyType' => [ $this, 'getPropertyType' ],
40 51
			'getQueryResult'  => [ $this, 'getQueryResult' ],
41 51
			'info'            => [ $this, 'info' ],
42 51
			'set'             => [ $this, 'set' ],
43 51
			'subobject'       => [ $this, 'subobject' ],
44
		];
45
46 51
		$this->getEngine()->registerInterface( __DIR__ . '/' . 'mw.smw.lua', $lib, [] );
47 51
	}
48
49
	/**
50
	 * This mirrors the functionality of the parser function #ask and makes it
51
	 * available in lua.
52
	 *
53
	 * @since 1.0
54
	 *
55
	 * @param string|array $arguments parameters passed from lua, string or array depending on call
56
	 *
57
	 * @return array    array( null ) or array[]
58
	 */
59 10
	public function ask( $arguments = null ) {
60
61 10
		$queryResult = $this->getLibraryFactory()->newQueryResultFrom(
62 10
			$this->processLuaArguments( $arguments )
63
		);
64
65 10
		if ( !$this->isAQueryResult( $queryResult ) ) {
66 1
			return [ $queryResult ];
67
		}
68
69 9
		$luaResultProcessor = $this->getLibraryFactory()->newLuaAskResultProcessor(
70 9
			$queryResult
71
		);
72
73 9
		$result = $luaResultProcessor->getProcessedResult();
74
75 9
		return [ $this->convertArrayToLuaTable( $result ) ];
76
	}
77
78
	/**
79
	 * Returns property type
80
	 *
81
	 * @since 1.0
82
	 *
83
	 * @param string $propertyName
84
	 *
85
	 * @return array
86
	 */
87 5
	public function getPropertyType( $propertyName = null ) {
88
89 5
		$this->checkType( 'getPropertyType', 1, $propertyName, 'string' );
90 5
		$propertyName = trim( $propertyName );
91
92 5
		if ( $propertyName === '' ) {
93 3
			return [ null ];
94
		}
95
96 3
		$property = DIProperty::newFromUserLabel( $propertyName );
97
98 3
		if ( $property === null ) {
99
			return [ null ];
100
		}
101
102 3
		return [ $property->findPropertyTypeID() ];
103
	}
104
105
	/**
106
	 * Returns query results in for of the standard API return format
107
	 *
108
	 * @since 1.0
109
	 *
110
	 * @param string|array $arguments
111
	 *
112
	 * @return array
113
	 */
114 8
	public function getQueryResult( $arguments = null ) {
115
116 8
		$queryResult = $this->getLibraryFactory()->newQueryResultFrom(
117 8
			$this->processLuaArguments( $arguments )
118
		);
119
120 8
		if ( !$this->isAQueryResult( $queryResult ) ) {
121 1
			return [ $queryResult ];
122
		}
123
124 7
		$result = $queryResult->toArray();
125
126 7
		if( !empty( $result["results"] ) ) {
127
			// as of now, "results" has page names as keys. lua is not very good, keeping non-number keys in order
128
			// so replace string keys with the corresponding number, starting with 0.
129 3
		    $result["results"] = array_combine( range( 0, count( $result["results"] ) - 1 ), array_values( $result["results"] ) );
130
		}
131
132 7
		return [ $this->convertArrayToLuaTable( $result ) ];
133
	}
134
135
	/**
136
	 * This mirrors the functionality of the parser function #info and makes it
137
	 * available to lua.
138
	 *
139
	 * @since 1.0
140
	 *
141
	 * @param string $text text to show inside the info popup
142
	 * @param string $icon identifier for the icon to use
143
	 *
144
	 * @return string[]
145
	 */
146 7
	public function info( $text, $icon = 'info' ) {
147
148
		// do some parameter processing
149 7
		if ( ! trim( $text ) || ! is_string( $text ) ) {
150
			// no info-text present, or wrong type. abort
151 3
			return null;
152
		}
153
154
		// check if icon is set and valid
155 5
		if ( !is_string( $icon ) || !in_array( $icon, [ 'note', 'warning' ] ) ) {
156 5
			$icon = 'info';
157
		}
158
159
		// the actual info message is easy to create:
160 5
		$result = smwfEncodeMessages(
161 5
			[ $text ],
162 5
			$icon,
163 5
			' <!--br-->',
164 5
			false // No escaping.
165
		);
166
167
		// to have all necessary data committed to output, use SMWOutputs::commitToParser()
168 5
		SMWOutputs::commitToParser(
169 5
			$this->getEngine()->getParser()
170
		);
171
172 5
		return [ $this->doPostProcessParserFunctionCallResult( $result ) ];
173
	}
174
175
	/**
176
	 * This mirrors the functionality of the parser function #set and makes it
177
	 * available in lua.
178
	 *
179
	 * @since 1.0
180
	 *
181
	 * @param string|array $arguments arguments passed from lua, string or array depending on call
182
	 *
183
	 * @return null|array|array[]
184
	 */
185 20
	public function set( $arguments ) {
186
187 20
		$arguments = $this->processLuaArguments( $arguments );
188
189 20
		$setParserFunction = $this->getLibraryFactory()->newSetParserFunction(
190 20
			$this->getEngine()->getParser()
191
		);
192
193 20
		$parserFunctionCallResult = $setParserFunction->parse(
194 20
			$this->getLibraryFactory()->newParserParameterProcessorFrom( $arguments )
195
		);
196
197
		// get usable result
198 20
		$result = $this->doPostProcessParserFunctionCallResult(
199 20
			$parserFunctionCallResult
200
		);
201
202 20
		if ( strlen( $result ) ) {
203
			// if result is a non empty string, assume an error message
204 3
			return [ [ 1 => false, self::SMW_ERROR_FIELD => preg_replace( '/<[^>]+>/', '', $result ) ] ];
205
		}
206
207
		// on success, return true
208 18
		return [ 1 => true ];
209
	}
210
211
	/**
212
	 * This mirrors the functionality of the parser function #subobject and
213
	 * makes it available to lua.
214
	 *
215
	 * @param string|array $arguments arguments passed from lua, string or array depending on call
216
	 * @param string $subobjectId if you need to manually assign an id, do this here
217
	 *
218
	 * @return null|array|array[]
219
	 */
220 23
	public function subobject( $arguments, $subobjectId = null ) {
221
222 23
		$arguments = $this->processLuaArguments( $arguments );
223
224
		// parameters[0] would be the subobject id, so unshift
225 23
		array_unshift( $arguments, null );
226
227
		// if subobject id was set, put it on position 0
228 23
		if ( !is_null( $subobjectId ) && $subobjectId ) {
229
			// user deliberately set an id for this subobject
230 11
			$arguments[0] = $subobjectId;
231
232
			// we need to ksort, otherwise ParameterProcessorFactory doesn't
233
			// recognize the id
234 11
			ksort( $arguments );
235
		}
236
237
		// prepare subobjectParserFunction object
238 23
		$subobjectParserFunction = $this->getLibraryFactory()->newSubobjectParserFunction(
239 23
			$this->getEngine()->getParser()
240
		);
241
242 23
		$parserFunctionCallResult = $subobjectParserFunction->parse(
243 23
			 $this->getLibraryFactory()->newParserParameterProcessorFrom( $arguments )
244
		);
245
246 23
		if ( strlen( $result = $this->doPostProcessParserFunctionCallResult( $parserFunctionCallResult ) ) ) {
247
			// if result a non empty string, assume an error message
248 4
			return [ [ 1 => false, self::SMW_ERROR_FIELD => preg_replace( '/<[^>]+>/', '', $result ) ] ];
249
		}
250
251
		// on success, return true
252 19
		return [ 1 => true ];
253
	}
254
255
	/**
256
	 * This takes an array and converts it so, that the result is a viable lua table.
257
	 * I.e. the resulting table has its numerical indices start with 1
258
	 * If `$ar` is not an array, it is simply returned
259
	 * @param mixed $ar
260
	 * @return mixed array
261
	 */
262 15
	private function convertArrayToLuaTable( $ar ) {
263
264 15
		if ( is_array( $ar) ) {
265 10
			foreach ( $ar as $key => $value ) {
266 10
				$ar[$key] = $this->convertArrayToLuaTable( $value );
267
			}
268 10
			array_unshift( $ar, '' );
269 10
			unset( $ar[0] );
270
		}
271 15
		return $ar;
272
	}
273
274
	/**
275
	 * Takes a result returned from a parser function call and prepares it to be
276
	 * used as parsed string.
277
	 *
278
	 * @since 1.0
279
	 *
280
	 * @param string|array $parserFunctionResult
281
	 *
282
	 * @return string
283
	 */
284 48
	private function doPostProcessParserFunctionCallResult( $parserFunctionResult ) {
285
286
		// parser function call can return string or array
287 48
		if ( is_array( $parserFunctionResult ) ) {
288 20
			$result = $parserFunctionResult[0];
289 20
			$noParse = isset( $parserFunctionResult['noparse'] ) ? $parserFunctionResult['noparse'] : true;
290
		} else {
291 28
			$result = $parserFunctionResult;
292 28
			$noParse = true;
293
		}
294
295 48
		if ( !$noParse ) {
296
			$result = $this->getEngine()->getParser()->recursiveTagParseFully( $result );
297
		}
298
299 48
		return trim( $result );
300
	}
301
302
	/**
303
	 * Returns the LibraryFactory singleton (and creates it first, if not already present)
304
	 *
305
	 * @since 1.0
306
	 *
307
	 * @return LibraryFactory
308
	 */
309 59
	private function getLibraryFactory() {
310
311 59
		if ( $this->libraryFactory !== null ) {
312 53
			return $this->libraryFactory;
313
		}
314
315 59
		$this->libraryFactory = new LibraryFactory(
316 59
			ApplicationFactory::getInstance()->getStore()
317
		);
318
319 59
		return $this->libraryFactory;
320
	}
321
322
	/**
323
	 * Tests, if supplied parameter `$queryResult` is a valid {@see QueryResult}
324
	 *
325
	 * @since 1.0
326
	 *
327
	 * @param mixed $queryResult
328
	 *
329
	 * @return bool
330
	 */
331 16
	private function isAQueryResult( $queryResult ) {
332 16
		return is_a( $queryResult, QueryResult::class );
333
	}
334
335
	/**
336
	 * Takes the $arguments passed from lua and pre-processes them: make sure,
337
	 * we have a sequence array (not associative)
338
	 *
339
	 * @since 1.0
340
	 *
341
	 * @param string|array $arguments
342
	 *
343
	 * @return array
344
	 */
345 59
	private function processLuaArguments( $arguments ) {
346
347
		// make sure, we have an array of parameters
348 59
		if ( !is_array( $arguments ) ) {
349 21
			$arguments = preg_split( "/(?<=[^\|])\|(?=[^\|])/", $arguments );
350
		}
351
352
		// if $arguments were supplied as key => value pair (aka associative array),
353
		// we rectify this here
354 59
		$processedArguments = [];
355 59
		foreach ( $arguments as $key => $value ) {
356 55
			if ( is_array( $value ) ) {
357
				// user supplied data in form property => [ 'value1', 'value2' ]
358 2
				foreach ( $value as $item ) {
359 2
					$processedArguments[] = $key . '=' . $item;
360
				}
361
			} else {
362 53
				if ( !is_int($key) && !preg_match('/^[0-9]+$/', $key) ) {
363 22
					$value = (string)$key . '=' . (string)$value;
364
				}
365 55
				$processedArguments[] = $value;
366
			}
367
		}
368
369 59
		return $processedArguments;
370
	}
371
372
}
373