Completed
Push — master ( d2d2ad...82f1a0 )
by mw
12s
created

SMWQueryProcessor   F

Complexity

Total Complexity 74

Size/Duplication

Total Lines 627
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 94.97%

Importance

Changes 0
Metric Value
dl 0
loc 627
ccs 189
cts 199
cp 0.9497
rs 3.4135
c 0
b 0
f 0
wmc 74
lcom 1
cbo 14

16 Methods

Rating   Name   Duplication   Size   Complexity  
A getProcessedParams() 0 5 1
A getValidatorForParams() 0 14 1
C createQuery() 0 62 13
C getSortKeys() 0 51 15
B addThisPrintout() 0 12 5
A processFunctionParams() 0 3 1
C getComponentsFromFunctionParams() 0 69 15
A getQueryAndParamsFromFunctionParams() 0 12 2
A getResultFromFunctionParams() 0 11 2
A getResultFromQueryString() 0 8 1
B getResultFromQuery() 0 38 6
A getStoreFromParams() 0 3 1
A getResultPrinter() 0 11 2
B getParameters() 0 88 4
A getSourceParam() 0 8 3
A getFormatParameters() 0 11 2

How to fix   Complexity   

Complex Class

Complex classes like SMWQueryProcessor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SMWQueryProcessor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
use ParamProcessor\Options;
4
use ParamProcessor\Param;
5
use ParamProcessor\ParamDefinition;
6
use ParamProcessor\Processor;
7
use SMW\Query\PrintRequest;
8
use SMW\Query\PrintRequestFactory;
9
use SMW\ApplicationFactory;
10
use SMW\Message;
11
12
/**
13
 * This file contains a static class for accessing functions to generate and execute
14
 * semantic queries and to serialise their results.
15
 *
16
 * @ingroup SMWQuery
17
 * @author Markus Krötzsch
18
 */
19
20
/**
21
 * Static class for accessing functions to generate and execute semantic queries
22
 * and to serialise their results.
23
 * @ingroup SMWQuery
24
 */
25
class SMWQueryProcessor {
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...
26
27
	// "query contexts" define restrictions during query parsing and
28
	// are used to preconfigure query (e.g. special pages show no further
29
	// results link):
30
	const SPECIAL_PAGE = 0; // query for special page
31
	const INLINE_QUERY = 1; // query for inline use
32
	const CONCEPT_DESC = 2; // query for concept definition
33
34
	/**
35
	 * Takes an array of unprocessed parameters, processes them using
36
	 * Validator, and returns them.
37
	 *
38
	 * Both input and output arrays are
39
	 * param name (string) => param value (mixed)
40
	 *
41
	 * @since 1.6.2
42
	 * The return value changed in SMW 1.8 from an array with result values
43
	 * to an array with Param objects.
44
	 *
45
	 * @param array $params
46
	 * @param array $printRequests
47
	 * @param boolean $unknownInvalid
48
	 *
49
	 * @return Param[]
50 90
	 */
51 90
	public static function getProcessedParams( array $params, array $printRequests = array(), $unknownInvalid = true ) {
52 90
		$validator = self::getValidatorForParams( $params, $printRequests, $unknownInvalid );
53 90
		$validator->processParameters();
54
		return $validator->getParameters();
0 ignored issues
show
Deprecated Code introduced by
The method ParamProcessor\Processor::getParameters() has been deprecated with message: since 1.0

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...
55
	}
56
57
	/**
58
	 * Takes an array of unprocessed parameters,
59
	 * and sets them on a new Validator object,
60
	 * which is returned and ready to process the parameters.
61
	 *
62
	 * @since 1.8
63
	 *
64
	 * @param array $params
65
	 * @param array $printRequests
66
	 * @param boolean $unknownInvalid
67
	 *
68
	 * @return Processor
69 90
	 */
70 90
	public static function getValidatorForParams( array $params, array $printRequests = array(), $unknownInvalid = true ) {
71
		$paramDefinitions = self::getParameters();
72 90
73
		$paramDefinitions['format']->setPrintRequests( $printRequests );
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface ParamProcessor\IParamDefinition as the method setPrintRequests() does only exist in the following implementations of said interface: SMWParamFormat.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
74 90
75 90
		$processorOptions = new Options();
76
		$processorOptions->setUnknownInvalid( $unknownInvalid );
77 90
78
		$validator = Processor::newFromOptions( $processorOptions );
79 90
80
		$validator->setParameters( $params, $paramDefinitions, false );
0 ignored issues
show
Unused Code introduced by
The call to Processor::setParameters() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
81 90
82
		return $validator;
83
	}
84
85
	/**
86
	 * Parse a query string given in SMW's query language to create
87
	 * an SMWQuery. Parameters are given as key-value-pairs in the
88
	 * given array. The parameter $context defines in what context the
89
	 * query is used, which affects ceretain general settings.
90
	 * An object of type SMWQuery is returned.
91
	 *
92
	 * The format string is used to specify the output format if already
93
	 * known. Otherwise it will be determined from the parameters when
94
	 * needed. This parameter is just for optimisation in a common case.
95
	 *
96
	 * @param string $queryString
97
	 * @param array $params These need to be the result of a list fed to getProcessedParams
98
	 * @param $context
99
	 * @param string $format
100
	 * @param array $extraPrintouts
101
	 *
102
	 * @return SMWQuery
103 90
	 */
104 90
	static public function createQuery( $queryString, array $params, $context = self::INLINE_QUERY, $format = '', array $extraPrintouts = array(), $contextPage = null ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
105
		global $smwgQDefaultNamespaces, $smwgQFeatures, $smwgQConceptFeatures;
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...
106
107 90
		// parse query:
108 90
		$queryfeatures = ( $context == self::CONCEPT_DESC ) ? $smwgQConceptFeatures : $smwgQFeatures;
109 90
		$qp = new SMWQueryParser( $queryfeatures );
110 90
		$qp->setContextPage( $contextPage );
111 90
		$qp->setDefaultNamespaces( $smwgQDefaultNamespaces );
112
		$desc = $qp->getQueryDescription( $queryString );
113 90
114 90
		if ( $format === '' || is_null( $format ) ) {
115
			$format = $params['format']->getValue();
116
		}
117 90
118 2
		if ( $format == 'count' ) {
119 89
			$querymode = SMWQuery::MODE_COUNT;
120 1
		} elseif ( $format == 'debug' ) {
121
			$querymode = SMWQuery::MODE_DEBUG;
122 88
		} else {
123 88
			$printer = self::getResultPrinter( $format, $context );
124
			$querymode = $printer->getQueryMode( $context );
125
		}
126 90
127 90
		$query = new SMWQuery( $desc, ( $context != self::SPECIAL_PAGE ), ( $context == self::CONCEPT_DESC ) );
128 90
		$query->setQueryString( $queryString );
129 90
		$query->setContextPage( $contextPage );
130 90
		$query->setExtraPrintouts( $extraPrintouts );
131 90
		$query->setMainLabel( $params['mainlabel']->getValue() );
132 90
		$query->addErrors( $qp->getErrors() ); // keep parsing errors for later output
133
		$query->setQuerySource( $params['source']->getValue() );
134
135 90
		// set mode, limit, and offset:
136 90
		$query->querymode = $querymode;
137 90
		if ( ( array_key_exists( 'offset', $params ) ) && ( is_int( $params['offset']->getValue() + 0 ) ) ) {
138
			$query->setOffset( max( 0, trim( $params['offset']->getValue() ) + 0 ) );
139
		}
140 90
141 2
		if ( $query->querymode == SMWQuery::MODE_COUNT ) { // largest possible limit for "count", even inline
142 2
			global $smwgQMaxLimit;
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...
143 2
			$query->setOffset( 0 );
144
			$query->setLimit( $smwgQMaxLimit, false );
145 89
		} else {
146 89
			if ( ( array_key_exists( 'limit', $params ) ) && ( is_int( trim( $params['limit']->getValue() ) + 0 ) ) ) {
147 89
				$query->setLimit( max( 0, trim( $params['limit']->getValue() ) + 0 ) );
148 89
				if ( ( trim( $params['limit']->getValue() ) + 0 ) < 0 ) { // limit < 0: always show further results link only
149
					$query->querymode = SMWQuery::MODE_NONE;
150
				}
151
			} else {
152
				global $smwgQDefaultLimit;
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...
153
				$query->setLimit( $smwgQDefaultLimit );
154
			}
155
		}
156 90
157 90
		$defaultSort = $format === 'rss' ? 'DESC' : 'ASC';
158
		$sort = self::getSortKeys( $params['sort']->getValue(), $params['order']->getValue(), $defaultSort );
159 90
160 90
		$query->sortkeys = $sort['keys'];
161 90
		$query->addErrors( $sort['errors'] );
162
		$query->sort = count( $query->sortkeys ) > 0; // TODO: Why would we do this here?
163 90
164
		return $query;
165
	}
166
167
	/**
168
	 * Takes the sort and order parameters and returns a list of sort keys and a list of errors.
169
	 *
170
	 * @since 1.7
171
	 *
172
	 * @param array $sortParam
173
	 * @param array $orders
0 ignored issues
show
Bug introduced by
There is no parameter named $orders. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
174
	 * @param string $defaultSort
175
	 *
176
	 * @return array ( keys => array(), errors => array() )
177 90
	 */
178 90
	protected static function getSortKeys( array $sortParam, array $orderParam, $defaultSort ) {
0 ignored issues
show
Coding Style introduced by
getSortKeys 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...
179 90
		$orders = array();
180 90
		$sortKeys = array();
181
		$sortErros = array();
182 90
183 10
		foreach ( $orderParam as $key => $order ) {
184 10
			$order = strtolower( trim( $order ) );
185 8
			if ( ( $order == 'descending' ) || ( $order == 'reverse' ) || ( $order == 'desc' ) ) {
186 9
				$orders[$key] = 'DESC';
187
			} elseif ( ( $order == 'random' ) || ( $order == 'rand' ) ) {
188
				$orders[$key] = 'RANDOM';
189 10
			} else {
190
				$orders[$key] = 'ASC';
191
			}
192
		}
193 90
194 90
		foreach ( $sortParam as $sort ) {
195
			$sortKey = false;
196
197
			// An empty string indicates we mean the page, such as element 0 on the next line.
198 90
			// sort=,Some property
199 86
			if ( trim( $sort ) === '' ) {
200
				$sortKey = '';
201
			}
202
			else {
203 7
204
				$sort = $GLOBALS['wgContLang']->getNsText( NS_CATEGORY ) == mb_convert_case( $sort, MB_CASE_TITLE ) ? '_INST' : $sort;
205 7
206
				$propertyValue = SMWPropertyValue::makeUserProperty( trim( $sort ) );
207 7
208 7
				if ( $propertyValue->isValid() ) {
209
					$sortKey = $propertyValue->getDataItem()->getKey();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMWDataItem as the method getKey() does only exist in the following sub-classes of SMWDataItem: SMWDIProperty, SMW\DIProperty. 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...
210
				} else {
211
					$sortErros = array_merge( $sortErros, $propertyValue->getErrors() );
212
				}
213
			}
214 90
215 90
			if ( $sortKey !== false ) {
216 90
				$order = empty( $orders ) ? $defaultSort : array_shift( $orders );
217
				$sortKeys[$sortKey] = $order;
218
			}
219
		}
220
221
		// If more sort arguments are provided then properties, assume the first one is for the page.
222 90
		// TODO: we might want to add errors if there is more then one.
223
		if ( !array_key_exists( '', $sortKeys ) && !empty( $orders ) ) {
224
			$sortKeys[''] = array_shift( $orders );
225
		}
226 90
227
		return array( 'keys' => $sortKeys, 'errors' => $sortErros );
228
	}
229
230
	/**
231
	 * Add the subject print request, unless mainlabel is set to "-".
232
	 *
233
	 * @since 1.7
234
	 *
235
	 * @param array $printRequests
236
	 * @param array $rawParams
237 84
	 */
238 84
	public static function addThisPrintout( array &$printRequests, array $rawParams ) {
239 84
		if ( !is_null( $printRequests ) ) {
240
			$hasMainlabel = array_key_exists( 'mainlabel', $rawParams );
241 84
242 83
			if  ( !$hasMainlabel || trim( $rawParams['mainlabel'] ) !== '-' ) {
243 83
				array_unshift( $printRequests, new PrintRequest(
244 83
					PrintRequest::PRINT_THIS,
245
					$hasMainlabel ? $rawParams['mainlabel'] : ''
246
				) );
247
			}
248 84
		}
249
	}
250
251
	/**
252
	 * Preprocess a query as given by an array of parameters as is typically
253
	 * produced by the #ask parser function. The parsing results in a querystring,
254
	 * an array of additional parameters, and an array of additional SMWPrintRequest
255
	 * objects, which are filled into call-by-ref parameters.
256
	 * $showMode is true if the input should be treated as if given by #show
257
	 *
258
	 * @param array $rawParams
259
	 * @param string $querystring
260
	 * @param array $params
261
	 * @param array $printouts array of SMWPrintRequest
262
	 * @param boolean $showMode
263
	 * @deprecated Will vanish after SMW 1.8 is released.
264
	 * Use getComponentsFromFunctionParams which has a cleaner interface.
265
	 */
266
	static public function processFunctionParams( array $rawParams, &$querystring, &$params, &$printouts, $showMode = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $querystring is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $params is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $printouts is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
267
		list( $querystring, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
268
	}
269
270
271
	/**
272
	 * Preprocess a query as given by an array of parameters as is
273
	 * typically produced by the #ask parser function or by Special:Ask.
274
	 * The parsing results in a querystring, an array of additional
275
	 * parameters, and an array of additional SMWPrintRequest objects,
276
	 * which are returned in an array with three components. If
277
	 * $showMode is true then the input will be processed as if for #show.
278
	 * This uses a slightly different way to get the query, and different
279
	 * default labels (empty) for additional print requests.
280
	 *
281
	 * @param array $rawParams
282
	 * @param boolean $showMode
283
	 * @return array( string, array( string => string ), array( SMWPrintRequest ) )
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
284 99
	 */
285 99
	static public function getComponentsFromFunctionParams( array $rawParams, $showMode ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
286 99
		$queryString = '';
287 99
		$parameters = array();
288
		$printouts = array();
289 99
290 99
		$lastprintout = null;
291
		$printRequestFactory = new PrintRequestFactory();
292 99
293
		foreach ( $rawParams as $name => $rawParam ) {
294
			// special handling for arrays - this can happen if the
295 98
			// parameter came from a checkboxes input in Special:Ask:
296
			if ( is_array( $rawParam ) ) {
297
				$rawParam = implode( ',', array_keys( $rawParam ) );
298
			}
299
300
			// Bug 32955 / #640
301 98
			// Modify (e.g. replace `=`) a condition string only if enclosed by [[ ... ]]
302 98
			$rawParam = preg_replace_callback(
303 98
				'/\[\[([^\[\]]*)\]\]/xu',
304 93
				function( array $matches ) {
305 98
					return str_replace( array( '=' ), array( '-3D' ), $matches[0] );
306
				},
307
				$rawParam
308
			);
309
310 98
			// accept 'name' => 'value' just as '' => 'name=value':
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
311
			if ( is_string( $name ) && ( $name !== '' ) ) {
312
				$rawParam = $name . '=' . $rawParam;
313
			}
314 98
315 98
			if ( $rawParam === '' ) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
316 69
			} elseif ( $rawParam { 0 } == '?' ) { // print statement
317 69
				$rawParam = substr( $rawParam, 1 );
318 69
				$lastprintout = $printRequestFactory->newPrintRequestFromText( $rawParam, $showMode );
319 69
				if ( !is_null( $lastprintout ) ) {
320
					$printouts[] = $lastprintout;
321 98
				}
322 8
			} elseif ( $rawParam[0] == '+' ) { // print request parameter
323 7
				if ( !is_null( $lastprintout ) ) {
324 7
					$rawParam = substr( $rawParam, 1 );
325 7
					$parts = explode( '=', $rawParam, 2 );
326 7
					if ( count( $parts ) == 2 ) {
327
						$lastprintout->setParameter( trim( $parts[0] ), $parts[1] );
328 8
					} else {
329
						$lastprintout->setParameter( trim( $parts[0] ), null );
330
					}
331
				}
332
			} else { // parameter or query
333
334 98
				// #1645
335
				$parts = $showMode && $name == 0 ? $rawParam : explode( '=', $rawParam, 2 );
336 98
337
				if ( count( $parts ) >= 2 ) {
338 71
					// don't trim here, some parameters care for " "
339
					$parameters[strtolower( trim( $parts[0] ) )] = $parts[1];
340 98
				} else {
341
					$queryString .= $rawParam;
342
				}
343
			}
344
		}
345 99
346
		$queryString = str_replace( array( '&lt;', '&gt;', '-3D' ), array( '<', '>', '=' ), $queryString );
347 99
348 11
		if ( $showMode ) {
349
			$queryString = "[[:$queryString]]";
350
		}
351 99
352
		return array( $queryString, $parameters, $printouts);
353
	}
354
355
	/**
356
	 * Process and answer a query as given by an array of parameters as is
357
	 * typically produced by the #ask parser function. The parameter
358
	 * $context defines in what context the query is used, which affects
359
	 * certain general settings.
360
	 *
361
	 * The main task of this function is to preprocess the raw parameters
362
	 * to obtain actual parameters, printout requests, and the query string
363
	 * for further processing.
364
	 *
365
	 * @since 1.8
366
	 * @param array $rawParams user-provided list of unparsed parameters
367
	 * @param integer $outputMode SMW_OUTPUT_WIKI, SMW_OUTPUT_HTML, ...
368
	 * @param integer $context INLINE_QUERY, SPECIAL_PAGE, CONCEPT_DESC
369
	 * @param boolean $showMode process like #show parser function?
370
	 * @return array( SMWQuery, array of IParam )
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
371 86
	 */
372 86
	static public function getQueryAndParamsFromFunctionParams( array $rawParams, $outputMode, $context, $showMode, $contextPage = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $outputMode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
373
		list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
374 86
375 82
		if ( !$showMode ) {
376
			self::addThisPrintout( $printouts, $params );
377
		}
378 86
379
		$params = self::getProcessedParams( $params, $printouts );
380 86
381 86
		$query  = self::createQuery( $queryString, $params, $context, '', $printouts, $contextPage );
382
		return array( $query, $params );
383
	}
384
385
	/**
386
	 * Process and answer a query as given by an array of parameters as is
387
	 * typically produced by the #ask parser function. The result is formatted
388
	 * according to the specified $outputformat. The parameter $context defines
389
	 * in what context the query is used, which affects ceretain general settings.
390
	 *
391
	 * The main task of this function is to preprocess the raw parameters to
392
	 * obtain actual parameters, printout requests, and the query string for
393
	 * further processing.
394
	 *
395
	 * @note Consider using getQueryAndParamsFromFunctionParams() and
396
	 * getResultFromQuery() instead.
397
	 * @deprecated Will vanish after release of SMW 1.8.
398
	 * See SMW_Ask.php for example code on how to get query results from
399
	 * #ask function parameters.
400
	 */
401
	static public function getResultFromFunctionParams( array $rawParams, $outputMode, $context = self::INLINE_QUERY, $showMode = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $outputMode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
402
		list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
403
404
		if ( !$showMode ) {
405
			self::addThisPrintout( $printouts, $params );
406
		}
407
408
		$params = self::getProcessedParams( $params, $printouts );
409
410
		return self::getResultFromQueryString( $queryString, $params, $printouts, SMW_OUTPUT_WIKI, $context );
0 ignored issues
show
Deprecated Code introduced by
The method SMWQueryProcessor::getResultFromQueryString() has been deprecated with message: Will vanish after release of SMW 1.8.
See SMW_Ask.php for example code on how to get query results from
#ask function parameters.

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...
411
	}
412
413
	/**
414
	 * Process a query string in SMW's query language and return a formatted
415
	 * result set as specified by $outputmode. A parameter array of key-value-pairs
416
	 * constrains the query and determines the serialisation mode for results. The
417
	 * parameter $context defines in what context the query is used, which affects
418
	 * certain general settings. Finally, $extraprintouts supplies additional
419
	 * printout requests for the query results.
420
	 *
421
	 * @param string $queryString
422
	 * @param array $params These need to be the result of a list fed to getProcessedParams
423
	 * @param $extraPrintouts
424
	 * @param $outputMode
425
	 * @param $context
426
	 *
427
	 * @return string
428
	 * @deprecated Will vanish after release of SMW 1.8.
429
	 * See SMW_Ask.php for example code on how to get query results from
430
	 * #ask function parameters.
431
	 */
432
	static public function getResultFromQueryString( $queryString, array $params, $extraPrintouts, $outputMode, $context = self::INLINE_QUERY ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
433
434
		$query  = self::createQuery( $queryString, $params, $context, '', $extraPrintouts );
435
		$result = self::getResultFromQuery( $query, $params, $outputMode, $context );
436
437
438
		return $result;
439
	}
440
441
	/**
442
	 * Create a fully formatted result string from a query and its
443
	 * parameters. The method takes care of processing various types of
444
	 * query result. Most cases are handled by printers, but counting and
445
	 * debugging uses special code.
446
	 *
447
	 * @param SMWQuery $query
448
	 * @param array $params These need to be the result of a list fed to getProcessedParams
449
	 * @param integer $outputMode
450
	 * @param integer $context
451
	 * @since before 1.7, but public only since 1.8
452
	 *
453
	 * @return string
454 77
	 */
455
	public static function getResultFromQuery( SMWQuery $query, array $params, $outputMode, $context ) {
456 77
457 76
		$res = self::getStoreFromParams( $params )->getQueryResult( $query );
458
		$start = microtime( true );
459 76
460 76
		if ( ( $query->querymode == SMWQuery::MODE_INSTANCES ) ||
461
			( $query->querymode == SMWQuery::MODE_NONE ) ) {
462 74
463 74
			$printer = self::getResultPrinter( $params['format']->getValue(), $context );
464
			$result = $printer->getResult( $res, $params, $outputMode );
465 74
466 74
			$query->setOption( SMWQuery::PROC_PRINT_TIME, microtime( true ) - $start );
467
			return $result;
468
		} else { // result for counting or debugging is just a string or number
469 2
470 1
			if ( $res instanceof SMWQueryResult ) {
471
				$res = $res->getCountValue();
472
			}
473 2
474 1
			if ( is_numeric( $res ) ) {
475
				$res = strval( $res );
476
			}
477 2
478 2
			if ( is_string( $res ) ) {
479 2
				$result = str_replace( '_', ' ', $params['intro']->getValue() )
480 2
					. $res
481 2
					. str_replace( '_', ' ', $params['outro']->getValue() )
482
					. smwfEncodeMessages( $query->getErrors() );
483
			} else {
484
				// When no valid result was obtained, $res will be a SMWQueryResult.
485
				$result = smwfEncodeMessages( $query->getErrors() );
486
			}
487 2
488
			$query->setOption( SMWQuery::PROC_PRINT_TIME, microtime( true ) - $start );
489 2
490
			return $result;
491
		}
492
	}
493 77
494 77
	private static function getStoreFromParams( array $params ) {
495
		return ApplicationFactory::getInstance()->getQuerySource( $params['source']->getValue() );
496
	}
497
498
	/**
499
	 * Find suitable SMWResultPrinter for the given format. The context in
500
	 * which the query is to be used determines some basic settings of the
501
	 * returned printer object. Possible contexts are
502
	 * SMWQueryProcessor::SPECIAL_PAGE, SMWQueryProcessor::INLINE_QUERY,
503
	 * SMWQueryProcessor::CONCEPT_DESC.
504
	 *
505
	 * @param string $format
506
	 * @param $context
507
	 *
508
	 * @return SMWResultPrinter
509
	 * @throws MWException if no printer is known for the given format
510 92
	 */
511 92
	static public function getResultPrinter( $format, $context = self::SPECIAL_PAGE ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
512
		global $smwgResultFormats;
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...
513 92
514
		if ( !array_key_exists( $format, $smwgResultFormats ) ) {
515
			throw new MWException( "There is no result format for '$format'." );
516
		}
517 92
518
		$formatClass = $smwgResultFormats[$format];
519 92
520
		return new $formatClass( $format, ( $context != self::SPECIAL_PAGE ) );
521
	}
522
523
	/**
524
	 * A function to describe the allowed parameters of a query using
525
	 * any specific format - most query printers should override this
526
	 * function.
527
	 *
528
	 * @since 1.6.2, return element type changed in 1.8
529
	 *
530
	 * @return IParamDefinition[]
531 92
	 */
532 92
	public static function getParameters() {
0 ignored issues
show
Coding Style introduced by
getParameters 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...
533
		$params = array();
534 92
535
		$allowedFormats = $GLOBALS['smwgResultFormats'];
536 92
537 92
		foreach ( $GLOBALS['smwgResultAliases'] as $aliases ) {
538
			$allowedFormats += $aliases;
539
		}
540 92
541
		$allowedFormats[] = 'auto';
542 92
543
		$params['format'] = array(
544
			'type' => 'smwformat',
545
			'default' => 'auto',
546
		);
547
548
		// TODO $params['format']->setToLower( true );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
549
		// TODO $allowedFormats
550 92
551
		$params['source'] = self::getSourceParam();
552 92
553 92
		$params['limit'] = array(
554 92
			'type' => 'integer',
555
			'default' => $GLOBALS['smwgQDefaultLimit'],
556
			'negatives' => false,
557
		);
558 92
559 92
		$params['offset'] = array(
560 92
			'type' => 'integer',
561
			'default' => 0,
562 92
			'negatives' => false,
563
			'upperbound' => $GLOBALS['smwgQUpperbound'],
564
		);
565 92
566
		$params['link'] = array(
567
			'default' => 'all',
568
			'values' => array( 'all', 'subject', 'none' ),
569
		);
570 92
571
		$params['sort'] = array(
572
			'islist' => true,
573
			'default' => array( '' ), // The empty string represents the page itself, which should be sorted by default.
574
		);
575 92
576
		$params['order'] = array(
577
			'islist' => true,
578
			'default' => array(),
579
			'values' => array( 'descending', 'desc', 'asc', 'ascending', 'rand', 'random' ),
580
		);
581 92
582
		$params['headers'] = array(
583
			'default' => 'show',
584
			'values' => array( 'show', 'hide', 'plain' ),
585
		);
586 92
587
		$params['mainlabel'] = array(
588
			'default' => false,
589
		);
590 92
591
		$params['intro'] = array(
592
			'default' => '',
593
		);
594 92
595
		$params['outro'] = array(
596
			'default' => '',
597
		);
598 92
599 92
		$params['searchlabel'] = array(
600
			'default' => Message::get( 'smw_iq_moreresults', Message::TEXT, Message::USER_LANGUAGE )
601
		);
602 92
603
		$params['default'] = array(
604
			'default' => '',
605
		);
606
607
		// Give grep a chance to find the usages:
608
		// smw-paramdesc-format, smw-paramdesc-source, smw-paramdesc-limit, smw-paramdesc-offset,
609
		// smw-paramdesc-link, smw-paramdesc-sort, smw-paramdesc-order, smw-paramdesc-headers,
610
		// smw-paramdesc-mainlabel, smw-paramdesc-intro, smw-paramdesc-outro, smw-paramdesc-searchlabel,
611 92
		// smw-paramdesc-default
612 92
		foreach ( $params as $name => &$param ) {
613 92
			if ( is_array( $param ) ) {
614
				$param['message'] = 'smw-paramdesc-' . $name;
615
			}
616
		}
617 92
618
		return ParamDefinition::getCleanDefinitions( $params );
0 ignored issues
show
Documentation introduced by
$params is of type array<string,array<strin...fault\":\"string\"}>"}>, but the function expects a array<integer,object<Par...ssor\IParamDefinition>>.

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...
619
	}
620 92
621 92
	private static function getSourceParam() {
0 ignored issues
show
Coding Style introduced by
getSourceParam 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...
622
		$sourceValues = is_array( $GLOBALS['smwgQuerySources'] ) ? array_keys( $GLOBALS['smwgQuerySources'] ) : array();
623
624 92
		return array(
625 92
			'default' => array_key_exists( 'default', $sourceValues ) ? 'default' : '',
626
			'values' => $sourceValues,
627
		);
628
	}
629
630
	/**
631
	 * Returns the definitions of all parameters supported by the specified format.
632
	 *
633
	 * @since 1.8
634
	 *
635
	 * @param string $format
636
	 *
637
	 * @return ParamDefinition[]
638 1
	 */
639 1
	public static function getFormatParameters( $format ) {
0 ignored issues
show
Coding Style introduced by
getFormatParameters 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...
640
		SMWParamFormat::resolveFormatAliases( $format );
641 1
642 1
		if ( array_key_exists( $format, $GLOBALS['smwgResultFormats'] ) ) {
643 1
			return ParamDefinition::getCleanDefinitions(
644
				self::getResultPrinter( $format )->getParamDefinitions( self::getParameters() )
645
			);
646
		} else {
647
			return array();
648
		}
649
	}
650
651
}
652