PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.19
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.19
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 1.9.14 All 163 releases
woocommerce-pos / vendor_prefixed / sabberworm / php-css-parser / src / Property / Selector.php

Selector.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.19, at vendor_prefixed/sabberworm/php-css-parser/src/Property/Selector.php

204 lines 7.2 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\Property;
5
6 use WCPOS\Vendor\Sabberworm\CSS\Comment\Comment;
7 use WCPOS\Vendor\Sabberworm\CSS\OutputFormat;
8 use WCPOS\Vendor\Sabberworm\CSS\Parsing\ParserState;
9 use WCPOS\Vendor\Sabberworm\CSS\Parsing\UnexpectedTokenException;
10 use WCPOS\Vendor\Sabberworm\CSS\Property\Selector\Combinator;
11 use WCPOS\Vendor\Sabberworm\CSS\Property\Selector\Component;
12 use WCPOS\Vendor\Sabberworm\CSS\Property\Selector\CompoundSelector;
13 use WCPOS\Vendor\Sabberworm\CSS\Renderable;
14 use WCPOS\Vendor\Sabberworm\CSS\Settings;
15 use WCPOS\Vendor\Sabberworm\CSS\ShortClassNameProvider;
16 use function WCPOS\Vendor\Safe\preg_match;
17 /**
18 * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this
19 * class.
20 */
21 class Selector implements Renderable
22 {
23 use ShortClassNameProvider;
24 /**
25 * @internal since 8.5.2
26 */
27 public const SELECTOR_VALIDATION_RX = '/
28 ^(
29 # not whitespace only
30 (?!\\s*+$)
31 (?:
32 # any sequence of valid unescaped characters, except quotes
33 [a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*=~\\[\\]()\\-\\s\\.:#+>,]++
34 |
35 # one or more escaped characters
36 (?:\\\\.)++
37 |
38 # quoted text, like in `[id="example"]`
39 (?:
40 # opening quote
41 ([\'"])
42 (?:
43 # sequence of characters except closing quote or backslash
44 (?:(?!\\g{-1}|\\\\).)++
45 |
46 # one or more escaped characters
47 (?:\\\\.)++
48 )*+ # zero or more times
49 # closing quote or end (unmatched quote is currently allowed)
50 (?:\\g{-1}|$)
51 )
52 )*+ # zero or more times
53 )$
54 /ux';
55 /**
56 * @var non-empty-list<Component>
57 */
58 private $components;
59 /**
60 * @internal since V8.8.0
61 */
62 public static function isValid(string $selector) : bool
63 {
64 // Note: We need to use `static::` here as the constant is overridden in the `KeyframeSelector` class.
65 $numberOfMatches = preg_match(static::SELECTOR_VALIDATION_RX, $selector);
66 return $numberOfMatches === 1;
67 }
68 /**
69 * @param non-empty-string|non-empty-list<Component> $selector
70 * Providing a string is deprecated in version 9.2 and will not work from v10.0
71 *
72 * @throws UnexpectedTokenException if the selector is not valid
73 */
74 public final function __construct($selector)
75 {
76 if (\is_string($selector)) {
77 $this->setSelector($selector);
78 } else {
79 $this->setComponents($selector);
80 }
81 }
82 /**
83 * @param list<Comment> $comments
84 *
85 * @return non-empty-list<Component>
86 *
87 * @throws UnexpectedTokenException
88 */
89 private static function parseComponents(ParserState $parserState, array &$comments = []) : array
90 {
91 // Whitespace is a descendent combinator, not allowed around a compound selector.
92 // (It is allowed within, e.g. as part of a string or within a function like `:not()`.)
93 // Gobble any up now to get a clean start.
94 $parserState->consumeWhiteSpace($comments);
95 $selectorParts = [];
96 while (\true) {
97 try {
98 $selectorParts[] = CompoundSelector::parse($parserState, $comments);
99 } catch (UnexpectedTokenException $e) {
100 if ($selectorParts !== [] && \end($selectorParts)->getValue() === ' ') {
101 // The whitespace was not a descendent combinator, and was, in fact, arbitrary,
102 // after the end of the selector. Discard it.
103 \array_pop($selectorParts);
104 break;
105 } else {
106 throw $e;
107 }
108 }
109 try {
110 $selectorParts[] = Combinator::parse($parserState, $comments);
111 } catch (UnexpectedTokenException $e) {
112 // End of selector has been reached.
113 break;
114 }
115 }
116 return $selectorParts;
117 }
118 /**
119 * @param list<Comment> $comments
120 *
121 * @throws UnexpectedTokenException
122 *
123 * @internal
124 */
125 public static function parse(ParserState $parserState, array &$comments = []) : self
126 {
127 $selectorParts = self::parseComponents($parserState, $comments);
128 // Check that the selector has been fully parsed:
129 if (!\in_array($parserState->peek(), ['{', '}', ',', ''], \true)) {
130 throw new UnexpectedTokenException('`,`, `{`, `}` or EOF', $parserState->peek(5), 'literal', $parserState->currentLine());
131 }
132 return new static($selectorParts);
133 }
134 /**
135 * @return non-empty-list<Component>
136 */
137 public function getComponents() : array
138 {
139 return $this->components;
140 }
141 /**
142 * @param non-empty-list<Component> $components
143 * This should be an alternating sequence of `CompoundSelector` and `Combinator`, starting and ending with a
144 * `CompoundSelector`, and may be a single `CompoundSelector`.
145 */
146 public function setComponents(array $components) : self
147 {
148 $this->components = $components;
149 return $this;
150 }
151 /**
152 * @return non-empty-string
153 *
154 * @deprecated in version 9.2, will be removed in v10.0. Use either `getComponents()` or `render()` instead.
155 */
156 public function getSelector() : string
157 {
158 return $this->render(new OutputFormat());
159 }
160 /**
161 * @param non-empty-string $selector
162 *
163 * @throws UnexpectedTokenException if the selector is not valid
164 *
165 * @deprecated in version 9.2, will be removed in v10.0. Use `setComponents()` instead.
166 */
167 public function setSelector(string $selector) : void
168 {
169 $parserState = new ParserState($selector, Settings::create());
170 $components = self::parseComponents($parserState);
171 // Check that the selector has been fully parsed:
172 if (!$parserState->isEnd()) {
173 throw new UnexpectedTokenException('EOF', $parserState->peek(5), 'literal');
174 }
175 $this->components = $components;
176 }
177 /**
178 * @return int<0, max>
179 */
180 public function getSpecificity() : int
181 {
182 return \array_sum(\array_map(static function (Component $component) : int {
183 return $component->getSpecificity();
184 }, $this->components));
185 }
186 public function render(OutputFormat $outputFormat) : string
187 {
188 return \implode('', \array_map(static function (Component $component) use($outputFormat) : string {
189 return $component->render($outputFormat);
190 }, $this->components));
191 }
192 /**
193 * @return array<string, bool|int|float|string|array<mixed>|null>
194 *
195 * @internal
196 */
197 public function getArrayRepresentation() : array
198 {
199 return ['class' => $this->getShortClassName(), 'components' => \array_map(static function (Component $component) : array {
200 return $component->getArrayRepresentation();
201 }, $this->components)];
202 }
203 }
204