Completed
Push — newparam ( 72433c...c5fad3 )
by Jeroen De
01:21
created

QueryHandler   F

Complexity

Total Complexity 82

Size/Duplication

Total Lines 490
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 60.1%

Importance

Changes 0
Metric Value
wmc 82
lcom 1
cbo 4
dl 0
loc 490
ccs 116
cts 193
cp 0.601
rs 2
c 0
b 0
f 0

33 Methods

Rating   Name   Duplication   Size   Complexity  
A setIcon() 0 3 1
A setTitle() 0 3 1
A setText() 0 3 1
A setSubjectSeparator() 0 3 1
A __construct() 0 5 1
A setTemplate() 0 3 2
A setUserParam() 0 3 1
A setShowSubject() 0 3 1
A setLinkStyle() 0 3 1
A setHeaderStyle() 0 3 1
A getLocations() 0 5 2
A handlePageResult() 0 18 3
A getTitleAndText() 0 19 4
B getLocationsAndProperties() 0 32 8
A locationFromDataItem() 0 6 1
B getResultSubjectText() 0 27 7
A showArticleLink() 0 3 1
A handleResultProperty() 0 12 4
A getPropertyName() 0 22 5
A getPropertyLinker() 0 3 3
A getValueLinker() 0 3 2
A getPropertyValue() 0 22 3
A hasPage() 0 10 3
A hasTemplate() 0 3 1
B getLocationIcon() 0 38 9
A shouldGetActiveIconUrlFor() 0 6 3
A buildLocationsList() 0 32 4
A getTitleOutput() 0 7 3
A getParser() 0 3 1
A getHideNamespace() 0 3 1
A setHideNamespace() 0 3 1
A getActiveIcon() 0 3 1
A setActiveIcon() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like QueryHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Maps\SemanticMW\ResultPrinters;
4
5
use Html;
6
use Linker;
7
use Maps\Elements\Location;
8
use Maps\MapsFunctions;
9
use Maps\SemanticMW\DataValues\CoordinateValue;
10
use SMWDataValue;
11
use SMWDIGeoCoord;
12
use SMWPrintRequest;
13
use SMWQueryResult;
14
use SMWResultArray;
15
use SMWWikiPageValue;
16
use Title;
17
18
/**
19
 * @licence GNU GPL v2+
20
 * @author Jeroen De Dauw < [email protected] >
21
 */
22
class QueryHandler {
23
24
	/**
25
	 * The global icon.
26
	 * @var string
27
	 */
28
	public $icon = '';
29
30
	/**
31
	 * The global text.
32
	 * @var string
33
	 */
34
	public $text = '';
35
36
	/**
37
	 * The global title.
38
	 * @var string
39
	 */
40
	public $title = '';
41
42
	private $queryResult;
43
44
	private $outputMode;
45
46
	/**
47
	 * The template to use for the text, or false if there is none.
48
	 * @var string|boolean false
49
	 */
50
	private $template = false;
51
52
	/**
53
	 * Should link targets be made absolute (instead of relative)?
54
	 * @var boolean
55
	 */
56
	private $linkAbsolute;
57
58
	/**
59
	 * A separator to use between the subject and properties in the text field.
60
	 * @var string
61
	 */
62
	private $subjectSeparator = '<hr />';
63
64
	/**
65
	 * Show the subject in the text or not?
66
	 * @var boolean
67
	 */
68
	private $showSubject = true;
69
70
	/**
71
	 * Hide the namespace or not.
72
	 * @var boolean
73
	 */
74
	private $hideNamespace = false;
75
76
	/**
77
	 * Defines which article names in the result are hyperlinked, all normally is the default
78
	 * none, subject, all
79
	 */
80
	private $linkStyle = 'all';
81
82
	/*
83
	 * Show headers (with links), show headers (just text) or hide them. show is default
84
	 * show, plain, hide
85
	 */
86
	private $headerStyle = 'show';
87
88
	/**
89
	 * Marker icon to show when marker equals active page
90
	 * @var string|null
91
	 */
92
	private $activeIcon = null;
93
94
	/**
95
	 * @var string
96
	 */
97
	private $userParam = '';
98
99 1
	public function __construct( SMWQueryResult $queryResult, int $outputMode, bool $linkAbsolute = false ) {
100 1
		$this->queryResult = $queryResult;
101 1
		$this->outputMode = $outputMode;
102 1
		$this->linkAbsolute = $linkAbsolute;
103 1
	}
104
105 1
	public function setTemplate( string $template ) {
106 1
		$this->template = $template === '' ? false : $template;
107 1
	}
108
109 1
	public function setUserParam( string $userParam ) {
110 1
		$this->userParam = $userParam;
111 1
	}
112
113
	/**
114
	 * Sets the global icon.
115
	 */
116
	public function setIcon( string $icon ) {
117
		$this->icon = $icon;
118
	}
119
120
	/**
121
	 * Sets the global title.
122
	 */
123
	public function setTitle( string $title ) {
124
		$this->title = $title;
125
	}
126
127
	/**
128
	 * Sets the global text.
129
	 */
130
	public function setText( string $text ) {
131
		$this->text = $text;
132
	}
133
134
	public function setSubjectSeparator( string $subjectSeparator ) {
135
		$this->subjectSeparator = $subjectSeparator;
136
	}
137
138 1
	public function setShowSubject( bool $showSubject ) {
139 1
		$this->showSubject = $showSubject;
140 1
	}
141
142 1
	public function setLinkStyle( string $link ) {
143 1
		$this->linkStyle = $link;
144 1
	}
145
146 1
	public function setHeaderStyle( string $headers ) {
147 1
		$this->headerStyle = $headers;
148 1
	}
149
150
	/**
151
	 * @return Location[]
152
	 */
153 1
	public function getLocations(): iterable {
154 1
		while ( ( $row = $this->queryResult->getNext() ) !== false ) {
155 1
			yield from $this->handlePageResult( $row );
156
		}
157 1
	}
158
159
	/**
160
	 * @param SMWResultArray[] $row
161
	 * @return Location[]
162
	 */
163 1
	private function handlePageResult( array $row ): array {
164 1
		[ $title, $text ] = $this->getTitleAndText( $row[0] );
0 ignored issues
show
Bug introduced by
The variable $title does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $text does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
165 1
		[ $locations, $properties ] = $this->getLocationsAndProperties( $row );
0 ignored issues
show
Bug introduced by
The variable $locations does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $properties does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
166
167 1
		if ( $properties !== [] && $text !== '' ) {
168 1
			$text .= $this->subjectSeparator;
169
		}
170
171 1
		$icon = $this->getLocationIcon( $row );
172
173 1
		return $this->buildLocationsList(
174 1
			$locations,
175
			$text,
176
			$icon,
177
			$properties,
178 1
			Title::newFromText( $title )
179
		);
180
	}
181
182 1
	private function getTitleAndText( SMWResultArray $resultArray ): array {
183 1
		while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {
184 1
			if ( $dataValue instanceof SMWWikiPageValue ) {
0 ignored issues
show
Bug introduced by
The class SMWWikiPageValue does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
185
				return [
186 1
					$dataValue->getLongText( $this->outputMode, null ),
187 1
					$this->getResultSubjectText( $dataValue )
188
				];
189
			}
190
191
			if ( $dataValue->getTypeID() == '_str' ) {
192
				return [
193
					$dataValue->getLongText( $this->outputMode, null ),
194
					$dataValue->getLongText( $this->outputMode, smwfGetLinker() )
195
				];
196
			}
197
		}
198
199
		return [ '', '' ];
200
	}
201
202
	/**
203
	 * @param SMWResultArray[] $row
204
	 * @return array
205
	 */
206 1
	private function getLocationsAndProperties( array $row ): array {
207 1
		$locations = [];
208 1
		$properties = [];
209
210
		// Loop through all fields of the record.
211 1
		foreach ( $row as $i => $resultArray ) {
212 1
			if ( $i === 0 ) {
213 1
				continue;
214
			}
215
216
			// Loop through all the parts of the field value.
217 1
			while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {
218 1
				if ( $dataValue instanceof \SMWRecordValue ) {
0 ignored issues
show
Bug introduced by
The class SMWRecordValue 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...
219
					foreach ( $dataValue->getDataItems() as $dataItem ) {
220
						if ( $dataItem instanceof \SMWDIGeoCoord ) {
0 ignored issues
show
Bug introduced by
The class SMWDIGeoCoord does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
221
							$locations[] = $this->locationFromDataItem( $dataItem );
222
						}
223
					}
224 1
				} elseif ( $dataValue instanceof CoordinateValue ) {
225 1
					$locations[] = $this->locationFromDataItem( $dataValue->getDataItem() );
226
				}
227
				else {
228 1
					$properties[] = $this->handleResultProperty(
229 1
						$dataValue,
230 1
						$resultArray->getPrintRequest()
231
					);
232
				}
233
			}
234
		}
235
236 1
		return [ $locations, $properties ];
237
	}
238
239 1
	private function locationFromDataItem( SMWDIGeoCoord $dataItem ): Location {
240 1
		return Location::newFromLatLon(
241 1
			$dataItem->getLatitude(),
242 1
			$dataItem->getLongitude()
243
		);
244
	}
245
246
	/**
247
	 * Handles a SMWWikiPageValue subject value.
248
	 * Gets the plain text title and creates the HTML text with headers and the like.
249
	 *
250
	 * @param SMWWikiPageValue $object
251
	 *
252
	 * @return string
253
	 */
254 1
	private function getResultSubjectText( SMWWikiPageValue $object ): string {
255 1
		if ( !$this->showSubject ) {
256
			return '';
257
		}
258
		
259 1
		$dataItem = $object->getDataItem();
260
		
261 1
		if ( $this->showArticleLink() ) {
262 1
			if ( $this->linkAbsolute ) {
263
				$text = Html::element(
264
					'a',
265
					[ 'href' => $dataItem->getTitle()->getFullUrl() ],
266
					$this->hideNamespace ? $object->getText() : $dataItem->getTitle()->getFullText()
267
				);
268
			} else {
269 1
				if ( $this->hideNamespace ) {
270
					$text = $object->getShortHTMLText( smwfGetLinker() );
271
				} else {
272 1
					$text = $object->getLongHTMLText( smwfGetLinker() );
273
				}
274
			}
275
		} else {
276
			$text = $this->hideNamespace ? $object->getText() : $dataItem->getTitle()->getFullText();
277
		}
278
279 1
		return '<b>' . $text . '</b>';
280
	}
281
282 1
	private function showArticleLink() {
283 1
		return $this->linkStyle !== 'none';
284
	}
285
286
	/**
287
	 * Handles a single property (SMWPrintRequest) to be displayed for a record (SMWDataValue).
288
	 */
289 1
	private function handleResultProperty( SMWDataValue $object, SMWPrintRequest $printRequest ): string {
290 1
		if ( $this->hasTemplate() ) {
291
			if ( $object instanceof SMWWikiPageValue ) {
0 ignored issues
show
Bug introduced by
The class SMWWikiPageValue does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
292
				return $object->getDataItem()->getTitle()->getPrefixedText();
293
			}
294
295
			return $object->getLongText( SMW_OUTPUT_WIKI, null );
296
		}
297
298 1
		$propertyName = $this->getPropertyName( $printRequest );
299 1
		return $propertyName . ( $propertyName === '' ? '' : ': ' ) . $this->getPropertyValue( $object );
300
	}
301
302 1
	private function getPropertyName( SMWPrintRequest $printRequest ): string {
303 1
		if ( $this->headerStyle === 'hide' ) {
304
			return '';
305
		}
306
307 1
		if ( $this->linkAbsolute ) {
308
			$titleText = $printRequest->getText( null );
309
			$t = Title::newFromText( $titleText, SMW_NS_PROPERTY );
310
311
			if ( $t instanceof Title && $t->exists() ) {
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
312
				return  Html::element(
313
					'a',
314
					[ 'href' => $t->getFullUrl() ],
315
					$printRequest->getHTMLText( null )
316
				);
317
			}
318
319
			return $titleText;
320
		}
321
322 1
		return $printRequest->getHTMLText( $this->getPropertyLinker() );
323
	}
324
325 1
	private function getPropertyLinker(): ?Linker {
326 1
		return $this->headerStyle === 'show' && $this->linkStyle !== 'none' ? smwfGetLinker() : null;
327
	}
328
329 1
	private function getValueLinker(): ?Linker {
330 1
		return $this->linkStyle === 'all' ? smwfGetLinker() : null;
331
	}
332
333 1
	private function getPropertyValue( SMWDataValue $object ): string {
334 1
		if ( !$this->linkAbsolute ) {
335 1
			return $object->getLongHTMLText(
336 1
				$this->getValueLinker()
337
			);
338
		}
339
340
		if ( $this->hasPage( $object ) ) {
341
			return Html::element(
342
				'a',
343
				[
344
					'href' => Title::newFromText(
345
						$object->getLongText( $this->outputMode, null ),
346
						NS_MAIN
347
					)->getFullUrl()
348
				],
349
				$object->getLongText( $this->outputMode, null )
350
			);
351
		}
352
353
		return $object->getLongText( $this->outputMode, null );
354
	}
355
356
	private function hasPage( SMWDataValue $object ): bool {
357
		$hasPage = $object->getTypeID() == '_wpg';
358
359
		if ( $hasPage ) {
360
			$t = Title::newFromText( $object->getLongText( $this->outputMode, null ), NS_MAIN );
361
			$hasPage = $t !== null && $t->exists();
362
		}
363
364
		return $hasPage;
365
	}
366
367 1
	private function hasTemplate() {
368 1
		return is_string( $this->template );
369
	}
370
371
	/**
372
	 * Get the icon for a row.
373
	 *
374
	 * @param array $row
375
	 *
376
	 * @return string
377
	 */
378 1
	private function getLocationIcon( array $row ) {
379 1
		$icon = '';
380 1
		$legendLabels = [];
381
382
		//Check for activeicon parameter
383
384 1
		if ( $this->shouldGetActiveIconUrlFor( $row[0]->getResultSubject()->getTitle() ) ) {
385
			$icon = MapsFunctions::getFileUrl( $this->activeIcon );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MapsFunctions::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
386
		}
387
388
		// Look for display_options field, which can be set by Semantic Compound Queries
389
		// the location of this field changed in SMW 1.5
390 1
		$display_location = method_exists( $row[0], 'getResultSubject' ) ? $row[0]->getResultSubject() : $row[0];
391
392 1
		if ( property_exists( $display_location, 'display_options' ) && is_array(
393 1
				$display_location->display_options
394
			) ) {
395
			$display_options = $display_location->display_options;
396
			if ( array_key_exists( 'icon', $display_options ) ) {
397
				$icon = $display_options['icon'];
398
399
				// This is somewhat of a hack - if a legend label has been set, we're getting it for every point, instead of just once per icon
400
				if ( array_key_exists( 'legend label', $display_options ) ) {
401
402
					$legend_label = $display_options['legend label'];
403
404
					if ( !array_key_exists( $icon, $legendLabels ) ) {
405
						$legendLabels[$icon] = $legend_label;
406
					}
407
				}
408
			}
409
		} // Icon can be set even for regular, non-compound queries If it is, though, we have to translate the name into a URL here
410 1
		elseif ( $this->icon !== '' ) {
411
			$icon = MapsFunctions::getFileUrl( $this->icon );
0 ignored issues
show
Deprecated Code introduced by
The method Maps\MapsFunctions::getFileUrl() has been deprecated.

This method has been deprecated.

Loading history...
412
		}
413
414 1
		return $icon;
415
	}
416
417 1
	private function shouldGetActiveIconUrlFor( Title $title ) {
418 1
		global $wgTitle;
419
420 1
		return isset( $this->activeIcon ) && is_object( $wgTitle )
421 1
			&& $wgTitle->equals( $title );
422
	}
423
424
	/**
425
	 * Builds a set of locations with the provided title, text and icon.
426
	 *
427
	 * @param Location[] $locations
428
	 * @param string $text
429
	 * @param string $icon
430
	 * @param array $properties
431
	 * @param Title|null $title
432
	 *
433
	 * @return Location[]
434
	 */
435 1
	private function buildLocationsList( array $locations, $text, $icon, array $properties, Title $title = null ): array {
436 1
		if ( !$this->hasTemplate() ) {
437 1
			$text .= implode( '<br />', $properties );
438
		}
439
440 1
		$titleOutput = $this->getTitleOutput( $title );
441
442 1
		foreach ( $locations as &$location ) {
443 1
			if ( $this->hasTemplate() ) {
444
				$segments = array_merge(
445
					[
446
						$this->template,
447
						'title=' . $titleOutput,
448
						'latitude=' . $location->getCoordinates()->getLatitude(),
449
						'longitude=' . $location->getCoordinates()->getLongitude(),
450
						'userparam=' . $this->userParam
451
					],
452
					$properties
453
				);
454
455
				$text .= $this->getParser()->recursiveTagParseFully(
456
					'{{' . implode( '|', $segments ) . '}}'
457
				);
458
			}
459
460 1
			$location->setTitle( $titleOutput );
461 1
			$location->setText( $text );
462 1
			$location->setIcon( trim( $icon ) );
463
		}
464
465 1
		return $locations;
466
	}
467
468 1
	private function getTitleOutput( Title $title = null ) {
469 1
		if ( $title === null ) {
470
			return '';
471
		}
472
473 1
		return $this->hideNamespace ? $title->getText() : $title->getFullText();
474
	}
475
476
	/**
477
	 * @return \Parser
478
	 */
479
	private function getParser() {
480
		return $GLOBALS['wgParser'];
481
	}
482
483
	/**
484
	 * @return boolean
485
	 */
486
	public function getHideNamespace() {
487
		return $this->hideNamespace;
488
	}
489
490
	/**
491
	 * @param boolean $hideNamespace
492
	 */
493 1
	public function setHideNamespace( $hideNamespace ) {
494 1
		$this->hideNamespace = $hideNamespace;
495 1
	}
496
497
	/**
498
	 * @return string
499
	 */
500
	public function getActiveIcon() {
501
		return $this->activeIcon;
502
	}
503
504
	/**
505
	 * @param string $activeIcon
506
	 */
507 1
	public function setActiveIcon( $activeIcon ) {
508 1
		$this->activeIcon = $activeIcon;
509 1
	}
510
511
}
512