Completed
Pull Request — master (#538)
by
unknown
14:19
created

GraphFormatter::add()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace SRF\Graph;
4
5
use Html;
6
7
/**
8
 *
9
 *
10
 * @see https://www.semantic-mediawiki.org/wiki/Help:Graph_format
11
 *
12
 * @license GNU GPL v2+
13
 * @since 3.1
14
 *
15
 * @author Sebastian Schmid (gesinn.it)
16
 *
17
 */
18
19
class GraphFormatter {
20
21
	private $graph = "";
22
23
	protected $graphColors = [
24
		'black',
25
		'red',
26
		'green',
27
		'blue',
28
		'darkviolet',
29
		'gold',
30
		'deeppink',
31
		'brown',
32
		'bisque',
33
		'darkgreen',
34
		'yellow',
35
		'darkblue',
36
		'magenta',
37
		'steelblue2'
38
	];
39
	private $legendItem = [];
40
	private $options = [];
41
42 3
	public function __construct( $options ){
43 3
		$this->options = $options;
44 3
	}
45
46
	public function getGraph(){
47
		return $this->graph;
48
	}
49
50
	/*
51
	 * Add a single string to graph
52
	 *
53
	 * @param string $line
54
	 */
55 3
	private function add( $line ){
56 3
		$this->graph .= $line;
57 3
	}
58
59
60
	/*
61
	* Creates the DOT (graph description language) which can be processed by the graphviz lib
62
	*
63
	* @see https://www.graphviz.org/
64
	* @since 3.1
65
	*
66
	@param SRF\Graph\GraphNodes[] $nodes
67
	*/
68 3
	public function buildGraph($nodes){
69 3
		$this->add( "digraph " . $this->options['graphName'] . " {" );
70
71
		// set fontsize and fontname of graph, nodes and edges
72 3
		$this->add( "graph [fontsize=10, fontname=\"Verdana\"]\n" );
73 3
		$this->add( "node [fontsize=10, fontname=\"Verdana\"];\n" );
74 3
		$this->add( "edge [fontsize=10, fontname=\"Verdana\"];\n" );
75
76
		// choose graphsize, nodeshapes and rank direction
77 3
		if ( $this->options['graphSize'] != '' ) {
78 3
			$this->add("size=\"" . $this->options['graphSize'] . "\";");
79
		}
80
81 3
		if ( $this->options['nodeShape'] != '' ) {
82 3
			$this->add( "node [shape=" . $this->options['nodeShape'] . "];" );
83
		}
84
85 3
		$this->add( "rankdir=" . $this->options['rankDir'] . ";" );
86
87
		/** @var \SRF\GraphNode $node */
88 3
		foreach ( $nodes as $node ) {
89
90
			// take "displaytitle" as node-label if it is set
91 3
			if ( $this->options['nodeLabel'] === GraphPrinter::NODELABEL_DISPLAYTITLE) {
92 3
				$objectDisplayTitle = $node->getLabel();
93 3
				if ( !empty( $objectDisplayTitle )) {
94 3
					$nodeLabel = $this->getWordWrappedText( $objectDisplayTitle,
95 3
						$this->options['wordWrapLimit'] );
96
				}
97
			}
98
99
			/**
100
			 * Add nodes to the graph
101
			 *
102
			 * @var \SRF\Graph\GraphNode $node
103
			 */
104 3
			$this->add( "\"" . $node->getID() . "\"" );
105
106 3
			if ( $this->options['enableGraphLink'] ) {
107
108 3
				$nodeLinkURL = "[[" . $node->getID() . "]]";
109
110 3
				if( $nodeLabel === '' ) {
111
					$this->add( " [URL = \"$nodeLinkURL\"]" );
112
				} else {
113 3
					$this->add( " [URL = \"$nodeLinkURL\", label = \"$nodeLabel\"]" );
0 ignored issues
show
Bug introduced by
The variable $nodeLabel 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...
114
				}
115
			}
116 3
			$this->add( "; ");
117
		}
118
119
		/**
120
		 * Add edges to the graph
121
		 *
122
		 * @var \SRF\Graph\GraphNode $node
123
		 */
124 3
		foreach ( $nodes as $node ) {
125
126 3
			if ( count( $node->getParentNode() ) > 0 ) {
127
128 3
				foreach ( $node->getParentNode() as $parentNode ) {
129
130
					// handle parent/child switch (parentRelation)
131 3
					$this->add( $this->options['parentRelation'] ? " \"" . $parentNode['object']
132 3
						. "\" -> \"" . $node->getID() . "\""
133 3
						: " \"" . $node->getID() . "\" -> \"" . $parentNode['object'] . "\" " );
134
135 3
					if ( $this->options['showGraphLabel'] || $this->options['showGraphColor'] ) {
136 3
						$this->add( ' [' );
137
138
						// add legend item only if missing
139 3
						if ( array_search( $parentNode['predicate'], $this->legendItem, true ) === false ) {
140 3
							$this->legendItem[] = $parentNode['predicate'];
141
						}
142
143
						// assign color
144 3
						$color = $this->graphColors[array_search( $parentNode['predicate'], $this->legendItem, true )];
145
146
						// show arrow label (graphLabel is misleading but kept for compatibility reasons)
147 3
						if ( $this->options['showGraphLabel'] ) {
148 3
							$this->add( "label=\"" . $parentNode['predicate'] . "\"" );
149 3
							if ( $this->options['showGraphColor'] ) {
150 3
								$this->add( ",fontcolor=$color," );
151
							}
152
						}
153
154
						// colorize arrow
155 3
						if ( $this->options['showGraphColor'] ) {
156 3
							$this->add( "color=$color" );
157
						}
158 3
						$this->add( "]" );
159
					}
160 3
					$this->add( ";" );
161
				}
162
			}
163
		}
164 3
		$this->add( "}" );
165 3
	}
166
167
	/**
168
	 * Creates the graph legend
169
	 *
170
	 * @return string Html::rawElement
171
	 *
172
	 */
173 1
	public function getGraphLegend(){
174 1
		$itemsHtml = '';
175 1
		$colorCount = 0;
176 1
		$arraySize = count( $this->graphColors );
177
178 1
		if ( $this->options['showGraphLegend'] && $this->options['showGraphColor'] ) {
179 1
			foreach ( $this->legendItem as $legendLabel ) {
180 1
				if ( $colorCount >= $arraySize ) {
181
					$colorCount = 0;
182
				}
183
184 1
				$color = $this->graphColors[$colorCount];
185 1
				$itemsHtml .= Html::rawElement( 'div', [ 'class' => 'graphlegenditem', 'style' => "color: $color" ],
186 1
					"$color: $legendLabel" );
187
188 1
				$colorCount ++;
189
			}
190
		}
191
192 1
		return Html::rawElement( 'div', [ 'class' => 'graphlegend' ], "$itemsHtml");
193
	}
194
195
	/**
196
	 * Returns the word wrapped version of the provided text.
197
	 *
198
	 * @param string $text
199
	 * @param integer $charLimit
200
	 *
201
	 * @return string
202
	 */
203 3
	public static function getWordWrappedText( $text, $charLimit ) {
204 3
		$charLimit = max( [ $charLimit, 1 ] );
205 3
		$segments = [];
206
207 3
		while ( strlen( $text ) > $charLimit ) {
208
			// Find the last space in the allowed range.
209 1
			$splitPosition = strrpos( substr( $text, 0, $charLimit ), ' ' );
210
211 1
			if ( $splitPosition === false ) {
212
				// If there is no space (lond word), find the next space.
213
				$splitPosition = strpos( $text, ' ' );
214
215
				if ( $splitPosition === false ) {
216
					// If there are no spaces, everything goes on one line.
217
					$splitPosition = strlen( $text ) - 1;
218
				}
219
			}
220
221 1
			$segments[] = substr( $text, 0, $splitPosition + 1 );
222 1
			$text = substr( $text, $splitPosition + 1 );
223
		}
224
225 3
		$segments[] = $text;
226
227 3
		return implode( '\n', $segments );
228
	}
229
}