PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.0
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
← All changes | back/Libraries/Sabberworm/CSS/src/Value/Value.php +211 -0 10.0.0 → 11.1.0 View file →
@@ -1,0 +1,211 @@
1 +<?php
2 +
3 +declare(strict_types=1);
4 +
5 +namespace AcyMailing\Libraries\Sabberworm\CSS\Value;
6 +
7 +use AcyMailing\Libraries\Sabberworm\CSS\CSSElement;
8 +use AcyMailing\Libraries\Sabberworm\CSS\Parsing\ParserState;
9 +use AcyMailing\Libraries\Sabberworm\CSS\Parsing\SourceException;
10 +use AcyMailing\Libraries\Sabberworm\CSS\Parsing\UnexpectedEOFException;
11 +use AcyMailing\Libraries\Sabberworm\CSS\Parsing\UnexpectedTokenException;
12 +use AcyMailing\Libraries\Sabberworm\CSS\Position\Position;
13 +use AcyMailing\Libraries\Sabberworm\CSS\Position\Positionable;
14 +
15 +/**
16 + * Abstract base class for specific classes of CSS values: `Size`, `Color`, `CSSString` and `URL`, and another
17 + * abstract subclass `ValueList`.
18 + */
19 +abstract class Value implements CSSElement, Positionable
20 +{
21 + use Position;
22 +
23 + /**
24 + * @param int<1, max>|null $lineNumber
25 + */
26 + public function __construct(?int $lineNumber = null)
27 + {
28 + $this->setPosition($lineNumber);
29 + }
30 +
31 + /**
32 + * @param array<non-empty-string> $listDelimiters
33 + *
34 + * @return Value|string
35 + *
36 + * @throws UnexpectedTokenException
37 + * @throws UnexpectedEOFException
38 + *
39 + * @internal since V8.8.0
40 + */
41 + public static function parseValue(ParserState $parserState, array $listDelimiters = [])
42 + {
43 + /** @var list<Value|string> $stack */
44 + $stack = [];
45 + $parserState->consumeWhiteSpace();
46 + //Build a list of delimiters and parsed values
47 + while (
48 + !($parserState->comes('}') || $parserState->comes(';') || $parserState->comes('!')
49 + || $parserState->comes(')')
50 + || $parserState->isEnd())
51 + ) {
52 + if (\count($stack) > 0) {
53 + $foundDelimiter = false;
54 + foreach ($listDelimiters as $delimiter) {
55 + if ($parserState->comes($delimiter)) {
56 + \array_push($stack, $parserState->consume($delimiter));
57 + $parserState->consumeWhiteSpace();
58 + $foundDelimiter = true;
59 + break;
60 + }
61 + }
62 + if (!$foundDelimiter) {
63 + //Whitespace was the list delimiter
64 + \array_push($stack, ' ');
65 + }
66 + }
67 + \array_push($stack, self::parsePrimitiveValue($parserState));
68 + $parserState->consumeWhiteSpace();
69 + }
70 + // Convert the list to list objects
71 + foreach ($listDelimiters as $delimiter) {
72 + $stackSize = \count($stack);
73 + if ($stackSize === 1) {
74 + return $stack[0];
75 + }
76 + $newStack = [];
77 + for ($offset = 0; $offset < $stackSize; ++$offset) {
78 + if ($offset === ($stackSize - 1) || $delimiter !== $stack[$offset + 1]) {
79 + $newStack[] = $stack[$offset];
80 + continue;
81 + }
82 + $length = 2; //Number of elements to be joined
83 + for ($i = $offset + 3; $i < $stackSize; $i += 2, ++$length) {
84 + if ($delimiter !== $stack[$i]) {
85 + break;
86 + }
87 + }
88 + $list = new RuleValueList($delimiter, $parserState->currentLine());
89 + for ($i = $offset; $i - $offset < $length * 2; $i += 2) {
90 + $list->addListComponent($stack[$i]);
91 + }
92 + $newStack[] = $list;
93 + $offset += $length * 2 - 2;
94 + }
95 + $stack = $newStack;
96 + }
97 + if (!isset($stack[0])) {
98 + throw new UnexpectedTokenException(
99 + " {$parserState->peek()} ",
100 + $parserState->peek(1, -1) . $parserState->peek(2),
101 + 'literal',
102 + $parserState->currentLine()
103 + );
104 + }
105 + return $stack[0];
106 + }
107 +
108 + /**
109 + * @return CSSFunction|string
110 + *
111 + * @throws UnexpectedEOFException
112 + * @throws UnexpectedTokenException
113 + *
114 + * @internal since V8.8.0
115 + */
116 + public static function parseIdentifierOrFunction(ParserState $parserState, bool $ignoreCase = false)
117 + {
118 + $anchor = $parserState->anchor();
119 + $result = $parserState->parseIdentifier($ignoreCase);
120 +
121 + if ($parserState->comes('(')) {
122 + $anchor->backtrack();
123 + if ($parserState->streql('url', $result)) {
124 + $result = URL::parse($parserState);
125 + } elseif ($parserState->streql('calc', $result)) {
126 + $result = CalcFunction::parse($parserState);
127 + } else {
128 + $result = CSSFunction::parse($parserState, $ignoreCase);
129 + }
130 + }
131 +
132 + return $result;
133 + }
134 +
135 + /**
136 + * @return CSSFunction|CSSString|LineName|Size|URL|string
137 + *
138 + * @throws UnexpectedEOFException
139 + * @throws UnexpectedTokenException
140 + * @throws SourceException
141 + *
142 + * @internal since V8.8.0
143 + */
144 + public static function parsePrimitiveValue(ParserState $parserState)
145 + {
146 + $value = null;
147 + $parserState->consumeWhiteSpace();
148 + if (
149 + \is_numeric($parserState->peek())
150 + || ($parserState->comes('-.')
151 + && \is_numeric($parserState->peek(1, 2)))
152 + || (($parserState->comes('-') || $parserState->comes('.')) && \is_numeric($parserState->peek(1, 1)))
153 + ) {
154 + $value = Size::parse($parserState);
155 + } elseif ($parserState->comes('#') || $parserState->comes('rgb', true) || $parserState->comes('hsl', true)) {
156 + $value = Color::parse($parserState);
157 + } elseif ($parserState->comes("'") || $parserState->comes('"')) {
158 + $value = CSSString::parse($parserState);
159 + } elseif ($parserState->comes('progid:') && $parserState->getSettings()->usesLenientParsing()) {
160 + $value = self::parseMicrosoftFilter($parserState);
161 + } elseif ($parserState->comes('[')) {
162 + $value = LineName::parse($parserState);
163 + } elseif ($parserState->comes('U+')) {
164 + $value = self::parseUnicodeRangeValue($parserState);
165 + } else {
166 + $nextCharacter = $parserState->peek(1);
167 + try {
168 + $value = self::parseIdentifierOrFunction($parserState);
169 + } catch (UnexpectedTokenException $e) {
170 + if (\in_array($nextCharacter, ['+', '-', '*', '/'], true)) {
171 + $value = $parserState->consume(1);
172 + } else {
173 + throw $e;
174 + }
175 + }
176 + }
177 + $parserState->consumeWhiteSpace();
178 +
179 + return $value;
180 + }
181 +
182 + /**
183 + * @throws UnexpectedEOFException
184 + * @throws UnexpectedTokenException
185 + */
186 + private static function parseMicrosoftFilter(ParserState $parserState): CSSFunction
187 + {
188 + $function = $parserState->consumeUntil('(', false, true);
189 + $arguments = Value::parseValue($parserState, [',', '=']);
190 + return new CSSFunction($function, $arguments, ',', $parserState->currentLine());
191 + }
192 +
193 + /**
194 + * @throws UnexpectedEOFException
195 + * @throws UnexpectedTokenException
196 + */
197 + private static function parseUnicodeRangeValue(ParserState $parserState): string
198 + {
199 + $codepointMaxLength = 6; // Code points outside BMP can use up to six digits
200 + $range = '';
201 + $parserState->consume('U+');
202 + do {
203 + if ($parserState->comes('-')) {
204 + $codepointMaxLength = 13; // Max length is 2 six-digit code points + the dash(-) between them
205 + }
206 + $range .= $parserState->consume(1);
207 + } while (\strlen($range) < $codepointMaxLength && \preg_match('/[A-Fa-f0-9\\?-]/', $parserState->peek()));
208 +
209 + return "U+{$range}";
210 + }
211 +}