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 | * A query printer that uses the dygraphs JavaScript library |
||
5 | * |
||
6 | * @see http://www.semantic-mediawiki.org/wiki/Help:Flot_timeseries_chart |
||
7 | * @licence GNU GPL v2 or later |
||
8 | * |
||
9 | * @since 1.8 |
||
10 | * |
||
11 | * @author mwjames |
||
12 | */ |
||
13 | class SRFDygraphs extends SMWResultPrinter { |
||
14 | |||
15 | /** |
||
16 | * @see SMWResultPrinter::getName |
||
17 | * @return string |
||
18 | */ |
||
19 | public function getName() { |
||
20 | return wfMessage( 'srf-printername-dygraphs' )->text(); |
||
21 | } |
||
22 | |||
23 | /** |
||
24 | * @see SMWResultPrinter::getResultText |
||
25 | * |
||
26 | * @param SMWQueryResult $result |
||
27 | * @param $outputMode |
||
28 | * |
||
29 | * @return string |
||
30 | */ |
||
31 | protected function getResultText( SMWQueryResult $result, $outputMode ) { |
||
32 | |||
33 | // Output mode is fixed |
||
34 | $outputMode = SMW_OUTPUT_HTML; |
||
35 | |||
36 | // Data processing |
||
37 | $data = $this->getResultData( $result, $outputMode ); |
||
38 | |||
39 | // Post-data processing check |
||
40 | if ( $data === [] ) { |
||
41 | return $result->addErrors( [ wfMessage( 'srf-warn-empy-chart' )->inContentLanguage()->text() ] ); |
||
42 | } else { |
||
43 | $options['sask'] = SRFUtils::htmlQueryResultLink( $this->getLink( $result, SMW_OUTPUT_HTML ) ); |
||
0 ignored issues
–
show
|
|||
44 | return $this->getFormatOutput( $data, $options ); |
||
45 | } |
||
46 | } |
||
47 | |||
48 | /** |
||
49 | * Returns an array with numerical data |
||
50 | * |
||
51 | * @since 1.8 |
||
52 | * |
||
53 | * @param SMWQueryResult $result |
||
54 | * @param $outputMode |
||
55 | * |
||
56 | * @return array |
||
57 | */ |
||
58 | protected function getResultData( SMWQueryResult $result, $outputMode ) { |
||
59 | $aggregatedValues = []; |
||
60 | |||
61 | while ( $rows = $result->getNext() ) { // Objects (pages) |
||
62 | $annotation = []; |
||
63 | $dataSource = false; |
||
64 | |||
65 | /** |
||
66 | * @var SMWResultArray $field |
||
67 | * @var SMWDataValue $dataValue |
||
68 | */ |
||
69 | foreach ( $rows as $field ) { |
||
70 | |||
71 | // Use the subject marker to identify a possible data file |
||
72 | $subject = $field->getResultSubject(); |
||
73 | if ( $this->params['datasource'] === 'file' && $subject->getTitle()->getNamespace( |
||
74 | ) === NS_FILE && !$dataSource ) { |
||
75 | $aggregatedValues['subject'] = $this->makePageFromTitle( $subject->getTitle() )->getLongHTMLText( |
||
76 | $this->getLinker( $field->getResultSubject() ) |
||
77 | ); |
||
78 | $aggregatedValues['url'] = wfFindFile( $subject->getTitle() )->getUrl(); |
||
79 | $dataSource = true; |
||
80 | } |
||
81 | |||
82 | // Proceed only where a label is known otherwise items are of no use |
||
83 | // for being a potential object identifier |
||
84 | if ( $field->getPrintRequest()->getLabel() !== '' ) { |
||
85 | $propertyLabel = $field->getPrintRequest()->getLabel(); |
||
86 | } else { |
||
87 | continue; |
||
88 | } |
||
89 | |||
90 | while ( ( $dataValue = $field->getNextDataValue() ) !== false ) { // Data values |
||
91 | |||
92 | // Jump the column (indicated by continue) because we don't want the data source being part of the annotation array |
||
93 | if ( $dataValue->getDataItem()->getDIType( |
||
94 | ) == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'raw' && !$dataSource ) { |
||
95 | // Support data source = raw which pulls the url from a wikipage in raw format |
||
96 | $aggregatedValues['subject'] = $this->makePageFromTitle( |
||
97 | $dataValue->getTitle() |
||
98 | )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) ); |
||
99 | $aggregatedValues['url'] = $dataValue->getTitle()->getLocalURL( 'action=raw' ); |
||
100 | $dataSource = true; |
||
101 | continue; |
||
102 | } elseif ( $dataValue->getDataItem()->getDIType( |
||
103 | ) == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'file' && $dataValue->getTitle( |
||
104 | )->getNamespace() === NS_FILE && !$dataSource ) { |
||
105 | // Support data source = file which pulls the url from a uploaded file |
||
106 | $aggregatedValues['subject'] = $this->makePageFromTitle( |
||
107 | $dataValue->getTitle() |
||
108 | )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) ); |
||
109 | $aggregatedValues['url'] = wfFindFile( $dataValue->getTitle() )->getUrl(); |
||
110 | $dataSource = true; |
||
111 | continue; |
||
112 | } elseif ( $dataValue->getDataItem()->getDIType( |
||
113 | ) == SMWDataItem::TYPE_URI && $this->params['datasource'] === 'url' && !$dataSource ) { |
||
114 | // Support data source = url, pointing to an url data source |
||
115 | $aggregatedValues['link'] = $dataValue->getShortHTMLText( $this->getLinker( false ) ); |
||
116 | $aggregatedValues['url'] = $dataValue->getURL(); |
||
117 | $dataSource = true; |
||
118 | continue; |
||
119 | } |
||
120 | |||
121 | // The annotation should adhere outlined conventions as the label identifies the array object key |
||
122 | // series -> Required The name of the series to which the annotated point belongs |
||
123 | // x -> Required The x value of the point |
||
124 | // shortText -> Text that will appear as annotation flag |
||
125 | // text -> A longer description of the annotation |
||
126 | // @see http://dygraphs.com/annotations.html |
||
127 | if ( in_array( $propertyLabel, [ 'series', 'x', 'shortText', 'text' ] ) ) { |
||
128 | if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_NUMBER ) { |
||
129 | // Set unit if available |
||
130 | $dataValue->setOutputFormat( $this->params['unit'] ); |
||
131 | // Check if unit is available |
||
132 | $annotation[$propertyLabel] = $dataValue->getUnit() !== '' ? $dataValue->getShortWikiText( |
||
133 | ) : $dataValue->getNumber(); |
||
134 | } else { |
||
135 | $annotation[$propertyLabel] = $dataValue->getWikiValue(); |
||
136 | } |
||
137 | } |
||
138 | } |
||
139 | } |
||
140 | // Sum-up collected row items in a single array |
||
141 | if ( $annotation !== [] ) { |
||
142 | $aggregatedValues['annotation'][] = $annotation; |
||
143 | } |
||
144 | } |
||
145 | return $aggregatedValues; |
||
146 | } |
||
147 | |||
148 | private function makePageFromTitle( \Title $title ) { |
||
149 | $dataValue = new SMWWikiPageValue( '_wpg' ); |
||
150 | $dataItem = SMWDIWikiPage::newFromTitle( $title ); |
||
151 | $dataValue->setDataItem( $dataItem ); |
||
152 | return $dataValue; |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * Prepare data for the output |
||
157 | * |
||
158 | * @since 1.8 |
||
159 | * |
||
160 | * @param array $data |
||
161 | * |
||
162 | * @return string |
||
163 | */ |
||
164 | protected function getFormatOutput( $data, $options ) { |
||
165 | |||
166 | // Object count |
||
167 | static $statNr = 0; |
||
168 | $chartID = 'srf-dygraphs-' . ++$statNr; |
||
169 | |||
170 | $this->isHTML = true; |
||
171 | |||
172 | // Reorganize the raw data |
||
173 | if ( $this->params['datasource'] === 'page' ) { |
||
174 | foreach ( $data as $key => $values ) { |
||
175 | $dataObject[] = [ 'label' => $key, 'data' => $values ]; |
||
176 | } |
||
177 | } else { |
||
178 | $dataObject['source'] = $data; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$dataObject was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dataObject = 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 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...
|
|||
179 | } |
||
180 | |||
181 | // Prepare transfer array |
||
182 | $chartData = [ |
||
183 | 'data' => $dataObject, |
||
0 ignored issues
–
show
The variable
$dataObject 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
Loading history...
|
|||
184 | 'sask' => $options['sask'], |
||
185 | 'parameters' => [ |
||
186 | 'width' => $this->params['width'], |
||
187 | 'height' => $this->params['height'], |
||
188 | 'xlabel' => $this->params['xlabel'], |
||
189 | 'ylabel' => $this->params['ylabel'], |
||
190 | 'charttitle' => $this->params['charttitle'], |
||
191 | 'charttext' => $this->params['charttext'], |
||
192 | 'infotext' => $this->params['infotext'], |
||
193 | 'datasource' => $this->params['datasource'], |
||
194 | 'rollerperiod' => $this->params['mavg'], |
||
195 | 'gridview' => $this->params['gridview'], |
||
196 | 'errorbar' => $this->params['errorbar'], |
||
197 | ] |
||
198 | ]; |
||
199 | |||
200 | // Array encoding and output |
||
201 | $requireHeadItem = [ $chartID => FormatJson::encode( $chartData ) ]; |
||
202 | SMWOutputs::requireHeadItem( $chartID, Skin::makeVariablesScript( $requireHeadItem ) ); |
||
203 | |||
204 | SMWOutputs::requireResource( 'ext.srf.dygraphs' ); |
||
205 | |||
206 | if ( $this->params['gridview'] === 'tabs' ) { |
||
207 | SMWOutputs::requireResource( 'ext.srf.util.grid' ); |
||
208 | } |
||
209 | |||
210 | // Chart/graph placeholder |
||
211 | $chart = Html::rawElement( |
||
212 | 'div', |
||
213 | [ 'id' => $chartID, 'class' => 'container', 'style' => "display:none;" ], |
||
214 | null |
||
215 | ); |
||
216 | |||
217 | // Processing/loading image |
||
218 | $processing = SRFUtils::htmlProcessingElement( $this->isHTML ); |
||
219 | |||
220 | // Beautify class selector |
||
221 | $class = $this->params['class'] ? ' ' . $this->params['class'] : ' dygraphs-common'; |
||
222 | |||
223 | // General output marker |
||
224 | return Html::rawElement( |
||
225 | 'div', |
||
226 | [ 'class' => 'srf-dygraphs' . $class ], |
||
227 | $processing . $chart |
||
228 | ); |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * @see SMWResultPrinter::getParamDefinitions |
||
233 | * |
||
234 | * @since 1.8 |
||
235 | * |
||
236 | * @param $definitions array of IParamDefinition |
||
237 | * |
||
238 | * @return array of IParamDefinition|array |
||
239 | */ |
||
240 | public function getParamDefinitions( array $definitions ) { |
||
241 | $params = parent::getParamDefinitions( $definitions ); |
||
242 | |||
243 | $params['datasource'] = [ |
||
244 | 'message' => 'srf-paramdesc-datasource', |
||
245 | 'default' => 'file', |
||
246 | 'values' => [ 'file', 'raw', 'url' ], |
||
247 | ]; |
||
248 | |||
249 | $params['errorbar'] = [ |
||
250 | 'message' => 'srf-paramdesc-errorbar', |
||
251 | 'default' => '', |
||
252 | 'values' => [ 'fraction', 'sigma', 'range' ], |
||
253 | ]; |
||
254 | |||
255 | $params['min'] = [ |
||
256 | 'type' => 'integer', |
||
257 | 'message' => 'srf-paramdesc-minvalue', |
||
258 | 'default' => '', |
||
259 | ]; |
||
260 | |||
261 | $params['mavg'] = [ |
||
262 | 'type' => 'integer', |
||
263 | 'message' => 'srf-paramdesc-movingaverage', |
||
264 | 'default' => 14, |
||
265 | 'lowerbound' => 0, |
||
266 | ]; |
||
267 | |||
268 | $params['gridview'] = [ |
||
269 | 'message' => 'srf-paramdesc-gridview', |
||
270 | 'default' => 'none', |
||
271 | 'values' => [ 'none', 'tabs' ], |
||
272 | ]; |
||
273 | |||
274 | $params['infotext'] = [ |
||
275 | 'message' => 'srf-paramdesc-infotext', |
||
276 | 'default' => '', |
||
277 | ]; |
||
278 | |||
279 | $params['unit'] = [ |
||
280 | 'message' => 'srf-paramdesc-unit', |
||
281 | 'default' => '', |
||
282 | ]; |
||
283 | |||
284 | $params['height'] = [ |
||
285 | 'type' => 'integer', |
||
286 | 'message' => 'srf_paramdesc_chartheight', |
||
287 | 'default' => 400, |
||
288 | 'lowerbound' => 1, |
||
289 | ]; |
||
290 | |||
291 | $params['width'] = [ |
||
292 | 'message' => 'srf_paramdesc_chartwidth', |
||
293 | 'default' => '100%', |
||
294 | ]; |
||
295 | |||
296 | $params['charttitle'] = [ |
||
297 | 'message' => 'srf_paramdesc_charttitle', |
||
298 | 'default' => '', |
||
299 | ]; |
||
300 | |||
301 | $params['charttext'] = [ |
||
302 | 'message' => 'srf-paramdesc-charttext', |
||
303 | 'default' => '', |
||
304 | ]; |
||
305 | |||
306 | $params['infotext'] = [ |
||
307 | 'message' => 'srf-paramdesc-infotext', |
||
308 | 'default' => '', |
||
309 | ]; |
||
310 | |||
311 | $params['ylabel'] = [ |
||
312 | 'message' => 'srf-paramdesc-yaxislabel', |
||
313 | 'default' => '', |
||
314 | ]; |
||
315 | |||
316 | $params['xlabel'] = [ |
||
317 | 'message' => 'srf-paramdesc-xaxislabel', |
||
318 | 'default' => '', |
||
319 | ]; |
||
320 | |||
321 | $params['class'] = [ |
||
322 | 'message' => 'srf-paramdesc-class', |
||
323 | 'default' => '', |
||
324 | ]; |
||
325 | |||
326 | return $params; |
||
327 | } |
||
328 | } |
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:
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 thebar
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.