SMWAskPage::makeHTMLResult()   F
last analyzed

Complexity

Conditions 31
Paths > 20000

Size

Total Lines 188
Code Lines 108

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 72
CRAP Score 48.9446

Importance

Changes 0
Metric Value
cc 31
eloc 108
nc 323520
nop 0
dl 0
loc 188
ccs 72
cts 98
cp 0.7347
crap 48.9446
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
use ParamProcessor\Param;
4
use SMW\Query\PrintRequest;
5
use SMW\Query\QueryLinker;
6
use SMW\MediaWiki\Specials\Ask\ErrorFormWidget;
7
use SMW\MediaWiki\Specials\Ask\InputFormWidget;
8
use SMW\MediaWiki\Specials\Ask\ParametersFormWidget;
9
use SMW\ApplicationFactory;
10
11
/**
12
 * This special page for MediaWiki implements a customisable form for
13
 * executing queries outside of articles.
14
 *
15
 * @ingroup SMWSpecialPage
16
 * @ingroup SpecialPage
17
 *
18
 * @author Markus Krötzsch
19
 * @author Yaron Koren
20
 * @author Sanyam Goyal
21
 * @author Jeroen De Dauw
22
 * @author mwjames
23
 *
24
 * TODO: Split up the megamoths into sane methods.
25
 */
26
class SMWAskPage extends SpecialPage {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
27
28
	private $m_querystring = '';
29
	private $m_params = array();
30
	private $m_printouts = array();
31
	private $m_editquery = false;
32
	private $queryLinker = null;
33
34
	/**
35
	 * @var InputFormWidget
36
	 */
37
	private $inputFormWidget;
38
39
	/**
40
	 * @var ErrorFormWidget
41
	 */
42
	private $errorFormWidget;
43
44
	/**
45
	 * @var ParametersFormWidget
46
	 */
47
	private $parametersFormWidget;
48
49
	/**
50
	 * @var Param[]
51
	 */
52
	private $params = array();
53
54 2
	public function __construct() {
55 2
		parent::__construct( 'Ask' );
56
57 2
		$this->inputFormWidget = new InputFormWidget();
58 2
		$this->errorFormWidget = new ErrorFormWidget();
59 2
		$this->parametersFormWidget = new ParametersFormWidget();
60 2
	}
61
62
	/**
63
	 * SpecialPage::doesWrites
64
	 *
65
	 * @return boolean
66
	 */
67
	public function doesWrites() {
68
		return true;
69
	}
70
71
	/**
72
	 * Main entrypoint for the special page.
73
	 *
74
	 * @param string $p
75
	 */
76 7
	public function execute( $p ) {
77 7
		global $smwgQEnabled;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
78
79 7
		$out = $this->getOutput();
80 7
		$request = $this->getRequest();
81
82 7
		$this->parametersFormWidget->setTooltipDisplay(
83 7
			$this->getUser()->getOption( 'smw-prefs-ask-options-tooltip-display' )
84
		);
85
86 7
		$out->addModules( 'ext.smw.style' );
87 7
		$out->addModules( 'ext.smw.ask' );
88 7
		$out->addModules( 'ext.smw.property' );
89
90 7
		$this->setHeaders();
91
92 7
		if ( !$smwgQEnabled ) {
93
			$out->addHTML( '<br />' . wfMessage( 'smw_iq_disabled' )->escaped() );
94
		} else {
95 7
			if ( $request->getCheck( 'showformatoptions' ) ) {
96
				// handle Ajax action
97
				$format = $request->getVal( 'showformatoptions' );
98
				$params = $request->getArray( 'params' );
99
				$out->disable();
100
				echo $this->parametersFormWidget->createParametersForm( $format, $params );
101
			} else {
102 7
				$this->extractQueryParameters( $p );
103 7
				$this->makeHTMLResult();
104
			}
105
		}
106
107 7
		$this->addExternalHelpLinkFor( 'smw_ask_doculink' );
108
109 7
		SMWOutputs::commitToOutputPage( $out ); // make sure locally collected output data is pushed to the output!
110 7
	}
111
112
	/**
113
	 * This code rather hacky since there are many ways to call that special page, the most involved of
114
	 * which is the way that this page calls itself when data is submitted via the form (since the shape
115
	 * of the parameters then is governed by the UI structure, as opposed to being governed by reason).
116
	 *
117
	 * TODO: most of this can probably be killed now we are using Validator
118
	 *
119
	 * @param string $p
120
	 */
121 7
	protected function extractQueryParameters( $p ) {
122 7
		global $wgRequest, $smwgQMaxInlineLimit;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
123
124
		// First make all inputs into a simple parameter list that can again be parsed into components later.
125 7
		if ( $wgRequest->getCheck( 'q' ) ) { // called by own Special, ignore full param string in that case
126 2
			$query_val = $wgRequest->getVal( 'p' );
127
128 2
			if ( !empty( $query_val ) ) {
129
				// p is used for any additional parameters in certain links.
130
				$rawparams = SMWInfolink::decodeParameters( $query_val, false );
131
			}
132
			else {
133 2
				$query_values = $wgRequest->getArray( 'p' );
134
135 2
				if ( is_array( $query_values ) ) {
136
					foreach ( $query_values as $key => $val ) {
137
						if ( empty( $val ) ) {
138
							unset( $query_values[$key] );
139
						}
140
					}
141
				}
142
143
				// p is used for any additional parameters in certain links.
144 2
				$rawparams = SMWInfolink::decodeParameters( $query_values, false );
145
146
			}
147
		} else { // called from wiki, get all parameters
148 5
			$rawparams = SMWInfolink::decodeParameters( $p, true );
149
		}
150
151
		// Check for q= query string, used whenever this special page calls itself (via submit or plain link):
152 7
		$this->m_querystring = $wgRequest->getText( 'q' );
153 7
		if ( $this->m_querystring !== '' ) {
154 1
			$rawparams[] = $this->m_querystring;
155
		}
156
157
		// Check for param strings in po (printouts), appears in some links and in submits:
158 7
		$paramstring = $wgRequest->getText( 'po' );
159
160 7
		if ( $paramstring !== '' ) { // parameters from HTML input fields
161
			$ps = explode( "\n", $paramstring ); // params separated by newlines here (compatible with text-input for printouts)
162
163
			foreach ( $ps as $param ) { // add initial ? if omitted (all params considered as printouts)
164
				$param = trim( $param );
165
166
				if ( ( $param !== '' ) && ( $param { 0 } != '?' ) ) {
167
					$param = '?' . $param;
168
				}
169
170
				$rawparams[] = $param;
171
			}
172
		}
173
174
		// ?Has property=Foo|+index=1
175 7
		foreach ( $rawparams as $key => $value ) {
176
			if (
177 4
				( $key !== '' && $key{0} == '?' && strpos( $value, '|' ) !== false ) ||
178 4
				( is_string( $value ) && $value !== '' && $value{0} == '?' && strpos( $value, '|' ) !== false ) ) {
179
				$extra = explode( '|', $value );
180
181
				unset( $rawparams[$key] );
182
				foreach ( $extra as $k => $val) {
183 4
					$rawparams[] = $k == 0 && $key{0} == '?' ? $key . '=' . $val : $val;
184
				}
185
			}
186
		}
187
188 7
		unset( $rawparams['title'] );
189
190
		// Now parse parameters and rebuilt the param strings for URLs.
191 7
		list( $this->m_querystring, $this->m_params, $this->m_printouts ) = SMWQueryProcessor::getComponentsFromFunctionParams( $rawparams, false );
192
193
		// Try to complete undefined parameter values from dedicated URL params.
194 7
		if ( !array_key_exists( 'format', $this->m_params ) ) {
195 4
			$this->m_params['format'] = 'broadtable';
196
		}
197
198 7
		if ( !array_key_exists( 'order', $this->m_params ) ) {
199 7
			$order_values = $wgRequest->getArray( 'order' );
200
201 7
			if ( is_array( $order_values ) ) {
202
				$this->m_params['order'] = '';
203
204
				foreach ( $order_values as $order_value ) {
205
					if ( $order_value === '' ) {
206
						$order_value = 'ASC';
207
					}
208
					$this->m_params['order'] .= ( $this->m_params['order'] !== '' ? ',' : '' ) . $order_value;
209
				}
210
			}
211
		}
212
213 7
		$this->m_num_sort_values = 0;
214
215 7
		if  ( !array_key_exists( 'sort', $this->m_params ) ) {
216 7
			$sort_values = $wgRequest->getArray( 'sort' );
217 7
			if ( is_array( $sort_values ) ) {
218
				$this->m_params['sort'] = implode( ',', $sort_values );
219
				$this->m_num_sort_values = count( $sort_values );
220
			}
221
		}
222
223 7
		if ( !array_key_exists( 'offset', $this->m_params ) ) {
224 4
			$this->m_params['offset'] = $wgRequest->getVal( 'offset' );
225 4
			if ( $this->m_params['offset'] === '' )  {
226
				$this->m_params['offset'] = 0;
227
			}
228
		}
229
230 7
		if ( !array_key_exists( 'limit', $this->m_params ) ) {
231 6
			$this->m_params['limit'] = $wgRequest->getVal( 'limit' );
232
233 6
			if ( $this->m_params['limit'] === '' ) {
234
				 $this->m_params['limit'] = ( $this->m_params['format'] == 'rss' ) ? 10 : 20; // Standard limit for RSS.
235
			}
236
		}
237
238 7
		$this->m_params['limit'] = min( $this->m_params['limit'], $smwgQMaxInlineLimit );
239
240 7
		$this->m_editquery = ( $wgRequest->getVal( 'eq' ) == 'yes' ) || ( $this->m_querystring === '' );
241 7
	}
242
243 4
	private function getStoreFromParams( array $params ) {
244 4
		return ApplicationFactory::getInstance()->getQuerySourceFactory()->get( $params['source']->getValue() );
245
	}
246
247
	/**
248
	 * TODO: document
249
	 */
250 7
	protected function makeHTMLResult() {
251 7
		global $wgOut;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
252
253
		// TODO: hold into account $smwgAutocompleteInSpecialAsk
254
255 7
		$result = '';
256 7
		$res = null;
257
258
		// build parameter strings for URLs, based on current settings
259 7
		$urlArgs['q'] = $this->m_querystring;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$urlArgs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $urlArgs = 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...
260
261 7
		$tmp_parray = array();
262 7
		foreach ( $this->m_params as $key => $value ) {
263 7
			if ( !in_array( $key, array( 'sort', 'order', 'limit', 'offset', 'title' ) ) ) {
264 7
				$tmp_parray[$key] = $value;
265
			}
266
		}
267
268 7
		$urlArgs['p'] = SMWInfolink::encodeParameters( $tmp_parray );
269 7
		$printoutstring = '';
270 7
		$duration = 0;
271 7
		$navigation = '';
272 7
		$queryobj = null;
273
274
		/**
275
		 * @var PrintRequest $printout
276
		 */
277 7
		foreach ( $this->m_printouts as $printout ) {
278 3
			$printoutstring .= $printout->getSerialisation( true ) . "\n";
279
		}
280
281 7
		if ( $printoutstring !== '' ) {
282 3
			$urlArgs['po'] = $printoutstring;
283
		}
284
285 7
		if ( array_key_exists( 'sort', $this->m_params ) ) {
286
			$urlArgs['sort'] = $this->m_params['sort'];
287
		}
288
289 7
		if ( array_key_exists( 'order', $this->m_params ) ) {
290
			$urlArgs['order'] = $this->m_params['order'];
291
		}
292
293 7
		if ( $this->getRequest()->getCheck( 'bTitle' ) ) {
294
			$urlArgs['bTitle'] = $this->getRequest()->getVal( 'bTitle' );
295
			$urlArgs['bMsg'] = $this->getRequest()->getVal( 'bMsg' );
296
		}
297
298 7
		if ( $this->m_querystring !== '' ) {
299
			// FIXME: this is a hack
300 4
			SMWQueryProcessor::addThisPrintout( $this->m_printouts, $this->m_params );
301 4
			$params = SMWQueryProcessor::getProcessedParams( $this->m_params, $this->m_printouts );
302 4
			$this->m_params['format'] = $params['format']->getValue();
303
304 4
			$this->params = $params;
305
306 4
			$queryobj = SMWQueryProcessor::createQuery(
307 4
				$this->m_querystring,
308
				$params,
309 4
				SMWQueryProcessor::SPECIAL_PAGE,
310 4
				$this->m_params['format'],
311 4
				$this->m_printouts
312
			);
313
314
			/**
315
			 * @var SMWQueryResult $res
316
			 */
317
318 4
			$queryobj->setOption( SMWQuery::PROC_CONTEXT, 'SpecialAsk' );
319 4
			$this->queryLinker = QueryLinker::get( $queryobj, $this->m_params );
320
321
			// Determine query results
322 4
			$duration = microtime( true );
323 4
			$res = $this->getStoreFromParams( $params )->getQueryResult( $queryobj );
324 4
			$duration = number_format( (microtime( true ) - $duration), 4, '.', '' );
325
326
			// Try to be smart for rss/ical if no description/title is given and we have a concept query:
327 4
			if ( $this->m_params['format'] == 'rss' ) {
328
				$desckey = 'rssdescription';
329
				$titlekey = 'rsstitle';
330 4
			} elseif ( $this->m_params['format'] == 'icalendar' ) {
331
				$desckey = 'icalendardescription';
332
				$titlekey = 'icalendartitle';
333 4
			} else { $desckey = false;
334
			}
335
336 4
			if ( ( $desckey ) && ( $queryobj->getDescription() instanceof SMWConceptDescription ) &&
0 ignored issues
show
Bug Best Practice introduced by
The expression $desckey of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug introduced by
The class SMWConceptDescription does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
337 4
			     ( !isset( $this->m_params[$desckey] ) || !isset( $this->m_params[$titlekey] ) ) ) {
338
				$concept = $queryobj->getDescription()->getConcept();
339
340
				if ( !isset( $this->m_params[$titlekey] ) ) {
341
					$this->m_params[$titlekey] = $concept->getText();
0 ignored issues
show
Bug introduced by
The variable $titlekey 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...
342
				}
343
344
				if ( !isset( $this->m_params[$desckey] ) ) {
345
					// / @bug The current SMWStore will never return SMWConceptValue (an SMWDataValue) here; it might return SMWDIConcept (an SMWDataItem)
346
					$dv = end( \SMW\StoreFactory::getStore()->getPropertyValues( SMWWikiPageValue::makePageFromTitle( $concept ), new SMW\DIProperty( '_CONC' ) ) );
0 ignored issues
show
Bug introduced by
\SMW\StoreFactory::getSt...MW\DIProperty('_CONC')) cannot be passed to end() as the parameter $array expects a reference.
Loading history...
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...
347
					if ( $dv instanceof SMWConceptValue ) {
348
						$this->m_params[$desckey] = $dv->getDocu();
349
					}
350
				}
351
			}
352
353 4
			$printer = SMWQueryProcessor::getResultPrinter( $this->m_params['format'], SMWQueryProcessor::SPECIAL_PAGE );
354 4
			$printer->setShowErrors( false );
355
356 4
			global $wgRequest;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
357
358 4
			$hidequery = $wgRequest->getVal( 'eq' ) == 'no';
359 4
			$debug = '';
360
361
			// Allow to generate a debug output
362 4
			if ( $this->getRequest()->getVal( 'debug' ) ) {
363
364
				$queryobj = SMWQueryProcessor::createQuery(
365
					$this->m_querystring,
366
					$params,
367
					SMWQueryProcessor::SPECIAL_PAGE,
368
					'debug',
369
					$this->m_printouts
370
				);
371
372
				$debug = $this->getStoreFromParams( $params )->getQueryResult( $queryobj );
373
			}
374
375 4
			if ( !$printer->isExportFormat() ) {
376 4
				if ( $res->getCount() > 0 ) {
377 3
					if ( $this->m_editquery ) {
378
						$urlArgs['eq'] = 'yes';
379
					}
380 3
					elseif ( $hidequery ) {
381
						$urlArgs['eq'] = 'no';
382
					}
383
384 3
					$navigation = $this->getNavigationBar( $res, $urlArgs );
385 3
					$query_result = $printer->getResult( $res, $params, SMW_OUTPUT_HTML );
386
387 3
					if ( is_array( $query_result ) ) {
388
						$result .= $query_result[0];
389
					} else {
390 3
						$result .= $query_result;
391
					}
392
393 3
					$result .= $debug;
394
				} else {
395 1
					$result = $this->errorFormWidget->createNoResultFormElement();
396 1
					$result .= $debug;
397
				}
398
			}
399
		}
400
401
		// FileExport will override the header and cause issues during the unit
402
		// test when fetching the output stream therefore use the plain output
403 7
		if ( defined( 'MW_PHPUNIT_TEST' ) && isset( $printer ) && $printer->isExportFormat() ) {
404 1
			$result = $printer->getResult( $res, $params, SMW_OUTPUT_FILE );
0 ignored issues
show
Bug introduced by
The variable $params 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...
405 1
			$printer = null;
406
		}
407
408 7
		if ( isset( $printer ) && $printer->isExportFormat() ) {
409
			$wgOut->disable();
410
411
			/**
412
			 * @var SMWIExportPrinter $printer
413
			 */
414
			$printer->outputAsFile( $res, $params );
415
		} else {
416 7
			if ( $this->m_querystring ) {
417 4
				$this->getOutput()->setHTMLtitle( $this->m_querystring );
418
			} else {
419 3
				$this->getOutput()->setHTMLtitle( wfMessage( 'ask' )->text() );
420
			}
421
422 7
			$urlArgs['offset'] = $this->m_params['offset'];
423 7
			$urlArgs['limit'] = $this->m_params['limit'];
424
425 7
			$isFromCache = $res !== null ? $res->isFromCache() : false;
426
427 7
			$result = $this->getInputForm(
428
				$printoutstring,
429
				wfArrayToCGI( $urlArgs ),
430
				$navigation,
431
				$duration,
432
				$isFromCache
433 7
			) . $this->errorFormWidget->getFormattedQueryErrorElement( $queryobj ) . $result;
434
435 7
			$this->getOutput()->addHTML( $result );
436
		}
437 7
	}
438
439
	/**
440
	 * Generates the Search Box UI
441
	 *
442
	 * @param string $printoutstring
443
	 * @param string $urltail
444
	 *
445
	 * @return string
446
	 */
447 7
	protected function getInputForm( $printoutstring, $urltail, $navigation = '', $duration, $isFromCache = false ) {
0 ignored issues
show
Coding Style introduced by
getInputForm uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
448 7
		global $wgScript;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
449
450 7
		$result = '';
451
452
		// Deprecated: Use of SpecialPage::getTitle was deprecated in MediaWiki 1.23
453 7
		$title = method_exists( $this, 'getPageTitle') ? $this->getPageTitle() : $this->getTitle();
454
455 7
		$querySource = ApplicationFactory::getInstance()->getQuerySourceFactory()->getAsString(
456 7
			isset( $this->m_params['source'] ) ? $this->m_params['source'] : null
457
		);
458
459 7
		$downloadLink = $this->getExtraDownloadLinks();
460 7
		$searchInfoText = $duration > 0 ? wfMessage( 'smw-ask-query-search-info', $this->m_querystring, $querySource, $isFromCache, $duration )->parse() : '';
461 7
		$hideForm = false;
462
463 7
		$result .= Html::openElement( 'form',
464 7
			array( 'action' => $wgScript, 'name' => 'ask', 'method' => 'get' ) );
465
466 7
		if ( $this->m_editquery ) {
467 3
			$result .= Html::hidden( 'title', $title->getPrefixedDBKey() );
468
469
			// Table for main query and printouts.
470 3
			$result .= '<table class="smw-ask-query" style="width: 100%;"><tr><th>' . wfMessage( 'smw_ask_queryhead' )->escaped() . "</th>\n<th>" . wfMessage( 'smw_ask_printhead' )->escaped() . "<br />\n" .
471 3
				'<span style="font-weight: normal;">' . wfMessage( 'smw_ask_printdesc' )->escaped() . '</span>' . "</th></tr>\n" .
472 3
				'<tr><td style="padding-left: 0px; width: 50%;"><textarea class="smw-ask-query-condition" name="q" cols="20" rows="6">' . htmlspecialchars( $this->m_querystring ) . "</textarea></td>\n" .
473 3
				'<td style="padding-left: 7px; width: 50%;"><textarea id="smw-property-input" class="smw-ask-query-printout" name="po" cols="20" rows="6">' . htmlspecialchars( $printoutstring ) . '</textarea></td></tr></table>' . "\n";
474
475
			// Format selection
476 3
			$result .= self::getFormatSelection ( $this->m_params );
477
478
			// @TODO
479
			// Sorting inputs
480 3
			if ( $GLOBALS['smwgQSortingSupport'] ) {
481 3
				$result .= '<fieldset class="smw-ask-sorting"><legend>' . wfMessage( 'smw-ask-sorting' )->escaped() . "</legend>\n";
482 3
				$result .= self::getSortingOption( $this->m_params );
483 3
				$result .= "</fieldset>\n";
484
			}
485
486
			// Other options fieldset
487 3
			$result .= '<fieldset class="smw-ask-options-fields"><legend>' . wfMessage( 'smw_ask_otheroptions' )->escaped() . "</legend>\n";
488
489
			// Info text for when the fieldset is collapsed
490 3
			$result .= Html::element( 'div', array(
491 3
				'class' => 'collapsed-info',
492
				'style' => 'display:none;'
493 3
				), wfMessage( 'smw-ask-otheroptions-collapsed-info')->text()
494
			);
495
496
			// Individual options
497 3
			$result .= "<div id=\"other_options\">" .  $this->parametersFormWidget->createParametersForm( $this->m_params['format'], $this->m_params ) . "</div>";
498 3
			$result .= "</fieldset>\n";
499
500 3
			$urltail = str_replace( '&eq=yes', '', $urltail ) . '&eq=no'; // FIXME: doing it wrong, srysly
501 3
			$hideForm = true;
502
		} else { // if $this->m_editquery == false
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
503 4
			$urltail = str_replace( '&eq=no', '', $urltail ) . '&eq=yes';
504
		}
505
506
		// Submit
507 7
		$result .= '<fieldset class="smw-ask-actions" style="margin-top:0px;"><legend>' . wfMessage( 'smw-ask-search' )->escaped() . "</legend>\n" .
508 7
			'<p>' .  '' . '</p>' .
509
510 7
			$this->inputFormWidget->createFindResultLinkElement( $hideForm ) .
511 7
			' ' . $this->inputFormWidget->createShowHideLinkElement( SpecialPage::getSafeTitleFor( 'Ask' ), $urltail, $hideForm ) .
512 7
			' ' . $this->inputFormWidget->createEmbeddedCodeLinkElement() .
513 7
			' ' . $this->inputFormWidget->createClipboardLinkElement( $this->queryLinker ) .
514 7
			' ' . $this->inputFormWidget->createEmbeddedCodeElement( $this->getQueryAsCodeString() );
515
516 7
		$result .= '<p></p>';
517
518 7
		$this->doFinalModificationsOnBorrowedOutput(
519
			$result,
520
			$searchInfoText
521
		);
522
523 7
		$result .= ( $navigation !== '' ? '<p>'. $searchInfoText . '</p>' . '<hr class="smw-form-horizontalrule">' .  $navigation . '&#160;&#160;&#160;' . $downloadLink : '' ) .
524 7
			"\n</fieldset>\n</form>\n";
525
526
527 7
		$this->getOutput()->addModules(
528 7
			$this->inputFormWidget->getResourceModules()
529
		);
530
531 7
		return $result;
532
	}
533
534 7
	private function getQueryAsCodeString() {
535
536 7
		$code = $this->m_querystring ? htmlspecialchars( $this->m_querystring ) . "\n" : "\n";
537
538 7
		foreach ( $this->m_printouts as $printout ) {
539 4
			$serialization = $printout->getSerialisation( true );
540 4
			$mainlabel = isset( $this->m_params['mainlabel'] ) ? '?=' . $this->m_params['mainlabel'] . '#' : '';
541
542 4
			if ( $serialization !== '?#' && $serialization !== $mainlabel ) {
543 4
				$code .= ' |' . $serialization . "\n";
544
			}
545
		}
546
547 7
		foreach ( $this->params as $param ) {
548 4
			if ( !$param->wasSetToDefault() ) {
549 4
				$code .= ' |' . htmlspecialchars( $param->getName() ) . '=';
550 4
				$code .= htmlspecialchars( $this->m_params[$param->getName()] ) . "\n";
551
			}
552
		}
553
554 7
		return '{{#ask: ' . $code . '}}';
555
	}
556
557
	/**
558
	 * Build the format drop down
559
	 *
560
	 * @param array
561
	 *
562
	 * @return string
563
	 */
564 3
	protected static function getFormatSelection ( $params ) {
0 ignored issues
show
Coding Style introduced by
getFormatSelection uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
565 3
		$result = '';
566
567 3
		$printer = SMWQueryProcessor::getResultPrinter( 'broadtable', SMWQueryProcessor::SPECIAL_PAGE );
568 3
		$url = SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( 'showformatoptions=this.value' );
569
570 3
		foreach ( $params as $param => $value ) {
571 3
			if ( $param !== 'format' ) {
572 3
				$url .= '&params[' . rawurlencode( $param ) . ']=' . rawurlencode( $value );
573
			}
574
		}
575
576 3
		$result .= '<br /><span class="smw-ask-query-format" style="vertical-align:middle;">' . wfMessage( 'smw_ask_format_as' )->escaped() . ' <input type="hidden" name="eq" value="yes"/>' . "\n" .
577 3
			Html::openElement(
578 3
				'select',
579
				array(
580 3
					'class' => 'smw-ask-query-format-selector',
581 3
					'id' => 'formatSelector',
582 3
					'name' => 'p[format]',
583 3
					'data-url' => $url,
584
				)
585 3
			) . "\n" .
586 3
			'	<option value="broadtable"' . ( $params['format'] == 'broadtable' ? ' selected="selected"' : '' ) . '>' .
587 3
			htmlspecialchars( $printer->getName() ) . ' (' . wfMessage( 'smw_ask_defaultformat' )->escaped() . ')</option>' . "\n";
0 ignored issues
show
Bug introduced by
Consider using $printer->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
588
589 3
		$formats = array();
590
591 3
		foreach ( array_keys( $GLOBALS['smwgResultFormats'] ) as $format ) {
592
			// Special formats "count" and "debug" currently not supported.
593 3
			if ( $format != 'broadtable' && $format != 'count' && $format != 'debug' ) {
594 3
				$printer = SMWQueryProcessor::getResultPrinter( $format, SMWQueryProcessor::SPECIAL_PAGE );
595 3
				$formats[$format] = htmlspecialchars( $printer->getName() );
0 ignored issues
show
Bug introduced by
Consider using $printer->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
596
			}
597
		}
598
599 3
		natcasesort( $formats );
600
601 3
		foreach ( $formats as $format => $name ) {
602 3
			$result .= '	<option value="' . $format . '"' . ( $params['format'] == $format ? ' selected="selected"' : '' ) . '>' . $name . "</option>\n";
603
		}
604
605 3
		$result .= "</select></span>\n";
606
607 3
		return $result;
608
	}
609
610
	/**
611
	 * Build the sorting/order input
612
	 *
613
	 * @param array
614
	 *
615
	 * @return string
616
	 */
617 3
	protected static function getSortingOption ( $params ) {
618 3
		$result = '';
619
620 3
		if ( ! array_key_exists( 'sort', $params ) || ! array_key_exists( 'order', $params ) ) {
621 3
			$orders = array(); // do not even show one sort input here
622
		} else {
623
			$sorts = explode( ',', $params['sort'] );
624
			$orders = explode( ',', $params['order'] );
625
			reset( $sorts );
626
		}
627
628 3
		foreach ( $orders as $i => $order ) {
629
			$result .=  "<div id=\"sort_div_$i\">" . wfMessage( 'smw_ask_sortby' )->escaped() . ' <input type="text" name="sort[' . $i . ']" value="' .
630
				    htmlspecialchars( $sorts[$i] ) . "\" size=\"35\"/>\n" . '<select name="order[' . $i . ']"><option ';
0 ignored issues
show
Bug introduced by
The variable $sorts 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...
631
632
			if ( $order == 'ASC' ) {
633
				$result .= 'selected="selected" ';
634
			}
635
			$result .=  'value="ASC">' . wfMessage( 'smw_ask_ascorder' )->escaped() . '</option><option ';
636
			if ( $order == 'DESC' ) {
637
				$result .= 'selected="selected" ';
638
			}
639
640
			$result .=  'value="DESC">' . wfMessage( 'smw_ask_descorder' )->escaped() . "</option></select>\n";
641
			$result .= '[<a class="smw-ask-delete" data-target="sort_div_' . $i . '" href="#">' . wfMessage( 'delete' )->escaped() . '</a>]' . "\n";
642
			$result .= "</div>\n";
643
		}
644
645 3
		$result .=  '<div id="sorting_starter" style="display: none">' . wfMessage( 'smw_ask_sortby' )->escaped() . ' <input type="text" name="sort_num" size="35" class="smw-property-input" />' . "\n";
646 3
		$result .= ' <select name="order_num">' . "\n";
647 3
		$result .= '	<option value="ASC">' . wfMessage( 'smw_ask_ascorder' )->escaped() . "</option>\n";
648 3
		$result .= '	<option value="DESC">' . wfMessage( 'smw_ask_descorder' )->escaped() . "</option>\n</select>\n";
649 3
		$result .= "</div>\n";
650 3
		$result .= '<div id="sorting_main"></div>' . "\n";
651 3
		$result .= '<a class="smw-ask-add" href="#">' . wfMessage( 'smw_add_sortcondition' )->escaped() . '</a>' . "\n";
652
653 3
		return $result;
654
	}
655
656
	/**
657
	 * Build the navigation for some given query result, reuse url-tail parameters.
658
	 *
659
	 * @param SMWQueryResult $res
660
	 * @param array $urlArgs
661
	 *
662
	 * @return string
663
	 */
664 3
	protected function getNavigationBar( SMWQueryResult $res, array $urlArgs ) {
665 3
		global $smwgQMaxInlineLimit, $wgLang;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
666
667
		// Bug 49216
668 3
		$offset = $res->getQuery()->getOffset();
669 3
		$limit  = $this->params['limit']->getValue();
670 3
		$navigation = '';
671
672
		// @todo FIXME: i18n: Patchwork text.
673
		$navigation .=
674
			'<b>' .
675 3
				wfMessage( 'smw_result_results' )->escaped() . ' ' . $wgLang->formatNum( $offset + 1 ) .
676 3
			' &#150; ' .
677 3
				$wgLang->formatNum( $offset + $res->getCount() ) .
678 3
			'</b>&#160;&#160;&#160;&#160;';
679
680
		// Prepare navigation bar.
681 3
		if ( $offset > 0 ) {
682
			$navigation .= '(' . Html::element(
683
				'a',
684
				array(
685
					'href' => SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( array(
686
						'offset' => max( 0, $offset - $limit ),
687
						'limit' => $limit
688
					) + $urlArgs ),
689
					'rel' => 'nofollow'
690
				),
691
				wfMessage( 'smw_result_prev' )->text() . ' ' . $limit
692
			) . ' | ';
693
		} else {
694 3
			$navigation .= '(' . wfMessage( 'smw_result_prev' )->escaped() . ' ' . $limit . ' | ';
695
		}
696
697 3
		if ( $res->hasFurtherResults() ) {
698
			$navigation .= Html::element(
699
				'a',
700
				array(
701
					'href' => SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( array(
702
						'offset' => ( $offset + $limit ),
703
						'limit' => $limit
704
					)  + $urlArgs ),
705
					'rel' => 'nofollow'
706
				),
707
				wfMessage( 'smw_result_next' )->text() . ' ' . $limit
708
			) . ')';
709
		} else {
710 3
			$navigation .= wfMessage( 'smw_result_next' )->escaped() . ' ' . $limit . ')';
711
		}
712
713 3
		$first = true;
714
715 3
		foreach ( array( 20, 50, 100, 250, 500 ) as $l ) {
716 3
			if ( $l > $smwgQMaxInlineLimit ) {
717
				break;
718
			}
719
720 3
			if ( $first ) {
721 3
				$navigation .= '&#160;&#160;&#160;(';
722 3
				$first = false;
723
			} else {
724 3
				$navigation .= ' | ';
725
			}
726
727 3
			if ( $limit != $l ) {
728 3
				$navigation .= Html::element(
729 3
					'a',
730
					array(
731 3
						'href' => SpecialPage::getSafeTitleFor( 'Ask' )->getLocalURL( array(
732 3
							'offset' => $offset,
733 3
							'limit' => $l
734 3
						) + $urlArgs ),
735 3
						'rel' => 'nofollow'
736
					),
737
					$l
738
				);
739
			} else {
740 3
				$navigation .= '<b>' . $l . '</b>';
741
			}
742
		}
743
744 3
		$navigation .= ')';
745
746 3
		return $navigation;
747
	}
748
749
	protected function getGroupName() {
750
		return 'smw_group';
751
	}
752
753 7
	private function getExtraDownloadLinks() {
754
755 7
		$downloadLinks = '';
756
757 7
		if ( $this->queryLinker === null ) {
758 3
			return $downloadLinks;
759
		}
760
761 4
		$queryLinker = clone $this->queryLinker;
762
763 4
		$queryLinker->setParameter( 'true', 'prettyprint' );
764 4
		$queryLinker->setParameter( 'true', 'unescape' );
765 4
		$queryLinker->setParameter( 'json', 'format' );
766 4
		$queryLinker->setParameter( 'JSON', 'searchlabel' );
767 4
		$queryLinker->setCaption( 'JSON' );
768
769 4
		$downloadLinks .= $queryLinker->getHtml();
770
771 4
		$queryLinker->setCaption( 'CSV' );
772 4
		$queryLinker->setParameter( 'csv', 'format' );
773 4
		$queryLinker->setParameter( 'CSV', 'searchlabel' );
774
775 4
		$downloadLinks .= ' | ' . $queryLinker->getHtml();
776
777 4
		$queryLinker->setCaption( 'RSS' );
778 4
		$queryLinker->setParameter( 'rss', 'format' );
779 4
		$queryLinker->setParameter( 'RSS', 'searchlabel' );
780
781 4
		$downloadLinks .= ' | ' . $queryLinker->getHtml();
782
783 4
		$queryLinker->setCaption( 'RDF' );
784 4
		$queryLinker->setParameter( 'rdf', 'format' );
785 4
		$queryLinker->setParameter( 'RDF', 'searchlabel' );
786
787 4
		$downloadLinks .= ' | ' . $queryLinker->getHtml();
788
789 4
		return '(' . $downloadLinks . ')';
790
	}
791
792 7
	private function doFinalModificationsOnBorrowedOutput( &$html, &$searchInfoText ) {
793
794 7
		if ( !$this->getRequest()->getCheck( 'bTitle' ) ) {
795 7
			return;
796
		}
797
798
		$borrowedMessage = $this->getRequest()->getVal( 'bMsg' );
799
800
		$searchInfoText = '';
801
		$html = "\n<fieldset><p>" . ( $borrowedMessage !== null && wfMessage( $borrowedMessage )->exists() ? wfMessage( $borrowedMessage )->parse() : '' ) . "</p>";
802
803
		$borrowedTitle = $this->getRequest()->getVal( 'bTitle' );
804
805
		if ( $borrowedTitle !== null && wfMessage( $borrowedTitle )->exists() ) {
806
			$this->getOutput()->setPageTitle( wfMessage( $borrowedTitle )->text() );
807
		}
808
	}
809
810
	/**
811
	 * FIXME MW 1.25
812
	 */
813 7
	private function addExternalHelpLinkFor( $key ) {
814
815 7
		if ( !method_exists( $this, 'addHelpLink' ) ) {
816
			return null;
817
		}
818
819 7
		$this->addHelpLink( wfMessage( $key )->escaped(), true );
820 7
	}
821
822
}
823