Completed
Push — master ( ea0e5d...10ff2a )
by mw
02:37
created

formats/dygraphs/SRF_Dygraphs.php (8 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
 * 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 === array() ) {
41
			return $result->addErrors( array( wfMessage( 'srf-warn-empy-chart' )->inContentLanguage()->text() ) );
42
		} else {
43
			$options['sask'] = SRFUtils::htmlQueryResultLink( $this->getLink( $result, SMW_OUTPUT_HTML ) );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = 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...
$this->getLink($result, SMW_OUTPUT_HTML) is of type object<SMWInfolink>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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 = array();
60
		
61
		while ( $rows = $result->getNext() ) { // Objects (pages)
62
			$annotation = array();
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() === NS_FILE && !$dataSource ){
74
					$aggregatedValues['subject'] = SMWWikiPageValue::makePageFromTitle( $subject->getTitle() )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) );
0 ignored issues
show
Deprecated Code introduced by
The method SMWWikiPageValue::makePageFromTitle() has been deprecated with message: This method will vanish before SMW 1.7. If you really need this, simply copy its code.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
75
					$aggregatedValues['url'] = wfFindFile( $subject->getTitle() )->getUrl();
76
					$dataSource = true;
77
				}
78
79
				// Proceed only where a label is known otherwise items are of no use
80
				// for being a potential object identifier
81
				if ( $field->getPrintRequest()->getLabel() !== '' ){
82
					$propertyLabel = $field->getPrintRequest()->getLabel();
83
				}else{
84
					continue;
85
				}
86
87
				while ( ( $dataValue = $field->getNextDataValue() ) !== false ) { // Data values
88
89
					// Jump the column (indicated by continue) because we don't want the data source being part of the annotation array
90
					if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'raw' && !$dataSource ){
91
						// Support data source = raw which pulls the url from a wikipage in raw format
92
						$aggregatedValues['subject'] = SMWWikiPageValue::makePageFromTitle( $dataValue->getTitle() )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) );
0 ignored issues
show
Deprecated Code introduced by
The method SMWWikiPageValue::makePageFromTitle() has been deprecated with message: This method will vanish before SMW 1.7. If you really need this, simply copy its code.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
93
						$aggregatedValues['url'] = $dataValue->getTitle()->getLocalURL( 'action=raw' );
94
						$dataSource = true;
95
						continue;
96
					} elseif ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'file' && $dataValue->getTitle()->getNamespace() === NS_FILE && !$dataSource ) {
97
						// Support data source = file which pulls the url from a uploaded file
98
						$aggregatedValues['subject'] = SMWWikiPageValue::makePageFromTitle( $dataValue->getTitle() )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) );
0 ignored issues
show
Deprecated Code introduced by
The method SMWWikiPageValue::makePageFromTitle() has been deprecated with message: This method will vanish before SMW 1.7. If you really need this, simply copy its code.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
99
						$aggregatedValues['url'] = wfFindFile( $dataValue->getTitle() )->getUrl();
100
						$dataSource = true;
101
						continue;
102
					} elseif ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_URI && $this->params['datasource'] === 'url'  && !$dataSource ){
103
						// Support data source = url, pointing to an url data source
104
						$aggregatedValues['link'] = $dataValue->getShortHTMLText( $this->getLinker( false ) );
105
						$aggregatedValues['url'] = $dataValue->getURL();
106
						$dataSource = true;
107
						continue;
108
					}
109
110
					// The annotation should adhere outlined conventions as the label identifies the array object key
111
					// series -> Required The name of the series to which the annotated point belongs
112
					// x -> Required The x value of the point
113
					// shortText -> Text that will appear as annotation flag
114
					// text -> A longer description of the annotation
115
					// @see  http://dygraphs.com/annotations.html
116
					if ( in_array( $propertyLabel, array( 'series', 'x', 'shortText', 'text' ) ) ){
117
						if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_NUMBER ){
118
							// Set unit if available
119
							$dataValue->setOutputFormat( $this->params['unit'] );
120
							// Check if unit is available
121
							$annotation[$propertyLabel] = $dataValue->getUnit() !== '' ? $dataValue->getShortWikiText() : $dataValue->getNumber() ;
122
						} else {
123
							$annotation[$propertyLabel] = $dataValue->getWikiValue();
124
						}
125
					}
126
				}
127
			}
128
			// Sum-up collected row items in a single array
129
			if ( $annotation !== array() ){
130
				$aggregatedValues['annotation'][] =  $annotation;
131
			}
132
		}
133
		return $aggregatedValues;
134
	}
135
136
	/**
137
	 * Prepare data for the output
138
	 *
139
	 * @since 1.8
140
	 *
141
	 * @param array $data
142
	 *
143
	 * @return string
144
	 */
145
	protected function getFormatOutput( $data, $options ) {
146
147
		// Object count
148
		static $statNr = 0;
149
		$chartID = 'srf-dygraphs-' . ++$statNr;
150
151
		$this->isHTML = true;
152
153
		// Reorganize the raw data
154
		if ( $this->params['datasource'] === 'page' ){
155
			foreach ( $data as $key => $values ) {
156
				$dataObject[] = array ( 'label' => $key, 'data' => $values );
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 $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...
157
			}
158
		}else{
159
				$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 $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...
160
		}
161
162
		// Prepare transfer array
163
		$chartData = array (
164
			'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

  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...
165
			'sask' => $options['sask'],
166
			'parameters' => array (
167
				'width'        => $this->params['width'],
168
				'height'       => $this->params['height'],
169
				'xlabel'       => $this->params['xlabel'],
170
				'ylabel'       => $this->params['ylabel'],
171
				'charttitle'   => $this->params['charttitle'],
172
				'charttext'    => $this->params['charttext'],
173
				'infotext'     => $this->params['infotext'],
174
				'datasource'   => $this->params['datasource'],
175
				'rollerperiod' => $this->params['mavg'],
176
				'gridview'    => $this->params['gridview'],
177
				'errorbar'     => $this->params['errorbar'],
178
			)
179
		);
180
181
		// Array encoding and output
182
		$requireHeadItem = array ( $chartID => FormatJson::encode( $chartData ) );
183
		SMWOutputs::requireHeadItem( $chartID, Skin::makeVariablesScript( $requireHeadItem ) );
184
185
		SMWOutputs::requireResource( 'ext.srf.dygraphs' );
186
187
		if ( $this->params['gridview'] === 'tabs' ) {
188
			SMWOutputs::requireResource( 'ext.srf.util.grid' );
189
		}
190
191
		// Chart/graph placeholder
192
		$chart = Html::rawElement(
193
			'div',
194
			array('id' => $chartID, 'class' => 'container', 'style' => "display:none;" ),
195
			null
196
		);
197
198
		// Processing/loading image
199
		$processing = SRFUtils::htmlProcessingElement( $this->isHTML );
200
201
		// Beautify class selector
202
		$class = $this->params['class'] ? ' ' . $this->params['class'] : ' dygraphs-common';
203
204
		// General output marker
205
		return Html::rawElement(
206
			'div',
207
			array( 'class' => 'srf-dygraphs' . $class	),
208
			$processing . $chart
209
		);
210
	}
211
212
	/**
213
	 * @see SMWResultPrinter::getParamDefinitions
214
	 *
215
	 * @since 1.8
216
	 *
217
	 * @param $definitions array of IParamDefinition
218
	 *
219
	 * @return array of IParamDefinition|array
220
	 */
221
	public function getParamDefinitions( array $definitions ) {
222
		$params = parent::getParamDefinitions( $definitions );
223
224
		$params['datasource'] = array(
225
			'message' => 'srf-paramdesc-datasource',
226
			'default' => 'file',
227
			'values' => array( 'file', 'raw', 'url' ),
228
		);
229
230
		$params['errorbar'] = array(
231
			'message' => 'srf-paramdesc-errorbar',
232
			'default' => '',
233
			'values' => array( 'fraction', 'sigma', 'range' ),
234
		);
235
236
		$params['min'] = array(
237
			'type' => 'integer',
238
			'message' => 'srf-paramdesc-minvalue',
239
			'default' => '',
240
		);
241
242
		$params['mavg'] = array(
243
			'type' => 'integer',
244
			'message' => 'srf-paramdesc-movingaverage',
245
			'default' => 14,
246
			'lowerbound' => 0,
247
		);
248
249
		$params['gridview'] = array(
250
			'message' => 'srf-paramdesc-gridview',
251
			'default' => 'none',
252
			'values' => array( 'none' , 'tabs' ),
253
		);
254
255
		$params['infotext'] = array(
256
			'message' => 'srf-paramdesc-infotext',
257
			'default' => '',
258
		);
259
260
		$params['unit'] = array(
261
			'message' => 'srf-paramdesc-unit',
262
			'default' => '',
263
		);
264
265
		$params['height'] = array(
266
			'type' => 'integer',
267
			'message' => 'srf_paramdesc_chartheight',
268
			'default' => 400,
269
			'lowerbound' => 1,
270
		);
271
272
		$params['width'] = array(
273
			'message' => 'srf_paramdesc_chartwidth',
274
			'default' => '100%',
275
		);
276
277
		$params['charttitle'] = array(
278
			'message' => 'srf_paramdesc_charttitle',
279
			'default' => '',
280
		);
281
282
		$params['charttext'] = array(
283
			'message' => 'srf-paramdesc-charttext',
284
			'default' => '',
285
		);
286
287
		$params['infotext'] = array(
288
			'message' => 'srf-paramdesc-infotext',
289
			'default' => '',
290
		);
291
292
		$params['ylabel'] = array(
293
			'message' => 'srf-paramdesc-yaxislabel',
294
			'default' => '',
295
		);
296
297
		$params['xlabel'] = array(
298
			'message' => 'srf-paramdesc-xaxislabel',
299
			'default' => '',
300
		);
301
302
		$params['class'] = array(
303
			'message' => 'srf-paramdesc-class',
304
			'default' => '',
305
		);
306
307
		return $params;
308
	}
309
}