Completed
Push — rmdf ( 0cf718 )
by Jeroen De
108:37 queued 106:23
created

TreeResultPrinter::addError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
namespace SRF\Formats\Tree;
4
5
/**
6
 * File holding the Tree class.
7
 * @author Stephan Gambke
8
 */
9
use \Exception;
10
use Html;
11
use SMW\DIProperty;
12
use SMW\DIWikiPage;
13
use SMW\ListResultPrinter;
14
use SMWQueryResult;
15
use Title;
16
17
/**
18
 * Result printer that prints query results as a tree (nested html lists).
19
 *
20
 * The available formats are 'tree', 'ultree', 'oltree'. 'tree' is an alias of
21
 * 'ultree'. In an #ask query the parameter 'parent' must be set to contain the
22
 * name of the property, that gives the parent page of the subject page.
23
 *
24
 */
25
class TreeResultPrinter extends ListResultPrinter {
26
27
	private $standardTemplateParameters;
28
29
	/**
30
	 * @var SMWQueryResult | null
31
	 */
32
	private $queryResult = null;
33
34
	/**
35
	 * (non-PHPdoc)
36
	 * @see SMWResultPrinter::getName()
37
	 */
38
	public function getName() {
39
		// Give grep a chance to find the usages:
40
		// srf-printername-tree, srf-printername-ultree, srf-printername-oltree
41
		return \Message::newFromKey( 'srf-printername-' . $this->mFormat )->text();
42
	}
43
44
	/**
45
	 * @return SMWQueryResult
46
	 * @throws Exception
47
	 */
48 5
	public function getQueryResult() {
49
50 5
		if ( $this->queryResult === null ) {
51
			throw new Exception( __METHOD__ . ' called outside of ' . __CLASS__ . '::getResultText().');
52
		}
53
54 5
		return $this->queryResult;
55
	}
56
57
	/**
58
	 * @param SMWQueryResult | null $queryResult
59
	 */
60 7
	public function setQueryResult( $queryResult ) {
61 7
		$this->queryResult = $queryResult;
62 7
	}
63
64
	/**
65
	 * @see ResultPrinter::postProcessParameters()
66
	 */
67 8
	protected function postProcessParameters() {
68
69 8
		parent::postProcessParameters();
70
71
		// Don't support pagination in trees
72 8
		$this->mSearchlabel = null;
73
74 8
		if ( array_key_exists( 'template arguments', $this->params )
75 8
			&& $this->params['template arguments'] !== 'numbered' ) {
76
77 8
			if ( filter_var( $this->params[ 'named args' ], FILTER_VALIDATE_BOOLEAN ) === true ) {
78
				$this->params['template arguments'] = 'legacy';
79
			} elseif (
80 8
				$this->params['template arguments'] !== 'named' &&
81 8
				$this->params['template arguments'] !== 'legacy'
82
			) {
83
				// default
84 8
				$this->params['template arguments'] = 'numbered';
85
			}
86
		}
87
88
		// Allow "_" for encoding spaces, as documented
89 8
		$this->params[ 'sep' ] = str_replace( '_', ' ', $this->params[ 'sep' ] );
90
91 8
		if ( !ctype_digit( strval( $this->params[ 'start level' ] ) ) || $this->params[ 'start level' ] < 1 ) {
92
			$this->params[ 'start level' ] = 1;
93
		}
94
95 8
	}
96
97
	/**
98
	 * Return serialised results in specified format.
99
	 * @param SMWQueryResult $queryResult
100
	 * @param $outputmode
101
	 * @return string
102
	 */
103 7
	protected function getResultText( SMWQueryResult $queryResult, $outputmode ) {
104
105 7
		$this->setQueryResult( $queryResult );
106
107 7
		if ( $this->params[ 'parent' ] === '' ) {
108 2
			$this->addError( 'srf-tree-noparentprop' );
109 2
			return '';
110
		}
111
112 5
		$rootHash = $this->getRootHash();
113
114 5
		if ( $rootHash === false ) {
115
			$this->addError( 'srf-tree-rootinvalid', $this->params[ 'root' ] );
116
			return '';
117
		}
118
119 5
		$this->hasTemplates =
120 5
			$this->params[ 'introtemplate' ] !== '' ||
121 5
			$this->params[ 'outrotemplate' ] !== '' ||
122 5
			$this->params[ 'template' ] !== '';
123
124 5
		if ( $this->hasTemplates ) {
125 3
			$this->initalizeStandardTemplateParameters();
126
		}
127
128 5
		$tree = $this->buildTreeFromQueryResult( $rootHash );
129 5
		$lines = $this->buildLinesFromTree( $tree );
130
131
		// Display default if the result is empty
132 5
		if ( count( $lines ) === 0 ) {
133
			return $this->params[ 'default' ];
134
		}
135
136
		// FIXME: Linking to further events ($this->linkFurtherResults())
137
		// does not make sense for tree format. But maybe display a warning?
138
139 5
		$resultText = join( "\n", array_merge(
140 5
			[ $this->getTemplateCall( $this->params[ 'introtemplate' ] ) ],
141 5
			$lines,
142 5
			[ $this->getTemplateCall( $this->params[ 'outrotemplate' ] ) ]
143
		) );
144
145 5
		$this->setQueryResult( null );
146
147 5
		return Html::rawElement( 'div', [ 'class' => 'srf-tree' ], $resultText );
148
	}
149
150
	/**
151
	 * @param string $templateName
152
	 * @param string[] $params
153
	 * @return string
154
	 */
155 5
	public function getTemplateCall( $templateName, $params = [] ) {
156
157 5
		if ( $templateName === '' ) {
158 5
			return '';
159
		}
160
161 3
		return '{{' . $templateName . '|' . join( '|', $params ) . $this->standardTemplateParameters . '}}';
162
	}
163
164
	/**
165
	 * @see SMWResultPrinter::getParamDefinitions
166
	 *
167
	 * @since 1.8
168
	 *
169
	 * @param $definitions array of IParamDefinition
170
	 *
171
	 * @return array of IParamDefinition|array
172
	 */
173 8
	public function getParamDefinitions( array $definitions ) {
174 8
		$params = parent::getParamDefinitions( $definitions );
175
176 8
		$params[ 'parent' ] = [
177
			'default' => '',
178
			'message' => 'srf-paramdesc-tree-parent',
179
		];
180
181 8
		$params[ 'root' ] = [
182
			'default' => '',
183
			'message' => 'srf-paramdesc-tree-root',
184
		];
185
186 8
		$params[ 'start level' ] = [
187
			'default' => 1,
188
			'message' => 'srf-paramdesc-tree-startlevel',
189
			'type' => 'integer',
190
		];
191
192 8
		$params[ 'sep' ] = [
193
			'default' => ', ',
194
			'message' => 'smw-paramdesc-sep',
195
		];
196
197 8
		$params[ 'template arguments' ] = [
198
			'default' => '',
199
			'message' => 'smw-paramdesc-template-arguments',
200
		];
201
202 8
		return $params;
203
	}
204
205
	/**
206
	 * @param string $rootHash
207
	 * @return TreeNode
208
	 */
209 5
	protected function buildTreeFromQueryResult( $rootHash ) {
210
211 5
		$nodes = $this->getHashOfNodes();
212
213 5
		if ( $rootHash !== '' && !array_key_exists( $rootHash, $nodes ) ) {
214
			return new TreeNode();
215
		}
216
217 5
		return $this->buildTreeFromNodeList( $rootHash, $nodes );
218
	}
219
220
	/**
221
	 * @return string | false
222
	 */
223 5
	protected function getRootHash() {
224
225 5
		if ( $this->params[ 'root' ] === '' ) {
226 5
			return '';
227
		}
228
229
		// get the title object of the root page
230 1
		$rootTitle = Title::newFromText( $this->params[ 'root' ] );
231
232 1
		if ( $rootTitle !== null ) {
233 1
			return DIWikiPage::newFromTitle( $rootTitle )->getSerialization();
234
		}
235
236
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by SRF\Formats\Tree\TreeResultPrinter::getRootHash of type string.

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...
237
238
	}
239
240
	/**
241
	 * @return TreeNode[]
242
	 */
243 5
	protected function getHashOfNodes() {
244
245
		/** @var TreeNode[] $nodes */
246 5
		$nodes = [];
247
248 5
		$queryResult = $this->getQueryResult();
249
250 5
		$row = $queryResult->getNext();
251 5
		while ( $row !== false ) {
252 5
			$node = new TreeNode( $row );
253 5
			$nodes[ $node->getHash() ] = $node;
254 5
			$row = $queryResult->getNext();
255
		}
256
257 5
		return $nodes;
258
	}
259
260
	/**
261
	 * Returns a linker object for making hyperlinks
262
	 * @return \Linker
263
	 */
264 1
	public function getLinker( $firstcol = false ) {
265 1
		return $this->mLinker;
266
	}
267
268
	/**
269
	 * Depending on current linking settings, returns a linker object
270
	 * for making hyperlinks or NULL if no links should be created.
271
	 *
272
	 * @param int $column Column number
273
	 * @return \Linker|null
274
	 */
275 5
	public function getLinkerForColumn( $column ) {
276 5
		return parent::getLinker( $column === 0 );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getLinker() instead of getLinkerForColumn()). Are you sure this is correct? If so, you might want to change this to $this->getLinker().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
277
	}
278
279 3
	private function initalizeStandardTemplateParameters() {
280
281 3
		$query = $this->getQueryResult()->getQuery();
282
283 3
		$this->standardTemplateParameters =
284 3
			( trim( $this->params[ 'userparam' ] ) !== '' ? ( '|userparam=' . trim( $this->params['userparam'] ) ) : '' ) .
285 3
			'|smw-resultquerycondition=' . $query->getQueryString() .
286 3
			'|smw-resultquerylimit=' . $query->getLimit() .
287 3
			'|smw-resultqueryoffset=' . $query->getOffset();
288
289 3
	}
290
291
	/**
292
	 * @param string $rootHash
293
	 * @param TreeNode[] $nodes
294
	 * @return TreeNode
295
	 */
296 5
	protected function buildTreeFromNodeList( $rootHash, $nodes ) {
297
298 5
		$isRootSpecified = $rootHash !== '';
299
300 5
		$root = new TreeNode();
301 5
		if ( $isRootSpecified ) {
302 1
			$root->addChild( $nodes[ $rootHash ] );
303
		}
304
305 5
		$store = $this->getQueryResult()->getStore();
306 5
		$parentPointerProperty = DIProperty::newFromUserLabel( $this->params[ 'parent' ] );
307
308 5
		foreach ( $nodes as $hash => $node ) {
309
310 5
			$parents = $store->getPropertyValues(
311 5
				$node->getResultSubject(), $parentPointerProperty
312
			);
313
314 5
			if ( empty( $parents ) && !$isRootSpecified ) {
315
316 5
				$root->addChild( $node );
317
318
			} else {
319
320 4
				foreach ( $parents as $parent ) {
321
322 4
					$parentHash = $parent->getSerialization();
323
324
					try {
325 4
						if ( array_key_exists( $parentHash, $nodes ) ) {
326 4
							$errorCode = $nodes[ $parentHash ]->addChild( $node );
0 ignored issues
show
Unused Code introduced by
$errorCode is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
327
						} elseif ( !$isRootSpecified ) {
328
							$errorCode = $root->addChild( $node );
0 ignored issues
show
Unused Code introduced by
$errorCode is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
329
						} else {
330
							// Drop node. It is not part of the tree.
331 4
							$errorCode = null;
0 ignored issues
show
Unused Code introduced by
$errorCode is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
332
						}
333 1
					} catch ( Exception $e ) {
334 5
						$this->addError( $e->getMessage(), $node->getResultSubject()->getTitle()->getPrefixedText() );
335
					}
336
				}
337
			}
338
		}
339 5
		return $root;
340
	}
341
342
	/**
343
	 * @param TreeNode $tree
344
	 * @return mixed
345
	 */
346 5
	protected function buildLinesFromTree( $tree ) {
347
		$nodePrinterConfiguration = [
348 5
			'format' => trim( $this->params[ 'format' ] ),
349 5
			'template' => trim( $this->params[ 'template' ] ),
350 5
			'headers' => $this->params[ 'headers' ],
351 5
			'template arguments' => $this->params['template arguments'],
352 5
			'sep' => $this->params[ 'sep' ],
353
		];
354
355 5
		$visitor = new TreeNodePrinter( $this, $nodePrinterConfiguration );
356 5
		$lines = $tree->accept( $visitor );
357 5
		return $lines;
358
	}
359
360
	/**
361
	 * @param string $msgkey
362
	 * @param string | string[] $params
363
	 */
364 3
	protected function addError( $msgkey, $params = [] ) {
365
366 3
		parent::addError(
367 3
			\Message::newFromKey( $msgkey )
368 3
				->params( $params )
369 3
				->inContentLanguage()->text()
370
			);
371 3
	}
372
373
}
374
375