Completed
Push — master ( 5d1976...30add5 )
by mw
13s
created

includes/query/SMW_QueryProcessor.php (27 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
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
10
/**
11
 * This file contains a static class for accessing functions to generate and execute
12
 * semantic queries and to serialise their results.
13
 *
14
 * @ingroup SMWQuery
15
 * @author Markus Krötzsch
16
 */
17
18
/**
19
 * Static class for accessing functions to generate and execute semantic queries
20
 * and to serialise their results.
21
 * @ingroup SMWQuery
22
 */
23
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...
24
25
	// "query contexts" define restrictions during query parsing and
26
	// are used to preconfigure query (e.g. special pages show no further
27
	// results link):
28
	const SPECIAL_PAGE = 0; // query for special page
29
	const INLINE_QUERY = 1; // query for inline use
30
	const CONCEPT_DESC = 2; // query for concept definition
31
32
	/**
33
	 * Takes an array of unprocessed parameters, processes them using
34
	 * Validator, and returns them.
35
	 *
36
	 * Both input and output arrays are
37
	 * param name (string) => param value (mixed)
38
	 *
39
	 * @since 1.6.2
40
	 * The return value changed in SMW 1.8 from an array with result values
41
	 * to an array with Param objects.
42
	 *
43
	 * @param array $params
44
	 * @param array $printRequests
45
	 * @param boolean $unknownInvalid
46
	 *
47
	 * @return Param[]
48
	 */
49 65
	public static function getProcessedParams( array $params, array $printRequests = array(), $unknownInvalid = true ) {
50 65
		$validator = self::getValidatorForParams( $params, $printRequests, $unknownInvalid );
51 65
		$validator->processParameters();
52 65
		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...
53
	}
54
55
	/**
56
	 * Takes an array of unprocessed parameters,
57
	 * and sets them on a new Validator object,
58
	 * which is returned and ready to process the parameters.
59
	 *
60
	 * @since 1.8
61
	 *
62
	 * @param array $params
63
	 * @param array $printRequests
64
	 * @param boolean $unknownInvalid
65
	 *
66
	 * @return Processor
67
	 */
68 65
	public static function getValidatorForParams( array $params, array $printRequests = array(), $unknownInvalid = true ) {
69 65
		$paramDefinitions = self::getParameters();
70
71 65
		$paramDefinitions['format']->setPrintRequests( $printRequests );
0 ignored issues
show
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...
72
73 65
		$processorOptions = new Options();
74 65
		$processorOptions->setUnknownInvalid( $unknownInvalid );
75
76 65
		$validator = Processor::newFromOptions( $processorOptions );
77
78 65
		$validator->setParameters( $params, $paramDefinitions, false );
0 ignored issues
show
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...
79
80 65
		return $validator;
81
	}
82
83
	/**
84
	 * Parse a query string given in SMW's query language to create
85
	 * an SMWQuery. Parameters are given as key-value-pairs in the
86
	 * given array. The parameter $context defines in what context the
87
	 * query is used, which affects ceretain general settings.
88
	 * An object of type SMWQuery is returned.
89
	 *
90
	 * The format string is used to specify the output format if already
91
	 * known. Otherwise it will be determined from the parameters when
92
	 * needed. This parameter is just for optimisation in a common case.
93
	 *
94
	 * @param string $queryString
95
	 * @param array $params These need to be the result of a list fed to getProcessedParams
96
	 * @param $context
97
	 * @param string $format
98
	 * @param array $extraPrintouts
99
	 *
100
	 * @return SMWQuery
101
	 */
102 65
	static public function createQuery( $queryString, array $params, $context = self::INLINE_QUERY, $format = '', array $extraPrintouts = array() ) {
103 65
		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...
104
105
		// parse query:
106 65
		$queryfeatures = ( $context == self::CONCEPT_DESC ) ? $smwgQConceptFeatures : $smwgQFeatures;
107 65
		$qp = new SMWQueryParser( $queryfeatures );
108 65
		$qp->setDefaultNamespaces( $smwgQDefaultNamespaces );
109 65
		$desc = $qp->getQueryDescription( $queryString );
110
111 65
		if ( $format === '' || is_null( $format ) ) {
112 65
			$format = $params['format']->getValue();
113
		}
114
115 65
		if ( $format == 'count' ) {
116 1
			$querymode = SMWQuery::MODE_COUNT;
117 65
		} elseif ( $format == 'debug' ) {
118 1
			$querymode = SMWQuery::MODE_DEBUG;
119
		} else {
120 64
			$printer = self::getResultPrinter( $format, $context );
121 64
			$querymode = $printer->getQueryMode( $context );
122
		}
123
124 65
		$query = new SMWQuery( $desc, ( $context != self::SPECIAL_PAGE ), ( $context == self::CONCEPT_DESC ) );
125 65
		$query->setQueryString( $queryString );
126 65
		$query->setExtraPrintouts( $extraPrintouts );
127 65
		$query->setMainLabel( $params['mainlabel']->getValue() );
128 65
		$query->addErrors( $qp->getErrors() ); // keep parsing errors for later output
129 65
		$query->setQuerySource( $params['source']->getValue() );
130
131
		// set mode, limit, and offset:
132 65
		$query->querymode = $querymode;
133 65
		if ( ( array_key_exists( 'offset', $params ) ) && ( is_int( $params['offset']->getValue() + 0 ) ) ) {
134 65
			$query->setOffset( max( 0, trim( $params['offset']->getValue() ) + 0 ) );
135
		}
136
137 65
		if ( $query->querymode == SMWQuery::MODE_COUNT ) { // largest possible limit for "count", even inline
138 1
			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...
139 1
			$query->setOffset( 0 );
140 1
			$query->setLimit( $smwgQMaxLimit, false );
141
		} else {
142 65
			if ( ( array_key_exists( 'limit', $params ) ) && ( is_int( trim( $params['limit']->getValue() ) + 0 ) ) ) {
143 65
				$query->setLimit( max( 0, trim( $params['limit']->getValue() ) + 0 ) );
144 65
				if ( ( trim( $params['limit']->getValue() ) + 0 ) < 0 ) { // limit < 0: always show further results link only
145 65
					$query->querymode = SMWQuery::MODE_NONE;
146
				}
147
			} else {
148
				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...
149
				$query->setLimit( $smwgQDefaultLimit );
150
			}
151
		}
152
153 65
		$defaultSort = $format === 'rss' ? 'DESC' : 'ASC';
154 65
		$sort = self::getSortKeys( $params['sort']->getValue(), $params['order']->getValue(), $defaultSort );
155
156 65
		$query->sortkeys = $sort['keys'];
157 65
		$query->addErrors( $sort['errors'] );
158 65
		$query->sort = count( $query->sortkeys ) > 0; // TODO: Why would we do this here?
159
160 65
		return $query;
161
	}
162
163
	/**
164
	 * Takes the sort and order parameters and returns a list of sort keys and a list of errors.
165
	 *
166
	 * @since 1.7
167
	 *
168
	 * @param array $sortParam
169
	 * @param array $orders
0 ignored issues
show
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...
170
	 * @param string $defaultSort
171
	 *
172
	 * @return array ( keys => array(), errors => array() )
173
	 */
174 65
	protected static function getSortKeys( array $sortParam, array $orderParam, $defaultSort ) {
0 ignored issues
show
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...
175 65
		$orders = array();
176 65
		$sortKeys = array();
177 65
		$sortErros = array();
178
179 65
		foreach ( $orderParam as $key => $order ) {
180 8
			$order = strtolower( trim( $order ) );
181 8
			if ( ( $order == 'descending' ) || ( $order == 'reverse' ) || ( $order == 'desc' ) ) {
182 7
				$orders[$key] = 'DESC';
183 7
			} elseif ( ( $order == 'random' ) || ( $order == 'rand' ) ) {
184
				$orders[$key] = 'RANDOM';
185
			} else {
186 8
				$orders[$key] = 'ASC';
187
			}
188
		}
189
190 65
		foreach ( $sortParam as $sort ) {
191 65
			$sortKey = false;
192
193
			// An empty string indicates we mean the page, such as element 0 on the next line.
194
			// sort=,Some property
195 65
			if ( trim( $sort ) === '' ) {
196 62
				$sortKey = '';
197
			}
198
			else {
199
200 5
				$sort = $GLOBALS['wgContLang']->getNsText( NS_CATEGORY ) == mb_convert_case( $sort, MB_CASE_TITLE ) ? '_INST' : $sort;
201
202 5
				$propertyValue = SMWPropertyValue::makeUserProperty( trim( $sort ) );
203
204 5
				if ( $propertyValue->isValid() ) {
205 5
					$sortKey = $propertyValue->getDataItem()->getKey();
0 ignored issues
show
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...
206
				} else {
207
					$sortErros = array_merge( $sortErros, $propertyValue->getErrors() );
208
				}
209
			}
210
211 65
			if ( $sortKey !== false ) {
212 65
				$order = empty( $orders ) ? $defaultSort : array_shift( $orders );
213 65
				$sortKeys[$sortKey] = $order;
214
			}
215
		}
216
217
		// If more sort arguments are provided then properties, assume the first one is for the page.
218
		// TODO: we might want to add errors if there is more then one.
219 65
		if ( !array_key_exists( '', $sortKeys ) && !empty( $orders ) ) {
220
			$sortKeys[''] = array_shift( $orders );
221
		}
222
223 65
		return array( 'keys' => $sortKeys, 'errors' => $sortErros );
224
	}
225
226
	/**
227
	 * Add the subject print request, unless mainlabel is set to "-".
228
	 *
229
	 * @since 1.7
230
	 *
231
	 * @param array $printRequests
232
	 * @param array $rawParams
233
	 */
234 61
	public static function addThisPrintout( array &$printRequests, array $rawParams ) {
235 61
		if ( !is_null( $printRequests ) ) {
236 61
			$hasMainlabel = array_key_exists( 'mainlabel', $rawParams );
237
238 61
			if  ( !$hasMainlabel || trim( $rawParams['mainlabel'] ) !== '-' ) {
239 60
				array_unshift( $printRequests, new PrintRequest(
240 60
					PrintRequest::PRINT_THIS,
241 60
					$hasMainlabel ? $rawParams['mainlabel'] : ''
242
				) );
243
			}
244
		}
245 61
	}
246
247
	/**
248
	 * Preprocess a query as given by an array of parameters as is typically
249
	 * produced by the #ask parser function. The parsing results in a querystring,
250
	 * an array of additional parameters, and an array of additional SMWPrintRequest
251
	 * objects, which are filled into call-by-ref parameters.
252
	 * $showMode is true if the input should be treated as if given by #show
253
	 *
254
	 * @param array $rawParams
255
	 * @param string $querystring
256
	 * @param array $params
257
	 * @param array $printouts array of SMWPrintRequest
258
	 * @param boolean $showMode
259
	 * @deprecated Will vanish after SMW 1.8 is released.
260
	 * Use getComponentsFromFunctionParams which has a cleaner interface.
261
	 */
262
	static public function processFunctionParams( array $rawParams, &$querystring, &$params, &$printouts, $showMode = false ) {
0 ignored issues
show
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...
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...
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...
263
		list( $querystring, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
264
	}
265
266
267
	/**
268
	 * Preprocess a query as given by an array of parameters as is
269
	 * typically produced by the #ask parser function or by Special:Ask.
270
	 * The parsing results in a querystring, an array of additional
271
	 * parameters, and an array of additional SMWPrintRequest objects,
272
	 * which are returned in an array with three components. If
273
	 * $showMode is true then the input will be processed as if for #show.
274
	 * This uses a slightly different way to get the query, and different
275
	 * default labels (empty) for additional print requests.
276
	 *
277
	 * @param array $rawParams
278
	 * @param boolean $showMode
279
	 * @return array( string, array( string => string ), array( SMWPrintRequest ) )
0 ignored issues
show
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...
280
	 */
281 72
	static public function getComponentsFromFunctionParams( array $rawParams, $showMode ) {
282 72
		$queryString = '';
283 72
		$parameters = array();
284 72
		$printouts = array();
285
286 72
		$lastprintout = null;
287 72
		$printRequestFactory = new PrintRequestFactory();
288
289 72
		foreach ( $rawParams as $name => $rawParam ) {
290
			// special handling for arrays - this can happen if the
291
			// parameter came from a checkboxes input in Special:Ask:
292 72
			if ( is_array( $rawParam ) ) {
293
				$rawParam = implode( ',', array_keys( $rawParam ) );
294
			}
295
296
			// Bug 32955 / #640
297
			// Modify (e.g. replace `=`) a condition string only if enclosed by [[ ... ]]
298 72
			$rawParam = preg_replace_callback(
299 72
				'/\[\[(?:([^:][^]]*):[=:])+([^\[\]]*)\]\]/xu',
300 72
				function( array $matches ) {
301 48
					return str_replace( array( '=' ), array( '-3D' ), $matches[0] );
302 72
				},
303
				$rawParam
304
			);
305
306
			// 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...
307 72
			if ( is_string( $name ) && ( $name !== '' ) ) {
308
				$rawParam = $name . '=' . $rawParam;
309
			}
310
311 72
			if ( $rawParam === '' ) {
0 ignored issues
show
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...
312 72
			} elseif ( $rawParam { 0 } == '?' ) { // print statement
313 50
				$rawParam = substr( $rawParam, 1 );
314 50
				$lastprintout = $printRequestFactory->newPrintRequestFromText( $rawParam, $showMode );
315 50
				if ( !is_null( $lastprintout ) ) {
316 50
					$printouts[] = $lastprintout;
317
				}
318 72
			} elseif ( $rawParam[0] == '+' ) { // print request parameter
319 5
				if ( !is_null( $lastprintout ) ) {
320 4
					$rawParam = substr( $rawParam, 1 );
321 4
					$parts = explode( '=', $rawParam, 2 );
322 4
					if ( count( $parts ) == 2 ) {
323 4
						$lastprintout->setParameter( trim( $parts[0] ), $parts[1] );
324
					} else {
325 5
						$lastprintout->setParameter( trim( $parts[0] ), null );
326
					}
327
				}
328
			} else { // parameter or query
329 72
				$parts = explode( '=', $rawParam, 2 );
330
331 72
				if ( count( $parts ) >= 2 ) {
332
					// don't trim here, some parameters care for " "
333 54
					$parameters[strtolower( trim( $parts[0] ) )] = $parts[1];
334
				} else {
335 72
					$queryString .= $rawParam;
336
				}
337
			}
338
		}
339
340 72
		$queryString = str_replace( array( '&lt;', '&gt;', '-3D' ), array( '<', '>', '=' ), $queryString );
341
342 72
		if ( $showMode ) {
343 6
			$queryString = "[[:$queryString]]";
344
		}
345
346 72
		return array( $queryString, $parameters, $printouts);
347
	}
348
349
	/**
350
	 * Process and answer a query as given by an array of parameters as is
351
	 * typically produced by the #ask parser function. The parameter
352
	 * $context defines in what context the query is used, which affects
353
	 * certain general settings.
354
	 *
355
	 * The main task of this function is to preprocess the raw parameters
356
	 * to obtain actual parameters, printout requests, and the query string
357
	 * for further processing.
358
	 *
359
	 * @since 1.8
360
	 * @param array $rawParams user-provided list of unparsed parameters
361
	 * @param integer $outputMode SMW_OUTPUT_WIKI, SMW_OUTPUT_HTML, ...
362
	 * @param integer $context INLINE_QUERY, SPECIAL_PAGE, CONCEPT_DESC
363
	 * @param boolean $showMode process like #show parser function?
364
	 * @return array( SMWQuery, array of IParam )
0 ignored issues
show
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...
365
	 */
366 62
	static public function getQueryAndParamsFromFunctionParams( array $rawParams, $outputMode, $context, $showMode ) {
0 ignored issues
show
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...
367 62
		list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
368
369 62
		if ( !$showMode ) {
370 60
			self::addThisPrintout( $printouts, $params );
371
		}
372
373 62
		$params = self::getProcessedParams( $params, $printouts );
374
375 62
		$query  = self::createQuery( $queryString, $params, $context, '', $printouts );
376 62
		return array( $query, $params );
377
	}
378
379
	/**
380
	 * Process and answer a query as given by an array of parameters as is
381
	 * typically produced by the #ask parser function. The result is formatted
382
	 * according to the specified $outputformat. The parameter $context defines
383
	 * in what context the query is used, which affects ceretain general settings.
384
	 *
385
	 * The main task of this function is to preprocess the raw parameters to
386
	 * obtain actual parameters, printout requests, and the query string for
387
	 * further processing.
388
	 *
389
	 * @note Consider using getQueryAndParamsFromFunctionParams() and
390
	 * getResultFromQuery() instead.
391
	 * @deprecated Will vanish after release of SMW 1.8.
392
	 * See SMW_Ask.php for example code on how to get query results from
393
	 * #ask function parameters.
394
	 */
395
	static public function getResultFromFunctionParams( array $rawParams, $outputMode, $context = self::INLINE_QUERY, $showMode = false ) {
0 ignored issues
show
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...
396
		list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
397
398
		if ( !$showMode ) {
399
			self::addThisPrintout( $printouts, $params );
400
		}
401
402
		$params = self::getProcessedParams( $params, $printouts );
403
404
		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...
405
	}
406
407
	/**
408
	 * Process a query string in SMW's query language and return a formatted
409
	 * result set as specified by $outputmode. A parameter array of key-value-pairs
410
	 * constrains the query and determines the serialisation mode for results. The
411
	 * parameter $context defines in what context the query is used, which affects
412
	 * certain general settings. Finally, $extraprintouts supplies additional
413
	 * printout requests for the query results.
414
	 *
415
	 * @param string $queryString
416
	 * @param array $params These need to be the result of a list fed to getProcessedParams
417
	 * @param $extraPrintouts
418
	 * @param $outputMode
419
	 * @param $context
420
	 *
421
	 * @return string
422
	 * @deprecated Will vanish after release of SMW 1.8.
423
	 * See SMW_Ask.php for example code on how to get query results from
424
	 * #ask function parameters.
425
	 */
426
	static public function getResultFromQueryString( $queryString, array $params, $extraPrintouts, $outputMode, $context = self::INLINE_QUERY ) {
427
428
		$query  = self::createQuery( $queryString, $params, $context, '', $extraPrintouts );
429
		$result = self::getResultFromQuery( $query, $params, $outputMode, $context );
430
431
432
		return $result;
433
	}
434
435
	/**
436
	 * Create a fully formatted result string from a query and its
437
	 * parameters. The method takes care of processing various types of
438
	 * query result. Most cases are handled by printers, but counting and
439
	 * debugging uses special code.
440
	 *
441
	 * @param SMWQuery $query
442
	 * @param array $params These need to be the result of a list fed to getProcessedParams
443
	 * @param integer $outputMode
444
	 * @param integer $context
445
	 * @since before 1.7, but public only since 1.8
446
	 *
447
	 * @return string
448
	 */
449 54
	public static function getResultFromQuery( SMWQuery $query, array $params, $outputMode, $context ) {
450
451 54
		$res = self::getStoreFromParams( $params )->getQueryResult( $query );
452
453 54
		if ( ( $query->querymode == SMWQuery::MODE_INSTANCES ) ||
454 54
			( $query->querymode == SMWQuery::MODE_NONE ) ) {
455
456 53
			$printer = self::getResultPrinter( $params['format']->getValue(), $context );
457 53
			$result = $printer->getResult( $res, $params, $outputMode );
458
459
460 53
			return $result;
461
		} else { // result for counting or debugging is just a string or number
462
463 1
			if ( $res instanceof SMWQueryResult ) {
464
				$res = $res->getCountValue();
465
			}
466
467 1
			if ( is_numeric( $res ) ) {
468
				$res = strval( $res );
469
			}
470
471 1
			if ( is_string( $res ) ) {
472 1
				$result = str_replace( '_', ' ', $params['intro']->getValue() )
473 1
					. $res
474 1
					. str_replace( '_', ' ', $params['outro']->getValue() )
475 1
					. smwfEncodeMessages( $query->getErrors() );
476
			} else {
477
				// When no valid result was obtained, $res will be a SMWQueryResult.
478
				$result = smwfEncodeMessages( $query->getErrors() );
479
			}
480
481
482 1
			return $result;
483
		}
484
	}
485
486 54
	private static function getStoreFromParams( array $params ) {
0 ignored issues
show
getStoreFromParams 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...
487
488 54
		$storeId = null;
489 54
		$source  = $params['source']->getValue();
490
491 54
		if ( $source !== '' ) {
492 1
			$storeId = $GLOBALS['smwgQuerySources'][$source];
493
		}
494
495 54
		return \SMW\StoreFactory::getStore( $storeId );
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
	 */
511 66
	static public function getResultPrinter( $format, $context = self::SPECIAL_PAGE ) {
512 66
		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
514 66
		if ( !array_key_exists( $format, $smwgResultFormats ) ) {
515
			throw new MWException( "There is no result format for '$format'." );
516
		}
517
518 66
		$formatClass = $smwgResultFormats[$format];
519
520 66
		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
	 */
532 66
	public static function getParameters() {
0 ignored issues
show
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 66
		$params = array();
534
535 66
		$allowedFormats = $GLOBALS['smwgResultFormats'];
536
537 66
		foreach ( $GLOBALS['smwgResultAliases'] as $aliases ) {
538 66
			$allowedFormats += $aliases;
539
		}
540
541 66
		$allowedFormats[] = 'auto';
542
543 66
		$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
551 66
		$params['source'] = self::getSourceParam();
552
553 66
		$params['limit'] = array(
554 66
			'type' => 'integer',
555 66
			'default' => $GLOBALS['smwgQDefaultLimit'],
556
			'negatives' => false,
557
		);
558
559 66
		$params['offset'] = array(
560 66
			'type' => 'integer',
561 66
			'default' => 0,
562
			'negatives' => false,
563 66
			'upperbound' => $GLOBALS['smwgQUpperbound'],
564
		);
565
566 66
		$params['link'] = array(
567
			'default' => 'all',
568
			'values' => array( 'all', 'subject', 'none' ),
569
		);
570
571 66
		$params['sort'] = array(
572
			'islist' => true,
573
			'default' => array( '' ), // The empty string represents the page itself, which should be sorted by default.
574
		);
575
576 66
		$params['order'] = array(
577
			'islist' => true,
578
			'default' => array(),
579
			'values' => array( 'descending', 'desc', 'asc', 'ascending', 'rand', 'random' ),
580
		);
581
582 66
		$params['headers'] = array(
583
			'default' => 'show',
584
			'values' => array( 'show', 'hide', 'plain' ),
585
		);
586
587 66
		$params['mainlabel'] = array(
588
			'default' => false,
589
		);
590
591 66
		$params['intro'] = array(
592
			'default' => '',
593
		);
594
595 66
		$params['outro'] = array(
596
			'default' => '',
597
		);
598
599 66
		$params['searchlabel'] = array(
600 66
			'default' => wfMessage( 'smw_iq_moreresults' )->inContentLanguage()->text(),
601
		);
602
603 66
		$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
		// smw-paramdesc-default
612 66
		foreach ( $params as $name => &$param ) {
613 66
			if ( is_array( $param ) ) {
614 66
				$param['message'] = 'smw-paramdesc-' . $name;
615
			}
616
		}
617
618 66
		return ParamDefinition::getCleanDefinitions( $params );
0 ignored issues
show
$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
621 66
	private static function getSourceParam() {
0 ignored issues
show
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 66
		$sourceValues = is_array( $GLOBALS['smwgQuerySources'] ) ? array_keys( $GLOBALS['smwgQuerySources'] ) : array();
623
624
		return array(
625 66
			'default' => array_key_exists( 'default', $sourceValues ) ? 'default' : '',
626 66
			'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
	 */
639
	public static function getFormatParameters( $format ) {
0 ignored issues
show
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
642
		if ( array_key_exists( $format, $GLOBALS['smwgResultFormats'] ) ) {
643
			return ParamDefinition::getCleanDefinitions(
644
				self::getResultPrinter( $format )->getParamDefinitions( self::getParameters() )
645
			);
646
		} else {
647
			return array();
648
		}
649
	}
650
651
}
652