Completed
Push — master ( 6dc7d8...407c40 )
by Karsten
15:45
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 === [] ) {
48
			// Add an error message to return method
49
			return $result->addErrors( [ 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 = [
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 = [];
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
		 *
103
		 * @var SMWResultArray $rows
104
		 */
105
		while ( $rows = $result->getNext() ) {
106
107
			/**
108
			 * @var SMWResultArray $field
109
			 * @var SMWDataValue $dataValue
110
			 */
111
			foreach ( $rows as $field ) {
112
113
				// Initialize the array each time it passes a new row to avoid data from
114
				// a previous row is remaining
115
				$rowData = [];
116
117
				// Get the label for the current property
118
				$propertyLabel = $field->getPrintRequest()->getLabel();
119
120
				// Get the label for the current subject
121
				// getTitle()->getText() will return only the main text without the
122
				// fragment(#) which can be arbitrary in case subobjects are involved
123
124
				// getTitle()->getFullText() will return the text with the fragment(#)
125
				// which is important when using subobjects
126
				$subjectLabel = $field->getResultSubject()->getTitle()->getFullText();
127
128
				while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
129
130
					// Get the data value item
131
					$rowData[] = $this->getDataValueItem( $dataValue->getDataItem()->getDIType(), $dataValue );
132
				}
133
134
				// Example how to build a hierarchical array by collecting all values
135
				// belonging to one subject/row using labels as array key representation
136
				$data[$subjectLabel][$propertyLabel][] = $rowData;
137
			}
138
		}
139
140
		// Return the data
141
		// return array( 'labels' => $labels, 'subjects' => $subjects, 'data' => $data );
142
		return $data;
143
	}
144
145
	/**
146
	 * A quick getway method to find all SMWDIWikiPage objects that make up the results
147
	 *
148
	 * @since 1.8
149
	 *
150
	 * @param SMWQueryResult $result
151
	 *
152
	 * @return array
153
	 */
154
	private function getSubjects( $result ) {
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
155
		$subjects = [];
156
157
		foreach ( $result as $wikiDIPage ) {
0 ignored issues
show
The expression $result of type object<SMWQueryResult> is not traversable.
Loading history...
158
			$subjects[] = $wikiDIPage->getTitle()->getText();
159
		}
160
		return $subjects;
161
	}
162
163
	/**
164
	 * Get all print requests property labels
165
	 *
166
	 * @since 1.8
167
	 *
168
	 * @param SMWQueryResult $result
169
	 *
170
	 * @return array
171
	 */
172
	private function getLabels( $result ) {
0 ignored issues
show
This method is not used, and could be removed.
Loading history...
173
		$printRequestsLabels = [];
174
175
		foreach ( $result as $printRequests ) {
0 ignored issues
show
The expression $result of type object<SMWQueryResult> is not traversable.
Loading history...
176
			$printRequestsLabels[] = $printRequests->getLabel();
177
		}
178
		return $printRequestsLabels;
179
	}
180
181
	/**
182
	 * Get a single data value item
183
	 *
184
	 * @since 1.8
185
	 *
186
	 * @param integer $type
187
	 * @param SMWDataValue $dataValue
188
	 *
189
	 * @return mixed
190
	 */
191
	private function getDataValueItem( $type, SMWDataValue $dataValue ) {
192
193
		if ( $type == SMWDataItem::TYPE_NUMBER ) {
194
195
			// Set unit if available
196
			$dataValue->setOutputFormat( $this->params['unit'] );
197
198
			// Check if unit is available and return the converted value otherwise
199
			// just return a plain number
200
			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...
201
		} else {
202
203
			// For all other data types return the wikivalue
204
			return $dataValue->getWikiValue();
205
		}
206
	}
207
208
	/**
209
	 * Prepare data for the output
210
	 *
211
	 * @since 1.8
212
	 *
213
	 * @param array $data
214
	 * @param array $options
215
	 *
216
	 * @return string
217
	 */
218
	protected function getFormatOutput( $data, $options ) {
219
220
		// The generated ID is to distinguish similar instances of the same
221
		// printer that can appear within the same page
222
		static $statNr = 0;
223
		$ID = 'srf-boilerplate-' . ++$statNr;
224
225
		// or use the PHP uniqid() to generate an unambiguous ID
226
		// $ID = uniqid();
227
228
		// Used to set that the output and being treated as HTML (opposed to plain wiki text)
229
		$this->isHTML = true;
230
231
		// Correct escaping is vital to minimize possibilites of malicious code snippets
232
		// and also a coherent string evalution therefore it is recommended
233
		// that data transferred to the JS plugin is JSON encoded
234
235
		// Assign the ID to make a data instance readly available and distinguishable
236
		// from other content within the same page
237
		$requireHeadItem = [ $ID => FormatJson::encode( $data ) ];
238
		SMWOutputs::requireHeadItem( $ID, Skin::makeVariablesScript( $requireHeadItem ) );
239
240
		// Add resource definitions that has been registered with SRF_Resource.php
241
		// Resource definitions contain scripts, styles, messages etc.
242
		// SMWOutputs::requireResource( 'ext.srf.boilerplate.namespace' );
243
		SMWOutputs::requireResource( 'ext.srf.boilerplate.simple' );
244
245
		// Prepares an HTML element showing a rotating spinner indicating that something
246
		// will appear at this placeholder. The element will be visible as for as
247
		// long as jquery is not loaded and the JS plugin did not hide/removed the element.
248
		$processing = SRFUtils::htmlProcessingElement();
249
250
		// Add two elements a outer wrapper that is assigned a class which the JS plugin
251
		// can select and will fetch all instances of the same result printer and an innner
252
		// container which is set invisible (display=none) for as long as the JS plugin
253
		// holds the content hidden. It is normally the place where the "hard work"
254
		// is done hidden from the user until it is ready.
255
		// The JS plugin can prepare the output within this container without presenting
256
		// unfinished visual content, to avoid screen clutter and improve user experience.
257
		return Html::rawElement(
258
			'div',
259
			[
260
				'class' => 'srf-boilerplate'
261
			],
262
			$processing . Html::element(
263
				'div',
264
				[
265
					'id' => $ID,
266
					'class' => 'container',
267
					'style' => 'display:none;'
268
				],
269
				null
270
			)
271
		);
272
	}
273
274
	/**
275
	 * @see SMWResultPrinter::getParamDefinitions
276
	 *
277
	 * @since 1.8
278
	 *
279
	 * @param $definitions array of IParamDefinition
280
	 *
281
	 * @return array of IParamDefinition|array
282
	 */
283
	public function getParamDefinitions( array $definitions ) {
284
		$params = parent::getParamDefinitions( $definitions );
285
286
		// Add your parameters here
287
288
		// Example of a unit paramter
289
		$params['unit'] = [
290
			'message' => 'srf-paramdesc-unit',
291
			'default' => '',
292
		];
293
294
		return $params;
295
	}
296
}