PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / trunk
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress vtrunk
4.17.2 4.17.1 4.17.0 4.16.19 4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / third-party / vendor / sabberworm / php-css-parser / src / RuleSet / RuleSet.php
wp-user-avatar / third-party / vendor / sabberworm / php-css-parser / src / RuleSet Last commit date
AtRuleSet.php 5 days ago DeclarationBlock.php 5 days ago DeclarationList.php 5 days ago LegacyDeclarationListMethods.php 5 days ago RuleContainer.php 5 days ago RuleSet.php 5 days ago
RuleSet.php
346 lines
1 <?php
2
3 declare (strict_types=1);
4 namespace ProfilePressVendor\Sabberworm\CSS\RuleSet;
5
6 use ProfilePressVendor\Sabberworm\CSS\Comment\CommentContainer;
7 use ProfilePressVendor\Sabberworm\CSS\CSSElement;
8 use ProfilePressVendor\Sabberworm\CSS\CSSList\CSSListItem;
9 use ProfilePressVendor\Sabberworm\CSS\OutputFormat;
10 use ProfilePressVendor\Sabberworm\CSS\Parsing\ParserState;
11 use ProfilePressVendor\Sabberworm\CSS\Parsing\UnexpectedEOFException;
12 use ProfilePressVendor\Sabberworm\CSS\Parsing\UnexpectedTokenException;
13 use ProfilePressVendor\Sabberworm\CSS\Position\Position;
14 use ProfilePressVendor\Sabberworm\CSS\Position\Positionable;
15 use ProfilePressVendor\Sabberworm\CSS\Property\Declaration;
16 use ProfilePressVendor\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