PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.3
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.3
2.4.3 2.4.4 2.4.2 2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 All 60 releases
easy-invoice / vendor / sabberworm / php-css-parser / src / RuleSet / RuleSet.php

RuleSet.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.3, at vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php

358 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Sabberworm\CSS\RuleSet;
4
5 use Sabberworm\CSS\Comment\Comment;
6 use Sabberworm\CSS\Comment\Commentable;
7 use Sabberworm\CSS\CSSElement;
8 use Sabberworm\CSS\OutputFormat;
9 use Sabberworm\CSS\Parsing\ParserState;
10 use Sabberworm\CSS\Parsing\UnexpectedEOFException;
11 use Sabberworm\CSS\Parsing\UnexpectedTokenException;
12 use Sabberworm\CSS\Position\Position;
13 use Sabberworm\CSS\Position\Positionable;
14 use Sabberworm\CSS\Renderable;
15 use Sabberworm\CSS\Rule\Rule;
16
17 /**
18 * This class is a container for individual 'Rule'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`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)`
24 * (which accepts either a `Rule` or a rule name; optionally suffixed by a dash to remove all related rules).
25 */
26 abstract class RuleSet implements CSSElement, Commentable, Positionable
27 {
28 use Position;
29
30 /**
31 * the rules in this rule set, using the property name as the key,
32 * with potentially multiple rules per property name.
33 *
34 * @var array<string, array<int<0, max>, Rule>>
35 */
36 private $aRules;
37
38 /**
39 * @var array<array-key, Comment>
40 *
41 * @internal since 8.8.0
42 */
43 protected $aComments;
44
45 /**
46 * @param int $iLineNo
47 */
48 public function __construct($iLineNo = 0)
49 {
50 $this->aRules = [];
51 $this->setPosition($iLineNo);
52 $this->aComments = [];
53 }
54
55 /**
56 * @return void
57 *
58 * @throws UnexpectedTokenException
59 * @throws UnexpectedEOFException
60 *
61 * @internal since V8.8.0
62 */
63 public static function parseRuleSet(ParserState $oParserState, RuleSet $oRuleSet)
64 {
65 while ($oParserState->comes(';')) {
66 $oParserState->consume(';');
67 }
68 while (true) {
69 $commentsBeforeRule = $oParserState->consumeWhiteSpace();
70 if ($oParserState->comes('}')) {
71 break;
72 }
73 $oRule = null;
74 if ($oParserState->getSettings()->bLenientParsing) {
75 try {
76 $oRule = Rule::parse($oParserState, $commentsBeforeRule);
77 } catch (UnexpectedTokenException $e) {
78 try {
79 $sConsume = $oParserState->consumeUntil(["\n", ";", '}'], true);
80 // We need to “unfind” the matches to the end of the ruleSet as this will be matched later
81 if ($oParserState->streql(substr($sConsume, -1), '}')) {
82 $oParserState->backtrack(1);
83 } else {
84 while ($oParserState->comes(';')) {
85 $oParserState->consume(';');
86 }
87 }
88 } catch (UnexpectedTokenException $e) {
89 // We’ve reached the end of the document. Just close the RuleSet.
90 return;
91 }
92 }
93 } else {
94 $oRule = Rule::parse($oParserState, $commentsBeforeRule);
95 }
96 if ($oRule) {
97 $oRuleSet->addRule($oRule);
98 }
99 }
100 $oParserState->consume('}');
101 }
102
103 /**
104 * @param Rule|null $oSibling
105 *
106 * @return void
107 */
108 public function addRule(Rule $oRule, $oSibling = null)
109 {
110 $sRule = $oRule->getRule();
111 if (!isset($this->aRules[$sRule])) {
112 $this->aRules[$sRule] = [];
113 }
114
115 $iPosition = count($this->aRules[$sRule]);
116
117 if ($oSibling !== null) {
118 $iSiblingPos = array_search($oSibling, $this->aRules[$sRule], true);
119 if ($iSiblingPos !== false) {
120 $iPosition = $iSiblingPos;
121 $oRule->setPosition($oSibling->getLineNo(), $oSibling->getColNo() - 1);
122 }
123 }
124 if ($oRule->getLineNumber() === null) {
125 //this node is added manually, give it the next best line
126 $columnNumber = $oRule->getColNo();
127 $rules = $this->getRules();
128 $pos = count($rules);
129 if ($pos > 0) {
130 $last = $rules[$pos - 1];
131 $oRule->setPosition($last->getLineNo() + 1, $columnNumber);
132 } else {
133 $oRule->setPosition(1, $columnNumber);
134 }
135 } elseif ($oRule->getColumnNumber() === null) {
136 $oRule->setPosition($oRule->getLineNumber(), 0);
137 }
138
139 array_splice($this->aRules[$sRule], $iPosition, 0, [$oRule]);
140 }
141
142 /**
143 * Returns all rules matching the given rule name
144 *
145 * @example $oRuleSet->getRules('font') // returns array(0 => $oRule, …) or array().
146 *
147 * @example $oRuleSet->getRules('font-')
148 * //returns an array of all rules either beginning with font- or matching font.
149 *
150 * @param Rule|string|null $mRule
151 * Pattern to search for. If null, returns all rules.
152 * If the pattern ends with a dash, all rules starting with the pattern are returned
153 * as well as one matching the pattern with the dash excluded.
154 * Passing a `Rule` for this parameter is deprecated in version 8.9.0, and will not work from v9.0.
155 * Call `getRules($rule->getRule())` instead.
156 *
157 * @return array<int, Rule>
158 */
159 public function getRules($mRule = null)
160 {
161 if ($mRule instanceof Rule) {
162 $mRule = $mRule->getRule();
163 }
164 /** @var array<int, Rule> $aResult */
165 $aResult = [];
166 foreach ($this->aRules as $sName => $aRules) {
167 // Either no search rule is given or the search rule matches the found rule exactly
168 // or the search rule ends in “-” and the found rule starts with the search rule.
169 if (
170 !$mRule || $sName === $mRule
171 || (
172 strrpos($mRule, '-') === strlen($mRule) - strlen('-')
173 && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1))
174 )
175 ) {
176 $aResult = array_merge($aResult, $aRules);
177 }
178 }
179 usort($aResult, function (Rule $first, Rule $second) {
180 if ($first->getLineNo() === $second->getLineNo()) {
181 return $first->getColNo() - $second->getColNo();
182 }
183 return $first->getLineNo() - $second->getLineNo();
184 });
185 return $aResult;
186 }
187
188 /**
189 * Overrides all the rules of this set.
190 *
191 * @param array<array-key, Rule> $aRules The rules to override with.
192 *
193 * @return void
194 */
195 public function setRules(array $aRules)
196 {
197 $this->aRules = [];
198 foreach ($aRules as $rule) {
199 $this->addRule($rule);
200 }
201 }
202
203 /**
204 * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name
205 * as keys. This method exists mainly for backwards-compatibility and is really only partially useful.
206 *
207 * Note: This method loses some information: Calling this (with an argument of `background-`) on a declaration block
208 * like `{ background-color: green; background-color; rgba(0, 127, 0, 0.7); }` will only yield an associative array
209 * containing the rgba-valued rule while `getRules()` would yield an indexed array containing both.
210 *
211 * @param Rule|string|null $mRule $mRule
212 * Pattern to search for. If null, returns all rules. If the pattern ends with a dash,
213 * all rules starting with the pattern are returned as well as one matching the pattern with the dash
214 * excluded.
215 * Passing a `Rule` for this parameter is deprecated in version 8.9.0, and will not work from v9.0.
216 * Call `getRulesAssoc($rule->getRule())` instead.
217 *
218 * @return array<string, Rule>
219 */
220 public function getRulesAssoc($mRule = null)
221 {
222 /** @var array<string, Rule> $aResult */
223 $aResult = [];
224 foreach ($this->getRules($mRule) as $oRule) {
225 $aResult[$oRule->getRule()] = $oRule;
226 }
227 return $aResult;
228 }
229
230 /**
231 * Removes a `Rule` from this `RuleSet` by identity.
232 *
233 * @param Rule|string|null $mRule
234 * `Rule` to remove.
235 * Passing a `string` or `null` is deprecated in version 8.9.0, and will no longer work from v9.0.
236 * Use `removeMatchingRules()` or `removeAllRules()` instead.
237 */
238 public function removeRule($mRule)
239 {
240 if ($mRule instanceof Rule) {
241 $sRule = $mRule->getRule();
242 if (!isset($this->aRules[$sRule])) {
243 return;
244 }
245 foreach ($this->aRules[$sRule] as $iKey => $oRule) {
246 if ($oRule === $mRule) {
247 unset($this->aRules[$sRule][$iKey]);
248 }
249 }
250 } elseif ($mRule !== null) {
251 $this->removeMatchingRules($mRule);
252 } else {
253 $this->removeAllRules();
254 }
255 }
256
257 /**
258 * Removes rules by property name or search pattern.
259 *
260 * @param string $searchPattern
261 * pattern to remove.
262 * If the pattern ends in a dash,
263 * all rules starting with the pattern are removed as well as one matching the pattern with the dash
264 * excluded.
265 */
266 public function removeMatchingRules($searchPattern)
267 {
268 foreach ($this->aRules as $propertyName => $rules) {
269 // Either the search rule matches the found rule exactly
270 // or the search rule ends in “-” and the found rule starts with the search rule or equals it
271 // (without the trailing dash).
272 if (
273 $propertyName === $searchPattern
274 || (\strrpos($searchPattern, '-') === \strlen($searchPattern) - \strlen('-')
275 && (\strpos($propertyName, $searchPattern) === 0
276 || $propertyName === \substr($searchPattern, 0, -1)))
277 ) {
278 unset($this->aRules[$propertyName]);
279 }
280 }
281 }
282
283 public function removeAllRules()
284 {
285 $this->aRules = [];
286 }
287
288 /**
289 * @return string
290 *
291 * @deprecated in V8.8.0, will be removed in V9.0.0. Use `render` instead.
292 */
293 public function __toString()
294 {
295 return $this->render(new OutputFormat());
296 }
297
298 /**
299 * @return string
300 */
301 protected function renderRules(OutputFormat $oOutputFormat)
302 {
303 $sResult = '';
304 $bIsFirst = true;
305 $oNextLevel = $oOutputFormat->nextLevel();
306 foreach ($this->getRules() as $oRule) {
307 $sRendered = $oNextLevel->safely(function () use ($oRule, $oNextLevel) {
308 return $oRule->render($oNextLevel);
309 });
310 if ($sRendered === null) {
311 continue;
312 }
313 if ($bIsFirst) {
314 $bIsFirst = false;
315 $sResult .= $oNextLevel->spaceBeforeRules();
316 } else {
317 $sResult .= $oNextLevel->spaceBetweenRules();
318 }
319 $sResult .= $sRendered;
320 }
321
322 if (!$bIsFirst) {
323 // Had some output
324 $sResult .= $oOutputFormat->spaceAfterRules();
325 }
326
327 return $oOutputFormat->removeLastSemicolon($sResult);
328 }
329
330 /**
331 * @param array<string, Comment> $aComments
332 *
333 * @return void
334 */
335 public function addComments(array $aComments)
336 {
337 $this->aComments = array_merge($this->aComments, $aComments);
338 }
339
340 /**
341 * @return array<string, Comment>
342 */
343 public function getComments()
344 {
345 return $this->aComments;
346 }
347
348 /**
349 * @param array<string, Comment> $aComments
350 *
351 * @return void
352 */
353 public function setComments(array $aComments)
354 {
355 $this->aComments = $aComments;
356 }
357 }
358