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

formats/boilerplate/SRF_Boilerplate.php (6 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
 * Boilerplate query printer
5
 *
6
 * Add your description here ...
7
 *
8
 * @see http://www.semantic-mediawiki.org/wiki/Writing_result_printers
9
 *
10
 * @since 1.8
11
 *
12
 * @licence GNU GPL v2 or later
13
 * @author mwjames
14
 */
15
16
/**
17
 * Description ... this part is used for the doxygen processor
18
 *
19
 * @ingroup SemanticResultFormats
20
 */
21
class SRFBoilerplate extends SMWResultPrinter {
22
23
	/**
24
	 * @see SMWResultPrinter::getName
25
	 * @return string
26
	 */
27
	public function getName() {
28
		// Add your result printer name here
29
		return wfMessage( 'srf-printername-boilerplate' )->text();
30
	}
31
32
	/**
33
	 * @see SMWResultPrinter::getResultText
34
	 *
35
	 * @param SMWQueryResult $result
36
	 * @param $outputMode
37
	 *
38
	 * @return string
39
	 */
40
	protected function getResultText( SMWQueryResult $result, $outputMode ) {
41
42
		// Data processing
43
		// It is advisable to separate data processing from output logic
44
		$data = $this->getResultData( $result, $outputMode );
45
46
		// Check if the data processing returned any results otherwise just bailout
47
		if ( $data === array() ) {
48
			// Add an error message to return method
49
			return $result->addErrors( array( wfMessage( 'srf-no-results' )->inContentLanguage()->text() ) );
50
		} else {
51
			// Add options if needed to format the output
52
53
			// $outputMode can be specified as
54
			// SMW_OUTPUT_HTML
55
			// SMW_OUTPUT_FILE
56
			// SMW_OUTPUT_WIKI
57
58
			// For implementing template support this options has to be set but if you
59
			// manipulate data via jQuery/JavaScript it is less likely that you need
60
			// this option since templates will influence how wiki text is parsed
61
			// but will have no influence in how a HTML representation is altered
62
			// $this->hasTemplates = true;
63
64
			$options = array(
65
				'mode' => $outputMode
66
			);
67
68
			// Return formatted results
69
			return $this->getFormatOutput( $data, $options );
70
		}
71
	}
72
73
	/**
74
	 * Returns an array with data
75
	 *
76
	 * @since 1.8
77
	 *
78
	 * @param SMWQueryResult $result
79
	 * @param $outputMode
80
	 *
81
	 * @return array
82
	 */
83
	protected function getResultData( SMWQueryResult $result, $outputMode ) {
84
85
		$data = array();
86
87
		// This is an example implementation on how to select available data from
88
		// a result set. Please make appropriate adoptions necessary for your
89
		// application.
90
91
		// Some methods are declared as private to show case which objects are
92
		// directly accessible within SMWQueryResult
93
94
		// Get all SMWDIWikiPage objects that make up the results
95
		// $subjects = $this->getSubjects( $result->getResults() );
96
97
		// Get all print requests property labels
98
		// $labels = $this->getLabels( $result->getPrintRequests() );
99
100
		/**
101
		 * Get all values for all rows that belong to the result set
102
		 * @var SMWResultArray $rows
103
		 */
104
		while ( $rows = $result->getNext() ) {
105
106
			/**
107
			 * @var SMWResultArray $field
108
			 * @var SMWDataValue $dataValue
109
			 */
110
			foreach ( $rows as $field ) {
111
112
				// Initialize the array each time it passes a new row to avoid data from
113
				// a previous row is remaining
114
				$rowData = array();
115
116
				// Get the label for the current property
117
				$propertyLabel = $field->getPrintRequest()->getLabel();
118
119
				// Get the label for the current subject
120
				// getTitle()->getText() will return only the main text without the
121
				// fragment(#) which can be arbitrary in case subobjects are involved
122
123
				// getTitle()->getFullText() will return the text with the fragment(#)
124
				// which is important when using subobjects
125
				$subjectLabel = $field->getResultSubject()->getTitle()->getFullText();
126
127
				while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
128
129
					// Get the data value item
130
					$rowData[] = $this->getDataValueItem( $dataValue->getDataItem()->getDIType(), $dataValue );
131
				}
132
133
			// Example how to build a hierarchical array by collecting all values
134
			// belonging to one subject/row using labels as array key representation
135
			$data[$subjectLabel][$propertyLabel][] = $rowData;
136
			}
137
		}
138
139
		// Return the data
140
		// return array( 'labels' => $labels, 'subjects' => $subjects, 'data' => $data );
141
		return $data;
142
	}
143
144
	/**
145
	 * A quick getway method to find all SMWDIWikiPage objects that make up the results
146
	 *
147
	 * @since 1.8
148
	 *
149
	 * @param SMWQueryResult $result
150
	 *
151
	 * @return array
152
	 */
153
	private function getSubjects( $result ) {
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
154
		$subjects = array();
155
156
		foreach ( $result as $wikiDIPage ) {
0 ignored issues
show
The expression $result of type object<SMWQueryResult> is not traversable.
Loading history...
157
			$subjects[] = $wikiDIPage->getTitle()->getText();
158
		}
159
		return $subjects;
160
	}
161
162
	/**
163
	 * Get all print requests property labels
164
	 *
165
	 * @since 1.8
166
	 *
167
	 * @param SMWQueryResult $result
168
	 *
169
	 * @return array
170
	 */
171
	private function getLabels( $result ) {
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
172
		$printRequestsLabels = array();
173
174
		foreach ( $result as $printRequests ) {
0 ignored issues
show
The expression $result of type object<SMWQueryResult> is not traversable.
Loading history...
175
			$printRequestsLabels[] = $printRequests->getLabel();
176
		}
177
		return $printRequestsLabels;
178
	}
179
180
	/**
181
	 * Get a single data value item
182
	 *
183
	 * @since 1.8
184
	 *
185
	 * @param integer $type
186
	 * @param SMWDataValue $dataValue
187
	 *
188
	 * @return mixed
189
	 */
190
	private function getDataValueItem( $type, SMWDataValue $dataValue ) {
191
192
		if ( $type == SMWDataItem::TYPE_NUMBER ){
193
194
			// Set unit if available
195
			$dataValue->setOutputFormat( $this->params['unit'] );
196
197
			// Check if unit is available and return the converted value otherwise
198
			// just return a plain number
199
			return $dataValue->getUnit() !== '' ? $dataValue->getShortWikiText() : $dataValue->getNumber() ;
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getUnit() does only exist in the following sub-classes of SMWDataValue: SMWNumberValue, SMWQuantityValue, SMW\DataValues\TemperatureValue. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getNumber() does only exist in the following sub-classes of SMWDataValue: SMWNumberValue, SMWQuantityValue, SMW\DataValues\TemperatureValue. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
200
		} else {
201
202
			// For all other data types return the wikivalue
203
			return $dataValue->getWikiValue();
204
		}
205
	}
206
207
	/**
208
	 * Prepare data for the output
209
	 *
210
	 * @since 1.8
211
	 *
212
	 * @param array $data
213
	 * @param array $options
214
	 *
215
	 * @return string
216
	 */
217
	protected function getFormatOutput( $data, $options ) {
218
219
		// The generated ID is to distinguish similar instances of the same
220
		// printer that can appear within the same page
221
		static $statNr = 0;
222
		$ID = 'srf-boilerplate-' . ++$statNr;
223
224
		// or use the PHP uniqid() to generate an unambiguous ID
225
		// $ID = uniqid();
226
227
		// Used to set that the output and being treated as HTML (opposed to plain wiki text)
228
		$this->isHTML = true;
229
230
		// Correct escaping is vital to minimize possibilites of malicious code snippets
231
		// and also a coherent string evalution therefore it is recommended
232
		// that data transferred to the JS plugin is JSON encoded
233
234
		// Assign the ID to make a data instance readly available and distinguishable
235
		// from other content within the same page
236
		$requireHeadItem = array ( $ID => FormatJson::encode( $data ) );
237
		SMWOutputs::requireHeadItem( $ID, Skin::makeVariablesScript( $requireHeadItem ) );
238
239
		// Add resource definitions that has been registered with SRF_Resource.php
240
		// Resource definitions contain scripts, styles, messages etc.
241
		// SMWOutputs::requireResource( 'ext.srf.boilerplate.namespace' );
242
		SMWOutputs::requireResource( 'ext.srf.boilerplate.simple' );
243
244
		// Prepares an HTML element showing a rotating spinner indicating that something
245
		// will appear at this placeholder. The element will be visible as for as
246
		// long as jquery is not loaded and the JS plugin did not hide/removed the element.
247
		$processing = SRFUtils::htmlProcessingElement();
248
249
		// Add two elements a outer wrapper that is assigned a class which the JS plugin
250
		// can select and will fetch all instances of the same result printer and an innner
251
		// container which is set invisible (display=none) for as long as the JS plugin
252
		// holds the content hidden. It is normally the place where the "hard work"
253
		// is done hidden from the user until it is ready.
254
		// The JS plugin can prepare the output within this container without presenting
255
		// unfinished visual content, to avoid screen clutter and improve user experience.
256
		return Html::rawElement(
257
			'div',
258
			array(
259
				'class' => 'srf-boilerplate'
260
			),
261
			$processing . Html::element(
262
				'div',
263
				array(
264
					'id' => $ID,
265
					'class' => 'container',
266
					'style' => 'display:none;'
267
				),
268
				null
269
			)
270
		);
271
	}
272
273
	/**
274
	 * @see SMWResultPrinter::getParamDefinitions
275
	 *
276
	 * @since 1.8
277
	 *
278
	 * @param $definitions array of IParamDefinition
279
	 *
280
	 * @return array of IParamDefinition|array
281
	 */
282
	public function getParamDefinitions( array $definitions ) {
283
		$params = parent::getParamDefinitions( $definitions );
284
285
		// Add your parameters here
286
287
		// Example of a unit paramter
288
		$params['unit'] = array(
289
			'message' => 'srf-paramdesc-unit',
290
			'default' => '',
291
		);
292
293
		return $params;
294
	}
295
}