Completed
Push — master ( d6dd65...5463c4 )
by David
02:47
created

Wordlift_Sparql_Service::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
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 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
200
201
				// SPARQL supports 'true' and 'false', so we evaluate the $value
202
				// and return true/false accordingly.
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
203
				return $value ? 'true' : 'false';
204
205
			case Wordlift_Schema_Service::DATA_TYPE_DATE:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
206
207
				return sprintf( '"%s"^^xsd:date', self::escape( $value ) );
208
209
210
			case Wordlift_Schema_Service::DATA_TYPE_DOUBLE:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
211
212
				return sprintf( '"%s"^^xsd:double', self::escape( $value ) );
213
214
			case Wordlift_Schema_Service::DATA_TYPE_INTEGER:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
215
216
				return sprintf( '"%s"^^xsd:integer', self::escape( $value ) );
217
218
			case Wordlift_Schema_Service::DATA_TYPE_STRING:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
219
220
				return sprintf( '"%s"^^xsd:string', self::escape( $value ) );
221
222
			case Wordlift_Schema_Service::DATA_TYPE_URI:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
223
224
				return sprintf( '<%s>', self::escape_uri( $value ) );
225
226
			default:
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
269
		//    '\n'	U+000A (line feed)
270
		//    '\r'	U+000D (carriage return)
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
271
		//    '\b'	U+0008 (backspace)
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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.

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