PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.18
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.18
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 / RuleSet / RuleSet.php

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

346 lines 15.5 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\RuleSet;
5
6 use WCPOS\Vendor\Sabberworm\CSS\Comment\CommentContainer;
7 use WCPOS\Vendor\Sabberworm\CSS\CSSElement;
8 use WCPOS\Vendor\Sabberworm\CSS\CSSList\CSSListItem;
9 use WCPOS\Vendor\Sabberworm\CSS\OutputFormat;
10 use WCPOS\Vendor\Sabberworm\CSS\Parsing\ParserState;
11 use WCPOS\Vendor\Sabberworm\CSS\Parsing\UnexpectedEOFException;
12 use WCPOS\Vendor\Sabberworm\CSS\Parsing\UnexpectedTokenException;
13 use WCPOS\Vendor\Sabberworm\CSS\Position\Position;
14 use WCPOS\Vendor\Sabberworm\CSS\Position\Positionable;
15 use WCPOS\Vendor\Sabberworm\CSS\Property\Declaration;
16 use WCPOS\Vendor\Sabberworm\CSS\ShortClassNameProvider;
17 /**
18 * This class is a container for individual `Declaration`s.
19 *
20 * The most common form of a rule set is one constrained by a selector, i.e., a `DeclarationBlock`.
21 * However, unknown `AtRule`s (like `@font-face`) are rule sets as well.
22 *
23 * If you want to manipulate a `RuleSet`,
24 * use the methods `addDeclaration()`, `getDeclarations()`, `removeDeclaration()`, `removeMatchingDeclarations()`, etc.
25 *
26 * Note that `CSSListItem` extends both `Commentable` and `Renderable`, so those interfaces must also be implemented.
27 */
28 class RuleSet implements CSSElement, CSSListItem, Positionable, DeclarationList
29 {
30 use CommentContainer;
31 use LegacyDeclarationListMethods;
32 use Position;
33 use ShortClassNameProvider;
34 /**
35 * the declarations in this rule set, using the property name as the key,
36 * with potentially multiple declarations per property name.
37 *
38 * @var array<string, array<int<0, max>, Declaration>>
39 */
40 private $declarations = [];
41 /**
42 * @param int<1, max>|null $lineNumber
43 */
44 public function __construct(?int $lineNumber = null)
45 {
46 $this->setPosition($lineNumber);
47 }
48 /**
49 * @throws UnexpectedTokenException
50 * @throws UnexpectedEOFException
51 *
52 * @internal since V8.8.0
53 */
54 public static function parseRuleSet(ParserState $parserState, RuleSet $ruleSet) : void
55 {
56 while ($parserState->comes(';')) {
57 $parserState->consume(';');
58 }
59 while (\true) {
60 $commentsBeforeDeclaration = [];
61 $parserState->consumeWhiteSpace($commentsBeforeDeclaration);
62 if ($parserState->comes('}')) {
63 break;
64 }
65 $declaration = null;
66 if ($parserState->getSettings()->usesLenientParsing()) {
67 try {
68 $declaration = Declaration::parse($parserState, $commentsBeforeDeclaration);
69 } catch (UnexpectedTokenException $e) {
70 try {
71 $consumedText = $parserState->consumeUntil(["\n", ';', '}'], \true);
72 // We need to “unfind” the matches to the end of the ruleSet as this will be matched later
73 if ($parserState->streql(\substr($consumedText, -1), '}')) {
74 $parserState->backtrack(1);
75 } else {
76 while ($parserState->comes(';')) {
77 $parserState->consume(';');
78 }
79 }
80 } catch (UnexpectedTokenException $e) {
81 // We’ve reached the end of the document. Just close the RuleSet.
82 return;
83 }
84 }
85 } else {
86 $declaration = Declaration::parse($parserState, $commentsBeforeDeclaration);
87 }
88 if ($declaration instanceof Declaration) {
89 $ruleSet->addDeclaration($declaration);
90 }
91 }
92 $parserState->consume('}');
93 }
94 /**
95 * @throws \UnexpectedValueException
96 * if the last `Declaration` is needed as a basis for setting position, but does not have a valid position,
97 * which should never happen
98 */
99 public function addDeclaration(Declaration $declarationToAdd, ?Declaration $sibling = null) : void
100 {
101 $propertyName = $declarationToAdd->getPropertyName();
102 if (!isset($this->declarations[$propertyName])) {
103 $this->declarations[$propertyName] = [];
104 }
105 $position = \count($this->declarations[$propertyName]);
106 if ($sibling !== null) {
107 $siblingPosition = \array_search($sibling, $this->declarations[$propertyName], \true);
108 if ($siblingPosition !== \false) {
109 $siblingIsInSet = \true;
110 $position = $siblingPosition;
111 } else {
112 $siblingIsInSet = $this->hasDeclaration($sibling);
113 if ($siblingIsInSet) {
114 // Maintain ordering within `$this->declarations[$propertyName]`
115 // by inserting before first `Declaration` with a same-or-later position than the sibling.
116 foreach ($this->declarations[$propertyName] as $index => $declaration) {
117 if (self::comparePositionable($declaration, $sibling) >= 0) {
118 $position = $index;
119 break;
120 }
121 }
122 }
123 }
124 if ($siblingIsInSet) {
125 // Increment column number of all existing declarations on same line, starting at sibling
126 $siblingLineNumber = $sibling->getLineNumber();
127 $siblingColumnNumber = $sibling->getColumnNumber();
128 foreach ($this->declarations as $declarationsForAProperty) {
129 foreach ($declarationsForAProperty as $declaration) {
130 if ($declaration->getLineNumber() === $siblingLineNumber && $declaration->getColumnNumber() >= $siblingColumnNumber) {
131 $declaration->setPosition($siblingLineNumber, $declaration->getColumnNumber() + 1);
132 }
133 }
134 }
135 $declarationToAdd->setPosition($siblingLineNumber, $siblingColumnNumber);
136 }
137 }
138 if ($declarationToAdd->getLineNumber() === null) {
139 //this node is added manually, give it the next best line
140 $columnNumber = $declarationToAdd->getColumnNumber() ?? 0;
141 $declarations = $this->getDeclarations();
142 $declarationsCount = \count($declarations);
143 if ($declarationsCount > 0) {
144 $last = $declarations[$declarationsCount - 1];
145 $lastsLineNumber = $last->getLineNumber();
146 if (!\is_int($lastsLineNumber)) {
147 throw new \UnexpectedValueException('A Declaration without a line number was found during addDeclaration', 1750718399);
148 }
149 $declarationToAdd->setPosition($lastsLineNumber + 1, $columnNumber);
150 } else {
151 $declarationToAdd->setPosition(1, $columnNumber);
152 }
153 } elseif ($declarationToAdd->getColumnNumber() === null) {
154 $declarationToAdd->setPosition($declarationToAdd->getLineNumber(), 0);
155 }
156 \array_splice($this->declarations[$propertyName], $position, 0, [$declarationToAdd]);
157 }
158 /**
159 * Returns all declarations matching the given property name
160 *
161 * @example $ruleSet->getDeclarations('font') // returns array(0 => $declaration, …) or array().
162 *
163 * @example $ruleSet->getDeclarations('font-')
164 * //returns an array of all declarations either beginning with font- or matching font.
165 *
166 * @param string|null $searchPattern
167 * Pattern to search for. If null, returns all declarations.
168 * If the pattern ends with a dash, all declarations starting with the pattern are returned
169 * as well as one matching the pattern with the dash excluded.
170 *
171 * @return array<int<0, max>, Declaration>
172 */
173 public function getDeclarations(?string $searchPattern = null) : array
174 {
175 $result = [];
176 foreach ($this->declarations as $propertyName => $declarations) {
177 // Either no search pattern was given
178 // or the search pattern matches the found declaration's property name exactly
179 // or the search pattern ends in “-”
180 // ... and the found declaration's property name starts with the search pattern
181 if ($searchPattern === null || $propertyName === $searchPattern || \strrpos($searchPattern, '-') === \strlen($searchPattern) - \strlen('-') && (\strpos($propertyName, $searchPattern) === 0 || $propertyName === \substr($searchPattern, 0, -1))) {
182 $result = \array_merge($result, $declarations);
183 }
184 }
185 \usort($result, [self::class, 'comparePositionable']);
186 return $result;
187 }
188 /**
189 * Overrides all the declarations of this set.
190 *
191 * @param array<Declaration> $declarations
192 */
193 public function setDeclarations(array $declarations) : void
194 {
195 $this->declarations = [];
196 foreach ($declarations as $declaration) {
197 $this->addDeclaration($declaration);
198 }
199 }
200 /**
201 * Returns all declarations with property names matching the given pattern and returns them in an associative array
202 * with the property names as keys.
203 * This method exists mainly for backwards-compatibility and is really only partially useful.
204 *
205 * Note: This method loses some information: Calling this (with an argument of `background-`) on a declaration block
206 * like `{ background-color: green; background-color; rgba(0, 127, 0, 0.7); }` will only yield an associative array
207 * containing the rgba-valued declaration while `getDeclarations()` would yield an indexed array containing both.
208 *
209 * @param string|null $searchPattern
210 * Pattern to search for. If null, returns all declarations. If the pattern ends with a dash,
211 * all declarations starting with the pattern are returned as well as one matching the pattern with the dash
212 * excluded.
213 *
214 * @return array<string, Declaration>
215 */
216 public function getDeclarationsAssociative(?string $searchPattern = null) : array
217 {
218 /** @var array<string, Declaration> $result */
219 $result = [];
220 foreach ($this->getDeclarations($searchPattern) as $declaration) {
221 $result[$declaration->getPropertyName()] = $declaration;
222 }
223 return $result;
224 }
225 /**
226 * Removes a `Declaration` from this `RuleSet` by identity.
227 */
228 public function removeDeclaration(Declaration $declarationToRemove) : void
229 {
230 $nameOfPropertyToRemove = $declarationToRemove->getPropertyName();
231 if (!isset($this->declarations[$nameOfPropertyToRemove])) {
232 return;
233 }
234 foreach ($this->declarations[$nameOfPropertyToRemove] as $key => $declaration) {
235 if ($declaration === $declarationToRemove) {
236 unset($this->declarations[$nameOfPropertyToRemove][$key]);
237 }
238 }
239 }
240 /**
241 * Removes declarations by property name or search pattern.
242 *
243 * @param string $searchPattern
244 * pattern to remove.
245 * If the pattern ends in a dash,
246 * all declarations starting with the pattern are removed as well as one matching the pattern with the dash
247 * excluded.
248 */
249 public function removeMatchingDeclarations(string $searchPattern) : void
250 {
251 foreach ($this->declarations as $propertyName => $declarations) {
252 // Either the search pattern matches the found declaration's property name exactly
253 // or the search pattern ends in “-” and the found declaration's property name starts with the search
254 // pattern or equals it (without the trailing dash).
255 if ($propertyName === $searchPattern || \strrpos($searchPattern, '-') === \strlen($searchPattern) - \strlen('-') && (\strpos($propertyName, $searchPattern) === 0 || $propertyName === \substr($searchPattern, 0, -1))) {
256 unset($this->declarations[$propertyName]);
257 }
258 }
259 }
260 public function removeAllDeclarations() : void
261 {
262 $this->declarations = [];
263 }
264 /**
265 * @internal
266 */
267 public function render(OutputFormat $outputFormat) : string
268 {
269 return $this->renderDeclarations($outputFormat);
270 }
271 protected function renderDeclarations(OutputFormat $outputFormat) : string
272 {
273 $result = '';
274 $isFirst = \true;
275 $nextLevelFormat = $outputFormat->nextLevel();
276 foreach ($this->getDeclarations() as $declaration) {
277 $nextLevelFormatter = $nextLevelFormat->getFormatter();
278 $renderedDeclaration = $nextLevelFormatter->safely(static function () use($declaration, $nextLevelFormat) : string {
279 return $declaration->render($nextLevelFormat);
280 });
281 if ($renderedDeclaration === null) {
282 continue;
283 }
284 if ($isFirst) {
285 $isFirst = \false;
286 $result .= $nextLevelFormatter->spaceBeforeRules();
287 } else {
288 $result .= $nextLevelFormatter->spaceBetweenRules();
289 }
290 $result .= $renderedDeclaration;
291 }
292 $formatter = $outputFormat->getFormatter();
293 if (!$isFirst) {
294 // Had some output
295 $result .= $formatter->spaceAfterRules();
296 }
297 return $formatter->removeLastSemicolon($result);
298 }
299 /**
300 * @return array<string, bool|int|float|string|array<mixed>|null>
301 *
302 * @internal
303 */
304 public function getArrayRepresentation() : array
305 {
306 $declarationsArrayRepresentation = [];
307 foreach ($this->declarations as $propertyName => $declarationsForOneProperty) {
308 $declarationsArrayRepresentation[$propertyName] = \array_map(function (Declaration $declaration) : array {
309 return $declaration->getArrayRepresentation();
310 }, $declarationsForOneProperty);
311 }
312 return ['class' => $this->getShortClassName(), 'declarations' => $declarationsArrayRepresentation];
313 }
314 /**
315 * @return int negative if `$first` is before `$second`; zero if they have the same position; positive otherwise
316 *
317 * @throws \UnexpectedValueException if either argument does not have a valid position, which should never happen
318 */
319 private static function comparePositionable(Positionable $first, Positionable $second) : int
320 {
321 $firstsLineNumber = $first->getLineNumber();
322 $secondsLineNumber = $second->getLineNumber();
323 if (!\is_int($firstsLineNumber) || !\is_int($secondsLineNumber)) {
324 throw new \UnexpectedValueException('A Declaration without a line number was passed to comparePositionable', 1750637683);
325 }
326 if ($firstsLineNumber === $secondsLineNumber) {
327 $firstsColumnNumber = $first->getColumnNumber();
328 $secondsColumnNumber = $second->getColumnNumber();
329 if (!\is_int($firstsColumnNumber) || !\is_int($secondsColumnNumber)) {
330 throw new \UnexpectedValueException('A Declaration without a column number was passed to comparePositionable', 1750637761);
331 }
332 return $firstsColumnNumber - $secondsColumnNumber;
333 }
334 return $firstsLineNumber - $secondsLineNumber;
335 }
336 private function hasDeclaration(Declaration $declaration) : bool
337 {
338 foreach ($this->declarations as $declarationsForAProperty) {
339 if (\in_array($declaration, $declarationsForAProperty, \true)) {
340 return \true;
341 }
342 }
343 return \false;
344 }
345 }
346