Completed
Push — master ( ff2cd3...da92b9 )
by mw
236:43 queued 201:44
created

AskParserFunction::addQueryProfile()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 2
nop 2
dl 0
loc 30
ccs 14
cts 14
cp 1
crap 4
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace SMW\ParserFunctions;
4
5
use SMW\ParserData;
6
use SMW\MessageFormatter;
7
use SMW\Utils\CircularReferenceGuard;
8
use SMW\ApplicationFactory;
9
use SMW\ProcessingErrorMsgHandler;
10
use SMW\DIProperty;
11
use Parser;
12
use SMWQueryProcessor as QueryProcessor;
13
use SMWQuery as Query;
14
15
/**
16
 * Provides the {{#ask}} parser function
17
 *
18
 * @see http://www.semantic-mediawiki.org/wiki/Help:Ask
19
 *
20
 * @license GNU GPL v2+
21
 * @since 1.9
22
 *
23
 * @author Markus Krötzsch
24
 * @author Jeroen De Dauw
25
 * @author mwjames
26
 */
27
class AskParserFunction {
28
29
	/**
30
	 * @var ParserData
31
	 */
32
	private $parserData;
33
34
	/**
35
	 * @var MessageFormatter
36
	 */
37
	private $messageFormatter;
38
39
	/**
40
	 * @var CircularReferenceGuard
41
	 */
42
	private $circularReferenceGuard;
43
44
	/**
45
	 * @var boolean
46
	 */
47
	private $showMode = false;
48
49
	/**
50
	 * @var ApplicationFactory
51
	 */
52
	private $applicationFactory;
53
54
	/**
55
	 * @since 1.9
56
	 *
57
	 * @param ParserData $parserData
58
	 * @param MessageFormatter $messageFormatter
59
	 * @param CircularReferenceGuard $circularReferenceGuard
60
	 */
61 114
	public function __construct( ParserData $parserData, MessageFormatter $messageFormatter, CircularReferenceGuard $circularReferenceGuard ) {
62 114
		$this->parserData = $parserData;
63 114
		$this->messageFormatter = $messageFormatter;
64 114
		$this->circularReferenceGuard = $circularReferenceGuard;
65 114
	}
66
67
	/**
68
	 * Enable showMode (normally only invoked by {{#show}})
69
	 *
70
	 * @since 1.9
71
	 *
72
	 * @return AskParserFunction
73
	 */
74 14
	public function setShowMode( $mode ) {
75 14
		$this->showMode = $mode;
76 14
		return $this;
77
	}
78
79
	/**
80
	 * {{#ask}} is disabled (see $smwgQEnabled)
81
	 *
82
	 * @since 1.9
83
	 *
84
	 * @return string|null
85
	 */
86 2
	public function isQueryDisabled() {
87 2
		return $this->messageFormatter->addFromKey( 'smw_iq_disabled' )->getHtml();
88
	}
89
90
	/**
91
	 * Parse parameters, return results from the query printer and update the
92
	 * ParserOutput with meta data from the query
93
	 *
94
	 * FIXME $rawParams use IParameterFormatter -> QueryParameterFormatter class
95
	 * Parse parameters and return query results to the ParserOutput
96
	 * object and output result data from the QueryProcessor
97
	 *
98
	 * @todo $rawParams should be of IParameterFormatter
99
	 * QueryParameterFormatter class
100
	 *
101
	 * @since 1.9
102
	 *
103
	 * @param array $functionParams
104
	 *
105
	 * @return string|null
106
	 */
107 110
	public function parse( array $functionParams ) {
108
109
		// Do we still need this?
110
		// Reference found in SRF_Exhibit.php, SRF_Ploticus.php, SRF_Timeline.php, SRF_JitGraph.php
111 110
		global $smwgIQRunningNumber;
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...
112 110
		$smwgIQRunningNumber++;
113
114 110
		$this->applicationFactory = ApplicationFactory::getInstance();
115
116 110
		$functionParams = $this->prepareFunctionParameters(
117
			$functionParams
118
		);
119
120 110
		$result = $this->doFetchResultsFromFunctionParameters(
121
			$functionParams
122
		);
123
124 110
		$this->parserData->pushSemanticDataToParserOutput();
125
126
		// 1.23+ add options so changes are recognized in case of:
127
		// - 'userlang' will trigger a cache fragmentation by user language
128
		// - 'dateformat'  will trigger a cache fragmentation by date preference
129 110
		if ( method_exists( $this->parserData->getOutput(), 'recordOption' ) ) {
130 110
			$this->parserData->getOutput()->recordOption( 'userlang' );
131 110
			$this->parserData->getOutput()->recordOption( 'dateformat' );
132
		}
133
134 110
		return $result;
135
	}
136
137 110
	private function prepareFunctionParameters( array $functionParams ) {
138
139
		// Remove parser object from parameters array
140 110
		if( isset( $functionParams[0] ) && $functionParams[0] instanceof Parser ) {
0 ignored issues
show
Bug introduced by
The class Parser does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
141 91
			array_shift( $functionParams );
142
		}
143
144
		// Filter invalid parameters
145 110
		foreach ( $functionParams as $key => $value ) {
146
147
			// First and marked printrequests
148 109
			if (  $key == 0 || ( $value !== '' && $value{0} === '?' ) ) {
149 109
				continue;
150
			}
151
152
			// Filter parameters that can not be split into
153
			// argument=value
154 89
			if ( strpos( $value, '=' ) === false ) {
155 89
				unset( $functionParams[$key] );
156
			}
157
		}
158
159 110
		return $functionParams;
160
	}
161
162 110
	private function doFetchResultsFromFunctionParameters( array $functionParams ) {
0 ignored issues
show
Coding Style introduced by
doFetchResultsFromFunctionParameters 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...
163
164 110
		$contextPage = $this->parserData->getSubject();
165
166 110
		list( $query, $this->params ) = QueryProcessor::getQueryAndParamsFromFunctionParams(
0 ignored issues
show
Bug introduced by
The property params does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
167
			$functionParams,
168 110
			SMW_OUTPUT_WIKI,
169 110
			QueryProcessor::INLINE_QUERY,
170 110
			$this->showMode,
171
			$contextPage
172
		);
173
174 110
		$query->setContextPage(
175
			$contextPage
176
		);
177
178 110
		$queryHash = $query->getHash();
179
180 110
		$this->circularReferenceGuard->mark( $queryHash );
181
182
		// If we caught in a circular loop (due to a template referencing to itself)
183
		// then we stop here before the next query execution to avoid an infinite
184
		// self-reference
185 110
		if ( $this->circularReferenceGuard->isCircularByRecursionFor( $queryHash ) ) {
186 2
			return '';
187
		}
188
189 109
		$result = QueryProcessor::getResultFromQuery(
190
			$query,
191 109
			$this->params,
192 109
			SMW_OUTPUT_WIKI,
193 109
			QueryProcessor::INLINE_QUERY
194
		);
195
196 109
		$format = $this->params['format']->getValue();
197
198
		// FIXME Parser should be injected into the ResultPrinter
199
		// Enables specific formats to import its annotation data from
200
		// a recursive parse process in the result format
201
		// e.g. using ask query to search/set an invert property value
202 109
		if ( isset( $this->params['import-annotation'] ) && $this->params['import-annotation']->getValue() ) {
203 4
			$this->parserData->importFromParserOutput( $GLOBALS['wgParser']->getOutput() );
204
		}
205
206 109
		$this->circularReferenceGuard->unmark( $queryHash );
207
208
		// In case of an query error add a marker to the subject for discoverability
209
		// of a failed query, don't bail-out as we can have results and errors
210
		// at the same time
211 109
		$this->addProcessingError( $query->getErrors() );
212
213 109
		$this->addQueryProfile(
214
			$query,
215
			$format
216
		);
217
218 109
		return $result;
219
	}
220
221 109
	private function addQueryProfile( $query, $format ) {
222
223
		$settings = $this->applicationFactory->getSettings();
224 109
225 1
		// If the smwgQueryProfiler is marked with FALSE then just don't create a profile.
226
		if ( $settings->get( 'smwgQueryProfiler' ) === false ) {
227
			return;
228 108
		}
229 108
230 108
		$query->setOption(
231
			Query::PROC_QUERY_TIME,
232
			$settings->get( 'smwgQueryDurationEnabled' ) ? $query->getOption( Query::PROC_QUERY_TIME ) : 0
233 108
		);
234
235 108
		$query->setOption(
236
			Query::OPT_PARAMETERS,
237
			$settings->has( 'smwgQueryParametersEnabled' ) ? $settings->get( 'smwgQueryParametersEnabled' ) : false
238
		);
239
240 108
		$profileAnnotatorFactory = $this->applicationFactory->getQueryFactory()->newProfileAnnotatorFactory();
241 108
242
		$combinedProfileAnnotator = $profileAnnotatorFactory->newCombinedProfileAnnotator(
243 108
			$query,
244
			$format
245 109
		);
246
247 109
		$combinedProfileAnnotator->pushAnnotationsTo(
248 105
			$this->parserData->getSemanticData()
249
		);
250
	}
251 6
252 6
	private function addProcessingError( $errors ) {
253
254
		if ( $errors === array() ) {
255 6
			return;
256
		}
257 6
258 6
		$processingErrorMsgHandler = new ProcessingErrorMsgHandler(
259 6
			$this->parserData->getSubject()
260 6
		);
261
262
		$property = new DIProperty( '_ASK' );
263 6
264
		foreach ( $errors as $error ) {
265
			$processingErrorMsgHandler->addToSemanticData(
266
				$this->parserData->getSemanticData(),
267
				$processingErrorMsgHandler->newErrorContainerFromMsg( $error, $property )
268
			);
269
		}
270
	}
271
272
}
273