Completed
Push — master ( b76c08...ab03db )
by Peter
9s
created

SMMapPrinter::getResultText()   C

Complexity

Conditions 9
Paths 19

Size

Total Lines 59
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 6
Bugs 1 Features 1
Metric Value
cc 9
eloc 33
c 6
b 1
f 1
nc 19
nop 2
dl 0
loc 59
ccs 0
cts 0
cp 0
crap 90
rs 6.9133

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
use Maps\Elements\Location;
4
use Maps\Element;
5
use Maps\Elements\BaseElement;
6
use ParamProcessor\ParamDefinition;
7
8
/**
9
 * Query printer for maps. Is invoked via SMMapper.
10
 * Can be overridden per service to have custom output.
11
 *
12
 * @file SM_MapPrinter.php
13
 * @ingroup SemanticMaps
14
 *
15
 * @licence GNU GPL v2+
16
 * @author Jeroen De Dauw < [email protected] >
17
 * @author Peter Grassberger < [email protected] >
18
 */
19
class SMMapPrinter extends SMW\ResultPrinter {
20
	
21
	/**
22
	 * @since 0.6
23
	 * 
24
	 * @var iMappingService
25
	 */
26
	protected $service;	
27
	
28
	/**
29
	 * @since 1.0
30
	 * 
31
	 * @var string|boolean false
32
	 */
33
	protected $fatalErrorMsg = false;
34
	
35
	/**
36
	 * Constructor.
37
	 * 
38
	 * @param $format String
39
	 * @param $inline
40
	 */
41 2
	public function __construct( $format, $inline = true ) {
42 2
		$this->service = MapsMappingServices::getValidServiceInstance( $format, 'qp' );
43
		
44 2
		parent::__construct( $format, $inline );
45 2
	}
46
47
	/**
48
	 * Returns an array containing the parameter info.
49
	 * 
50
	 * @since 1.0
51
	 * 
52
	 * @return array
53
	 */
54
	protected function getParameterInfo() {
55
		global $smgQPShowTitle, $smgQPTemplate, $smgQPHideNamespace;
56
		
57
		$params = ParamDefinition::getCleanDefinitions( MapsMapper::getCommonParameters() );
0 ignored issues
show
Documentation introduced by
\MapsMapper::getCommonParameters() is of type array<string,array<strin...efault\":\"false\"}>"}>, but the function expects a array<integer,object<Par...ssor\IParamDefinition>>.

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 MapsMapper::getCommonParameters() has been deprecated.

This method has been deprecated.

Loading history...
58
59
		$this->service->addParameterInfo( $params );
60
61
		$params['staticlocations'] = [
62
			'type' => 'mapslocation',
63
			'aliases' => [ 'locations', 'points' ],
64
			'default' => [],
65
			'islist' => true,
66
			'delimiter' => ';',
67
			'message' => 'semanticmaps-par-staticlocations',
68
		];
69
70
		$params['showtitle'] = [
71
			'type' => 'boolean',
72
			'aliases' => 'show title',
73
			'default' => $smgQPShowTitle,
74
		];
75
76
		$params['hidenamespace'] = [
77
			'type' => 'boolean',
78
			'aliases' => 'hide namespace',
79
			'default' => $smgQPHideNamespace,
80
		];
81
82
		$params['template'] = [
83
			'default' => $smgQPTemplate,
84
		];
85
86
		$params['userparam'] = [
87
			'default' => '',
88
		];
89
90
		$params['activeicon'] =  [
91
			'type' => 'string',
92
			'default' => '',
93
		];
94
95
		$params['pagelabel'] =  [
96
			'type' => 'boolean',
97
			'default' => false,
98
		];
99
100
		$params['ajaxcoordproperty'] = array(
101
			'default' => 'Has coordinates',
102
		);
103
104
		$params['ajaxquery'] = array(
105
			'default' => '',
106
			'type' => 'string'
107
		);
108
109
		// Messages:
110
		// semanticmaps-par-staticlocations, semanticmaps-par-showtitle, semanticmaps-par-hidenamespace,
111
		// semanticmaps-par-template, semanticmaps-par-userparam, semanticmaps-par-activeicon,
112
		// semanticmaps-par-pagelabel, semanticmaps-par-ajaxcoordproperty semanticmaps-par-ajaxquery
113
		foreach ( $params as $name => &$data ) {
114
			if ( is_array( $data ) && !array_key_exists( 'message', $data ) ) {
115
				$data['message'] = 'semanticmaps-par-' . $name;
116
			}
117
		}
118
119
		$params = array_merge( $params, MapsDisplayMap::getCommonMapParams() );
0 ignored issues
show
Deprecated Code introduced by
The method MapsDisplayMap::getCommonMapParams() has been deprecated.

This method has been deprecated.

Loading history...
120
		
121
		return $params;
122
	}
123
	
124
	/**
125
	 * Builds up and returns the HTML for the map, with the queried coordinate data on it.
126
	 *
127
	 * @param SMWQueryResult $res
128
	 * @param $outputmode
129
	 * 
130
	 * @return array or string
131
	 */
132
	public final function getResultText( SMWQueryResult $res, $outputmode ) {
0 ignored issues
show
Coding Style introduced by
getResultText uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
133
		if ( $this->fatalErrorMsg !== false ) {
134
			return $this->fatalErrorMsg;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->fatalErrorMsg; (string|boolean) is incompatible with the return type documented by SMMapPrinter::getResultText of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
135
		}
136
137
		/**
138
		 * @var Parser $wgParser
139
		 */
140
		global $wgParser;
141
142
		if ( $GLOBALS['egMapsEnableCategory'] && $wgParser->getOutput() !== null ) {
143
			$wgParser->addTrackingCategory( 'maps-tracking-category' );
144
		}
145
146
		$params = $this->params;
147
148
		$queryHandler = new SMQueryHandler( $res, $outputmode );
149
		$queryHandler->setLinkStyle($params['link']);
150
		$queryHandler->setHeaderStyle($params['headers']);
151
		$queryHandler->setShowSubject( $params['showtitle'] );
152
		$queryHandler->setTemplate( $params['template'] );
153
		$queryHandler->setUserParam( $params['userparam'] );
154
		$queryHandler->setHideNamespace( $params['hidenamespace'] );
155
		$queryHandler->setActiveIcon( $params['activeicon'] );
156
157
		$this->handleMarkerData( $params, $queryHandler );
158
		$locationAmount = count( $params['locations'] );
159
160
		$params['ajaxquery'] = urlencode( $params['ajaxquery'] );
161
162
		if ( $locationAmount > 0 ) {
163
			// We can only take care of the zoom defaulting here,
164
			// as not all locations are available in whats passed to Validator.
165
			if ( $this->fullParams['zoom']->wasSetToDefault() && $locationAmount > 1 ) {
166
				$params['zoom'] = false;
167
			}
168
169
			$mapName = $this->service->getMapId();
170
171
			SMWOutputs::requireHeadItem(
172
				$mapName,
173
				$this->service->getDependencyHtml() .
174
				$configVars = Skin::makeVariablesScript( $this->service->getConfigVariables() )
175
			);
176
177
			foreach ( $this->service->getResourceModules() as $resourceModule ) {
178
				SMWOutputs::requireResource( $resourceModule );
179
			}
180
181
			if ( array_key_exists( 'source', $params ) ) {
182
				unset( $params['source'] );
183
			}
184
185
			return $this->getMapHTML( $params, $wgParser, $mapName );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getMapHTML..., $wgParser, $mapName); (string) is incompatible with the return type documented by SMMapPrinter::getResultText of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
186
		}
187
		else {
188
			return $params['default'];
189
		}
190
	}
191
192
	/**
193
	 * Returns the HTML to display the map.
194
	 *
195
	 * @since 1.1
196
	 *
197
	 * @param array $params
198
	 * @param Parser $parser
199
	 * @param string $mapName
200
	 *
201
	 * @return string
202
	 */
203
	protected function getMapHTML( array $params, Parser $parser, $mapName ) {
204
		return Html::rawElement(
205
			'div',
206
			[
207
				'id' => $mapName,
208
				'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
209
				'class' => 'maps-map maps-' . $this->service->getName()
210
			],
211
			wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
212
				Html::element(
213
					'div',
214
					[ 'style' => 'display:none', 'class' => 'mapdata' ],
215
					FormatJson::encode( $this->getJSONObject( $params, $parser ) )
216
				)
217
		);
218
	}
219
220
	/**
221
	 * Returns a PHP object to encode to JSON with the map data.
222
	 *
223
	 * @since 1.1
224
	 *
225
	 * @param array $params
226
	 * @param Parser $parser
227
	 *
228
	 * @return mixed
229
	 */
230
	protected function getJSONObject( array $params, Parser $parser ) {
231
		return $params;
232
	}
233
	
234
	/**
235
	 * Converts the data in the coordinates parameter to JSON-ready objects.
236
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
237
	 * 
238
	 * @since 1.0
239
	 * 
240
	 * @param array &$params
241
	 * @param SMQueryHandler $queryHandler
242
	 */
243
	protected function handleMarkerData( array &$params, SMQueryHandler $queryHandler ) {
244
		if ( is_object( $params['centre'] ) ) {
245
			$params['centre'] = $params['centre']->getJSONObject();
246
		}
247
248
		$iconUrl = MapsMapper::getFileUrl( $params['icon'] );
0 ignored issues
show
Deprecated Code introduced by
The method MapsMapper::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
249
		$visitedIconUrl = MapsMapper::getFileUrl( $params['visitedicon'] );
0 ignored issues
show
Deprecated Code introduced by
The method MapsMapper::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
250
251
		$params['locations'] = $this->getJsonForStaticLocations(
252
			$params['staticlocations'],
253
			$params,
254
			$iconUrl,
255
			$visitedIconUrl
256
		);
257
258
		unset( $params['staticlocations'] );
259
260
		$this->addShapeData( $queryHandler->getShapes(), $params, $iconUrl, $visitedIconUrl );
261
262
		if ( $params['format'] === 'openlayers' ) {
263
			$params['layers'] = MapsDisplayMapRenderer::evilOpenLayersHack( $params['layers'] );
0 ignored issues
show
Deprecated Code introduced by
The method MapsDisplayMapRenderer::evilOpenLayersHack() has been deprecated.

This method has been deprecated.

Loading history...
264
		}
265
	}
266
267
	protected function getJsonForStaticLocations( array $staticLocations, array $params, $iconUrl, $visitedIconUrl ) {
0 ignored issues
show
Coding Style introduced by
getJsonForStaticLocations uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
268
		/**
269
		 * @var Parser $wgParser
270
		 */
271
		global $wgParser;
272
273
		$parser = version_compare( $GLOBALS['wgVersion'], '1.18', '<' ) ? $wgParser : clone $wgParser;
274
275
		$locationsJson = [];
276
277
		foreach ( $staticLocations as $location ) {
278
			$locationsJson[] = $this->getJsonForStaticLocation(
279
				$location,
280
				$params,
281
				$iconUrl,
282
				$visitedIconUrl,
283
				$parser
284
			);
285
		}
286
287
		return $locationsJson;
288
	}
289
290
	protected function getJsonForStaticLocation( Location $location, array $params, $iconUrl, $visitedIconUrl, Parser $parser ) {
291
		$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
292
293
		$jsonObj['title'] = $parser->parse( $jsonObj['title'], $parser->getTitle(), new ParserOptions() )->getText();
294
		$jsonObj['text'] = $parser->parse( $jsonObj['text'], $parser->getTitle(), new ParserOptions() )->getText();
295
296
		$hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== '';
297
		$jsonObj['text'] = ( $hasTitleAndtext ? '<b>' . $jsonObj['title'] . '</b><hr />' : $jsonObj['title'] ) . $jsonObj['text'];
298
		$jsonObj['title'] = strip_tags( $jsonObj['title'] );
299
300
		if ( $params['pagelabel'] ) {
301
			$jsonObj['inlineLabel'] = Linker::link( Title::newFromText( $jsonObj['title'] ) );
302
		}
303
304
		return $jsonObj;
305
	}
306
307
	/**
308
	 * @param Element[] $queryShapes
309
	 * @param array $params
310
	 * @param string $iconUrl
311
	 * @param string $visitedIconUrl
312
	 */
313
	protected function addShapeData( array $queryShapes, array &$params, $iconUrl, $visitedIconUrl ) {
314
		$params['locations'] = array_merge(
315
			$params['locations'],
316
			$this->getJsonForLocations(
317
				$queryShapes['locations'],
318
				$params,
319
				$iconUrl,
320
				$visitedIconUrl
321
			)
322
		);
323
324
		$params['lines'] = $this->getElementJsonArray( $queryShapes['lines'], $params );
325
		$params['polygons'] = $this->getElementJsonArray( $queryShapes['polygons'], $params );
326
	}
327
328
	/**
329
	 * @param Location[] $locations
330
	 * @param array $params
331
	 * @param string $iconUrl
332
	 * @param string $visitedIconUrl
333
	 *
334
	 * @return array
335
	 */
336
	protected function getJsonForLocations( array $locations, array $params, $iconUrl, $visitedIconUrl ) {
337
		$locationsJson = [];
338
339
		foreach ( $locations as $location ) {
340
			$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '', $visitedIconUrl );
341
342
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
343
344
			$locationsJson[] = $jsonObj;
345
		}
346
347
		return $locationsJson;
348
	}
349
350
	/**
351
	 * @param BaseElement[] $elements
352
	 * @param array $params
353
	 *
354
	 * @return array
355
	 */
356
	protected function getElementJsonArray( array $elements, array $params ) {
357
		$elementsJson = [];
358
359
		foreach ( $elements as $element ) {
360
			$jsonObj = $element->getJSONObject( $params['title'], $params['label'] );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\Elements\BaseElement::getJSONObject() has been deprecated.

This method has been deprecated.

Loading history...
361
			$elementsJson[] = $jsonObj;
362
		}
363
364
		return $elementsJson;
365
	}
366
367
	/**
368
	 * Returns the internationalized name of the mapping service.
369
	 * 
370
	 * @return string
371
	 */
372
	public final function getName() {
373
		return wfMessage( 'maps_' . $this->service->getName() )->text();
374
	}
375
	
376
	/**
377
	 * Returns a list of parameter information, for usage by Special:Ask and others.
378
	 * 
379
	 * @return array
380
	 */
381
    public function getParameters() {
382
        $params = parent::getParameters();
0 ignored issues
show
Deprecated Code introduced by
The method SMW\ResultPrinter::getParameters() has been deprecated with message: since 1.8, use getParamDefinitions instead.

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...
383
        $paramInfo = $this->getParameterInfo();
384
        
385
        // Do not display this as an option, as the format already determines it
386
        // TODO: this can probably be done cleaner with some changes in Maps
387
        unset( $paramInfo['mappingservice'] );
388
        
389
        $params = array_merge( $params, $paramInfo );
390
391
		return $params;
392
    }
393
}
394