Completed
Push — master ( a1be4d...3302e2 )
by Jeroen De
08:02 queued 06:40
created

MapPrinter::getResultText()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 7.0066

Importance

Changes 0
Metric Value
dl 0
loc 65
ccs 37
cts 39
cp 0.9487
rs 7.8303
c 0
b 0
f 0
cc 7
nc 10
nop 2
crap 7.0066

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
namespace Maps\SemanticMW\ResultPrinters;
4
5
use FormatJson;
6
use Html;
7
use Linker;
8
use Maps\Elements\BaseElement;
9
use Maps\Elements\Location;
10
use Maps\FileUrlFinder;
11
use Maps\MappingService;
12
use Maps\MapsFunctions;
13
use Maps\Presentation\ElementJsonSerializer;
14
use Maps\Presentation\MapHtmlBuilder;
15
use Maps\Presentation\WikitextParser;
16
use Maps\Presentation\WikitextParsers\LocationParser;
17
use ParamProcessor\ParamDefinition;
18
use Parser;
19
use SMW\Query\ResultPrinters\ResultPrinter;
20
use SMWOutputs;
21
use SMWQueryResult;
22
use Title;
23
24
/**
25
 * @licence GNU GPL v2+
26
 * @author Jeroen De Dauw < [email protected] >
27
 * @author Peter Grassberger < [email protected] >
28
 */
29
class MapPrinter extends ResultPrinter {
30
31
	private static $services = [];
32
33
	/**
34
	 * @var LocationParser
35
	 */
36
	private $locationParser;
37
38
	/**
39
	 * @var FileUrlFinder
40
	 */
41
	private $fileUrlFinder;
42
43
	/**
44
	 * @var MappingService
45
	 */
46
	private $service;
47
48
	/**
49
	 * @var WikitextParser
50
	 */
51
	private $wikitextParser;
52
53
	/**
54
	 * @var ElementJsonSerializer
55
	 */
56
	private $elementSerializer;
57
58
	/**
59
	 * @var string|boolean
60
	 */
61
	private $fatalErrorMsg = false;
62
63
	/**
64
	 * @param string $format
65
	 * @param bool $inline
66
	 */
67 1
	public function __construct( $format, $inline = true ) {
68 1
		$this->service = self::$services[$format];
69
70 1
		parent::__construct( $format, $inline );
71 1
	}
72
73
	/**
74
	 * @since 3.4
75
	 * FIXME: this is a temporary hack that should be replaced when SMW allows for dependency
76
	 * injection in query printers.
77
	 *
78
	 * @param MappingService $service
79
	 */
80
	public static function registerService( MappingService $service ) {
81
		self::$services[$service->getName()] = $service;
82
	}
83
84
	public static function registerDefaultService( $serviceName ) {
85
		self::$services['map'] = self::$services[$serviceName];
86
	}
87
88 1
	private function getParser(): Parser {
89 1
		$parser = $GLOBALS['wgParser'];
90
91 1
		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...
92 1
			return $parser->_newObject();
93
		}
94
95
		return $parser;
96
	}
97
98 1
	private function getParserClone(): Parser {
99 1
		$parser = $this->getParser();
100 1
		return clone $parser;
101
	}
102
103
	/**
104
	 * Builds up and returns the HTML for the map, with the queried coordinate data on it.
105
	 *
106
	 * @param SMWQueryResult $res
107
	 * @param int $outputMode
108
	 *
109
	 * @return string
110
	 */
111 1
	public final function getResultText( SMWQueryResult $res, $outputMode ) {
112 1
		if ( $this->fatalErrorMsg !== false ) {
113
			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 113 which is incompatible with the return type documented by Maps\SemanticMW\ResultPr...pPrinter::getResultText of type string.
Loading history...
114
		}
115
116 1
		$factory = \Maps\MapsFactory::newDefault();
117 1
		$this->locationParser = $factory->newLocationParser();
118 1
		$this->fileUrlFinder = $factory->getFileUrlFinder();
119
120 1
		$this->wikitextParser = new WikitextParser( $this->getParserClone() );
121 1
		$this->elementSerializer = new ElementJsonSerializer( $this->wikitextParser );
122
123 1
		$this->addTrackingCategoryIfNeeded();
124
125 1
		$params = $this->params;
126
127 1
		$queryHandler = new QueryHandler( $res, $outputMode );
128 1
		$queryHandler->setLinkStyle( $params['link'] );
129 1
		$queryHandler->setHeaderStyle( $params['headers'] );
130 1
		$queryHandler->setShowSubject( $params['showtitle'] );
131 1
		$queryHandler->setTemplate( $params['template'] );
132 1
		$queryHandler->setUserParam( $params['userparam'] );
133 1
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
134 1
		$queryHandler->setActiveIcon( $params['activeicon'] );
135
136 1
		$this->handleMarkerData( $params, $queryHandler );
137
138 1
		$params['lines'] = $this->elementsToJson( $params['lines'] );
139 1
		$params['polygons'] = $this->elementsToJson( $params['polygons'] );
140 1
		$params['circles'] = $this->elementsToJson( $params['circles'] );
141 1
		$params['rectangles'] = $this->elementsToJson( $params['rectangles'] );
142
143 1
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
144
145 1
		if ( $params['locations'] === [] ) {
146
			return $params['default'];
147
		}
148
149
		// We can only take care of the zoom defaulting here,
150
		// as not all locations are available in whats passed to Validator.
151 1
		if ( $this->fullParams['zoom']->wasSetToDefault() && count( $params['locations'] ) > 1 ) {
152 1
			$params['zoom'] = false;
153
		}
154
155 1
		$mapId = $this->service->newMapId();
156
157 1
		SMWOutputs::requireHeadItem(
158 1
			$mapId,
159 1
			$this->service->getDependencyHtml( $params )
160
		);
161
162 1
		foreach ( $this->service->getResourceModules() as $resourceModule ) {
163 1
			SMWOutputs::requireResource( $resourceModule );
164
		}
165
166 1
		if ( array_key_exists( 'source', $params ) ) {
167 1
			unset( $params['source'] );
168
		}
169
170 1
		return ( new MapHtmlBuilder() )->getMapHTML(
171 1
			$params,
172
			$mapId,
173 1
			$this->service->getName()
174
		);
175
	}
176
177 1
	private function elementsToJson( array $elements ) {
178 1
		return array_map(
179 1
			function( BaseElement $element ) {
180
				return $this->elementSerializer->elementToJson( $element );
181 1
			},
182
			$elements
183
		);
184
	}
185
186 1
	private function addTrackingCategoryIfNeeded() {
187
		/**
188
		 * @var Parser $wgParser
189
		 */
190 1
		global $wgParser;
191
192 1
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
193
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
194
		}
195 1
	}
196
197
	/**
198
	 * Converts the data in the coordinates parameter to JSON-ready objects.
199
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
200
	 *
201
	 * @param array &$params
202
	 * @param QueryHandler $queryHandler
203
	 */
204 1
	private function handleMarkerData( array &$params, QueryHandler $queryHandler ) {
205 1
		$params['centre'] = $this->getCenter( $params['centre'] );
206
207 1
		$iconUrl = $this->fileUrlFinder->getUrlForFileName( $params['icon'] );
208 1
		$visitedIconUrl = $this->fileUrlFinder->getUrlForFileName( $params['visitedicon'] );
209
210 1
		$params['locations'] = $this->getJsonForStaticLocations(
211 1
			$params['staticlocations'],
212
			$params,
213
			$iconUrl,
214
			$visitedIconUrl
215
		);
216
217 1
		unset( $params['staticlocations'] );
218
219 1
		$params['locations'] = array_merge(
220 1
			$params['locations'],
221 1
			$this->getJsonForLocations(
222 1
				$queryHandler->getLocations(),
223
				$params,
224
				$iconUrl,
225
				$visitedIconUrl
226
			)
227
		);
228 1
	}
229
230 1
	private function getCenter( $coordinatesOrAddress ) {
231 1
		if ( $coordinatesOrAddress === false ) {
232 1
			return false;
233
		}
234
235
		try {
236
			// FIXME: a Location makes no sense here, since the non-coordinate data is not used
237
			$location = $this->locationParser->parse( $coordinatesOrAddress );
238
		}
239
		catch ( \Exception $ex ) {
240
			// TODO: somehow report this to the user
241
			return false;
242
		}
243
244
		return $location->getJSONObject();
245
	}
246
247 1
	private function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
248 1
		$locationsJson = [];
249
250 1
		foreach ( $staticLocations as $location ) {
251
			$locationsJson[] = $this->getJsonForStaticLocation(
252
				$location,
253
				$params,
254
				$iconUrl,
255
				$visitedIconUrl
256
			);
257
		}
258
259 1
		return $locationsJson;
260
	}
261
262
	private function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl ) {
263
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
264
265
		$this->elementSerializer->titleAndText( $jsonObj );
266
267
		if ( $params['pagelabel'] ) {
268
			$jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
269
		}
270
271
		return $jsonObj;
272
	}
273
274
	/**
275
	 * @param Location[] $locations
276
	 * @param array $params
277
	 * @param string $iconUrl
278
	 * @param string $visitedIconUrl
279
	 *
280
	 * @return array
281
	 */
282 1
	private function getJsonForLocations( iterable $locations, array $params, string $iconUrl, string $visitedIconUrl ): array {
283 1
		$locationsJson = [];
284
285 1
		foreach ( $locations as $location ) {
286 1
			$jsonObj = $location->getJSONObject(
287 1
				$params['title'],
288 1
				$params['label'],
289
				$iconUrl,
290 1
				'',
291 1
				'',
292
				$visitedIconUrl
293
			);
294
295 1
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
296
297 1
			$locationsJson[] = $jsonObj;
298
		}
299
300 1
		return $locationsJson;
301
	}
302
303
	/**
304
	 * Returns the internationalized name of the mapping service.
305
	 *
306
	 * @return string
307
	 */
308
	public final function getName() {
309
		return wfMessage( 'maps_' . $this->service->getName() )->text();
310
	}
311
312
	/**
313
	 * Returns a list of parameter information, for usage by Special:Ask and others.
314
	 *
315
	 * @return array
316
	 */
317 1
	public function getParameters() {
318 1
		$params = parent::getParameters();
319 1
		$paramInfo = $this->getParameterInfo();
320
321
		// Do not display this as an option, as the format already determines it
322
		// TODO: this can probably be done cleaner with some changes in Maps
323 1
		unset( $paramInfo['mappingservice'] );
324
325 1
		$params = array_merge( $params, $paramInfo );
326
327 1
		return $params;
328
	}
329
330
	/**
331
	 * Returns an array containing the parameter info.
332
	 *
333
	 * @return array
334
	 */
335 1
	private function getParameterInfo() {
336 1
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
337
338 1
		$params = array_merge(
339 1
			ParamDefinition::getCleanDefinitions( MapsFunctions::getCommonParameters() ),
0 ignored issues
show
Documentation introduced by
\Maps\MapsFunctions::getCommonParameters() is of type array<string,array<strin...efault\":\"false\"}>"}>, but the function expects a array<integer,object<Par...essor\ParamDefinition>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method ParamProcessor\ParamDefi...::getCleanDefinitions() has been deprecated with message: since 1.7 - use ParamDefinitionFactory

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
340 1
			$this->service->getParameterInfo()
341
		);
342
343 1
		$params['staticlocations'] = [
344
			'type' => 'mapslocation',
345
			'aliases' => [ 'locations', 'points' ],
346
			'default' => [],
347
			'islist' => true,
348
			'delimiter' => ';',
349
			'message' => 'semanticmaps-par-staticlocations',
350
		];
351
352 1
		$params['showtitle'] = [
353 1
			'type' => 'boolean',
354 1
			'aliases' => 'show title',
355 1
			'default' => $smgQPShowTitle,
356
		];
357
358 1
		$params['hidenamespace'] = [
359 1
			'type' => 'boolean',
360 1
			'aliases' => 'hide namespace',
361 1
			'default' => $smgQPHideNamespace,
362
		];
363
364 1
		$params['template'] = [
365 1
			'default' => $smgQPTemplate,
366
		];
367
368 1
		$params['userparam'] = [
369
			'default' => '',
370
		];
371
372 1
		$params['activeicon'] = [
373
			'type' => 'string',
374
			'default' => '',
375
		];
376
377 1
		$params['pagelabel'] = [
378
			'type' => 'boolean',
379
			'default' => false,
380
		];
381
382 1
		$params['ajaxcoordproperty'] = [
383
			'default' => '',
384
		];
385
386 1
		$params['ajaxquery'] = [
387
			'default' => '',
388
			'type' => 'string'
389
		];
390
391
		// Messages:
392
		// semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
393
		// semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
394
		// semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
395 1
		foreach ( $params as $name => &$data ) {
396 1
			if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
397 1
				$data['message'] = 'semanticmaps-par-' . $name;
398
			}
399
		}
400
401 1
		return $params;
402
	}
403
}
404