PluginProbe
SQLite Database Integration / 2.2.21
SQLite Database Integration v2.2.21
3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 2.2.3 All 32 releases
sqlite-database-integration / wp-includes / sqlite / class-wp-sqlite-query-rewriter.php

class-wp-sqlite-query-rewriter.php in SQLite Database Integration 2.2.21, at wp-includes/sqlite/class-wp-sqlite-query-rewriter.php

344 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class WP_SQLite_Query_Rewriter
4 *
5 * @package wp-sqlite-integration
6 */
7
8 /**
9 * The query rewriter class.
10 */
11 class WP_SQLite_Query_Rewriter {
12
13 /**
14 * An array of input token objects.
15 *
16 * @var WP_SQLite_Token[]
17 */
18 public $input_tokens = array();
19
20 /**
21 * An array of output token objects.
22 *
23 * @var WP_SQLite_Token[]
24 */
25 public $output_tokens = array();
26
27 /**
28 * The current index.
29 *
30 * @var int
31 */
32 public $index = -1;
33
34 /**
35 * The maximum index.
36 *
37 * @var int
38 */
39 public $max = -1;
40
41 /**
42 * The call stack.
43 *
44 * @var array
45 */
46 public $call_stack = array();
47
48 /**
49 * The current depth.
50 *
51 * @var int
52 */
53 public $depth = 0;
54
55 /**
56 * The current token.
57 *
58 * @var WP_SQLite_Token
59 */
60 private $token;
61
62 /**
63 * The last function call.
64 *
65 * @var WP_SQLite_Token
66 */
67 private $last_function_call;
68
69 /**
70 * Constructor.
71 *
72 * @param WP_SQLite_Token[] $input_tokens Array of token objects.
73 */
74 public function __construct( $input_tokens ) {
75 $this->input_tokens = $input_tokens;
76 $this->max = count( $input_tokens );
77 }
78
79 /**
80 * Returns the updated query.
81 *
82 * @return string
83 */
84 public function get_updated_query() {
85 $query = '';
86 foreach ( $this->output_tokens as $token ) {
87 $query .= $token->token;
88 }
89 return $query;
90 }
91
92 /**
93 * Add a token to the output.
94 *
95 * @param WP_SQLite_Token $token Token object.
96 */
97 public function add( $token ) {
98 if ( $token ) {
99 $this->output_tokens[] = $token;
100 }
101 }
102
103 /**
104 * Add multiple tokens to the output.
105 *
106 * @param WP_SQLite_Token[] $tokens Array of token objects.
107 */
108 public function add_many( $tokens ) {
109 $this->output_tokens = array_merge( $this->output_tokens, $tokens );
110 }
111
112 /**
113 * Replaces all tokens.
114 *
115 * @param WP_SQLite_Token[] $tokens Array of token objects.
116 */
117 public function replace_all( $tokens ) {
118 $this->output_tokens = $tokens;
119 }
120
121 /**
122 * Peek at the next tokens and return one that matches the given criteria.
123 *
124 * @param array $query Optional. Search query.
125 * [
126 * 'type' => string|null, // Token type.
127 * 'flags' => int|null, // Token flags.
128 * 'values' => string|null, // Token values.
129 * ].
130 *
131 * @return WP_SQLite_Token
132 */
133 public function peek( $query = array() ) {
134 $type = isset( $query['type'] ) ? $query['type'] : null;
135 $flags = isset( $query['flags'] ) ? $query['flags'] : null;
136 $values = isset( $query['value'] )
137 ? ( is_array( $query['value'] ) ? $query['value'] : array( $query['value'] ) )
138 : null;
139
140 $i = $this->index;
141 while ( ++$i < $this->max ) {
142 if ( $this->input_tokens[ $i ]->matches( $type, $flags, $values ) ) {
143 return $this->input_tokens[ $i ];
144 }
145 }
146 }
147
148 /**
149 * Move forward and return the next tokens that match the given criteria.
150 *
151 * @param int $nth The nth token to return.
152 *
153 * @return WP_SQLite_Token
154 */
155 public function peek_nth( $nth ) {
156 $found = 0;
157 for ( $i = $this->index + 1;$i < $this->max;$i++ ) {
158 $token = $this->input_tokens[ $i ];
159 if ( ! $token->is_semantically_void() ) {
160 ++$found;
161 }
162 if ( $found === $nth ) {
163 return $this->input_tokens[ $i ];
164 }
165 }
166 }
167
168 /**
169 * Consume all the tokens.
170 *
171 * @param array $query Search query.
172 *
173 * @return void
174 */
175 public function consume_all( $query = array() ) {
176 while ( $this->consume( $query ) ) {
177 // Do nothing.
178 }
179 }
180
181 /**
182 * Consume the next tokens and return one that matches the given criteria.
183 *
184 * @param array $query Search query.
185 * [
186 * 'type' => null, // Optional. Token type.
187 * 'flags' => null, // Optional. Token flags.
188 * 'values' => null, // Optional. Token values.
189 * ].
190 *
191 * @return WP_SQLite_Token|null
192 */
193 public function consume( $query = array() ) {
194 $tokens = $this->move_forward( $query );
195 $this->output_tokens = array_merge( $this->output_tokens, $tokens );
196 return $this->token;
197 }
198
199 /**
200 * Drop the last consumed token and return it.
201 *
202 * @return WP_SQLite_Token|null
203 */
204 public function drop_last() {
205 return array_pop( $this->output_tokens );
206 }
207
208 /**
209 * Skip over the next tokens and return one that matches the given criteria.
210 *
211 * @param array $query Search query.
212 * [
213 * 'type' => null, // Optional. Token type.
214 * 'flags' => null, // Optional. Token flags.
215 * 'values' => null, // Optional. Token values.
216 * ].
217 *
218 * @return WP_SQLite_Token|null
219 */
220 public function skip( $query = array() ) {
221 $this->skip_and_return_all( $query );
222 return $this->token;
223 }
224
225 /**
226 * Skip over the next tokens until one matches the given criteria,
227 * and return all the skipped tokens.
228 *
229 * @param array $query Search query.
230 * [
231 * 'type' => null, // Optional. Token type.
232 * 'flags' => null, // Optional. Token flags.
233 * 'values' => null, // Optional. Token values.
234 * ].
235 *
236 * @return WP_SQLite_Token[]
237 */
238 public function skip_and_return_all( $query = array() ) {
239 $tokens = $this->move_forward( $query );
240
241 /*
242 * When skipping over whitespaces, make sure to consume
243 * at least one to avoid SQL syntax errors.
244 */
245 foreach ( $tokens as $token ) {
246 if ( $token->matches( WP_SQLite_Token::TYPE_WHITESPACE ) ) {
247 $this->add( $token );
248 break;
249 }
250 }
251
252 return $tokens;
253 }
254
255 /**
256 * Returns the next tokens that match the given criteria.
257 *
258 * @param array $query Search query.
259 * [
260 * 'type' => string|null, // Optional. Token type.
261 * 'flags' => int|null, // Optional. Token flags.
262 * 'values' => string|null, // Optional. Token values.
263 * ].
264 *
265 * @return array
266 */
267 private function move_forward( $query = array() ) {
268 $type = isset( $query['type'] ) ? $query['type'] : null;
269 $flags = isset( $query['flags'] ) ? $query['flags'] : null;
270 $values = isset( $query['value'] )
271 ? ( is_array( $query['value'] ) ? $query['value'] : array( $query['value'] ) )
272 : null;
273 $depth = isset( $query['depth'] ) ? $query['depth'] : null;
274
275 $buffered = array();
276 while ( true ) {
277 if ( ++$this->index >= $this->max ) {
278 $this->token = null;
279 $this->call_stack = array();
280 break;
281 }
282 $this->token = $this->input_tokens[ $this->index ];
283 $this->update_call_stack();
284 $buffered[] = $this->token;
285 if (
286 ( null === $depth || $this->depth === $depth )
287 && $this->token->matches( $type, $flags, $values )
288 ) {
289 break;
290 }
291 }
292
293 return $buffered;
294 }
295
296 /**
297 * Returns the last call stack element.
298 *
299 * @return array|null
300 */
301 public function last_call_stack_element() {
302 return count( $this->call_stack ) ? $this->call_stack[ count( $this->call_stack ) - 1 ] : null;
303 }
304
305 /**
306 * Updates the call stack.
307 *
308 * @return void
309 */
310 private function update_call_stack() {
311 if ( $this->token->flags & WP_SQLite_Token::FLAG_KEYWORD_FUNCTION ) {
312 $this->last_function_call = $this->token->value;
313 }
314 if ( WP_SQLite_Token::TYPE_OPERATOR === $this->token->type ) {
315 switch ( $this->token->value ) {
316 case '(':
317 if ( $this->last_function_call ) {
318 array_push(
319 $this->call_stack,
320 array(
321 'function' => $this->last_function_call,
322 'depth' => $this->depth,
323 )
324 );
325 $this->last_function_call = null;
326 }
327 ++$this->depth;
328 break;
329
330 case ')':
331 --$this->depth;
332 $call_parent = $this->last_call_stack_element();
333 if (
334 $call_parent &&
335 $call_parent['depth'] === $this->depth
336 ) {
337 array_pop( $this->call_stack );
338 }
339 break;
340 }
341 }
342 }
343 }
344