Completed
Push — master ( 3b7974...7a2449 )
by mw
58:29 queued 42:05
created

AskParserFunction::createQueryProfile()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 12
c 2
b 0
f 0
nc 2
nop 3
dl 0
loc 24
ccs 10
cts 10
cp 1
crap 2
rs 8.9713
1
<?php
2
3
namespace SMW;
4
5
use Parser;
6
use SMWQueryProcessor;
7
8
/**
9
 * Provides the {{#ask}} parser function
10
 *
11
 * @see http://www.semantic-mediawiki.org/wiki/Help:Ask
12
 *
13
 * @license GNU GPL v2+
14
 * @since 1.9
15
 *
16
 * @author Markus Krötzsch
17
 * @author Jeroen De Dauw
18
 * @author mwjames
19
 */
20
class AskParserFunction {
21
22
	/**
23
	 * @var ParserData
24
	 */
25
	private $parserData;
26
27
	/**
28
	 * @var MessageFormatter
29
	 */
30
	private $messageFormatter;
31
32
	/**
33
	 * @var CircularReferenceGuard
34
	 */
35
	private $circularReferenceGuard;
36
37
	/**
38
	 * @var boolean
39
	 */
40
	private $showMode = false;
41
42
	/**
43
	 * @var ApplicationFactory
44
	 */
45
	private $applicationFactory;
46
47
	/**
48
	 * @since 1.9
49
	 *
50
	 * @param ParserData $parserData
51
	 * @param MessageFormatter $messageFormatter
52
	 * @param CircularReferenceGuard $circularReferenceGuard
53
	 */
54 88
	public function __construct( ParserData $parserData, MessageFormatter $messageFormatter, CircularReferenceGuard $circularReferenceGuard ) {
55 88
		$this->parserData = $parserData;
56 88
		$this->messageFormatter = $messageFormatter;
57 88
		$this->circularReferenceGuard = $circularReferenceGuard;
58 88
	}
59
60
	/**
61
	 * Enable showMode (normally only invoked by {{#show}})
62
	 *
63
	 * @since 1.9
64
	 *
65
	 * @return AskParserFunction
66
	 */
67 11
	public function setShowMode( $mode ) {
68 11
		$this->showMode = $mode;
69 11
		return $this;
70
	}
71
72
	/**
73
	 * {{#ask}} is disabled (see $smwgQEnabled)
74
	 *
75
	 * @since 1.9
76
	 *
77
	 * @return string|null
78
	 */
79 2
	public function isQueryDisabled() {
80 2
		return $this->messageFormatter->addFromKey( 'smw_iq_disabled' )->getHtml();
81
	}
82
83
	/**
84
	 * Parse parameters, return results from the query printer and update the
85
	 * ParserOutput with meta data from the query
86
	 *
87
	 * FIXME $rawParams use IParameterFormatter -> QueryParameterFormatter class
88
	 * Parse parameters and return query results to the ParserOutput
89
	 * object and output result data from the SMWQueryProcessor
90
	 *
91
	 * @todo $rawParams should be of IParameterFormatter
92
	 * QueryParameterFormatter class
93
	 *
94
	 * @since 1.9
95
	 *
96
	 * @param array $params
0 ignored issues
show
Documentation introduced by
There is no parameter named $params. Did you maybe mean $rawParams?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
97
	 *
98
	 * @return string|null
99
	 */
100 84
	public function parse( array $rawParams ) {
101
102
		// Do we still need this?
103
		// Reference found in SRF_Exhibit.php, SRF_Ploticus.php, SRF_Timeline.php, SRF_JitGraph.php
104 84
		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...
105 84
		$smwgIQRunningNumber++;
106
107 84
		$this->applicationFactory = ApplicationFactory::getInstance();
108
109 84
		$rawParams = $this->prepareRawParameters(
110
			$rawParams
111
		);
112
113 84
		$result = $this->doFetchResultsForRawParameters(
114
			$rawParams
115
		);
116
117 84
		$this->parserData->pushSemanticDataToParserOutput();
118
119
		// 1.23+ add options so changes are recognized in case of:
120
		// - 'userlang' will trigger a cache fragmentation by user language
121
		// - 'dateformat'  will trigger a cache fragmentation by date preference
122 84
		if ( method_exists( $this->parserData->getOutput(), 'recordOption' ) ) {
123 84
			$this->parserData->getOutput()->recordOption( 'userlang' );
124 84
			$this->parserData->getOutput()->recordOption( 'dateformat' );
125
		}
126
127 84
		return $result;
128
	}
129
130 84
	private function prepareRawParameters( array $rawParams ) {
131
132
		// Remove parser object from parameters array
133 84
		if( isset( $rawParams[0] ) && $rawParams[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...
134 67
			array_shift( $rawParams );
135
		}
136
137
		// Filter invalid parameters
138 84
		foreach ( $rawParams as $key => $value ) {
139
140
			// First and marked printrequests
141 83
			if (  $key == 0 || ( $value !== '' && $value{0} === '?' ) ) {
142 83
				continue;
143
			}
144
145
			// Filter parameters that can not be split into
146
			// argument=value
147 68
			if ( strpos( $value, '=' ) === false ) {
148 68
				unset( $rawParams[$key] );
149
			}
150
		}
151
152 84
		return $rawParams;
153
	}
154
155 84
	private function doFetchResultsForRawParameters( array $rawParams ) {
0 ignored issues
show
Coding Style introduced by
doFetchResultsForRawParameters 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...
156
157
		// FIXME QueryDuration should be a property of the QueryProcessor or
158
		// QueryEngine but since we don't want to open the pandora's box and
159
		// increase issues within the current QueryProcessor implementation
160
		// we will track the time outside of the actual execution framework
161 84
		$queryDuration = 0;
162 84
		$start = microtime( true );
163
164 84
		$contextPage = $this->parserData->getSubject();
165
166 84
		list( $this->query, $this->params ) = SMWQueryProcessor::getQueryAndParamsFromFunctionParams(
0 ignored issues
show
Bug introduced by
The property query 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...
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
			$rawParams,
168 84
			SMW_OUTPUT_WIKI,
169 84
			SMWQueryProcessor::INLINE_QUERY,
170 84
			$this->showMode,
171
			$contextPage
172
		);
173
174 84
		$this->query->setContextPage(
175
			$contextPage
176
		);
177
178 84
		$queryHash = $this->query->getHash();
179
180 84
		$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 84
		if ( $this->circularReferenceGuard->isCircularByRecursionFor( $queryHash ) ) {
186 2
			return '';
187
		}
188
189 83
		$result = SMWQueryProcessor::getResultFromQuery(
190 83
			$this->query,
191 83
			$this->params,
192 83
			SMW_OUTPUT_WIKI,
193 83
			SMWQueryProcessor::INLINE_QUERY
194
		);
195
196 83
		$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 83
		if ( isset( $this->params['import-annotation'] ) && $this->params['import-annotation']->getValue() ) {
203 4
			$this->parserData->importFromParserOutput( $GLOBALS['wgParser']->getOutput() );
204
		}
205
206 83
		if ( $this->applicationFactory->getSettings()->get( 'smwgQueryDurationEnabled' ) ) {
207 1
			$queryDuration = microtime( true ) - $start;
208
		}
209
210 83
		$this->circularReferenceGuard->unmark( $queryHash );
211
212 83
		$this->createQueryProfile(
213 83
			$this->query,
214
			$format,
215
			$queryDuration
216
		);
217
218 83
		return $result;
219
	}
220
221 83
	private function createQueryProfile( $query, $format, $duration ) {
222
223
		// In case of an query error add a marker to the subject for discoverability
224
		// of a failed query, don't bail-out as we can have results and errors
225
		// at the same time
226 83
		if ( $query->getErrors() !== array() ) {
227 6
			$this->addProcessingError( $query->getErrors() );
228
		}
229
230 83
		$queryProfileAnnotatorFactory = $this->applicationFactory->newQueryProfileAnnotatorFactory();
231
232 83
		$jointProfileAnnotator = $queryProfileAnnotatorFactory->newJointProfileAnnotator(
233
			$query,
234
			$format,
235
			$duration
236
		);
237
238 83
		$jointProfileAnnotator->addAnnotation();
239
240 83
		$this->parserData->getSemanticData()->addPropertyObjectValue(
241 83
			$jointProfileAnnotator->getProperty(),
242 83
			$jointProfileAnnotator->getContainer()
243
		);
244 83
	}
245
246 6
	private function addProcessingError( $errors ) {
247
248 6
		$processingErrorMsgHandler = new ProcessingErrorMsgHandler(
249 6
			$this->parserData->getSubject()
250
		);
251
252 6
		foreach ( $errors as $error ) {
253 6
			$processingErrorMsgHandler->pushTo(
254 6
				$this->parserData->getSemanticData(),
255 6
				$processingErrorMsgHandler->getErrorContainerFromMsg( $error, new DIProperty( '_ASK' ) )
256
			);
257
		}
258 6
	}
259
260
}
261