PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
woocommerce-pos / vendor_prefixed / sabberworm / php-css-parser / src / Parsing / ParserState.php

ParserState.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.20, at vendor_prefixed/sabberworm/php-css-parser/src/Parsing/ParserState.php

421 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare (strict_types=1);
4 namespace WCPOS\Vendor\Sabberworm\CSS\Parsing;
5
6 use WCPOS\Vendor\Sabberworm\CSS\Comment\Comment;
7 use WCPOS\Vendor\Sabberworm\CSS\Settings;
8 use function WCPOS\Vendor\Safe\iconv;
9 use function WCPOS\Vendor\Safe\preg_match;
10 use function WCPOS\Vendor\Safe\preg_split;
11 /**
12 * @internal since 8.7.0
13 */
14 class ParserState
15 {
16 public const EOF = null;
17 /**
18 * @var Settings
19 */
20 private $parserSettings;
21 /**
22 * @var string
23 */
24 private $text;
25 /**
26 * @var array<int, string>
27 */
28 private $characters;
29 /**
30 * @var int<0, max>
31 */
32 private $currentPosition = 0;
33 /**
34 * will only be used if the CSS does not contain an `@charset` declaration
35 *
36 * @var string
37 */
38 private $charset;
39 /**
40 * @var int<1, max> $lineNumber
41 */
42 private $lineNumber;
43 /**
44 * @param string $text the complete CSS as text (i.e., usually the contents of a CSS file)
45 * @param int<1, max> $lineNumber
46 */
47 public function __construct(string $text, Settings $parserSettings, int $lineNumber = 1)
48 {
49 $this->parserSettings = $parserSettings;
50 $this->text = $text;
51 $this->lineNumber = $lineNumber;
52 $this->setCharset($this->parserSettings->getDefaultCharset());
53 }
54 /**
55 * Sets the charset to be used if the CSS does not contain an `@charset` declaration.
56 */
57 public function setCharset(string $charset) : void
58 {
59 $this->charset = $charset;
60 $this->characters = $this->strsplit($this->text);
61 }
62 /**
63 * @return int<1, max>
64 */
65 public function currentLine() : int
66 {
67 return $this->lineNumber;
68 }
69 /**
70 * @return int<0, max>
71 */
72 public function currentColumn() : int
73 {
74 return $this->currentPosition;
75 }
76 public function getSettings() : Settings
77 {
78 return $this->parserSettings;
79 }
80 public function anchor() : Anchor
81 {
82 return new Anchor($this->currentPosition, $this);
83 }
84 /**
85 * @param int<0, max> $position
86 */
87 public function setPosition(int $position) : void
88 {
89 $this->currentPosition = $position;
90 }
91 /**
92 * @return non-empty-string
93 *
94 * @throws UnexpectedTokenException
95 */
96 public function parseIdentifier(bool $ignoreCase = \true) : string
97 {
98 if ($this->isEnd()) {
99 throw new UnexpectedEOFException('', '', 'identifier', $this->lineNumber);
100 }
101 $result = $this->parseCharacter(\true);
102 if ($result === null) {
103 throw new UnexpectedTokenException('', $this->peek(5), 'identifier', $this->lineNumber);
104 }
105 while (!$this->isEnd() && ($character = $this->parseCharacter(\true)) !== null) {
106 if (preg_match('/[a-zA-Z0-9\\x{00A0}-\\x{FFFF}_-]/Sux', $character) !== 0) {
107 $result .= $character;
108 } else {
109 $result .= '\\' . $character;
110 }
111 }
112 if ($ignoreCase) {
113 $result = $this->strtolower($result);
114 }
115 return $result;
116 }
117 /**
118 * @throws UnexpectedEOFException
119 * @throws UnexpectedTokenException
120 */
121 public function parseCharacter(bool $isForIdentifier) : ?string
122 {
123 if ($this->peek() === '\\') {
124 $this->consume('\\');
125 if ($this->comes('\\n') || $this->comes('\\r')) {
126 return '';
127 }
128 if (preg_match('/[0-9a-fA-F]/Su', $this->peek()) === 0) {
129 return $this->consume(1);
130 }
131 $hexCodePoint = $this->consumeExpression('/^[0-9a-fA-F]{1,6}/u', 6);
132 if ($this->strlen($hexCodePoint) < 6) {
133 // Consume whitespace after incomplete unicode escape
134 if (preg_match('/\\s/isSu', $this->peek()) !== 0) {
135 if ($this->comes('WCPOS\\Vendor\\r\\n')) {
136 $this->consume(2);
137 } else {
138 $this->consume(1);
139 }
140 }
141 }
142 $codePoint = \intval($hexCodePoint, 16);
143 $utf32EncodedCharacter = '';
144 for ($i = 0; $i < 4; ++$i) {
145 $utf32EncodedCharacter .= \chr($codePoint & 0xff);
146 $codePoint = $codePoint >> 8;
147 }
148 return iconv('utf-32le', $this->charset, $utf32EncodedCharacter);
149 }
150 if ($isForIdentifier) {
151 $peek = \ord($this->peek());
152 // Ranges: a-z A-Z 0-9 - _
153 if ($peek >= 97 && $peek <= 122 || $peek >= 65 && $peek <= 90 || $peek >= 48 && $peek <= 57 || $peek === 45 || $peek === 95 || $peek > 0xa1) {
154 return $this->consume(1);
155 }
156 } else {
157 return $this->consume(1);
158 }
159 return null;
160 }
161 /**
162 * Consumes whitespace and/or comments until the next non-whitespace character that isn't a slash opening a comment.
163 *
164 * @param list<Comment> $comments Any comments consumed will be appended to this array.
165 *
166 * @return string the whitespace consumed, without the comments
167 *
168 * @throws UnexpectedEOFException
169 * @throws UnexpectedTokenException
170 *
171 * @phpstan-impure
172 * This method may change the state of the object by advancing the internal position;
173 * it does not simply 'get' a value.
174 */
175 public function consumeWhiteSpace(array &$comments = []) : string
176 {
177 $consumed = '';
178 do {
179 while (preg_match('/\\s/isSu', $this->peek()) === 1) {
180 $consumed .= $this->consume(1);
181 }
182 if ($this->parserSettings->usesLenientParsing()) {
183 try {
184 $comment = $this->consumeComment();
185 } catch (UnexpectedEOFException $e) {
186 $this->currentPosition = \count($this->characters);
187 break;
188 }
189 } else {
190 $comment = $this->consumeComment();
191 }
192 if ($comment instanceof Comment) {
193 $comments[] = $comment;
194 }
195 } while ($comment instanceof Comment);
196 return $consumed;
197 }
198 /**
199 * @param non-empty-string $string
200 */
201 public function comes(string $string, bool $caseInsensitive = \false) : bool
202 {
203 $peek = $this->peek(\strlen($string));
204 return $peek !== '' && $this->streql($peek, $string, $caseInsensitive);
205 }
206 /**
207 * @param int<1, max> $length
208 * @param int<0, max> $offset
209 */
210 public function peek(int $length = 1, int $offset = 0) : string
211 {
212 $offset += $this->currentPosition;
213 if ($offset >= \count($this->characters)) {
214 return '';
215 }
216 return $this->substr($offset, $length);
217 }
218 /**
219 * @param string|int<1, max> $value
220 *
221 * @throws UnexpectedEOFException
222 * @throws UnexpectedTokenException
223 */
224 public function consume($value = 1) : string
225 {
226 if (\is_string($value)) {
227 $numberOfLines = \substr_count($value, "\n");
228 $length = $this->strlen($value);
229 if (!$this->streql($this->substr($this->currentPosition, $length), $value)) {
230 throw new UnexpectedTokenException($value, $this->peek(\max($length, 5)), 'literal', $this->lineNumber);
231 }
232 $this->lineNumber += $numberOfLines;
233 $this->currentPosition += $this->strlen($value);
234 $result = $value;
235 } else {
236 if ($this->currentPosition + $value > \count($this->characters)) {
237 throw new UnexpectedEOFException((string) $value, $this->peek(5), 'count', $this->lineNumber);
238 }
239 $result = $this->substr($this->currentPosition, $value);
240 $numberOfLines = \substr_count($result, "\n");
241 $this->lineNumber += $numberOfLines;
242 $this->currentPosition += $value;
243 }
244 return $result;
245 }
246 /**
247 * If the possibly-expected next content is next, consume it.
248 *
249 * @param non-empty-string $nextContent
250 *
251 * @return bool whether the possibly-expected content was found and consumed
252 */
253 public function consumeIfComes(string $nextContent) : bool
254 {
255 $length = $this->strlen($nextContent);
256 if (!$this->streql($this->substr($this->currentPosition, $length), $nextContent)) {
257 return \false;
258 }
259 $numberOfLines = \substr_count($nextContent, "\n");
260 $this->lineNumber += $numberOfLines;
261 $this->currentPosition += $this->strlen($nextContent);
262 return \true;
263 }
264 /**
265 * @param string $expression
266 * @param int<1, max>|null $maximumLength
267 *
268 * @throws UnexpectedEOFException
269 * @throws UnexpectedTokenException
270 */
271 public function consumeExpression(string $expression, ?int $maximumLength = null) : string
272 {
273 $matches = null;
274 $input = $maximumLength !== null ? $this->peek($maximumLength) : $this->inputLeft();
275 if (preg_match($expression, $input, $matches, \PREG_OFFSET_CAPTURE) !== 1) {
276 throw new UnexpectedTokenException($expression, $this->peek(5), 'expression', $this->lineNumber);
277 }
278 return $this->consume($matches[0][0]);
279 }
280 /**
281 * @return Comment|false
282 */
283 public function consumeComment()
284 {
285 $lineNumber = $this->lineNumber;
286 $comment = null;
287 if ($this->comes('/*')) {
288 $this->consume(1);
289 $comment = '';
290 while (($char = $this->consume(1)) !== '') {
291 $comment .= $char;
292 if ($this->comes('*/')) {
293 $this->consume(2);
294 break;
295 }
296 }
297 }
298 // We skip the * which was included in the comment.
299 return \is_string($comment) ? new Comment(\substr($comment, 1), $lineNumber) : \false;
300 }
301 public function isEnd() : bool
302 {
303 return $this->currentPosition >= \count($this->characters);
304 }
305 /**
306 * @param list<string|self::EOF>|string|self::EOF $stopCharacters
307 * @param list<Comment> $comments
308 *
309 * @throws UnexpectedEOFException
310 * @throws UnexpectedTokenException
311 */
312 public function consumeUntil($stopCharacters, bool $includeEnd = \false, bool $consumeEnd = \false, array &$comments = []) : string
313 {
314 $stopCharacters = \is_array($stopCharacters) ? $stopCharacters : [$stopCharacters];
315 $consumedCharacters = '';
316 $start = $this->currentPosition;
317 $comments = \array_merge($comments, $this->consumeComments());
318 while (!$this->isEnd()) {
319 $character = $this->consume(1);
320 if (\in_array($character, $stopCharacters, \true)) {
321 if ($includeEnd) {
322 $consumedCharacters .= $character;
323 } elseif (!$consumeEnd) {
324 $this->currentPosition -= $this->strlen($character);
325 }
326 return $consumedCharacters;
327 }
328 $consumedCharacters .= $character;
329 $comments = \array_merge($comments, $this->consumeComments());
330 }
331 if (\in_array(self::EOF, $stopCharacters, \true)) {
332 return $consumedCharacters;
333 }
334 $this->currentPosition = $start;
335 throw new UnexpectedEOFException('One of ("' . \implode('","', $stopCharacters) . '")', $this->peek(5), 'search', $this->lineNumber);
336 }
337 private function inputLeft() : string
338 {
339 return $this->substr($this->currentPosition, -1);
340 }
341 public function streql(string $string1, string $string2, bool $caseInsensitive = \true) : bool
342 {
343 return $caseInsensitive ? $this->strtolower($string1) === $this->strtolower($string2) : $string1 === $string2;
344 }
345 /**
346 * @param int<1, max> $numberOfCharacters
347 */
348 public function backtrack(int $numberOfCharacters) : void
349 {
350 $this->currentPosition -= $numberOfCharacters;
351 }
352 /**
353 * @return int<0, max>
354 */
355 public function strlen(string $string) : int
356 {
357 return $this->parserSettings->hasMultibyteSupport() ? \mb_strlen($string, $this->charset) : \strlen($string);
358 }
359 /**
360 * @param int<0, max> $offset
361 */
362 private function substr(int $offset, int $length) : string
363 {
364 if ($length < 0) {
365 $length = \count($this->characters) - $offset + $length;
366 }
367 if ($offset + $length > \count($this->characters)) {
368 $length = \count($this->characters) - $offset;
369 }
370 $result = '';
371 while ($length > 0) {
372 $result .= $this->characters[$offset];
373 $offset++;
374 $length--;
375 }
376 return $result;
377 }
378 /**
379 * @return ($string is non-empty-string ? non-empty-string : string)
380 */
381 private function strtolower(string $string) : string
382 {
383 return $this->parserSettings->hasMultibyteSupport() ? \mb_strtolower($string, $this->charset) : \strtolower($string);
384 }
385 /**
386 * @return list<string>
387 */
388 private function strsplit(string $string) : array
389 {
390 if ($this->parserSettings->hasMultibyteSupport()) {
391 if ($this->streql($this->charset, 'utf-8')) {
392 $result = preg_split('//u', $string, -1, \PREG_SPLIT_NO_EMPTY);
393 } else {
394 $length = \mb_strlen($string, $this->charset);
395 $result = [];
396 for ($i = 0; $i < $length; ++$i) {
397 $result[] = \mb_substr($string, $i, 1, $this->charset);
398 }
399 }
400 } else {
401 $result = $string !== '' ? \str_split($string) : [];
402 }
403 return $result;
404 }
405 /**
406 * @return list<Comment>
407 */
408 private function consumeComments() : array
409 {
410 $comments = [];
411 while (\true) {
412 $comment = $this->consumeComment();
413 if ($comment instanceof Comment) {
414 $comments[] = $comment;
415 } else {
416 return $comments;
417 }
418 }
419 }
420 }
421