Completed
Push — master ( 6dc7d8...407c40 )
by Karsten
15:45
created

formats/graphviz/SRF_Graph.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * SMW result printer for graphs using graphViz.
5
 * In order to use this printer you need to have both
6
 * the graphViz library installed on your system and
7
 * have the graphViz MediaWiki extension installed.
8
 *
9
 * @file SRF_Graph.php
10
 * @ingroup SemanticResultFormats
11
 *
12
 * @licence GNU GPL v2+
13
 * @author Frank Dengler
14
 * @author Jeroen De Dauw < [email protected] >
15
 */
16
class SRFGraph extends SMWResultPrinter {
17
18
	public static $NODE_SHAPES = [
19
		'box',
20
		'box3d',
21
		'circle',
22
		'component',
23
		'diamond',
24
		'doublecircle',
25
		'doubleoctagon',
26
		'egg',
27
		'ellipse',
28
		'folder',
29
		'hexagon',
30
		'house',
31
		'invhouse',
32
		'invtrapezium',
33
		'invtriangle',
34
		'Mcircle',
35
		'Mdiamond',
36
		'Msquare',
37
		'none',
38
		'note',
39
		'octagon',
40
		'parallelogram',
41
		'pentagon ',
42
		'plaintext',
43
		'point',
44
		'polygon',
45
		'rect',
46
		'rectangle',
47
		'septagon',
48
		'square',
49
		'tab',
50
		'trapezium',
51
		'triangle',
52
		'tripleoctagon',
53
	];
54
55
	protected $m_graphName;
56
	protected $m_graphLabel;
57
	protected $m_graphColor;
58
	protected $m_graphLegend;
59
	protected $m_graphLink;
60
	protected $m_rankdir;
61
	protected $m_graphSize;
62
	protected $m_labelArray = [];
63
	protected $m_graphColors = [
64
		'black',
65
		'red',
66
		'green',
67
		'blue',
68
		'darkviolet',
69
		'gold',
70
		'deeppink',
71
		'brown',
72
		'bisque',
73
		'darkgreen',
74
		'yellow',
75
		'darkblue',
76
		'magenta',
77
		'steelblue2' ];
78
	protected $m_nameProperty;
79
	protected $m_nodeShape;
80
	protected $m_parentRelation;
81
	protected $m_wordWrapLimit;
82
83
	/**
84
	 * (non-PHPdoc)
85
	 * @see SMWResultPrinter::handleParameters()
86
	 */
87
	protected function handleParameters( array $params, $outputmode ) {
88
		parent::handleParameters( $params, $outputmode );
89
90
		$this->m_graphName = trim( $params['graphname'] );
91
		$this->m_graphSize = trim( $params['graphsize'] );
92
93
		$this->m_graphLegend = $params['graphlegend'];
94
		$this->m_graphLabel = $params['graphlabel'];
95
96
		$this->m_rankdir = strtoupper( trim( $params['arrowdirection'] ) );
97
98
		$this->m_graphLink = $params['graphlink'];
99
		$this->m_graphColor = $params['graphcolor'];
100
101
		$this->m_nameProperty = $params['nameproperty'] === false ? false : trim( $params['nameproperty'] );
102
103
		$this->m_parentRelation = strtolower( trim( $params['relation'] ) ) == 'parent';
104
105
		$this->m_nodeShape = $params['nodeshape'];
106
		$this->m_wordWrapLimit = $params['wordwraplimit'];
107
	}
108
109
	protected function getResultText( SMWQueryResult $res, $outputmode ) {
0 ignored issues
show
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...
110
111
		if ( !class_exists( 'GraphViz' )
112
			&& !class_exists( '\\MediaWiki\\Extension\\GraphViz\\GraphViz' )
113
		) {
114
			wfWarn( 'The SRF Graph printer needs the GraphViz extension to be installed.' );
115
			return '';
116
		}
117
118
		$this->isHTML = true;
119
120
		$graphInput = "digraph $this->m_graphName {";
121
		if ( $this->m_graphSize != '' ) {
122
			$graphInput .= "size=\"$this->m_graphSize\";";
123
		}
124
		if ( $this->m_nodeShape ) {
125
			$graphInput .= "node [shape=$this->m_nodeShape];";
126
		}
127
		$graphInput .= "rankdir=$this->m_rankdir;";
128
129
		while ( $row = $res->getNext() ) {
130
			$graphInput .= $this->getGVForItem( $row, $outputmode );
131
		}
132
133
		$graphInput .= "}";
134
135
		// Calls graphvizParserHook function from MediaWiki GraphViz extension
136
		$result = $GLOBALS['wgParser']->recursiveTagParse( "<graphviz>$graphInput</graphviz>" );
137
138
		if ( $this->m_graphLegend && $this->m_graphColor ) {
139
			$arrayCount = 0;
140
			$arraySize = count( $this->m_graphColors );
141
			$result .= "<P>";
142
143
			foreach ( $this->m_labelArray as $m_label ) {
144
				if ( $arrayCount >= $arraySize ) {
145
					$arrayCount = 0;
146
				}
147
148
				$color = $this->m_graphColors[$arrayCount];
149
				$result .= "<font color=$color>$color: $m_label </font><br />";
150
151
				$arrayCount += 1;
152
			}
153
154
			$result .= "</P>";
155
		}
156
157
		return $result;
158
	}
159
160
	/**
161
	 * Returns the GV for a single subject.
162
	 *
163
	 * @since 1.5.4
164
	 *
165
	 * @param array $row
166
	 * @param $outputmode
167
	 *
168
	 * @return string
169
	 */
170
	protected function getGVForItem( array /* of SMWResultArray */
171
	$row, $outputmode ) {
172
		$segments = [];
173
174
		// Loop throught all fields of the record.
175
		foreach ( $row as $i => $resultArray ) {
176
177
			// Loop throught all the parts of the field value.
178
			while ( ( $object = $resultArray->getNextDataValue() ) !== false ) {
179
				$propName = $resultArray->getPrintRequest()->getLabel();
180
				$isName = $this->m_nameProperty ? ( $i != 0 && $this->m_nameProperty === $propName ) : $i == 0;
181
182
				if ( $isName ) {
183
					$name = $this->getWordWrappedText( $object->getShortText( $outputmode ), $this->m_wordWrapLimit );
184
				}
185
186
				if ( !( $this->m_nameProperty && $i == 0 ) ) {
187
					$segments[] = $this->getGVForDataValue( $object, $outputmode, $isName, $name, $propName );
0 ignored issues
show
The variable $name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
188
				}
189
			}
190
		}
191
192
		return implode( "\n", $segments );
193
	}
194
195
	/**
196
	 * Returns the GV for a single SMWDataValue.
197
	 *
198
	 * @since 1.5.4
199
	 *
200
	 * @param SMWDataValue $object
201
	 * @param $outputmode
202
	 * @param boolean $isName Is this the name that should be used for the node?
203
	 * @param string $name
204
	 * @param string $labelName
205
	 *
206
	 * @return string
207
	 */
208
	protected function getGVForDataValue( SMWDataValue $object, $outputmode, $isName, $name, $labelName ) {
209
		$graphInput = '';
210
		$text = $object->getShortText( $outputmode );
211
212
		if ( $this->m_graphLink ) {
213
			$nodeLinkURL = "[[" . $text . "]]";
214
		}
215
216
		$text = $this->getWordWrappedText( $text, $this->m_wordWrapLimit );
217
218
		if ( $this->m_graphLink ) {
219
			$graphInput .= " \"$text\" [URL = \"$nodeLinkURL\"]; ";
0 ignored issues
show
The variable $nodeLinkURL does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
220
		}
221
222
		if ( !$isName ) {
223
			$graphInput .= $this->m_parentRelation ? " \"$text\" -> \"$name\" " : " \"$name\" -> \"$text\" ";
224
225
			if ( $this->m_graphLabel || $this->m_graphColor ) {
226
				$graphInput .= ' [';
227
228
				if ( array_search( $labelName, $this->m_labelArray, true ) === false ) {
229
					$this->m_labelArray[] = $labelName;
230
				}
231
232
				$color = $this->m_graphColors[array_search( $labelName, $this->m_labelArray, true )];
233
234
				if ( $this->m_graphLabel ) {
235
					$graphInput .= "label=\"$labelName\"";
236
					if ( $this->m_graphColor ) {
237
						$graphInput .= ",fontcolor=$color,";
238
					}
239
				}
240
241
				if ( $this->m_graphColor ) {
242
					$graphInput .= "color=$color";
243
				}
244
245
				$graphInput .= ']';
246
247
			}
248
249
			$graphInput .= ';';
250
		}
251
252
		return $graphInput;
253
	}
254
255
	/**
256
	 * Returns the word wrapped version of the provided text.
257
	 *
258
	 * @since 1.5.4
259
	 *
260
	 * @param string $text
261
	 * @param integer $charLimit
262
	 *
263
	 * @return string
264
	 */
265
	protected function getWordWrappedText( $text, $charLimit ) {
266
		$charLimit = max( [ $charLimit, 1 ] );
267
		$segments = [];
268
269
		while ( strlen( $text ) > $charLimit ) {
270
			// Find the last space in the allowed range.
271
			$splitPosition = strrpos( substr( $text, 0, $charLimit ), ' ' );
272
273
			if ( $splitPosition === false ) {
274
				// If there is no space (lond word), find the next space.
275
				$splitPosition = strpos( $text, ' ' );
276
277
				if ( $splitPosition === false ) {
278
					// If there are no spaces, everything goes on one line.
279
					$splitPosition = strlen( $text ) - 1;
280
				}
281
			}
282
283
			$segments[] = substr( $text, 0, $splitPosition + 1 );
284
			$text = substr( $text, $splitPosition + 1 );
285
		}
286
287
		$segments[] = $text;
288
289
		return implode( '\n', $segments );
290
	}
291
292
	/**
293
	 * (non-PHPdoc)
294
	 * @see SMWResultPrinter::getName()
295
	 */
296
	public function getName() {
297
		return wfMessage( 'srf-printername-graph' )->text();
298
	}
299
300
	/**
301
	 * @see SMWResultPrinter::getParamDefinitions
302
	 *
303
	 * @since 1.8
304
	 *
305
	 * @param $definitions array of IParamDefinition
306
	 *
307
	 * @return array of IParamDefinition|array
308
	 */
309
	public function getParamDefinitions( array $definitions ) {
310
		$params = parent::getParamDefinitions( $definitions );
311
312
		$params['graphname'] = [
313
			'default' => 'QueryResult',
314
			'message' => 'srf-paramdesc-graphname',
315
		];
316
317
		$params['graphsize'] = [
318
			'type' => 'string',
319
			'default' => '',
320
			'message' => 'srf-paramdesc-graphsize',
321
			'manipulatedefault' => false,
322
		];
323
324
		$params['graphlegend'] = [
325
			'type' => 'boolean',
326
			'default' => false,
327
			'message' => 'srf-paramdesc-graphlegend',
328
		];
329
330
		$params['graphlabel'] = [
331
			'type' => 'boolean',
332
			'default' => false,
333
			'message' => 'srf-paramdesc-graphlabel',
334
		];
335
336
		$params['graphlink'] = [
337
			'type' => 'boolean',
338
			'default' => false,
339
			'message' => 'srf-paramdesc-graphlink',
340
		];
341
342
		$params['graphcolor'] = [
343
			'type' => 'boolean',
344
			'default' => false,
345
			'message' => 'srf-paramdesc-graphcolor',
346
		];
347
348
		$params['arrowdirection'] = [
349
			'aliases' => 'rankdir',
350
			'default' => 'LR',
351
			'message' => 'srf-paramdesc-rankdir',
352
			'values' => [ 'LR', 'RL', 'TB', 'BT' ],
353
		];
354
355
		$params['nodeshape'] = [
356
			'default' => false,
357
			'message' => 'srf-paramdesc-graph-nodeshape',
358
			'manipulatedefault' => false,
359
			'values' => self::$NODE_SHAPES,
360
		];
361
362
		$params['relation'] = [
363
			'default' => 'child',
364
			'message' => 'srf-paramdesc-graph-relation',
365
			'manipulatedefault' => false,
366
			'values' => [ 'parent', 'child' ],
367
		];
368
369
		$params['nameproperty'] = [
370
			'default' => false,
371
			'message' => 'srf-paramdesc-graph-nameprop',
372
			'manipulatedefault' => false,
373
		];
374
375
		$params['wordwraplimit'] = [
376
			'type' => 'integer',
377
			'default' => 25,
378
			'message' => 'srf-paramdesc-graph-wwl',
379
			'manipulatedefault' => false,
380
		];
381
382
		return $params;
383
	}
384
385
}
386