PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.4
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.4
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
acymailing / back / Libraries / Sabberworm / CSS / src / Value / Value.php

Value.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.4, at back/Libraries/Sabberworm/CSS/src/Value/Value.php

212 lines 7.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
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 }
212