Completed
Push — rmpagelinktext ( 197ff5 )
by Jeroen De
03:28
created

QueryHandler   F

Complexity

Total Complexity 82

Size/Duplication

Total Lines 488
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 82
lcom 1
cbo 4
dl 0
loc 488
ccs 0
cts 272
cp 0
rs 2
c 0
b 0
f 0

33 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A setTemplate() 0 3 2
A setUserParam() 0 3 1
A setIcon() 0 3 1
A setTitle() 0 3 1
A setText() 0 3 1
A setSubjectSeparator() 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 25 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
	public function __construct( SMWQueryResult $queryResult, int $outputMode, bool $linkAbsolute = false ) {
100
		$this->queryResult = $queryResult;
101
		$this->outputMode = $outputMode;
102
		$this->linkAbsolute = $linkAbsolute;
103
	}
104
105
	public function setTemplate( string $template ) {
106
		$this->template = $template === '' ? false : $template;
107
	}
108
109
	public function setUserParam( string $userParam ) {
110
		$this->userParam = $userParam;
111
	}
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
	public function setShowSubject( bool $showSubject ) {
139
		$this->showSubject = $showSubject;
140
	}
141
142
	public function setLinkStyle( string $link ) {
143
		$this->linkStyle = $link;
144
	}
145
146
	public function setHeaderStyle( string $headers ) {
147
		$this->headerStyle = $headers;
148
	}
149
150
	/**
151
	 * @return Location[]
152
	 */
153
	public function getLocations(): iterable {
154
		while ( ( $row = $this->queryResult->getNext() ) !== false ) {
155
			yield from $this->handlePageResult( $row );
156
		}
157
	}
158
159
	/**
160
	 * @param SMWResultArray[] $row
161
	 * @return Location[]
162
	 */
163
	private function handlePageResult( array $row ): array {
164
		[ $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
		[ $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
		if ( $properties !== [] && $text !== '' ) {
168
			$text .= $this->subjectSeparator;
169
		}
170
171
		$icon = $this->getLocationIcon( $row );
172
173
		return $this->buildLocationsList(
174
			$locations,
175
			$text,
176
			$icon,
177
			$properties,
178
			Title::newFromText( $title )
179
		);
180
	}
181
182
	private function getTitleAndText( SMWResultArray $resultArray ): array {
183
		while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {
184
			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
					$dataValue->getLongText( $this->outputMode, null ),
187
					$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
	private function getLocationsAndProperties( array $row ): array {
207
		$locations = [];
208
		$properties = [];
209
210
		// Loop through all fields of the record.
211
		foreach ( $row as $i => $resultArray ) {
212
			if ( $i === 0 ) {
213
				continue;
214
			}
215
216
			// Loop through all the parts of the field value.
217
			while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {
218
				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
				} elseif ( $dataValue instanceof CoordinateValue ) {
225
					$locations[] = $this->locationFromDataItem( $dataValue->getDataItem() );
226
				}
227
				else {
228
					$properties[] = $this->handleResultProperty(
229
						$dataValue,
230
						$resultArray->getPrintRequest()
231
					);
232
				}
233
			}
234
		}
235
236
		return [ $locations, $properties ];
237
	}
238
239
	private function locationFromDataItem( SMWDIGeoCoord $dataItem ): Location {
240
		return Location::newFromLatLon(
241
			$dataItem->getLatitude(),
242
			$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
	private function getResultSubjectText( SMWWikiPageValue $object ): string {
255
		if ( !$this->showSubject ) {
256
			return '';
257
		}
258
259
		if ( $this->showArticleLink() ) {
260
			if ( $this->linkAbsolute ) {
261
				$text = Html::element(
262
					'a',
263
					[ 'href' => $object->getTitle()->getFullUrl() ],
264
					$this->hideNamespace ? $object->getText() : $object->getTitle()->getFullText()
265
				);
266
			} else {
267
				if ( $this->hideNamespace ) {
268
					$text = $object->getShortHTMLText( smwfGetLinker() );
269
				} else {
270
					$text = $object->getLongHTMLText( smwfGetLinker() );
271
				}
272
			}
273
		} else {
274
			$text = $this->hideNamespace ? $object->getText() : $object->getTitle()->getFullText();
275
		}
276
277
		return '<b>' . $text . '</b>';
278
	}
279
280
	private function showArticleLink() {
281
		return $this->linkStyle !== 'none';
282
	}
283
284
	/**
285
	 * Handles a single property (SMWPrintRequest) to be displayed for a record (SMWDataValue).
286
	 */
287
	private function handleResultProperty( SMWDataValue $object, SMWPrintRequest $printRequest ): string {
288
		if ( $this->hasTemplate() ) {
289
			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...
290
				return $object->getTitle()->getPrefixedText();
291
			}
292
293
			return $object->getLongText( SMW_OUTPUT_WIKI, null );
294
		}
295
296
		$propertyName = $this->getPropertyName( $printRequest );
297
		return $propertyName . ( $propertyName === '' ? '' : ': ' ) . $this->getPropertyValue( $object );
298
	}
299
300
	private function getPropertyName( SMWPrintRequest $printRequest ): string {
301
		if ( $this->headerStyle === 'hide' ) {
302
			return '';
303
		}
304
305
		if ( $this->linkAbsolute ) {
306
			$titleText = $printRequest->getText( null );
307
			$t = Title::newFromText( $titleText, SMW_NS_PROPERTY );
308
309
			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...
310
				return  Html::element(
311
					'a',
312
					[ 'href' => $t->getFullUrl() ],
313
					$printRequest->getHTMLText( null )
314
				);
315
			}
316
317
			return $titleText;
318
		}
319
320
		return $printRequest->getHTMLText( $this->getPropertyLinker() );
321
	}
322
323
	private function getPropertyLinker(): ?Linker {
324
		return $this->headerStyle === 'show' && $this->linkStyle !== 'none' ? smwfGetLinker() : null;
325
	}
326
327
	private function getValueLinker(): ?Linker {
328
		return $this->linkStyle === 'all' ? smwfGetLinker() : null;
329
	}
330
331
	private function getPropertyValue( SMWDataValue $object ): string {
332
		if ( !$this->linkAbsolute ) {
333
			return $object->getLongHTMLText(
334
				$this->getValueLinker()
335
			);
336
		}
337
338
		if ( $this->hasPage( $object ) ) {
339
			return Html::element(
340
				'a',
341
				[
342
					'href' => Title::newFromText(
343
						$object->getLongText( $this->outputMode, null ),
344
						NS_MAIN
345
					)->getFullUrl()
346
				],
347
				$object->getLongText( $this->outputMode, null )
348
			);
349
		}
350
351
		return $object->getLongText( $this->outputMode, null );
352
	}
353
354
	private function hasPage( SMWDataValue $object ): bool {
355
		$hasPage = $object->getTypeID() == '_wpg';
356
357
		if ( $hasPage ) {
358
			$t = Title::newFromText( $object->getLongText( $this->outputMode, null ), NS_MAIN );
359
			$hasPage = $t !== null && $t->exists();
360
		}
361
362
		return $hasPage;
363
	}
364
365
	private function hasTemplate() {
366
		return is_string( $this->template );
367
	}
368
369
	/**
370
	 * Get the icon for a row.
371
	 *
372
	 * @param array $row
373
	 *
374
	 * @return string
375
	 */
376
	private function getLocationIcon( array $row ) {
377
		$icon = '';
378
		$legendLabels = [];
379
380
		//Check for activeicon parameter
381
382
		if ( $this->shouldGetActiveIconUrlFor( $row[0]->getResultSubject()->getTitle() ) ) {
383
			$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...
384
		}
385
386
		// Look for display_options field, which can be set by Semantic Compound Queries
387
		// the location of this field changed in SMW 1.5
388
		$display_location = method_exists( $row[0], 'getResultSubject' ) ? $row[0]->getResultSubject() : $row[0];
389
390
		if ( property_exists( $display_location, 'display_options' ) && is_array(
391
				$display_location->display_options
392
			) ) {
393
			$display_options = $display_location->display_options;
394
			if ( array_key_exists( 'icon', $display_options ) ) {
395
				$icon = $display_options['icon'];
396
397
				// 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
398
				if ( array_key_exists( 'legend label', $display_options ) ) {
399
400
					$legend_label = $display_options['legend label'];
401
402
					if ( !array_key_exists( $icon, $legendLabels ) ) {
403
						$legendLabels[$icon] = $legend_label;
404
					}
405
				}
406
			}
407
		} // Icon can be set even for regular, non-compound queries If it is, though, we have to translate the name into a URL here
408
		elseif ( $this->icon !== '' ) {
409
			$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...
410
		}
411
412
		return $icon;
413
	}
414
415
	private function shouldGetActiveIconUrlFor( Title $title ) {
416
		global $wgTitle;
417
418
		return isset( $this->activeIcon ) && is_object( $wgTitle )
419
			&& $wgTitle->equals( $title );
420
	}
421
422
	/**
423
	 * Builds a set of locations with the provided title, text and icon.
424
	 *
425
	 * @param Location[] $locations
426
	 * @param string $text
427
	 * @param string $icon
428
	 * @param array $properties
429
	 * @param Title|null $title
430
	 *
431
	 * @return Location[]
432
	 */
433
	private function buildLocationsList( array $locations, $text, $icon, array $properties, Title $title = null ): array {
434
		if ( !$this->hasTemplate() ) {
435
			$text .= implode( '<br />', $properties );
436
		}
437
438
		$titleOutput = $this->getTitleOutput( $title );
439
440
		foreach ( $locations as &$location ) {
441
			if ( $this->hasTemplate() ) {
442
				$segments = array_merge(
443
					[
444
						$this->template,
445
						'title=' . $titleOutput,
446
						'latitude=' . $location->getCoordinates()->getLatitude(),
447
						'longitude=' . $location->getCoordinates()->getLongitude(),
448
						'userparam=' . $this->userParam
449
					],
450
					$properties
451
				);
452
453
				$text .= $this->getParser()->recursiveTagParseFully(
454
					'{{' . implode( '|', $segments ) . '}}'
455
				);
456
			}
457
458
			$location->setTitle( $titleOutput );
459
			$location->setText( $text );
460
			$location->setIcon( trim( $icon ) );
461
		}
462
463
		return $locations;
464
	}
465
466
	private function getTitleOutput( Title $title = null ) {
467
		if ( $title === null ) {
468
			return '';
469
		}
470
471
		return $this->hideNamespace ? $title->getText() : $title->getFullText();
472
	}
473
474
	/**
475
	 * @return \Parser
476
	 */
477
	private function getParser() {
478
		return $GLOBALS['wgParser'];
479
	}
480
481
	/**
482
	 * @return boolean
483
	 */
484
	public function getHideNamespace() {
485
		return $this->hideNamespace;
486
	}
487
488
	/**
489
	 * @param boolean $hideNamespace
490
	 */
491
	public function setHideNamespace( $hideNamespace ) {
492
		$this->hideNamespace = $hideNamespace;
493
	}
494
495
	/**
496
	 * @return string
497
	 */
498
	public function getActiveIcon() {
499
		return $this->activeIcon;
500
	}
501
502
	/**
503
	 * @param string $activeIcon
504
	 */
505
	public function setActiveIcon( $activeIcon ) {
506
		$this->activeIcon = $activeIcon;
507
	}
508
509
}
510