PluginProbe
Media Cloud Sync / 1.2.12
Media Cloud Sync v1.2.12
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / sdk / s3 / JmesPath / Lexer.php

Lexer.php in Media Cloud Sync 1.2.12, at includes/sdk/s3/JmesPath/Lexer.php

236 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Dudlewebs\WPMCS\s3\JmesPath;
4
5 /**
6 * Tokenizes JMESPath expressions
7 */
8 class Lexer
9 {
10 const T_DOT = 'dot';
11 const T_STAR = 'star';
12 const T_COMMA = 'comma';
13 const T_COLON = 'colon';
14 const T_CURRENT = 'current';
15 const T_EXPREF = 'expref';
16 const T_LPAREN = 'lparen';
17 const T_RPAREN = 'rparen';
18 const T_LBRACE = 'lbrace';
19 const T_RBRACE = 'rbrace';
20 const T_LBRACKET = 'lbracket';
21 const T_RBRACKET = 'rbracket';
22 const T_FLATTEN = 'flatten';
23 const T_IDENTIFIER = 'identifier';
24 const T_NUMBER = 'number';
25 const T_QUOTED_IDENTIFIER = 'quoted_identifier';
26 const T_UNKNOWN = 'unknown';
27 const T_PIPE = 'pipe';
28 const T_OR = 'or';
29 const T_AND = 'and';
30 const T_NOT = 'not';
31 const T_FILTER = 'filter';
32 const T_LITERAL = 'literal';
33 const T_EOF = 'eof';
34 const T_COMPARATOR = 'comparator';
35 const STATE_IDENTIFIER = 0;
36 const STATE_NUMBER = 1;
37 const STATE_SINGLE_CHAR = 2;
38 const STATE_WHITESPACE = 3;
39 const STATE_STRING_LITERAL = 4;
40 const STATE_QUOTED_STRING = 5;
41 const STATE_JSON_LITERAL = 6;
42 const STATE_LBRACKET = 7;
43 const STATE_PIPE = 8;
44 const STATE_LT = 9;
45 const STATE_GT = 10;
46 const STATE_EQ = 11;
47 const STATE_NOT = 12;
48 const STATE_AND = 13;
49 /** @var array We know what token we are consuming based on each char */
50 private static $transitionTable = ['<' => self::STATE_LT, '>' => self::STATE_GT, '=' => self::STATE_EQ, '!' => self::STATE_NOT, '[' => self::STATE_LBRACKET, '|' => self::STATE_PIPE, '&' => self::STATE_AND, '`' => self::STATE_JSON_LITERAL, '"' => self::STATE_QUOTED_STRING, "'" => self::STATE_STRING_LITERAL, '-' => self::STATE_NUMBER, '0' => self::STATE_NUMBER, '1' => self::STATE_NUMBER, '2' => self::STATE_NUMBER, '3' => self::STATE_NUMBER, '4' => self::STATE_NUMBER, '5' => self::STATE_NUMBER, '6' => self::STATE_NUMBER, '7' => self::STATE_NUMBER, '8' => self::STATE_NUMBER, '9' => self::STATE_NUMBER, ' ' => self::STATE_WHITESPACE, "\t" => self::STATE_WHITESPACE, "\n" => self::STATE_WHITESPACE, "\r" => self::STATE_WHITESPACE, '.' => self::STATE_SINGLE_CHAR, '*' => self::STATE_SINGLE_CHAR, ']' => self::STATE_SINGLE_CHAR, ',' => self::STATE_SINGLE_CHAR, ':' => self::STATE_SINGLE_CHAR, '@' => self::STATE_SINGLE_CHAR, '(' => self::STATE_SINGLE_CHAR, ')' => self::STATE_SINGLE_CHAR, '{' => self::STATE_SINGLE_CHAR, '}' => self::STATE_SINGLE_CHAR, '_' => self::STATE_IDENTIFIER, 'A' => self::STATE_IDENTIFIER, 'B' => self::STATE_IDENTIFIER, 'C' => self::STATE_IDENTIFIER, 'D' => self::STATE_IDENTIFIER, 'E' => self::STATE_IDENTIFIER, 'F' => self::STATE_IDENTIFIER, 'G' => self::STATE_IDENTIFIER, 'H' => self::STATE_IDENTIFIER, 'I' => self::STATE_IDENTIFIER, 'J' => self::STATE_IDENTIFIER, 'K' => self::STATE_IDENTIFIER, 'L' => self::STATE_IDENTIFIER, 'M' => self::STATE_IDENTIFIER, 'N' => self::STATE_IDENTIFIER, 'O' => self::STATE_IDENTIFIER, 'P' => self::STATE_IDENTIFIER, 'Q' => self::STATE_IDENTIFIER, 'R' => self::STATE_IDENTIFIER, 'S' => self::STATE_IDENTIFIER, 'T' => self::STATE_IDENTIFIER, 'U' => self::STATE_IDENTIFIER, 'V' => self::STATE_IDENTIFIER, 'W' => self::STATE_IDENTIFIER, 'X' => self::STATE_IDENTIFIER, 'Y' => self::STATE_IDENTIFIER, 'Z' => self::STATE_IDENTIFIER, 'a' => self::STATE_IDENTIFIER, 'b' => self::STATE_IDENTIFIER, 'c' => self::STATE_IDENTIFIER, 'd' => self::STATE_IDENTIFIER, 'e' => self::STATE_IDENTIFIER, 'f' => self::STATE_IDENTIFIER, 'g' => self::STATE_IDENTIFIER, 'h' => self::STATE_IDENTIFIER, 'i' => self::STATE_IDENTIFIER, 'j' => self::STATE_IDENTIFIER, 'k' => self::STATE_IDENTIFIER, 'l' => self::STATE_IDENTIFIER, 'm' => self::STATE_IDENTIFIER, 'n' => self::STATE_IDENTIFIER, 'o' => self::STATE_IDENTIFIER, 'p' => self::STATE_IDENTIFIER, 'q' => self::STATE_IDENTIFIER, 'r' => self::STATE_IDENTIFIER, 's' => self::STATE_IDENTIFIER, 't' => self::STATE_IDENTIFIER, 'u' => self::STATE_IDENTIFIER, 'v' => self::STATE_IDENTIFIER, 'w' => self::STATE_IDENTIFIER, 'x' => self::STATE_IDENTIFIER, 'y' => self::STATE_IDENTIFIER, 'z' => self::STATE_IDENTIFIER];
51 /** @var array Valid identifier characters after first character */
52 private $validIdentifier = ['A' => \true, 'B' => \true, 'C' => \true, 'D' => \true, 'E' => \true, 'F' => \true, 'G' => \true, 'H' => \true, 'I' => \true, 'J' => \true, 'K' => \true, 'L' => \true, 'M' => \true, 'N' => \true, 'O' => \true, 'P' => \true, 'Q' => \true, 'R' => \true, 'S' => \true, 'T' => \true, 'U' => \true, 'V' => \true, 'W' => \true, 'X' => \true, 'Y' => \true, 'Z' => \true, 'a' => \true, 'b' => \true, 'c' => \true, 'd' => \true, 'e' => \true, 'f' => \true, 'g' => \true, 'h' => \true, 'i' => \true, 'j' => \true, 'k' => \true, 'l' => \true, 'm' => \true, 'n' => \true, 'o' => \true, 'p' => \true, 'q' => \true, 'r' => \true, 's' => \true, 't' => \true, 'u' => \true, 'v' => \true, 'w' => \true, 'x' => \true, 'y' => \true, 'z' => \true, '_' => \true, '0' => \true, '1' => \true, '2' => \true, '3' => \true, '4' => \true, '5' => \true, '6' => \true, '7' => \true, '8' => \true, '9' => \true];
53 /** @var array Valid number characters after the first character */
54 private $numbers = ['0' => \true, '1' => \true, '2' => \true, '3' => \true, '4' => \true, '5' => \true, '6' => \true, '7' => \true, '8' => \true, '9' => \true];
55 /** @var array Map of simple single character tokens */
56 private $simpleTokens = ['.' => self::T_DOT, '*' => self::T_STAR, ']' => self::T_RBRACKET, ',' => self::T_COMMA, ':' => self::T_COLON, '@' => self::T_CURRENT, '(' => self::T_LPAREN, ')' => self::T_RPAREN, '{' => self::T_LBRACE, '}' => self::T_RBRACE];
57 /**
58 * Tokenize the JMESPath expression into an array of tokens hashes that
59 * contain a 'type', 'value', and 'key'.
60 *
61 * @param string $input JMESPath input
62 *
63 * @return array
64 * @throws SyntaxErrorException
65 */
66 public function tokenize($input)
67 {
68 $tokens = [];
69 if ($input === '') {
70 goto eof;
71 }
72 $chars = \str_split($input);
73 while (\false !== ($current = \current($chars))) {
74 // Every character must be in the transition character table.
75 if (!isset(self::$transitionTable[$current])) {
76 $tokens[] = ['type' => self::T_UNKNOWN, 'pos' => \key($chars), 'value' => $current];
77 \next($chars);
78 continue;
79 }
80 $state = self::$transitionTable[$current];
81 if ($state === self::STATE_SINGLE_CHAR) {
82 // Consume simple tokens like ".", ",", "@", etc.
83 $tokens[] = ['type' => $this->simpleTokens[$current], 'pos' => \key($chars), 'value' => $current];
84 \next($chars);
85 } elseif ($state === self::STATE_IDENTIFIER) {
86 // Consume identifiers
87 $start = \key($chars);
88 $buffer = '';
89 do {
90 $buffer .= $current;
91 $current = \next($chars);
92 } while ($current !== \false && isset($this->validIdentifier[$current]));
93 $tokens[] = ['type' => self::T_IDENTIFIER, 'value' => $buffer, 'pos' => $start];
94 } elseif ($state === self::STATE_WHITESPACE) {
95 // Skip whitespace
96 \next($chars);
97 } elseif ($state === self::STATE_LBRACKET) {
98 // Consume "[", "[?", and "[]"
99 $position = \key($chars);
100 $actual = \next($chars);
101 if ($actual === ']') {
102 \next($chars);
103 $tokens[] = ['type' => self::T_FLATTEN, 'pos' => $position, 'value' => '[]'];
104 } elseif ($actual === '?') {
105 \next($chars);
106 $tokens[] = ['type' => self::T_FILTER, 'pos' => $position, 'value' => '[?'];
107 } else {
108 $tokens[] = ['type' => self::T_LBRACKET, 'pos' => $position, 'value' => '['];
109 }
110 } elseif ($state === self::STATE_STRING_LITERAL) {
111 // Consume raw string literals
112 $t = $this->inside($chars, "'", self::T_LITERAL);
113 $t['value'] = \str_replace("\\'", "'", $t['value']);
114 $tokens[] = $t;
115 } elseif ($state === self::STATE_PIPE) {
116 // Consume pipe and OR
117 $tokens[] = $this->matchOr($chars, '|', '|', self::T_OR, self::T_PIPE);
118 } elseif ($state == self::STATE_JSON_LITERAL) {
119 // Consume JSON literals
120 $token = $this->inside($chars, '`', self::T_LITERAL);
121 if ($token['type'] === self::T_LITERAL) {
122 $token['value'] = \str_replace('\\`', '`', $token['value']);
123 $token = $this->parseJson($token);
124 }
125 $tokens[] = $token;
126 } elseif ($state == self::STATE_NUMBER) {
127 // Consume numbers
128 $start = \key($chars);
129 $buffer = '';
130 do {
131 $buffer .= $current;
132 $current = \next($chars);
133 } while ($current !== \false && isset($this->numbers[$current]));
134 $tokens[] = ['type' => self::T_NUMBER, 'value' => (int) $buffer, 'pos' => $start];
135 } elseif ($state === self::STATE_QUOTED_STRING) {
136 // Consume quoted identifiers
137 $token = $this->inside($chars, '"', self::T_QUOTED_IDENTIFIER);
138 if ($token['type'] === self::T_QUOTED_IDENTIFIER) {
139 $token['value'] = '"' . $token['value'] . '"';
140 $token = $this->parseJson($token);
141 }
142 $tokens[] = $token;
143 } elseif ($state === self::STATE_EQ) {
144 // Consume equals
145 $tokens[] = $this->matchOr($chars, '=', '=', self::T_COMPARATOR, self::T_UNKNOWN);
146 } elseif ($state == self::STATE_AND) {
147 $tokens[] = $this->matchOr($chars, '&', '&', self::T_AND, self::T_EXPREF);
148 } elseif ($state === self::STATE_NOT) {
149 // Consume not equal
150 $tokens[] = $this->matchOr($chars, '!', '=', self::T_COMPARATOR, self::T_NOT);
151 } else {
152 // either '<' or '>'
153 // Consume less than and greater than
154 $tokens[] = $this->matchOr($chars, $current, '=', self::T_COMPARATOR, self::T_COMPARATOR);
155 }
156 }
157 eof:
158 $tokens[] = ['type' => self::T_EOF, 'pos' => \mb_strlen($input, 'UTF-8'), 'value' => null];
159 return $tokens;
160 }
161 /**
162 * Returns a token based on whether or not the next token matches the
163 * expected value. If it does, a token of "$type" is returned. Otherwise,
164 * a token of "$orElse" type is returned.
165 *
166 * @param array $chars Array of characters by reference.
167 * @param string $current The current character.
168 * @param string $expected Expected character.
169 * @param string $type Expected result type.
170 * @param string $orElse Otherwise return a token of this type.
171 *
172 * @return array Returns a conditional token.
173 */
174 private function matchOr(array &$chars, $current, $expected, $type, $orElse)
175 {
176 if (\next($chars) === $expected) {
177 \next($chars);
178 return ['type' => $type, 'pos' => \key($chars) - 1, 'value' => $current . $expected];
179 }
180 return ['type' => $orElse, 'pos' => \key($chars) - 1, 'value' => $current];
181 }
182 /**
183 * Returns a token the is the result of consuming inside of delimiter
184 * characters. Escaped delimiters will be adjusted before returning a
185 * value. If the token is not closed, "unknown" is returned.
186 *
187 * @param array $chars Array of characters by reference.
188 * @param string $delim The delimiter character.
189 * @param string $type Token type.
190 *
191 * @return array Returns the consumed token.
192 */
193 private function inside(array &$chars, $delim, $type)
194 {
195 $position = \key($chars);
196 $current = \next($chars);
197 $buffer = '';
198 while ($current !== $delim) {
199 if ($current === '\\') {
200 $buffer .= '\\';
201 $current = \next($chars);
202 }
203 if ($current === \false) {
204 // Unclosed delimiter
205 return ['type' => self::T_UNKNOWN, 'value' => $buffer, 'pos' => $position];
206 }
207 $buffer .= $current;
208 $current = \next($chars);
209 }
210 \next($chars);
211 return ['type' => $type, 'value' => $buffer, 'pos' => $position];
212 }
213 /**
214 * Parses a JSON token or sets the token type to "unknown" on error.
215 *
216 * @param array $token Token that needs parsing.
217 *
218 * @return array Returns a token with a parsed value.
219 */
220 private function parseJson(array $token)
221 {
222 $value = \json_decode($token['value'], \true);
223 if ($error = \json_last_error()) {
224 // Legacy support for elided quotes. Try to parse again by adding
225 // quotes around the bad input value.
226 $value = \json_decode('"' . $token['value'] . '"', \true);
227 if ($error = \json_last_error()) {
228 $token['type'] = self::T_UNKNOWN;
229 return $token;
230 }
231 }
232 $token['value'] = $value;
233 return $token;
234 }
235 }
236