Completed
Push — master ( 4c0a47...4e7d8d )
by
unknown
17:45
created

SRFjqPlotChart   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 3
dl 0
loc 220
ccs 0
cts 88
cp 0
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getName() 0 3 1
B getFormatOutput() 0 50 6
A preparePieData() 0 25 4
B prepareBarData() 0 52 8
B addCommonOptions() 0 25 8
A getParamDefinitions() 0 11 1
1
<?php
2
3
/**
4
 * A query printer for bar, line, pie and donut chart on aggregated values
5
 * using the jqPlot JavaScript library.
6
 *
7
 * @since 1.8
8
 *
9
 * @file SRF_jqPlotChart.php
10
 * @ingroup SemanticResultFormats
11
 * @licence GNU GPL v2 or later
12
 *
13
 * @author mwjames
14
 * @author Jeroen De Dauw < [email protected] >
15
 * @author Yaron Koren
16
 * @author Sanyam Goyal
17
 */
18
class SRFjqPlotChart extends SRFjqPlot {
19
20
	/**
21
	 * Corresponding message name
22
	 *
23
	 */
24
	public function getName() {
25
		return wfMessage( 'srf-printername-jqplotchart' )->text();
26
	}
27
28
	/**
29
	 * Prepare data output
30
	 *
31
	 * @since 1.8
32
	 *
33
	 * @param array $data label => value
34
	 */
35
	protected function getFormatOutput( array $data ) {
36
37
		static $statNr = 0;
38
		$chartID = 'jqplot-' . $this->params['charttype'] . '-' . ++$statNr;
39
40
		$this->isHTML = true;
41
42
		// Prepare data objects
43
		if ( in_array( $this->params['charttype'], [ 'bar', 'line' ] ) ) {
44
			// Parse bar relevant data
45
			$dataObject = $this->prepareBarData( $data );
46
		} elseif ( in_array( $this->params['charttype'], [ 'pie', 'donut' ] ) ){
47
			//Parse pie/donut relevant data
48
			$dataObject = $this->preparePieData( $data );
49
		} else {
50
			// Return with an error
51
			return Html::rawElement( 'span', [
52
				'class' => "error"
53
				], wfMessage( 'srf-error-missing-layout' )->inContentLanguage()->text()
54
			);
55
		}
56
57
		// Encode data objects
58
		$requireHeadItem =  [ $chartID => FormatJson::encode( $dataObject ) ];
59
		SMWOutputs::requireHeadItem( $chartID, Skin::makeVariablesScript($requireHeadItem ) );
60
61
		// Processing placeholder
62
		$processing = SRFUtils::htmlProcessingElement( $this->isHTML );
63
64
		// Ensure right conversion
65
		$width = strstr( $this->params['width'] ,"%") ? $this->params['width'] : $this->params['width'] . 'px';
66
67
		// Chart/graph placeholder
68
		$chart = Html::rawElement( 'div', [
69
			'id'    => $chartID,
70
			'class' => 'container',
71
			'style' => "display:none; width: {$width}; height: {$this->params['height']}px;"
72
			], null
73
		);
74
75
		// Beautify class selector
76
		$class = $this->params['charttype'] ?  '-' . $this->params['charttype'] : '';
77
		$class = $this->params['class'] ? $class . ' ' . $this->params['class'] : $class . ' jqplot-common';
78
79
		// Chart/graph wrappper
80
		return Html::rawElement( 'div', [
81
			'class' => 'srf-jqplot' . $class,
82
			], $processing . $chart
83
		);
84
	}
85
86
	/**
87
	 * Prepare pie/donut chart specific data and parameters
88
	 *
89
	 * @since 1.8
90
	 *
91
	 * @param array $rawdata label => value
92
	 * @return array
93
	 */
94
	private function preparePieData( $rawdata ) {
95
96
		// Init
97
		$mode = 'single';
98
99
		// Reorganize the data in accordance with the pie chart req.
100
		foreach ( $rawdata as $name => $value ) {
101
			if ( $value >= $this->params['min'] ) {
102
				$data[] = [ $name , $value ];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
103
			}
104
		}
105
106
		if ( $this->params['charttype'] === 'donut' ) {
107
			SMWOutputs::requireResource( 'ext.srf.jqplot.donut' );
108
		} else {
109
			SMWOutputs::requireResource( 'ext.srf.jqplot.pie' );
110
		}
111
112
		return  [
113
			'data'       =>  [ $data ],
0 ignored issues
show
Bug introduced by
The variable $data 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
			'renderer'   => $this->params['charttype'],
115
			'mode'       => $mode,
116
			'parameters' => $this->addCommonOptions()
117
		];
118
	}
119
120
	/**
121
	 * Prepare bar/line chart specific data and parameters
122
	 *
123
	 * Data can be an array of y values, or an array of [label, value] pairs;
124
	 * While labels are used only on the first series with labels on
125
	 * subsequent series being ignored
126
	 *
127
	 *  @since 1.8
128
	 *
129
	 * @param array $rawdata label => value
130
	 * @return array
131
	 */
132
	private function prepareBarData( $rawdata ) {
133
134
		// Init
135
		$total = 0;
136
		$mode = 'single';
137
138
		// Find min and max values to determine the graphs axis parameter
139
		$maxValue = count( $rawdata ) == 0 ? 0 : max( $rawdata );
140
141
		if ( $this->params['min'] === false ) {
142
			$minValue = count( $rawdata ) == 0 ? 0 : min( $rawdata );
143
		} else {
144
			$minValue = $this->params['min'];
145
		}
146
147
		// Get number ticks
148
		$data['numbersticks'] = SRFjqPlot::getNumbersTicks( $minValue, $maxValue );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
149
150
		// Reorganize the data in accordance with the bar/line chart req.
151
		foreach ( $rawdata as $key => $value ) {
152
			if ( $value >= $this->params['min'] ) {
153
				$data['series'][] =  [ $key, $value ];
154
				$total = $total + $value;
155
			}
156
		}
157
158
		// Bar/line module
159
		SMWOutputs::requireResource( 'ext.srf.jqplot.bar' );
160
161
		// Highlighter plugin
162
		if ( $this->params['highlighter'] ) {
163
			SMWOutputs::requireResource( 'ext.srf.jqplot.highlighter' );
164
		}
165
166
		// Pointlabels plugin
167
		if ( in_array( $this->params['datalabels'], [ 'value', 'label', 'percent' ] ) ) {
168
			SMWOutputs::requireResource( 'ext.srf.jqplot.pointlabels' );
169
		}
170
171
		return  [
172
			'data'      =>  [ $data['series'] ],
173
			'ticks'     => $data['numbersticks'],
174
			'labels'    => array_keys( $data['series'] ),
175
			'numbers'   => array_values( $data['series'] ),
176
			'max'       => $maxValue,
177
			'total'     => $total,
178
			'mode'      => $mode,
179
			'series'    => [],
180
			'renderer'  => $this->params['charttype'],
181
			'parameters'=> $this->addCommonOptions()
182
		];
183
	}
184
185
	/**
186
	 * jqPlot common parameters
187
	 *
188
	 * @since 1.8
189
	 *
190
	 */
191
	private function addCommonOptions() {
192
193
		// Series colour
194
		$seriescolors = $this->params['chartcolor'] !== '' ? array_filter( explode( ",", $this->params['chartcolor'] ) ): null;
195
196
		return  [
197
			'numbersaxislabel' => $this->params['numbersaxislabel'],
198
			'labelaxislabel' => $this->params['labelaxislabel'],
199
			'charttitle'   => $this->params['charttitle'],
200
			'charttext'    => $this->params['charttext'],
201
			'theme'        => $this->params['theme'] ? $this->params['theme'] : null,
202
			'ticklabels'   => $this->params['ticklabels'],
203
			'highlighter'  => $this->params['highlighter'],
204
			'direction'    => $this->params['direction'],
205
			'smoothlines'  => $this->params['smoothlines'],
206
			'filling'      => $this->params['filling'],
207
			'datalabels'   => $this->params['datalabels'],
208
			'valueformat'  => $this->params['valueformat'],
209
			'chartlegend'  => $this->params['chartlegend'] !== '' ? $this->params['chartlegend'] : 'none',
210
			'colorscheme'  => $this->params['colorscheme'] !== '' ? $this->params['colorscheme'] : null ,
211
			'pointlabels'  => $this->params['datalabels'] === 'none' ? false : $this->params['datalabels'],
212
			'grid' => $this->params['theme'] === 'vector' ?  [ 'borderColor' => '#a7d7f9' ] : ( $this->params['theme'] === 'simple' ?  [ 'borderColor' => '#ddd' ] : null ),
213
			'seriescolors' => $seriescolors
214
		];
215
	}
216
217
	/**
218
	 * @see SMWResultPrinter::getParamDefinitions
219
	 *
220
	 * @since 1.8
221
	 *
222
	 * @param $definitions array of IParamDefinition
223
	 *
224
	 * @return array of IParamDefinition|array
225
	 */
226
	 public function getParamDefinitions( array $definitions ) {
227
		$params = self::getCommonParams();
228
229
		$params['charttype'] = [
230
			'message' => 'srf-paramdesc-charttype',
231
			'default' => 'bar',
232
			'values' => [ 'bar', 'line', 'pie', 'donut' ],
233
		];
234
235
		return array_merge( parent::getParamDefinitions( $definitions ), $params );
236
	}
237
}
238