GraphBuilder   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 271
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 13
Bugs 1 Features 6
Metric Value
wmc 21
c 13
b 1
f 6
lcom 1
cbo 4
dl 0
loc 271
rs 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A where() 0 20 1
A also() 0 11 3
A filter() 0 10 1
A filterExists() 0 6 1
A filterNotExists() 0 6 1
A optional() 0 5 1
A getGraphBuilder() 0 8 2
A union() 0 9 2
A subquery() 0 11 1
A getSPARQL() 0 15 2
A formatPredicates() 0 5 1
A formatOptionals() 0 5 1
A formatFilters() 0 5 1
A formatUnions() 0 3 1
A formatSubqueries() 0 5 1
1
<?php
2
3
namespace Asparagus;
4
5
use InvalidArgumentException;
6
7
/**
8
 * Abstraction layer to create graphs for SPARQL queries
9
 *
10
 * @todo support filter, optional, union, minus
11
 *
12
 * @since 0.3 (package-private since 0.1)
13
 *
14
 * @license GNU GPL v2+
15
 * @author Bene* < [email protected] >
16
 */
17
class GraphBuilder {
18
19
	/**
20
	 * @var array nested list of conditions, grouped by subject and predicate
21
	 */
22
	private $conditions = array();
23
24
	/**
25
	 * @var string[] list of optional expressions
26
	 */
27
	private $optionals = array();
28
29
	/**
30
	 * @var string[] list of filter expressions
31
	 */
32
	private $filters = array();
33
34
	/**
35
	 * @var string[] list of unions
36
	 */
37
	private $unions = array();
38
39
	/**
40
	 * @var string[] list of subqueries
41
	 */
42
	private $subqueries = array();
43
44
	/**
45
	 * @var string
46
	 */
47
	private $currentSubject = null;
48
49
	/**
50
	 * @var string
51
	 */
52
	private $currentPredicate = null;
53
54
	/**
55
	 * @var ExpressionValidator
56
	 */
57
	private $expressionValidator;
58
59
	/**
60
	 * @var UsageValidator
61
	 */
62
	private $usageValidator;
63
64
	/**
65
	 * Package-private constructor, use QueryBuilder::newSubgraph instead
66
	 *
67
	 * @param UsageValidator $usageValidator
68
	 * @throws InvalidArgumentException
69
	 */
70
	public function __construct( UsageValidator $usageValidator ) {
71
		$this->expressionValidator = new ExpressionValidator();
72
		$this->usageValidator = $usageValidator;
73
	}
74
75
	/**
76
	 * Adds the given triple as a condition.
77
	 *
78
	 * @param string $subject
79
	 * @param string $predicate
80
	 * @param string $object
81
	 * @return self
82
	 * @throws InvalidArgumentException
83
	 */
84
	public function where( $subject, $predicate, $object ) {
85
		$this->expressionValidator->validate( $subject,
86
			ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_PREFIXED_IRI
87
		);
88
		$this->expressionValidator->validate( $predicate,
89
			ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_PATH
90
		);
91
		$this->expressionValidator->validate( $object,
92
			ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_PREFIXED_IRI | ExpressionValidator::VALIDATE_NATIVE
93
		);
94
95
		$this->usageValidator->trackUsedPrefixes(  $subject . ' ' . $predicate . ' ' . $object );
96
		$this->usageValidator->trackDefinedVariables( $subject  . ' ' . $predicate . ' ' . $object );
97
98
		$this->currentSubject = $subject;
99
		$this->currentPredicate = $predicate;
100
		$this->conditions[$subject][$predicate][] = $object;
101
102
		return $this;
103
	}
104
105
	/**
106
	 * Adds the given triple/double/single value as an additional condition
107
	 * to the previously added condition.
108
	 *
109
	 * @param string $subject
110
	 * @param string|null $predicate
111
	 * @param string|null $object
112
	 * @return self
113
	 * @throws InvalidArgumentException
114
	 */
115
	public function also( $subject, $predicate = null, $object = null ) {
116
		if ( $predicate === null ) {
117
			$this->where( $this->currentSubject, $this->currentPredicate, $subject );
118
		} else if ( $object === null ) {
119
			$this->where( $this->currentSubject, $subject, $predicate );
120
		} else {
121
			$this->where( $subject, $predicate, $object );
122
		}
123
124
		return $this;
125
	}
126
127
	/**
128
	 * Adds the given expression as a filter to this query.
129
	 *
130
	 * @param string $expression
131
	 * @return self
132
	 * @throws InvalidArgumentException
133
	 */
134
	public function filter( $expression ) {
135
		$this->expressionValidator->validate( $expression, ExpressionValidator::VALIDATE_FUNCTION );
136
137
		$this->usageValidator->trackUsedPrefixes( $expression );
138
		$this->usageValidator->trackUsedVariables( $expression );
139
140
		$this->filters[] = '(' . $expression . ')';
141
142
		return $this;
143
	}
144
145
	/**
146
	 * Adds a filter that the given graph or triple exists.
147
	 *
148
	 * @param string|GraphBuilder $subject
149
	 * @param string|null $predicate
150
	 * @param string|null $object
151
	 * @return self
152
	 * @throws InvalidArgumentException
153
	 */
154
	public function filterExists( $subject, $predicate = null, $object = null ) {
155
		$graphBuilder = $this->getGraphBuilder( $subject, $predicate, $object );
156
		$this->filters[] = 'EXISTS {' . $graphBuilder->getSPARQL() . ' }';
157
158
		return $this;
159
	}
160
161
	/**
162
	 * Adds a filter that the given graph or triple does not exist.
163
	 *
164
	 * @param string|GraphBuilder $subject
165
	 * @param string|null $predicate
166
	 * @param string|null $object
167
	 * @return self
168
	 * @throws InvalidArgumentException
169
	 */
170
	public function filterNotExists( $subject, $predicate = null, $object = null ) {
171
		$graphBuilder = $this->getGraphBuilder( $subject, $predicate, $object );
172
		$this->filters[] = 'NOT EXISTS {' . $graphBuilder->getSPARQL() . ' }';
173
174
		return $this;
175
	}
176
177
	/**
178
	 * Adds the given graph or triple as an optional condition.
179
	 *
180
	 * @param string|GraphBuilder $subject
181
	 * @param string|null $predicate
182
	 * @param string|null $object
183
	 * @return self
184
	 * @throws InvalidArgumentException
185
	 */
186
	public function optional( $subject, $predicate = null, $object = null ) {
187
		$graphBuilder = $this->getGraphBuilder( $subject, $predicate, $object );
188
		$this->optionals[] = $graphBuilder->getSPARQL();
189
		return $this;
190
	}
191
192
	private function getGraphBuilder( $subject, $predicate, $object ) {
193
		if ( $subject instanceof GraphBuilder ) {
194
			return $subject;
195
		}
196
197
		$graphBuilder = new GraphBuilder( $this->usageValidator );
198
		return $graphBuilder->where( $subject, $predicate, $object );
199
	}
200
201
	/**
202
	 * Adds the given graphs as alternative conditions.
203
	 *
204
	 * @param GraphBuilder|GraphBuilder[] $graphs
205
	 * @return self
206
	 * @throws InvalidArgumentException
207
	 */
208
	public function union( $graphs /* graphs ... */ ) {
209
		$graphs = is_array( $graphs ) ? $graphs : func_get_args();
210
211
		$this->unions[] = implode( ' UNION', array_map( function( GraphBuilder $graph ) {
212
			return ' {' . $graph->getSPARQL() . ' }';
213
		}, $graphs ) );
214
215
		return $this;
216
	}
217
218
	/**
219
	 * Adds the given subquery.
220
	 *
221
	 * @param QueryBuilder $queryBuilder
222
	 * @return self
223
	 * @throws InvalidArgumentException
224
	 */
225
	public function subquery( QueryBuilder $queryBuilder ) {
226
		$this->subqueries[] = $queryBuilder->getSPARQL( false );
227
		$this->usageValidator->trackDefinedVariables( implode( ' ', $queryBuilder->getSelects() ) );
228
229
		// @todo temp hack to add AS definitions to defined variables
230
		$regexHelper = new RegexHelper();
231
		$matches = $regexHelper->getMatches( 'AS \{variable}', implode( ' ', $queryBuilder->getSelects() ) );
232
		$this->usageValidator->trackDefinedVariables( $matches );
233
234
		return $this;
235
	}
236
237
	/**
238
	 * Returns the plain SPARQL string of these conditions.
239
	 * Surrounding brackets are not included.
240
	 *
241
	 * @return string
242
	 */
243
	public function getSPARQL() {
244
		// add subqueries to the beginning because they are logically evaluated first
245
		$sparql = $this->formatSubqueries();
246
247
		foreach ( $this->conditions as $subject => $predicates ) {
248
			$sparql .= ' ' . $subject;
249
			$sparql .= $this->formatPredicates( $predicates ) . ' .';
250
		}
251
252
		$sparql .= $this->formatOptionals();
253
		$sparql .= $this->formatFilters();
254
		$sparql .= $this->formatUnions();
255
256
		return $sparql;
257
	}
258
259
	private function formatPredicates( array $predicates ) {
260
		return implode( ' ;', array_map( function( $predicate, $objects ) {
261
			return ' ' . $predicate . ' ' . implode( ' , ', $objects );
262
		}, array_keys( $predicates ), $predicates ) );
263
	}
264
265
	private function formatOptionals() {
266
		return implode( array_map( function( $optional ) {
267
			return ' OPTIONAL {' . $optional . ' }';
268
		}, $this->optionals ) );
269
	}
270
271
	private function formatFilters() {
272
		return implode( array_map( function( $filter ) {
273
			return ' FILTER ' . $filter;
274
		}, $this->filters ) );
275
	}
276
277
	private function formatUnions() {
278
		return implode( $this->unions );
279
	}
280
281
	private function formatSubqueries() {
282
		return implode( array_map( function( $subquery ) {
283
			return ' { ' . $subquery . ' }';
284
		}, $this->subqueries ) );
285
	}
286
287
}
288