|
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
|
|
|
|
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.