|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* Define the Wordlift_Sparql_Service class. |
|
5
|
|
|
*/ |
|
6
|
|
|
|
|
7
|
|
|
/** |
|
8
|
|
|
* The Wordlift_Sparql_Service class provides functions related to SPARQL queries. |
|
9
|
|
|
* |
|
10
|
|
|
* @since 3.6.0 |
|
11
|
|
|
*/ |
|
12
|
|
|
class Wordlift_Sparql_Service { |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* A {@link Wordlift_Log_Service} instance. |
|
16
|
|
|
* |
|
17
|
|
|
* @since 3.6.0 |
|
18
|
|
|
* @access private |
|
19
|
|
|
* @var \Wordlift_Log_Service $log A {@link Wordlift_Log_Service} instance. |
|
20
|
|
|
*/ |
|
21
|
|
|
private $log; |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* The {@link Wordlift_Sparql_Service} singleton instance. |
|
25
|
|
|
* |
|
26
|
|
|
* @since 3.6.0 |
|
27
|
|
|
* @access private |
|
28
|
|
|
* @var \Wordlift_Sparql_Service $instance The {@link Wordlift_Sparql_Service} singleton instance. |
|
29
|
|
|
*/ |
|
30
|
|
|
private static $instance; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Create a {@link Wordlift_Sparql_Service} instance. |
|
34
|
|
|
* |
|
35
|
|
|
* @since 3.6.0 |
|
36
|
|
|
*/ |
|
37
|
|
|
public function __construct() { |
|
38
|
|
|
|
|
39
|
|
|
$this->log = Wordlift_Log_Service::get_logger( 'Wordlift_Sparql_Service' ); |
|
40
|
|
|
|
|
41
|
|
|
self::$instance = $this; |
|
42
|
|
|
|
|
43
|
|
|
} |
|
44
|
|
|
|
|
45
|
|
|
/** |
|
46
|
|
|
* Get the singleton instance of the {@link Wordlift_Sparql_Service}. |
|
47
|
|
|
* |
|
48
|
|
|
* @since 3.6.0 |
|
49
|
|
|
* @return \Wordlift_Sparql_Service |
|
50
|
|
|
*/ |
|
51
|
|
|
public static function get_instance() { |
|
52
|
|
|
|
|
53
|
|
|
return self::$instance; |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* Queue a SPARQL statement for execution. |
|
58
|
|
|
* |
|
59
|
|
|
* @since 3.6.0 |
|
60
|
|
|
* |
|
61
|
|
|
* @param string $stmt The SPARQL statement. |
|
62
|
|
|
* @param bool $queue Whether to queue the statement for asynchronous |
|
63
|
|
|
* execution. |
|
64
|
|
|
*/ |
|
65
|
|
|
public function execute( $stmt, $queue = WL_ENABLE_SPARQL_UPDATE_QUERIES_BUFFERING ) { |
|
66
|
|
|
|
|
67
|
|
|
rl_execute_sparql_update_query( $stmt, $queue ); |
|
68
|
|
|
|
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
/** |
|
72
|
|
|
* Run the SPARQL queries buffered for the specified request id. |
|
73
|
|
|
* |
|
74
|
|
|
* @since 3.13.2 |
|
75
|
|
|
* |
|
76
|
|
|
* @param string $request_id A unique request id. |
|
77
|
|
|
*/ |
|
78
|
|
|
public function run_sparql_query( $request_id ) { |
|
79
|
|
|
|
|
80
|
|
|
$this->log->debug( "Running SPARQL queries..." ); |
|
81
|
|
|
|
|
82
|
|
|
// Look for a free temporary filename. |
|
83
|
|
|
for ( $index = 1; $index < 1000; $index ++ ) { |
|
84
|
|
|
$filename = WL_TEMP_DIR . $request_id . "-$index.sparql"; |
|
85
|
|
|
|
|
86
|
|
|
// Bail out if there are no files left. |
|
87
|
|
|
if ( ! file_exists( $filename ) ) { |
|
88
|
|
|
break; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
$this->log->debug( "Running SPARQL from $filename..." ); |
|
92
|
|
|
|
|
93
|
|
|
// Get the query saved in the file. |
|
94
|
|
|
$query = file_get_contents( $filename ); |
|
95
|
|
|
|
|
96
|
|
|
// Execute the SPARQL query. |
|
97
|
|
|
rl_execute_sparql_update_query( $query, false ); |
|
98
|
|
|
|
|
99
|
|
|
// Delete the temporary file. |
|
100
|
|
|
unlink( $filename ); |
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
// Reindex the triple store. |
|
104
|
|
|
wordlift_reindex_triple_store(); |
|
105
|
|
|
|
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
/** |
|
109
|
|
|
* Queue a SPARQL statement for asynchronous execution. |
|
110
|
|
|
* |
|
111
|
|
|
* @since 3.13.2 |
|
112
|
|
|
* |
|
113
|
|
|
* @param string $stmt The SPARQL statement. |
|
114
|
|
|
*/ |
|
115
|
|
|
public function queue( $stmt ) { |
|
116
|
|
|
|
|
117
|
|
|
// Get a temporary filename. |
|
118
|
|
|
$filename = $this->get_temporary_file_for_sparql(); |
|
119
|
|
|
|
|
120
|
|
|
$this->log->debug( "Buffering SPARQL to file $filename..." ); |
|
121
|
|
|
|
|
122
|
|
|
// Write the contents to the temporary filename. |
|
123
|
|
|
file_put_contents( $filename, $stmt . "\n", FILE_APPEND ); |
|
124
|
|
|
|
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
/** |
|
128
|
|
|
* Get a temporary filename where to store SPARQL queries. |
|
129
|
|
|
* |
|
130
|
|
|
* @since 3.13.2 |
|
131
|
|
|
* |
|
132
|
|
|
* @return string The filename. |
|
133
|
|
|
* @throws Exception An exception is thrown if there are already 1.000 |
|
134
|
|
|
* temporary files for this request. |
|
135
|
|
|
*/ |
|
136
|
|
|
private function get_temporary_file_for_sparql() { |
|
137
|
|
|
|
|
138
|
|
|
// Look for a free temporary filename. |
|
139
|
|
|
for ( $index = 1; $index < 1000; $index ++ ) { |
|
140
|
|
|
$filename = WL_TEMP_DIR . WL_REQUEST_ID . "-$index.sparql"; |
|
141
|
|
|
|
|
142
|
|
|
if ( ! file_exists( $filename ) ) { |
|
143
|
|
|
|
|
144
|
|
|
// Only if this it the first buffered SPARQL, then launch the |
|
145
|
|
|
// action which will be handled by the Async Task. The Async |
|
146
|
|
|
// Task will take care of all the buffered files. |
|
147
|
|
|
if ( 1 === $index ) { |
|
148
|
|
|
do_action( 'wl_run_sparql_query', WL_REQUEST_ID ); |
|
149
|
|
|
} |
|
150
|
|
|
|
|
151
|
|
|
// Return the temporary filename. |
|
152
|
|
|
return $filename; |
|
153
|
|
|
} |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
throw new Exception( 'Cannot create a temporary file.' ); |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* Execute the SELECT query. |
|
161
|
|
|
* |
|
162
|
|
|
* @since 3.12.2 |
|
163
|
|
|
* |
|
164
|
|
|
* @param string $query The SELECT query to execute. |
|
165
|
|
|
* |
|
166
|
|
|
* @return WP_Error|array The response or WP_Error on failure. |
|
167
|
|
|
*/ |
|
168
|
|
View Code Duplication |
public function select( $query ) { |
|
|
|
|
|
|
169
|
|
|
|
|
170
|
|
|
// Prepare the SPARQL statement by prepending the default namespaces. |
|
171
|
|
|
$sparql = rl_sparql_prefixes() . "\n" . $query; |
|
172
|
|
|
|
|
173
|
|
|
// Get the SPARQL SELECT URL. |
|
174
|
|
|
$url = wl_configuration_get_query_select_url( 'csv' ) . urlencode( $sparql ); |
|
175
|
|
|
|
|
176
|
|
|
// Prepare the request. |
|
177
|
|
|
$args = unserialize( WL_REDLINK_API_HTTP_OPTIONS ); |
|
178
|
|
|
|
|
179
|
|
|
return wp_remote_get( $url, $args ); |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* Formats the provided value according to the specified type in order to |
|
184
|
|
|
* insert the value using SPARQL. The value is also escaped. |
|
185
|
|
|
* |
|
186
|
|
|
* @since 3.6.0 |
|
187
|
|
|
* |
|
188
|
|
|
* @param string $value The value. |
|
189
|
|
|
* @param string $type The value type. |
|
190
|
|
|
* |
|
191
|
|
|
* @return string The formatted value for SPARQL statements. |
|
192
|
|
|
*/ |
|
193
|
|
|
public function format( $value, $type ) { |
|
194
|
|
|
|
|
195
|
|
|
// see https://www.w3.org/TR/sparql11-query/. |
|
196
|
|
|
|
|
197
|
|
|
switch ( $type ) { |
|
198
|
|
|
|
|
199
|
|
|
case Wordlift_Schema_Service::DATA_TYPE_BOOLEAN: |
|
|
|
|
|
|
200
|
|
|
|
|
201
|
|
|
// SPARQL supports 'true' and 'false', so we evaluate the $value |
|
202
|
|
|
// and return true/false accordingly. |
|
|
|
|
|
|
203
|
|
|
return $value ? 'true' : 'false'; |
|
204
|
|
|
|
|
205
|
|
|
case Wordlift_Schema_Service::DATA_TYPE_DATE: |
|
|
|
|
|
|
206
|
|
|
|
|
207
|
|
|
return sprintf( '"%s"^^xsd:date', self::escape( $value ) ); |
|
208
|
|
|
|
|
209
|
|
|
|
|
210
|
|
|
case Wordlift_Schema_Service::DATA_TYPE_DOUBLE: |
|
|
|
|
|
|
211
|
|
|
|
|
212
|
|
|
return sprintf( '"%s"^^xsd:double', self::escape( $value ) ); |
|
213
|
|
|
|
|
214
|
|
|
case Wordlift_Schema_Service::DATA_TYPE_INTEGER: |
|
|
|
|
|
|
215
|
|
|
|
|
216
|
|
|
return sprintf( '"%s"^^xsd:integer', self::escape( $value ) ); |
|
217
|
|
|
|
|
218
|
|
|
case Wordlift_Schema_Service::DATA_TYPE_STRING: |
|
|
|
|
|
|
219
|
|
|
|
|
220
|
|
|
return sprintf( '"%s"^^xsd:string', self::escape( $value ) ); |
|
221
|
|
|
|
|
222
|
|
|
case Wordlift_Schema_Service::DATA_TYPE_URI: |
|
|
|
|
|
|
223
|
|
|
|
|
224
|
|
|
return sprintf( '<%s>', self::escape_uri( $value ) ); |
|
225
|
|
|
|
|
226
|
|
|
default: |
|
|
|
|
|
|
227
|
|
|
|
|
228
|
|
|
$this->log->warn( "Unknown data type [ type :: $type ]" ); |
|
229
|
|
|
|
|
230
|
|
|
// Try to insert the value anyway. |
|
231
|
|
|
return sprintf( '"%s"', self::escape( $value ) ); |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
} |
|
235
|
|
|
|
|
236
|
|
|
/** |
|
237
|
|
|
* Escapes an URI for a SPARQL statement. |
|
238
|
|
|
* |
|
239
|
|
|
* @since 3.6.0 |
|
240
|
|
|
* |
|
241
|
|
|
* @param string $uri The URI to escape. |
|
242
|
|
|
* |
|
243
|
|
|
* @return string The escaped URI. |
|
244
|
|
|
*/ |
|
245
|
|
|
public static function escape_uri( $uri ) { |
|
246
|
|
|
|
|
247
|
|
|
// Should we validate the IRI? |
|
248
|
|
|
// http://www.w3.org/TR/sparql11-query/#QSynIRI |
|
249
|
|
|
|
|
250
|
|
|
$uri = str_replace( '<', '\<', $uri ); |
|
251
|
|
|
$uri = str_replace( '>', '\>', $uri ); |
|
252
|
|
|
|
|
253
|
|
|
return $uri; |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* Escapes a string for a SPARQL statement. |
|
258
|
|
|
* |
|
259
|
|
|
* @since 3.6.0 |
|
260
|
|
|
* |
|
261
|
|
|
* @param string $string The string to escape. |
|
262
|
|
|
* |
|
263
|
|
|
* @return string The escaped string. |
|
264
|
|
|
*/ |
|
265
|
|
|
public static function escape( $string ) { |
|
266
|
|
|
|
|
267
|
|
|
// see http://www.w3.org/TR/rdf-sparql-query/ |
|
268
|
|
|
// '\t' U+0009 (tab) |
|
|
|
|
|
|
269
|
|
|
// '\n' U+000A (line feed) |
|
270
|
|
|
// '\r' U+000D (carriage return) |
|
|
|
|
|
|
271
|
|
|
// '\b' U+0008 (backspace) |
|
|
|
|
|
|
272
|
|
|
// '\f' U+000C (form feed) |
|
273
|
|
|
// '\"' U+0022 (quotation mark, double quote mark) |
|
274
|
|
|
// "\'" U+0027 (apostrophe-quote, single quote mark) |
|
275
|
|
|
// '\\' U+005C (backslash) |
|
|
|
|
|
|
276
|
|
|
|
|
277
|
|
|
$string = str_replace( '\\', '\\\\', $string ); |
|
278
|
|
|
$string = str_replace( '\'', '\\\'', $string ); |
|
279
|
|
|
$string = str_replace( '"', '\\"', $string ); |
|
280
|
|
|
$string = str_replace( "\f", '\\f', $string ); |
|
281
|
|
|
$string = str_replace( "\b", '\\b', $string ); |
|
282
|
|
|
$string = str_replace( "\r", '\\r', $string ); |
|
283
|
|
|
$string = str_replace( "\n", '\\n', $string ); |
|
284
|
|
|
$string = str_replace( "\t", '\\t', $string ); |
|
285
|
|
|
|
|
286
|
|
|
return $string; |
|
287
|
|
|
} |
|
288
|
|
|
|
|
289
|
|
|
} |
|
290
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.