|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* A builder to build SPARQL queries. |
|
5
|
|
|
* |
|
6
|
|
|
* @since 3.1.7 |
|
7
|
|
|
*/ |
|
8
|
|
|
class Wordlift_Query_Builder { |
|
|
|
|
|
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* The INSERT statement template. |
|
12
|
|
|
* |
|
13
|
|
|
* @since 3.1.7 |
|
14
|
|
|
*/ |
|
15
|
|
|
const INSERT = 'INSERT DATA { %s };'; |
|
16
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* The DELETE statement template (it repeats the statements in the WHERE clause. |
|
19
|
|
|
* |
|
20
|
|
|
* @since 3.1.7 |
|
21
|
|
|
*/ |
|
22
|
|
|
const DELETE = 'DELETE { %s } WHERE { %1$s };'; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* Tell the statement function to guess the object type (URI, value or parameter). |
|
26
|
|
|
* |
|
27
|
|
|
* @since 3.1.7 |
|
28
|
|
|
*/ |
|
29
|
|
|
const OBJECT_AUTO = - 1; |
|
30
|
|
|
|
|
31
|
|
|
/** |
|
32
|
|
|
* Tell the statement function that the object is a URI. |
|
33
|
|
|
* |
|
34
|
|
|
* @since 3.1.7 |
|
35
|
|
|
*/ |
|
36
|
|
|
const OBJECT_URI = 0; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* Tell the statement function that the object is a value. |
|
40
|
|
|
* |
|
41
|
|
|
* @since 3.1.7 |
|
42
|
|
|
*/ |
|
43
|
|
|
const OBJECT_VALUE = 1; |
|
44
|
|
|
|
|
45
|
|
|
/** |
|
46
|
|
|
* Tell the statement function that the object is a parameter. |
|
47
|
|
|
* |
|
48
|
|
|
* @since 3.1.7 |
|
49
|
|
|
*/ |
|
50
|
|
|
const OBJECT_PARAMETER = 2; |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* The RDFS type. |
|
54
|
|
|
* |
|
55
|
|
|
* @since 3.1.7 |
|
56
|
|
|
*/ |
|
57
|
|
|
const RDFS_TYPE_URI = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* The schema.org/Person type. |
|
61
|
|
|
* |
|
62
|
|
|
* @since 3.1.7 |
|
63
|
|
|
*/ |
|
64
|
|
|
const SCHEMA_PERSON_URI = 'http://schema.org/Person'; |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* The schema.org given name predicate. |
|
68
|
|
|
* |
|
69
|
|
|
* @since 3.1.7 |
|
70
|
|
|
*/ |
|
71
|
|
|
const SCHEMA_GIVEN_NAME_URI = 'http://schema.org/givenName'; |
|
72
|
|
|
|
|
73
|
|
|
/** |
|
74
|
|
|
* The schema.org family name predicate. |
|
75
|
|
|
* |
|
76
|
|
|
* @since 3.1.7 |
|
77
|
|
|
*/ |
|
78
|
|
|
const SCHEMA_FAMILY_NAME_URI = 'http://schema.org/familyName'; |
|
79
|
|
|
|
|
80
|
|
|
/** |
|
81
|
|
|
* The schema.org url predicate. |
|
82
|
|
|
* |
|
83
|
|
|
* @since 3.1.7 |
|
84
|
|
|
*/ |
|
85
|
|
|
const SCHEMA_URL_URI = 'http://schema.org/url'; |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* The RDF label. |
|
89
|
|
|
* |
|
90
|
|
|
* @since 3.1.7 |
|
91
|
|
|
*/ |
|
92
|
|
|
const RDFS_LABEL_URI = 'http://www.w3.org/2000/01/rdf-schema#label'; |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* Hold the template (INSERT or DELETE). |
|
96
|
|
|
* |
|
97
|
|
|
* @since 3.1.7 |
|
98
|
|
|
* @access private |
|
99
|
|
|
* @var string $template The query template. |
|
100
|
|
|
*/ |
|
101
|
|
|
private $template; |
|
102
|
|
|
|
|
103
|
|
|
/** |
|
104
|
|
|
* An array of statements (in the form of subject, predicate, object). |
|
105
|
|
|
* |
|
106
|
|
|
* @since 3.1.7 |
|
107
|
|
|
* @access private |
|
108
|
|
|
* @var array $statements An array of statements. |
|
109
|
|
|
*/ |
|
110
|
|
|
private $statements = array(); |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* Create a new instance of the Query builder (compatible with PHP 5.3). |
|
114
|
|
|
* |
|
115
|
|
|
* @since 3.1.7 |
|
116
|
|
|
* @return Wordlift_Query_Builder A new instance of the Query builder. |
|
117
|
|
|
*/ |
|
118
|
|
|
public static function new_instance() { |
|
119
|
|
|
|
|
120
|
|
|
return new Wordlift_Query_Builder(); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
/** |
|
124
|
|
|
* Set the query to INSERT. |
|
125
|
|
|
* |
|
126
|
|
|
* @since 3.1.7 |
|
127
|
|
|
* @return $this \Wordlift_Query_Builder The Query builder. |
|
128
|
|
|
*/ |
|
129
|
|
|
public function insert() { |
|
130
|
|
|
|
|
131
|
|
|
$this->template = self::INSERT; |
|
132
|
|
|
|
|
133
|
|
|
return $this; |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
/** |
|
137
|
|
|
* Set the query to DELETE. |
|
138
|
|
|
* |
|
139
|
|
|
* @since 3.1.7 |
|
140
|
|
|
* @return $this \Wordlift_Query_Builder The Query builder. |
|
141
|
|
|
*/ |
|
142
|
|
|
public function delete() { |
|
143
|
|
|
|
|
144
|
|
|
$this->template = self::DELETE; |
|
145
|
|
|
|
|
146
|
|
|
return $this; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Add a statement. |
|
151
|
|
|
* |
|
152
|
|
|
* @since 3.1.7 |
|
153
|
|
|
* |
|
154
|
|
|
* @param string $subject The subject of the statement (must be a URI). |
|
155
|
|
|
* @param string $predicate The predicate (must be a URI). |
|
156
|
|
|
* @param string $object The object, can be a URI or a value. |
|
157
|
|
|
* @param int $object_type The object type, either a {@link OBJECT_URI} or a value {@link OBJECT_VALUE}. If set to {@link OBJECT_AUTO}, the Query builder will try to guess. |
|
158
|
|
|
* @param string|null $data_type The data type (or null). |
|
159
|
|
|
* @param string|null $language The language code (or null). |
|
160
|
|
|
* |
|
161
|
|
|
* @return $this \Wordlift_Query_Builder The Query builder. |
|
162
|
|
|
*/ |
|
163
|
|
|
public function statement( $subject, $predicate, $object, $object_type = self::OBJECT_AUTO, $data_type = null, $language = null ) { |
|
164
|
|
|
|
|
165
|
|
|
// If no value has been provided, we don't set any statement. |
|
166
|
|
|
if ( empty( $object ) ) { |
|
167
|
|
|
return $this; |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
|
|
// Get the object type if set, otherwise try to guess it. |
|
171
|
|
|
$object_value_type = ( self::OBJECT_AUTO !== $object_type ?: $this->guess_object_type( $predicate, $object ) ); |
|
172
|
|
|
|
|
173
|
|
|
// Prepare the statement template. |
|
174
|
|
|
$template = '<%1$s> <%2$s> ' . ( |
|
175
|
|
|
self::OBJECT_URI === $object_value_type ? '<%3$s>' : ( |
|
176
|
|
|
self::OBJECT_PARAMETER === $object_value_type ? '%3$s' : |
|
177
|
|
|
// self::OBJECT_VALUE === $object_value_type |
|
|
|
|
|
|
178
|
|
|
'"%3$s"' . ( isset( $data_type ) ? '^^%4$s' : '' ) . ( isset( $language ) ? '@%5$s' : '' ) ) ); |
|
179
|
|
|
|
|
180
|
|
|
// Escape the subject, predicate and object. |
|
181
|
|
|
$escaped_subject = $this->escape_uri( $subject ); |
|
182
|
|
|
$escaped_predicate = $this->escape_uri( $predicate ); |
|
183
|
|
|
$escaped_object = ( self::OBJECT_URI === $object_value_type ? $this->escape_uri( $object ) : $this->escape_value( $object ) ); |
|
184
|
|
|
|
|
185
|
|
|
// Prepare the statement and add it to the list of statements. |
|
186
|
|
|
$this->statements[] = sprintf( $template, $escaped_subject, $escaped_predicate, $escaped_object, $data_type, $language ); |
|
187
|
|
|
|
|
188
|
|
|
return $this; |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
|
|
/** |
|
192
|
|
|
* Build the query. |
|
193
|
|
|
* |
|
194
|
|
|
* @since 3.1.7 |
|
195
|
|
|
* @return string The query string. |
|
196
|
|
|
*/ |
|
197
|
|
|
public function build() { |
|
198
|
|
|
|
|
199
|
|
|
return sprintf( $this->template, implode( ' . ', $this->statements ) ) . "\n"; |
|
200
|
|
|
} |
|
201
|
|
|
|
|
202
|
|
|
/** |
|
203
|
|
|
* Guess the statement object type. |
|
204
|
|
|
* |
|
205
|
|
|
* @since 3.1.7 |
|
206
|
|
|
* |
|
207
|
|
|
* @param string $predicate The predicate. |
|
208
|
|
|
* @param string $object The object. |
|
209
|
|
|
* |
|
210
|
|
|
* @return int {@link Wordlift_Query_Builder::OBJECT_URI} if the Query builder thinks the object must be an URI, {@link Wordlift_Query_Builder::OBJECT_VALUE} otherwise. |
|
211
|
|
|
*/ |
|
212
|
|
|
private function guess_object_type( $predicate, $object ) { |
|
213
|
|
|
|
|
214
|
|
|
// If the object starts with a question mark, it's a parameter. |
|
215
|
|
|
if ( 0 === strpos( $object, '?' ) ) { |
|
216
|
|
|
return self::OBJECT_PARAMETER; |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
// Guess based on the predicate. |
|
220
|
|
|
switch ( $predicate ) { |
|
221
|
|
|
|
|
222
|
|
|
case self::RDFS_TYPE_URI: |
|
223
|
|
|
case self::SCHEMA_URL_URI: |
|
224
|
|
|
return self::OBJECT_URI; |
|
|
|
|
|
|
225
|
|
|
|
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
return self::OBJECT_VALUE; |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
/** |
|
232
|
|
|
* Escape a URI. |
|
233
|
|
|
* |
|
234
|
|
|
* @since 3.1.7 |
|
235
|
|
|
* |
|
236
|
|
|
* @param string $uri The URI to escape. |
|
237
|
|
|
* |
|
238
|
|
|
* @return string The escaped URI. |
|
239
|
|
|
*/ |
|
240
|
|
|
private function escape_uri( $uri ) { |
|
241
|
|
|
|
|
242
|
|
|
// Should we validate the IRI? |
|
243
|
|
|
// http://www.w3.org/TR/sparql11-query/#QSynIRI |
|
244
|
|
|
|
|
245
|
|
|
$escaped_uri = str_replace( '<', '\<', $uri ); |
|
246
|
|
|
$escaped_uri = str_replace( '>', '\>', $escaped_uri ); |
|
247
|
|
|
|
|
248
|
|
|
return $escaped_uri; |
|
249
|
|
|
} |
|
250
|
|
|
|
|
251
|
|
|
/** |
|
252
|
|
|
* Escape a value. |
|
253
|
|
|
* |
|
254
|
|
|
* @since 3.1.7 |
|
255
|
|
|
* |
|
256
|
|
|
* @param string $value The value to escape. |
|
257
|
|
|
* |
|
258
|
|
|
* @return string The escaped value. |
|
259
|
|
|
*/ |
|
260
|
|
View Code Duplication |
private function escape_value( $value ) { |
|
|
|
|
|
|
261
|
|
|
|
|
262
|
|
|
// see http://www.w3.org/TR/rdf-sparql-query/ |
|
263
|
|
|
// '\t' U+0009 (tab) |
|
|
|
|
|
|
264
|
|
|
// '\n' U+000A (line feed) |
|
265
|
|
|
// '\r' U+000D (carriage return) |
|
|
|
|
|
|
266
|
|
|
// '\b' U+0008 (backspace) |
|
|
|
|
|
|
267
|
|
|
// '\f' U+000C (form feed) |
|
268
|
|
|
// '\"' U+0022 (quotation mark, double quote mark) |
|
269
|
|
|
// "\'" U+0027 (apostrophe-quote, single quote mark) |
|
270
|
|
|
// '\\' U+005C (backslash) |
|
|
|
|
|
|
271
|
|
|
|
|
272
|
|
|
$escaped_value = str_replace( '\\', '\\\\', $value ); |
|
273
|
|
|
$escaped_value = str_replace( '\'', '\\\'', $escaped_value ); |
|
274
|
|
|
$escaped_value = str_replace( '"', '\\"', $escaped_value ); |
|
275
|
|
|
$escaped_value = str_replace( "\f", '\\f', $escaped_value ); |
|
276
|
|
|
$escaped_value = str_replace( "\b", '\\b', $escaped_value ); |
|
277
|
|
|
$escaped_value = str_replace( "\r", '\\r', $escaped_value ); |
|
278
|
|
|
$escaped_value = str_replace( "\n", '\\n', $escaped_value ); |
|
279
|
|
|
$escaped_value = str_replace( "\t", '\\t', $escaped_value ); |
|
280
|
|
|
|
|
281
|
|
|
return $escaped_value; |
|
282
|
|
|
} |
|
283
|
|
|
|
|
284
|
|
|
} |
|
|
|
|
|
|
285
|
|
|
|