Completed
Push — master ( d75d5d...45122e )
by David
04:06
created

Wordlift_Query_Builder::escape_value()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 10

Duplication

Lines 23
Ratio 100 %
Metric Value
dl 23
loc 23
rs 9.0857
cc 1
eloc 10
nc 1
nop 1
1
<?php
2
3
/**
4
 * A builder to build SPARQL queries.
5
 *
6
 * @since 3.1.7
7
 */
8
class Wordlift_Query_Builder {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the opening brace for this class should be on a new line.
Loading history...
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
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
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;
1 ignored issue
show
Coding Style introduced by
Terminating statement must be indented to the same level as the CASE body
Loading history...
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 ) {
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...
261
262
		// see http://www.w3.org/TR/rdf-sparql-query/
263
		//    '\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...
264
		//    '\n'	U+000A (line feed)
265
		//    '\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...
266
		//    '\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...
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)
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...
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
}
1 ignored issue
show
Coding Style introduced by
According to PSR2, the closing brace of classes should be placed on the next line directly after the body.

Below you find some examples:

// Incorrect placement according to PSR2
class MyClass
{
    public function foo()
    {

    }
    // This blank line is not allowed.

}

// Correct
class MyClass
{
    public function foo()
    {

    } // No blank lines after this line.
}
Loading history...
285