Completed
Push — master ( 17dff3...f2d821 )
by mw
42:32 queued 07:38
created

QuerySegmentListBuilder::getQuerySegmentFrom()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 27
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 1
dl 0
loc 27
ccs 10
cts 10
cp 1
crap 3
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace SMW\SQLStore\QueryEngine;
4
5
use InvalidArgumentException;
6
use OutOfBoundsException;
7
use SMW\Utils\CircularReferenceGuard;
8
use SMW\Query\Language\Description;
9
use SMW\Query\Language\Conjuncton;
10
use SMW\Store;
11
use SMW\Message;
12
13
/**
14
 * @license GNU GPL v2+
15
 * @since 2.2
16
 *
17
 * @author Markus Krötzsch
18
 * @author Jeroen De Dauw
19
 * @author mwjames
20
 */
21
class QuerySegmentListBuilder {
22
23
	/**
24
	 * @var Store
25
	 */
26
	private $store;
27
28
	/**
29
	 * @var DispatchingDescriptionInterpreter
30
	 */
31
	private $dispatchingDescriptionInterpreter = null;
32
33
	/**
34
	 * @var boolean
35
	 */
36
	private $isFilterDuplicates = true;
37
38
	/**
39
	 * Array of generated QueryContainer query descriptions (index => object).
40
	 *
41
	 * @var QuerySegment[]
42
	 */
43
	private $querySegments = array();
44
45
	/**
46
	 * Array of sorting requests ("Property_name" => "ASC"/"DESC"). Used during query
47
	 * processing (where these property names are searched while compiling the query
48
	 * conditions).
49
	 *
50
	 * @var string[]
51
	 */
52
	private $sortKeys = array();
53
54
	/**
55
	 * @var string[]
56
	 */
57
	private $errors = array();
58
59
	/**
60
	 * @var integer
61
	 */
62
	private $lastQuerySegmentId = -1;
63
64
	/**
65
	 * @since 2.2
66
	 *
67
	 * @param Store $store
68
	 * @param DescriptionInterpreterFactory $descriptionInterpreterFactory
69
	 */
70 190
	public function __construct( Store $store, DescriptionInterpreterFactory $descriptionInterpreterFactory ) {
71 190
		$this->store = $store;
72 190
		$this->dispatchingDescriptionInterpreter = $descriptionInterpreterFactory->newDispatchingDescriptionInterpreter( $this );
0 ignored issues
show
Documentation Bug introduced by
It seems like $descriptionInterpreterF...ptionInterpreter($this) of type object<SMW\SQLStore\Quer...DescriptionInterpreter> is incompatible with the declared type object<SMW\SQLStore\Quer...DescriptionInterpreter> of property $dispatchingDescriptionInterpreter.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
73 190
		$this->circularReferenceGuard = new CircularReferenceGuard( 'sql-query' );
0 ignored issues
show
Bug introduced by
The property circularReferenceGuard 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...
74 190
		$this->circularReferenceGuard->setMaxRecursionDepth( 2 );
75
76 190
		QuerySegment::$qnum = 0;
77 190
	}
78
79
	/**
80
	 * Filter dulicate segments that represent the same query and to be identified
81
	 * by the same hash.
82
	 *
83
	 * @since 2.5
84
	 *
85
	 * @param boolean $isFilterDuplicates
86
	 */
87 170
	public function isFilterDuplicates( $isFilterDuplicates ) {
88 170
		$this->isFilterDuplicates = (bool)$isFilterDuplicates;
89 170
	}
90
91
	/**
92
	 * @since 2.2
93
	 *
94
	 * @return Store
95
	 */
96 173
	public function getStore() {
97 173
		return $this->store;
98
	}
99
100
	/**
101
	 * @since 2.2
102
	 *
103
	 * @param array $sortKeys
104
	 *
105
	 * @return $this
106
	 */
107 170
	public function setSortKeys( $sortKeys ) {
108 170
		$this->sortKeys = $sortKeys;
109 170
		return $this;
110
	}
111
112
	/**
113
	 * @since 2.2
114
	 *
115
	 * @return string[]
116
	 */
117 133
	public function getSortKeys() {
118 133
		return $this->sortKeys;
119
	}
120
121
	/**
122
	 * @since 2.2
123
	 *
124
	 * @return CircularReferenceGuard
125
	 */
126 4
	public function getCircularReferenceGuard() {
127 4
		return $this->circularReferenceGuard;
128
	}
129
130
	/**
131
	 * @since 2.2
132
	 *
133
	 * @param int $id
134
	 *
135
	 * @return QuerySegment
136
	 * @throws InvalidArgumentException
137
	 * @throws OutOfBoundsException
138
	 */
139 174
	public function findQuerySegment( $id ) {
140
141 174
		if ( !is_int( $id ) ) {
142 1
			throw new InvalidArgumentException( '$id needs to be an integer' );
143
		}
144
145 173
		if ( !array_key_exists( $id, $this->querySegments ) ) {
146 1
			throw new OutOfBoundsException( 'There is no query segment with id ' . $id );
147
		}
148
149 172
		return $this->querySegments[$id];
150
	}
151
152
	/**
153
	 * @since 2.2
154
	 *
155
	 * @return QuerySegment[]
156
	 */
157 175
	public function getQuerySegmentList() {
158 175
		return $this->querySegments;
159
	}
160
161
	/**
162
	 * @since 2.2
163
	 *
164
	 * @param QuerySegment $query
165
	 */
166 175
	public function addQuerySegment( QuerySegment $query ) {
167 175
		$this->querySegments[$query->queryNumber] = $query;
168 175
	}
169
170
	/**
171
	 * @since 2.2
172
	 *
173
	 * @return integer
174
	 */
175 173
	public function getLastQuerySegmentId() {
176 173
		return $this->lastQuerySegmentId;
177
	}
178
179
	/**
180
	 * @since 2.2
181
	 *
182
	 * @return array
183
	 */
184 173
	public function getErrors() {
185 173
		return $this->errors;
186
	}
187
188
	/**
189
	 * @since 2.2
190
	 *
191
	 * @param string $error
192
	 */
193 2
	public function addError( $error, $type = Message::TEXT ) {
194 2
		$this->errors[Message::getHash( $error, $type )] = Message::encode( $error, $type );
0 ignored issues
show
Documentation introduced by
$error is of type string, but the function expects a array.

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...
195 2
	}
196
197
	/**
198
	 * Create a new QueryContainer object that can be used to obtain results
199
	 * for the given description. The result is stored in $this->queries
200
	 * using a numeric key that is returned as a result of the function.
201
	 * Returns -1 if no query was created.
202
	 *
203
	 * @param Description $description
204
	 *
205
	 * @return integer
206
	 */
207 173
	public function getQuerySegmentFrom( Description $description ) {
208
209 173
		$fingerprint = $description->getFingerprint();
210
211
		// Get membership of descriptions that are resolved recursively
212 173
		if ( $description->getMembership() !== '' ) {
213 79
			$fingerprint = $fingerprint . $description->getMembership();
214
		}
215
216 173
		if ( ( $querySegment = $this->findDuplicates( $fingerprint ) ) ) {
217 1
			return $querySegment;
218
		}
219
220 173
		$querySegment = $this->dispatchingDescriptionInterpreter->interpretDescription(
221
			$description
222
		);
223
224 173
		$querySegment->fingerprint = $fingerprint;
225
		//$querySegment->membership = $description->getMembership();
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
226
		//$querySegment->queryString = $description->getQueryString();
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
227
228 173
		$this->lastQuerySegmentId = $this->registerQuerySegment(
229
			$querySegment
230
		);
231
232 173
		return $this->lastQuerySegmentId;
233
	}
234
235
	/**
236
	 * Register a query object to the internal query list, if the query is
237
	 * valid. Also make sure that sortkey information is propagated down
238
	 * from subqueries of this query.
239
	 *
240
	 * @param QuerySegment $query
241
	 */
242 173
	private function registerQuerySegment( QuerySegment $query ) {
243 173
		if ( $query->type === QuerySegment::Q_NOQUERY ) {
244 24
			return -1;
245
		}
246
247 173
		$this->addQuerySegment( $query );
248
249
		// Propagate sortkeys from subqueries:
250 173
		if ( $query->type !== QuerySegment::Q_DISJUNCTION ) {
251
			// Sortkeys are killed by disjunctions (not all parts may have them),
252
			// NOTE: preprocessing might try to push disjunctions downwards to safe sortkey, but this seems to be minor
253 173
			foreach ( $query->components as $cid => $field ) {
254 152
				$query->sortfields = array_merge( $this->findQuerySegment( $cid )->sortfields, $query->sortfields );
255
			}
256
		}
257
258 173
		return $query->queryNumber;
259
	}
260
261 173
	private function findDuplicates( $fingerprint ) {
262
263 173
		if ( $this->errors !== array() || $this->isFilterDuplicates === false ) {
264 169
			return false;
265
		}
266
267 4
		foreach ( $this->querySegments as $querySegment ) {
268 2
			if ( $querySegment->fingerprint === $fingerprint ) {
269 2
				return $querySegment->queryNumber;
270
			};
271
		}
272
273 4
		return false;
274
	}
275
276
}
277