1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SMW\SPARQLStore\RepositoryConnectors; |
4
|
|
|
|
5
|
|
|
use Onoi\HttpRequest\HttpRequest; |
6
|
|
|
use SMW\SPARQLStore\BadHttpResponseMapper; |
7
|
|
|
use SMW\SPARQLStore\Exception\BadHttpDatabaseResponseException; |
8
|
|
|
use SMW\SPARQLStore\QueryEngine\RepositoryResult; |
9
|
|
|
use SMW\SPARQLStore\QueryEngine\XmlResponseParser; |
10
|
|
|
use SMW\SPARQLStore\RepositoryClient; |
11
|
|
|
use SMW\SPARQLStore\RepositoryConnection; |
12
|
|
|
use SMWExporter as Exporter; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Basic database connector for exchanging data via SPARQL. |
16
|
|
|
* |
17
|
|
|
* @license GNU GPL v2+ |
18
|
|
|
* @since 1.6 |
19
|
|
|
* |
20
|
|
|
* @author Markus Krötzsch |
21
|
|
|
*/ |
22
|
|
|
class GenericHttpRepositoryConnector implements RepositoryConnection { |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Flag denoting endpoints being capable of querying |
26
|
|
|
*/ |
27
|
|
|
const EP_TYPE_QUERY = 1; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Flag denoting endpoints being capable of updating |
31
|
|
|
*/ |
32
|
|
|
const EP_TYPE_UPDATE = 2; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Flag denoting endpoints being capable of SPARQL HTTP graph management |
36
|
|
|
*/ |
37
|
|
|
const EP_TYPE_DATA = 4; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var RepositoryClient |
41
|
|
|
*/ |
42
|
|
|
protected $repositoryClient; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @note Handles the curl handle and is reused throughout the instance to |
46
|
|
|
* safe some initialization effort |
47
|
|
|
* |
48
|
|
|
* @var HttpRequest |
49
|
|
|
*/ |
50
|
|
|
protected $httpRequest; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var BadHttpResponseMapper |
54
|
|
|
*/ |
55
|
|
|
private $badHttpResponseMapper; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @note It is suggested to use the RepositoryConnectionProvider to create |
59
|
|
|
* a valid instance |
60
|
|
|
* |
61
|
|
|
* @since 2.2 |
62
|
|
|
* |
63
|
|
|
* @param RepositoryClient $repositoryClient |
64
|
|
|
* @param HttpRequest $httpRequest |
65
|
|
|
*/ |
66
|
57 |
|
public function __construct( RepositoryClient $repositoryClient, HttpRequest $httpRequest ) { |
67
|
57 |
|
$this->repositoryClient = $repositoryClient; |
68
|
57 |
|
$this->httpRequest = $httpRequest; |
69
|
|
|
|
70
|
57 |
|
$this->httpRequest->setOption( CURLOPT_FORBID_REUSE, false ); |
71
|
57 |
|
$this->httpRequest->setOption( CURLOPT_FRESH_CONNECT, false ); |
72
|
57 |
|
$this->httpRequest->setOption( CURLOPT_RETURNTRANSFER, true ); // put result into variable |
73
|
57 |
|
$this->httpRequest->setOption( CURLOPT_FAILONERROR, true ); |
74
|
|
|
|
75
|
57 |
|
$this->setConnectionTimeoutInSeconds( 10 ); |
76
|
57 |
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Get the URI of the default graph that this database connector is |
80
|
|
|
* using, or the empty string if none is used (no graph related |
81
|
|
|
* statements in queries/updates). |
82
|
|
|
* |
83
|
|
|
* @return string graph UIR or empty |
84
|
|
|
*/ |
85
|
|
|
public function getDefaultGraph() { |
86
|
|
|
return $this->repositoryClient->getDefaultGraph(); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Check if the database can be contacted. |
91
|
|
|
* |
92
|
|
|
* @todo SPARQL endpoints sometimes return errors if no (valid) query |
93
|
|
|
* is posted. The current implementation tries to catch this, but this |
94
|
|
|
* might not be entirely correct. Especially, the SPARQL 1.1 HTTP error |
95
|
|
|
* codes for Update are not defined yet (April 15 2011). |
96
|
|
|
* |
97
|
|
|
* @param $pingQueryEndpoint boolean true if the query endpoint should be |
98
|
|
|
* pinged, false if the update endpoint should be pinged |
99
|
|
|
* |
100
|
|
|
* @return boolean to indicate success |
101
|
|
|
*/ |
102
|
|
|
public function ping( $endpointType = self::EP_TYPE_QUERY ) { |
103
|
|
|
if ( $endpointType == self::EP_TYPE_QUERY ) { |
104
|
|
|
$this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getQueryEndpoint() ); |
105
|
|
|
$this->httpRequest->setOption( CURLOPT_NOBODY, true ); |
106
|
|
|
$this->httpRequest->setOption( CURLOPT_POST, true ); |
107
|
|
|
} elseif ( $endpointType == self::EP_TYPE_UPDATE ) { |
108
|
|
|
|
109
|
|
|
if ( $this->repositoryClient->getUpdateEndpoint() === '' ) { |
110
|
|
|
return false; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
$this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getUpdateEndpoint() ); |
114
|
|
|
$this->httpRequest->setOption( CURLOPT_NOBODY, false ); // 4Store gives 404 instead of 500 with CURLOPT_NOBODY |
115
|
|
|
|
116
|
|
|
} else { // ( $endpointType == self::EP_TYPE_DATA ) |
|
|
|
|
117
|
|
|
|
118
|
|
|
if ( $this->repositoryClient->getDataEndpoint() === '' ) { |
119
|
|
|
return false; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
// try an empty POST |
123
|
|
|
return $this->doHttpPost( '' ); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
$this->httpRequest->execute(); |
127
|
|
|
|
128
|
|
|
if ( $this->httpRequest->getLastErrorCode() == 0 ) { |
129
|
|
|
return true; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
// valid HTTP responses from a complaining SPARQL endpoint that is alive and kicking |
133
|
|
|
$httpCode = $this->httpRequest->getLastTransferInfo( CURLINFO_HTTP_CODE ); |
134
|
|
|
return ( ( $httpCode == 500 ) || ( $httpCode == 400 ) ); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* SELECT wrapper. |
139
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
140
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
141
|
|
|
* $extraNamespaces. |
142
|
|
|
* |
143
|
|
|
* @param $vars mixed array or string, field name(s) to be retrieved, can be '*' |
144
|
|
|
* @param $where string WHERE part of the query, without surrounding { } |
145
|
|
|
* @param $options array (associative) of options, e.g. array( 'LIMIT' => '10' ) |
146
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
147
|
|
|
* |
148
|
|
|
* @return RepositoryResult |
149
|
|
|
*/ |
150
|
|
|
public function select( $vars, $where, $options = array(), $extraNamespaces = array() ) { |
151
|
|
|
return $this->doQuery( $this->getSparqlForSelect( $vars, $where, $options, $extraNamespaces ) ); |
|
|
|
|
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Build the SPARQL query that is used by GenericHttpDatabaseConnector::select(). |
156
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
157
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
158
|
|
|
* $extraNamespaces. |
159
|
|
|
* |
160
|
|
|
* @param $where string WHERE part of the query, without surrounding { } |
161
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
162
|
|
|
* |
163
|
|
|
* @return string SPARQL query |
164
|
|
|
*/ |
165
|
|
|
public function getSparqlForSelect( $vars, $where, $options = array(), $extraNamespaces = array() ) { |
166
|
|
|
|
167
|
|
|
$sparql = self::getPrefixString( $extraNamespaces ) . 'SELECT '; |
168
|
|
|
|
169
|
|
|
if ( array_key_exists( 'DISTINCT', $options ) ) { |
170
|
|
|
$sparql .= 'DISTINCT '; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
if ( is_array( $vars ) ) { |
174
|
|
|
$sparql .= implode( ',', $vars ); |
175
|
|
|
} else { |
176
|
|
|
$sparql .= $vars; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
$sparql .= " WHERE {\n" . $where . "\n}"; |
180
|
|
|
|
181
|
|
|
if ( array_key_exists( 'ORDER BY', $options ) ) { |
182
|
|
|
$sparql .= "\nORDER BY " . $options['ORDER BY']; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
if ( array_key_exists( 'OFFSET', $options ) ) { |
186
|
|
|
$sparql .= "\nOFFSET " . $options['OFFSET']; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
if ( array_key_exists( 'LIMIT', $options ) ) { |
190
|
|
|
$sparql .= "\nLIMIT " . $options['LIMIT']; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
return $sparql; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* ASK wrapper. |
198
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
199
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
200
|
|
|
* $extraNamespaces. |
201
|
|
|
* |
202
|
|
|
* @param $where string WHERE part of the query, without surrounding { } |
203
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
204
|
|
|
* |
205
|
|
|
* @return RepositoryResult |
206
|
|
|
*/ |
207
|
7 |
|
public function ask( $where, $extraNamespaces = array() ) { |
208
|
7 |
|
return $this->doQuery( $this->getSparqlForAsk( $where, $extraNamespaces ) ); |
|
|
|
|
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Build the SPARQL query that is used by GenericHttpDatabaseConnector::ask(). |
213
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
214
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
215
|
|
|
* $extraNamespaces. |
216
|
|
|
* |
217
|
|
|
* @param $where string WHERE part of the query, without surrounding { } |
218
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
219
|
|
|
* |
220
|
|
|
* @return string SPARQL query |
221
|
|
|
*/ |
222
|
7 |
|
public function getSparqlForAsk( $where, $extraNamespaces = array() ) { |
223
|
7 |
|
return self::getPrefixString( $extraNamespaces ) . "ASK {\n" . $where . "\n}"; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* SELECT wrapper for counting results. |
228
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
229
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
230
|
|
|
* $extraNamespaces. |
231
|
|
|
* |
232
|
|
|
* @param $variable string variable name or '*' |
233
|
|
|
* @param $where string WHERE part of the query, without surrounding { } |
234
|
|
|
* @param $options array (associative) of options, e.g. array('LIMIT' => '10') |
235
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
236
|
|
|
* |
237
|
|
|
* @return RepositoryResult |
238
|
|
|
*/ |
239
|
1 |
|
public function selectCount( $variable, $where, $options = array(), $extraNamespaces = array() ) { |
240
|
|
|
|
241
|
1 |
|
$sparql = self::getPrefixString( $extraNamespaces ) . 'SELECT (COUNT('; |
242
|
|
|
|
243
|
1 |
|
if ( array_key_exists( 'DISTINCT', $options ) ) { |
244
|
1 |
|
$sparql .= 'DISTINCT '; |
245
|
|
|
} |
246
|
|
|
|
247
|
1 |
|
$sparql .= $variable . ") AS ?count) WHERE {\n" . $where . "\n}"; |
248
|
|
|
|
249
|
1 |
|
if ( array_key_exists( 'OFFSET', $options ) ) { |
250
|
1 |
|
$sparql .= "\nOFFSET " . $options['OFFSET']; |
251
|
|
|
} |
252
|
|
|
|
253
|
1 |
|
if ( array_key_exists( 'LIMIT', $options ) ) { |
254
|
1 |
|
$sparql .= "\nLIMIT " . $options['LIMIT']; |
255
|
|
|
} |
256
|
|
|
|
257
|
1 |
|
return $this->doQuery( $sparql ); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* DELETE wrapper. |
262
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
263
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
264
|
|
|
* $extraNamespaces. |
265
|
|
|
* |
266
|
|
|
* @param $deletePattern string CONSTRUCT pattern of tripples to delete |
267
|
|
|
* @param $where string condition for data to delete |
268
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
269
|
|
|
* |
270
|
|
|
* @return boolean stating whether the operations succeeded |
271
|
|
|
*/ |
272
|
5 |
|
public function delete( $deletePattern, $where, $extraNamespaces = array() ) { |
273
|
|
|
|
274
|
5 |
|
$defaultGraph = $this->repositoryClient->getDefaultGraph(); |
275
|
|
|
|
276
|
5 |
|
$sparql = self::getPrefixString( $extraNamespaces ) . |
277
|
5 |
|
( ( $defaultGraph !== '' )? "WITH <{$defaultGraph}> " : '' ) . |
278
|
5 |
|
"DELETE { $deletePattern } WHERE { $where }"; |
279
|
|
|
|
280
|
5 |
|
return $this->doUpdate( $sparql ); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Convenience method for deleting all triples that have a subject that |
285
|
|
|
* occurs in a triple with the given property and object. This is used |
286
|
|
|
* in SMW to delete subobjects with all their data. Some RDF stores fail |
287
|
|
|
* on complex delete queries, hence a wrapper function is provided to |
288
|
|
|
* allow more pedestrian implementations. |
289
|
|
|
* |
290
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
291
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
292
|
|
|
* $extraNamespaces. |
293
|
|
|
* |
294
|
|
|
* @param $propertyName string Turtle name of marking property |
295
|
|
|
* @param $objectName string Turtle name of marking object/value |
296
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
297
|
|
|
* |
298
|
|
|
* @return boolean stating whether the operations succeeded |
299
|
|
|
*/ |
300
|
|
|
public function deleteContentByValue( $propertyName, $objectName, $extraNamespaces = array() ) { |
301
|
|
|
return $this->delete( "?s ?p ?o", "?s $propertyName $objectName . ?s ?p ?o", $extraNamespaces ); |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* Convenience method for deleting all triples of the entire store |
306
|
|
|
* |
307
|
|
|
* @return boolean |
308
|
|
|
*/ |
309
|
|
|
public function deleteAll() { |
310
|
|
|
return $this->delete( "?s ?p ?o", "?s ?p ?o" ); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* INSERT DELETE wrapper. |
315
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
316
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
317
|
|
|
* $extraNamespaces. |
318
|
|
|
* |
319
|
|
|
* @param $insertPattern string CONSTRUCT pattern of tripples to insert |
320
|
|
|
* @param $deletePattern string CONSTRUCT pattern of tripples to delete |
321
|
|
|
* @param $where string condition for data to delete |
322
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
323
|
|
|
* |
324
|
|
|
* @return boolean stating whether the operations succeeded |
325
|
|
|
*/ |
326
|
|
|
public function insertDelete( $insertPattern, $deletePattern, $where, $extraNamespaces = array() ) { |
327
|
|
|
|
328
|
|
|
$defaultGraph = $this->repositoryClient->getDefaultGraph(); |
329
|
|
|
|
330
|
|
|
$sparql = self::getPrefixString( $extraNamespaces ) . |
331
|
|
|
( ( $defaultGraph !== '' )? "WITH <{$defaultGraph}> " : '' ) . |
332
|
|
|
"DELETE { $deletePattern } INSERT { $insertPattern } WHERE { $where }"; |
333
|
|
|
|
334
|
|
|
return $this->doUpdate( $sparql ); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* INSERT DATA wrapper. |
339
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
340
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
341
|
|
|
* $extraNamespaces. |
342
|
|
|
* |
343
|
|
|
* @param $triples string of triples to insert |
344
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
345
|
|
|
* |
346
|
|
|
* @return boolean stating whether the operations succeeded |
347
|
|
|
*/ |
348
|
5 |
|
public function insertData( $triples, $extraNamespaces = array() ) { |
349
|
|
|
|
350
|
5 |
|
if ( $this->repositoryClient->getDataEndpoint() !== '' ) { |
351
|
5 |
|
$turtle = self::getPrefixString( $extraNamespaces, false ) . $triples; |
352
|
5 |
|
return $this->doHttpPost( $turtle ); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
$defaultGraph = $this->repositoryClient->getDefaultGraph(); |
356
|
|
|
|
357
|
|
|
$sparql = self::getPrefixString( $extraNamespaces, true ) . |
358
|
|
|
"INSERT DATA " . |
359
|
|
|
( ( $defaultGraph !== '' )? " { GRAPH <{$defaultGraph}> " : '' ) . |
360
|
|
|
"{ $triples } " . |
361
|
|
|
( ( $defaultGraph !== '' )? " } " : '' ); |
362
|
|
|
|
363
|
|
|
return $this->doUpdate( $sparql ); |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* DELETE DATA wrapper. |
368
|
|
|
* The function declares the standard namespaces wiki, swivt, rdf, owl, |
369
|
|
|
* rdfs, property, xsd, so these do not have to be included in |
370
|
|
|
* $extraNamespaces. |
371
|
|
|
* |
372
|
|
|
* @param $triples string of triples to delete |
373
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
374
|
|
|
* |
375
|
|
|
* @return boolean stating whether the operations succeeded |
376
|
|
|
*/ |
377
|
|
|
public function deleteData( $triples, $extraNamespaces = array() ) { |
378
|
|
|
|
379
|
|
|
$defaultGraph = $this->repositoryClient->getDefaultGraph(); |
380
|
|
|
|
381
|
|
|
$sparql = self::getPrefixString( $extraNamespaces ) . |
382
|
|
|
"DELETE DATA { " . |
383
|
|
|
( ( $defaultGraph !== '' )? "GRAPH <{$defaultGraph}> " : '' ) . |
384
|
|
|
"{ $triples } }"; |
385
|
|
|
|
386
|
|
|
return $this->doUpdate( $sparql ); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* Execute a SPARQL query and return an RepositoryResult object |
392
|
|
|
* that contains the results. The method throws exceptions based on |
393
|
|
|
* GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this |
394
|
|
|
* method does not throw anything, then an empty result with an error |
395
|
|
|
* code is returned. |
396
|
|
|
* |
397
|
|
|
* @note This function sets the graph that is to be used as part of the |
398
|
|
|
* request. Queries should not include additional graph information. |
399
|
|
|
* |
400
|
|
|
* @param $sparql string with the complete SPARQL query (SELECT or ASK) |
401
|
|
|
* |
402
|
|
|
* @return RepositoryResult |
403
|
|
|
*/ |
404
|
9 |
|
public function doQuery( $sparql ) { |
405
|
|
|
|
406
|
9 |
|
if ( $this->repositoryClient->getQueryEndpoint() === '' ) { |
407
|
4 |
|
throw new BadHttpDatabaseResponseException( BadHttpDatabaseResponseException::ERROR_NOSERVICE, $sparql, 'not specified' ); |
408
|
|
|
} |
409
|
|
|
|
410
|
5 |
|
$this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getQueryEndpoint() ); |
411
|
|
|
|
412
|
5 |
|
$this->httpRequest->setOption( CURLOPT_HTTPHEADER, array( |
413
|
5 |
|
'Accept: application/sparql-results+xml,application/xml;q=0.8', |
414
|
|
|
'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' |
415
|
|
|
) ); |
416
|
|
|
|
417
|
5 |
|
$this->httpRequest->setOption( CURLOPT_POST, true ); |
418
|
|
|
|
419
|
5 |
|
$defaultGraph = $this->repositoryClient->getDefaultGraph(); |
420
|
|
|
|
421
|
5 |
|
$parameterString = "query=" . urlencode( $sparql ) . |
422
|
5 |
|
( ( $defaultGraph !== '' )? '&default-graph-uri=' . urlencode( $defaultGraph ) : '' ); |
423
|
|
|
|
424
|
5 |
|
$this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString ); |
425
|
|
|
|
426
|
5 |
|
$httpResponse = $this->httpRequest->execute(); |
427
|
|
|
|
428
|
5 |
|
if ( $this->httpRequest->getLastErrorCode() == 0 ) { |
429
|
4 |
|
$xmlResponseParser = new XmlResponseParser(); |
430
|
4 |
|
return $xmlResponseParser->parse( $httpResponse ); |
|
|
|
|
431
|
|
|
} |
432
|
|
|
|
433
|
1 |
|
$this->mapHttpRequestError( $this->repositoryClient->getQueryEndpoint(), $sparql ); |
434
|
|
|
|
435
|
1 |
|
$repositoryResult = new RepositoryResult(); |
436
|
1 |
|
$repositoryResult->setErrorCode( RepositoryResult::ERROR_UNREACHABLE ); |
437
|
|
|
|
438
|
1 |
|
return $repositoryResult; |
|
|
|
|
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* Execute a SPARQL update and return a boolean to indicate if the |
443
|
|
|
* operations was successful. The method throws exceptions based on |
444
|
|
|
* GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this |
445
|
|
|
* method does not throw anything, then false is returned. |
446
|
|
|
* |
447
|
|
|
* @note When this is written, it is not clear if the update protocol |
448
|
|
|
* supports a default-graph-uri parameter. Hence the target graph for |
449
|
|
|
* all updates is generally encoded in the query string and not fixed |
450
|
|
|
* when sending the query. Direct callers to this function must include |
451
|
|
|
* the graph information in the queries that they build. |
452
|
|
|
* |
453
|
|
|
* @param $sparql string with the complete SPARQL update query (INSERT or DELETE) |
454
|
|
|
* |
455
|
|
|
* @return boolean |
456
|
|
|
*/ |
457
|
6 |
|
public function doUpdate( $sparql ) { |
458
|
|
|
|
459
|
6 |
|
if ( $this->repositoryClient->getUpdateEndpoint() === '' ) { |
460
|
3 |
|
throw new BadHttpDatabaseResponseException( BadHttpDatabaseResponseException::ERROR_NOSERVICE, $sparql, 'not specified' ); |
461
|
|
|
} |
462
|
|
|
|
463
|
3 |
|
$this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getUpdateEndpoint() ); |
464
|
3 |
|
$this->httpRequest->setOption( CURLOPT_POST, true ); |
465
|
|
|
|
466
|
3 |
|
$parameterString = "update=" . urlencode( $sparql ); |
467
|
|
|
|
468
|
3 |
|
$this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString ); |
469
|
3 |
|
$this->httpRequest->setOption( CURLOPT_HTTPHEADER, array( 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' ) ); |
470
|
|
|
|
471
|
3 |
|
$this->httpRequest->execute(); |
472
|
|
|
|
473
|
3 |
|
if ( $this->httpRequest->getLastErrorCode() == 0 ) { |
474
|
3 |
|
return true; |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
$this->mapHttpRequestError( $this->repositoryClient->getUpdateEndpoint(), $sparql ); |
478
|
|
|
return false; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* Execute a HTTP-based SPARQL POST request according to |
483
|
|
|
* http://www.w3.org/2009/sparql/docs/http-rdf-update/. |
484
|
|
|
* The method throws exceptions based on |
485
|
|
|
* GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this |
486
|
|
|
* method does not throw anything, then an empty result with an error |
487
|
|
|
* code is returned. |
488
|
|
|
* |
489
|
|
|
* @note This protocol is not part of the SPARQL standard and may not |
490
|
|
|
* be supported by all stores. To avoid using it, simply do not provide |
491
|
|
|
* a data endpoint URL when configuring the SPARQL database. If used, |
492
|
|
|
* the protocol might lead to a better performance since there is less |
493
|
|
|
* parsing required to fetch the data from the request. |
494
|
|
|
* @note Some stores (e.g. 4Store) support another mode of posting data |
495
|
|
|
* that may be implemented in a special database handler. |
496
|
|
|
* |
497
|
|
|
* @param $payload string Turtle serialization of data to send |
498
|
|
|
* |
499
|
|
|
* @return boolean |
500
|
|
|
*/ |
501
|
15 |
|
public function doHttpPost( $payload ) { |
502
|
|
|
|
503
|
15 |
|
if ( $this->repositoryClient->getDataEndpoint() === '' ) { |
504
|
5 |
|
throw new BadHttpDatabaseResponseException( BadHttpDatabaseResponseException::ERROR_NOSERVICE, "SPARQL POST with data: $payload", 'not specified' ); |
505
|
|
|
} |
506
|
|
|
|
507
|
10 |
|
$defaultGraph = $this->repositoryClient->getDefaultGraph(); |
508
|
|
|
|
509
|
10 |
|
$this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getDataEndpoint() . |
510
|
10 |
|
( ( $defaultGraph !== '' )? '?graph=' . urlencode( $defaultGraph ) : '?default' ) ); |
511
|
10 |
|
$this->httpRequest->setOption( CURLOPT_POST, true ); |
512
|
|
|
|
513
|
|
|
// POST as file (fails in 4Store) |
514
|
10 |
|
$payloadFile = tmpfile(); |
515
|
10 |
|
fwrite( $payloadFile, $payload ); |
516
|
10 |
|
fseek( $payloadFile, 0 ); |
517
|
|
|
|
518
|
10 |
|
$this->httpRequest->setOption( CURLOPT_INFILE, $payloadFile ); |
519
|
10 |
|
$this->httpRequest->setOption( CURLOPT_INFILESIZE, strlen( $payload ) ); |
520
|
10 |
|
$this->httpRequest->setOption( CURLOPT_HTTPHEADER, array( 'Content-Type: application/x-turtle' ) ); |
521
|
|
|
|
522
|
10 |
|
$this->httpRequest->execute(); |
523
|
|
|
|
524
|
10 |
|
if ( $this->httpRequest->getLastErrorCode() == 0 ) { |
525
|
5 |
|
return true; |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
// TODO The error reporting based on SPARQL (Update) is not adequate for the HTTP POST protocol |
529
|
5 |
|
$this->mapHttpRequestError( $this->repositoryClient->getDataEndpoint(), $payload ); |
530
|
|
|
return false; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Create the standard PREFIX declarations for SPARQL or Turtle, |
535
|
|
|
* possibly with additional namespaces involved. |
536
|
|
|
* |
537
|
|
|
* @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
538
|
|
|
* @param $forSparql boolean true to use SPARQL prefix syntax, false to use Turtle prefix syntax |
539
|
|
|
* |
540
|
|
|
* @return string |
541
|
|
|
*/ |
542
|
22 |
|
public static function getPrefixString( $extraNamespaces = array(), $forSparql = true ) { |
543
|
22 |
|
$prefixString = ''; |
544
|
22 |
|
$prefixIntro = $forSparql ? 'PREFIX ' : '@prefix '; |
545
|
22 |
|
$prefixOutro = $forSparql ? "\n" : " .\n"; |
546
|
|
|
|
547
|
22 |
|
foreach ( array( 'wiki', 'rdf', 'rdfs', 'owl', 'swivt', 'property', 'xsd' ) as $shortname ) { |
548
|
22 |
|
$prefixString .= "{$prefixIntro}{$shortname}: <" . Exporter::getInstance()->getNamespaceUri( $shortname ) . ">$prefixOutro"; |
549
|
22 |
|
unset( $extraNamespaces[$shortname] ); // avoid double declaration |
550
|
|
|
} |
551
|
|
|
|
552
|
22 |
|
foreach ( $extraNamespaces as $shortname => $uri ) { |
553
|
7 |
|
$prefixString .= "{$prefixIntro}{$shortname}: <$uri>$prefixOutro"; |
554
|
|
|
} |
555
|
|
|
|
556
|
22 |
|
return $prefixString; |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
/** |
560
|
|
|
* @param $endpoint string URL of endpoint that was used |
561
|
|
|
* @param $sparql string query that caused the problem |
562
|
|
|
*/ |
563
|
8 |
|
protected function mapHttpRequestError( $endpoint, $sparql ) { |
564
|
|
|
|
565
|
8 |
|
if ( $this->badHttpResponseMapper === null ) { |
566
|
8 |
|
$this->badHttpResponseMapper = new BadHttpResponseMapper( $this->httpRequest ); |
567
|
|
|
} |
568
|
|
|
|
569
|
8 |
|
$this->badHttpResponseMapper->mapResponseToHttpRequest( $endpoint, $sparql ); |
570
|
1 |
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* @since 2.0 |
574
|
|
|
* |
575
|
|
|
* @param integer $timeout |
576
|
|
|
* |
577
|
|
|
* @return SparqlDatabase |
578
|
|
|
*/ |
579
|
57 |
|
public function setConnectionTimeoutInSeconds( $timeout = 10 ) { |
580
|
57 |
|
$this->httpRequest->setOption( CURLOPT_CONNECTTIMEOUT, $timeout ); |
581
|
57 |
|
return $this; |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
} |
585
|
|
|
|
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.