Completed
Push — master ( 4d0076...81538e )
by Jeroen De
26s queued 11s
created

MapPrinter::getResultText()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 6.0067

Importance

Changes 0
Metric Value
dl 0
loc 57
ccs 33
cts 35
cp 0.9429
rs 8.3158
c 0
b 0
f 0
cc 6
nc 6
nop 2
crap 6.0067

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace Maps\Map\SemanticFormat;
6
7
use Linker;
8
use Maps\Elements\BaseElement;
9
use Maps\Elements\Location;
10
use Maps\FileUrlFinder;
11
use Maps\Map\MapOutput;
12
use Maps\Map\MapOutputBuilder;
13
use Maps\MappingService;
14
use Maps\Presentation\ElementJsonSerializer;
15
use Maps\Map\MapHtmlBuilder;
16
use Maps\Presentation\WikitextParser;
17
use Maps\Presentation\WikitextParsers\LocationParser;
18
use Maps\SemanticMW\ResultPrinters\QueryHandler;
19
use Parser;
20
use SMW\Query\ResultPrinters\ResultPrinter;
21
use SMWOutputs;
22
use SMWQueryResult;
23
use Title;
24
25
/**
26
 * @licence GNU GPL v2+
27
 * @author Jeroen De Dauw < [email protected] >
28
 * @author Peter Grassberger < [email protected] >
29
 */
30
class MapPrinter extends ResultPrinter {
31
32
	private static $services = [];
33
34
	/**
35
	 * @var LocationParser
36
	 */
37
	private $locationParser;
38
39
	/**
40
	 * @var FileUrlFinder
41
	 */
42
	private $fileUrlFinder;
43
44
	/**
45
	 * @var MappingService
46
	 */
47
	private $service;
48
49
	/**
50
	 * @var WikitextParser
51
	 */
52
	private $wikitextParser;
53
54
	/**
55
	 * @var ElementJsonSerializer
56
	 */
57
	private $elementSerializer;
58
59
	/**
60
	 * @var string|boolean
61
	 */
62
	private $fatalErrorMsg = false;
63
64
	/**
65
	 * @param string $format
66
	 * @param bool $inline
67
	 */
68 2
	public function __construct( $format, $inline = true ) {
69 2
		$this->service = self::$services[$format];
70
71 2
		parent::__construct( $format, $inline );
72 2
	}
73
74
	/**
75
	 * @since 3.4
76
	 * FIXME: this is a temporary hack that should be replaced when SMW allows for dependency
77
	 * injection in query printers.
78
	 *
79
	 * @param MappingService $service
80
	 */
81
	public static function registerService( MappingService $service ) {
82
		self::$services[$service->getName()] = $service;
83
	}
84
85
	public static function registerDefaultService( $serviceName ) {
86
		self::$services['map'] = self::$services[$serviceName];
87
	}
88
89 2
	private function getParser(): Parser {
90 2
		$parser = $GLOBALS['wgParser'];
91
92 2
		if ( $parser instanceof \StubObject ) {
0 ignored issues
show
Bug introduced by
The class StubObject does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
93 2
			return $parser->_newObject();
94
		}
95
96
		return $parser;
97
	}
98
99 2
	private function getParserClone(): Parser {
100 2
		$parser = $this->getParser();
101 2
		return clone $parser;
102
	}
103
104
	/**
105
	 * Builds up and returns the HTML for the map, with the queried coordinate data on it.
106
	 *
107
	 * @param SMWQueryResult $res
108
	 * @param int $outputMode
109
	 *
110
	 * @return string
111
	 */
112 2
	public final function getResultText( SMWQueryResult $res, $outputMode ) {
113 2
		if ( $this->fatalErrorMsg !== false ) {
114
			return $this->fatalErrorMsg;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->fatalErrorMsg; of type string|boolean adds the type boolean to the return on line 114 which is incompatible with the return type documented by Maps\Map\SemanticFormat\MapPrinter::getResultText of type string.
Loading history...
115
		}
116
117 2
		$this->isHTML = true;
118
119 2
		$factory = \Maps\MapsFactory::globalInstance();
120 2
		$this->locationParser = $factory->newLocationParser();
121 2
		$this->fileUrlFinder = $factory->getFileUrlFinder();
122
123 2
		$this->wikitextParser = new WikitextParser( $this->getParserClone() );
124 2
		$this->elementSerializer = new ElementJsonSerializer( $this->wikitextParser );
125
126 2
		$this->addTrackingCategoryIfNeeded();
127
128 2
		$params = $this->params;
129
130 2
		$queryHandler = new QueryHandler( $res, $outputMode );
131 2
		$queryHandler->setLinkStyle( $params['link'] );
132 2
		$queryHandler->setHeaderStyle( $params['headers'] );
133 2
		$queryHandler->setShowSubject( $params['showtitle'] );
134 2
		$queryHandler->setTemplate( $params['template'] );
135 2
		$queryHandler->setUserParam( $params['userparam'] );
136 2
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
137 2
		$queryHandler->setActiveIcon( $params['activeicon'] );
138
139 2
		$this->handleMarkerData( $params, $queryHandler );
140
141 2
		$params['lines'] = $this->elementsToJson( $params['lines'] );
142 2
		$params['polygons'] = $this->elementsToJson( $params['polygons'] );
143 2
		$params['circles'] = $this->elementsToJson( $params['circles'] );
144 2
		$params['rectangles'] = $this->elementsToJson( $params['rectangles'] );
145
146 2
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
147
148 2
		if ( $params['locations'] === [] ) {
149
			return $params['default'];
150
		}
151
152
		// We can only take care of the zoom defaulting here,
153
		// as not all locations are available in whats passed to Validator.
154 2
		if ( $this->fullParams['zoom']->wasSetToDefault() && count( $params['locations'] ) > 1 ) {
155 2
			$params['zoom'] = false;
156
		}
157
158 2
		if ( array_key_exists( 'source', $params ) ) {
159 2
			unset( $params['source'] );
160
		}
161
162 2
		$outputBuilder = new MapOutputBuilder();
163 2
		$mapOutput = $outputBuilder->buildOutput( $this->service, $this->service->newMapDataFromParameters( $params ) );
164
165 2
		$this->outputResources( $mapOutput );
166
167 2
		return $mapOutput->getHtml();
168
	}
169
170 2
	private function outputResources( MapOutput $mapOutput ) {
171 2
		SMWOutputs::requireHeadItem(
172 2
			$this->randomString(),
173 2
			$mapOutput->getHeadItems()
174
		);
175
176 2
		foreach ( $mapOutput->getResourceModules() as $resourceModule ) {
177 2
			SMWOutputs::requireResource( $resourceModule );
178
		}
179 2
	}
180
181 2
	private function randomString(): string {
182 2
		return substr( str_shuffle( '0123456789abcdefghijklmnopqrstuvwxyz' ), 0, 10 );
183
	}
184
185 2
	private function elementsToJson( array $elements ) {
186 2
		return array_map(
187 2
			function( BaseElement $element ) {
188
				return $this->elementSerializer->elementToJson( $element );
189 2
			},
190
			$elements
191
		);
192
	}
193
194 2
	private function addTrackingCategoryIfNeeded() {
195
		/**
196
		 * @var Parser $wgParser
197
		 */
198 2
		global $wgParser;
199
200 2
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
201
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
202
		}
203 2
	}
204
205
	/**
206
	 * Converts the data in the coordinates parameter to JSON-ready objects.
207
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
208
	 *
209
	 * @param array &$params
210
	 * @param QueryHandler $queryHandler
211
	 */
212 2
	private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
213 2
		$params['centre'] = $this->getCenter( $params['centre'] );
214
215 2
		$iconUrl = $this->fileUrlFinder->getUrlForFileName( $params['icon'] );
216 2
		$visitedIconUrl = $this->fileUrlFinder->getUrlForFileName( $params['visitedicon'] ?? '' );
217
218 2
		$params['locations'] = $this->getJsonForStaticLocations(
219 2
			$params['staticlocations'],
220
			$params,
221
			$iconUrl,
222
			$visitedIconUrl
223
		);
224
225 2
		unset( $params['staticlocations'] );
226
227 2
		$params['locations'] = array_merge(
228 2
			$params['locations'],
229 2
			$this->getJsonForLocations(
230 2
				$queryHandler->getLocations(),
231
				$params,
232
				$iconUrl,
233
				$visitedIconUrl
234
			)
235
		);
236 2
	}
237
238 2
	private function getCenter( $coordinatesOrAddress ) {
239 2
		if ( $coordinatesOrAddress === false ) {
240 2
			return false;
241
		}
242
243
		try {
244
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
245
			$location = $this->locationParser->parse( $coordinatesOrAddress );
246
		}
247
		catch ( \Exception $ex ) {
248
			// TODO: somehow report this to the user
249
			return false;
250
		}
251
252
		return $location->getJSONObject();
253
	}
254
255 2
	private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
256 2
		$locationsJson = [];
257
258 2
		foreach ( $staticLocations as $location ) {
259
			$locationsJson[] = $this->getJsonForStaticLocation(
260
				$location,
261
				$params,
262
				$iconUrl,
263
				$visitedIconUrl
264
			);
265
		}
266
267 2
		return $locationsJson;
268
	}
269
270
	private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl ) {
271
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
272
273
		$this->elementSerializer->titleAndText( $jsonObj );
274
275
		if ( $params['pagelabel'] ) {
276
			$jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
277
		}
278
279
		return $jsonObj;
280
	}
281
282
	/**
283
	 * @param Location[] $locations
284
	 * @param array $params
285
	 * @param string $iconUrl
286
	 * @param string $visitedIconUrl
287
	 *
288
	 * @return array
289
	 */
290 2
	private function getJsonForLocations( iterable $locations, array $params, string $iconUrl, string $visitedIconUrl ): array {
291 2
		$locationsJson = [];
292
293 2
		foreach ( $locations as $location ) {
294 2
			$jsonObj = $location->getJSONObject(
295 2
				$params['title'],
296 2
				$params['label'],
297
				$iconUrl,
298 2
				'',
299 2
				'',
300
				$visitedIconUrl
301
			);
302
303 2
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
304
305 2
			$locationsJson[] = $jsonObj;
306
		}
307
308 2
		return $locationsJson;
309
	}
310
311
	/**
312
	 * Returns the internationalized name of the mapping service.
313
	 *
314
	 * @return string
315
	 */
316
	public final function getName() {
317
		return wfMessage( 'maps_' . $this->service->getName() )->text();
318
	}
319
320
	/**
321
	 * Returns a list of parameter information, for usage by Special:Ask and others.
322
	 *
323
	 * @return array
324
	 */
325 2
	public function getParameters() {
326 2
		$params = parent::getParameters();
327 2
		$paramInfo = $this->getParameterInfo();
328
329 2
		$params = array_merge( $params, $paramInfo );
330
331 2
		return $params;
332
	}
333
334
	/**
335
	 * Returns an array containing the parameter info.
336
	 *
337
	 * @return array
338
	 */
339 2
	private function getParameterInfo() {
340 2
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
341
342 2
		$params = $this->service->getParameterInfo();
343
344 2
		$params['staticlocations'] = [
345
			'type' => 'mapslocation',
346
			'aliases' => [ 'locations', 'points' ],
347
			'default' => [],
348
			'islist' => true,
349
			'delimiter' => ';',
350
			'message' => 'semanticmaps-par-staticlocations',
351
		];
352
353 2
		$params['showtitle'] = [
354 2
			'type' => 'boolean',
355 2
			'aliases' => 'show title',
356 2
			'default' => $smgQPShowTitle,
357
		];
358
359 2
		$params['hidenamespace'] = [
360 2
			'type' => 'boolean',
361 2
			'aliases' => 'hide namespace',
362 2
			'default' => $smgQPHideNamespace,
363
		];
364
365 2
		$params['template'] = [
366 2
			'default' => $smgQPTemplate,
367
		];
368
369 2
		$params['userparam'] = [
370
			'default' => '',
371
		];
372
373 2
		$params['activeicon'] = [
374
			'type' => 'string',
375
			'default' => '',
376
		];
377
378 2
		$params['pagelabel'] = [
379
			'type' => 'boolean',
380
			'default' => false,
381
		];
382
383 2
		$params['ajaxcoordproperty'] = [
384
			'default' => '',
385
		];
386
387 2
		$params['ajaxquery'] = [
388
			'default' => '',
389
			'type' => 'string'
390
		];
391
392
		// Messages:
393
		// semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
394
		// semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
395
		// semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
396 2
		foreach ( $params as $name => &$data ) {
397 2
			if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
398 2
				$data['message'] = 'semanticmaps-par-' . $name;
399
			}
400
		}
401
402 2
		return $params;
403
	}
404
}
405