• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MyIntervals / PHP-CSS-Parser / 13574263130

27 Feb 2025 07:24PM UTC coverage: 54.655%. Remained the same
13574263130

push

github

web-flow
[CLEANUP] Avoid Hungarian notation for `rules` (#1024)

Part of #756

0 of 17 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

1045 of 1912 relevant lines covered (54.65%)

12.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/RuleSet/RuleSet.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Sabberworm\CSS\RuleSet;
6

7
use Sabberworm\CSS\Comment\Comment;
8
use Sabberworm\CSS\Comment\Commentable;
9
use Sabberworm\CSS\OutputFormat;
10
use Sabberworm\CSS\Parsing\ParserState;
11
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
12
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
13
use Sabberworm\CSS\Renderable;
14
use Sabberworm\CSS\Rule\Rule;
15

16
/**
17
 * This class is a container for individual 'Rule's.
18
 *
19
 * The most common form of a rule set is one constrained by a selector, i.e., a `DeclarationBlock`.
20
 * However, unknown `AtRule`s (like `@font-face`) are rule sets as well.
21
 *
22
 * If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)`
23
 * (which accepts either a `Rule` or a rule name; optionally suffixed by a dash to remove all related rules).
24
 */
25
abstract class RuleSet implements Renderable, Commentable
26
{
27
    /**
28
     * @var array<string, Rule>
29
     */
30
    private $rules = [];
31

32
    /**
33
     * @var int<0, max>
34
     *
35
     * @internal since 8.8.0
36
     */
37
    protected $lineNumber;
38

39
    /**
40
     * @var array<array-key, Comment>
41
     *
42
     * @internal since 8.8.0
43
     */
44
    protected $comments = [];
45

46
    /**
47
     * @param int<0, max> $lineNumber
48
     */
49
    public function __construct($lineNumber = 0)
×
50
    {
51
        $this->lineNumber = $lineNumber;
×
52
    }
×
53

54
    /**
55
     * @throws UnexpectedTokenException
56
     * @throws UnexpectedEOFException
57
     *
58
     * @internal since V8.8.0
59
     */
60
    public static function parseRuleSet(ParserState $parserState, RuleSet $ruleSet): void
×
61
    {
62
        while ($parserState->comes(';')) {
×
63
            $parserState->consume(';');
×
64
        }
65
        while (!$parserState->comes('}')) {
×
66
            $rule = null;
×
67
            if ($parserState->getSettings()->usesLenientParsing()) {
×
68
                try {
69
                    $rule = Rule::parse($parserState);
×
70
                } catch (UnexpectedTokenException $e) {
×
71
                    try {
72
                        $sConsume = $parserState->consumeUntil(["\n", ';', '}'], true);
×
73
                        // We need to “unfind” the matches to the end of the ruleSet as this will be matched later
74
                        if ($parserState->streql(\substr($sConsume, -1), '}')) {
×
75
                            $parserState->backtrack(1);
×
76
                        } else {
77
                            while ($parserState->comes(';')) {
×
78
                                $parserState->consume(';');
×
79
                            }
80
                        }
81
                    } catch (UnexpectedTokenException $e) {
×
82
                        // We’ve reached the end of the document. Just close the RuleSet.
83
                        return;
×
84
                    }
85
                }
86
            } else {
87
                $rule = Rule::parse($parserState);
×
88
            }
89
            if ($rule instanceof Rule) {
×
90
                $ruleSet->addRule($rule);
×
91
            }
92
        }
93
        $parserState->consume('}');
×
94
    }
×
95

96
    /**
97
     * @return int<0, max>
98
     */
99
    public function getLineNo(): int
×
100
    {
101
        return $this->lineNumber;
×
102
    }
103

104
    public function addRule(Rule $rule, ?Rule $oSibling = null): void
×
105
    {
106
        $sRule = $rule->getRule();
×
NEW
107
        if (!isset($this->rules[$sRule])) {
×
NEW
108
            $this->rules[$sRule] = [];
×
109
        }
110

NEW
111
        $position = \count($this->rules[$sRule]);
×
112

113
        if ($oSibling !== null) {
×
NEW
114
            $iSiblingPos = \array_search($oSibling, $this->rules[$sRule], true);
×
115
            if ($iSiblingPos !== false) {
×
116
                $position = $iSiblingPos;
×
117
                $rule->setPosition($oSibling->getLineNo(), $oSibling->getColNo() - 1);
×
118
            }
119
        }
120
        if ($rule->getLineNo() === 0 && $rule->getColNo() === 0) {
×
121
            //this node is added manually, give it the next best line
122
            $rules = $this->getRules();
×
123
            $pos = \count($rules);
×
124
            if ($pos > 0) {
×
125
                $last = $rules[$pos - 1];
×
126
                $rule->setPosition($last->getLineNo() + 1, 0);
×
127
            }
128
        }
129

NEW
130
        \array_splice($this->rules[$sRule], $position, 0, [$rule]);
×
131
    }
×
132

133
    /**
134
     * Returns all rules matching the given rule name
135
     *
136
     * @example $ruleSet->getRules('font') // returns array(0 => $rule, …) or array().
137
     *
138
     * @example $ruleSet->getRules('font-')
139
     *          //returns an array of all rules either beginning with font- or matching font.
140
     *
141
     * @param Rule|string|null $mRule
142
     *        Pattern to search for. If null, returns all rules.
143
     *        If the pattern ends with a dash, all rules starting with the pattern are returned
144
     *        as well as one matching the pattern with the dash excluded.
145
     *        Passing a Rule behaves like calling `getRules($mRule->getRule())`.
146
     *
147
     * @return array<int, Rule>
148
     */
149
    public function getRules($mRule = null)
×
150
    {
151
        if ($mRule instanceof Rule) {
×
152
            $mRule = $mRule->getRule();
×
153
        }
154
        /** @var array<int, Rule> $result */
155
        $result = [];
×
NEW
156
        foreach ($this->rules as $sName => $rules) {
×
157
            // Either no search rule is given or the search rule matches the found rule exactly
158
            // or the search rule ends in “-” and the found rule starts with the search rule.
159
            if (
160
                !$mRule || $sName === $mRule
×
161
                || (
162
                    \strrpos($mRule, '-') === \strlen($mRule) - \strlen('-')
×
163
                    && (\strpos($sName, $mRule) === 0 || $sName === \substr($mRule, 0, -1))
×
164
                )
165
            ) {
NEW
166
                $result = \array_merge($result, $rules);
×
167
            }
168
        }
169
        \usort($result, static function (Rule $first, Rule $second): int {
170
            if ($first->getLineNo() === $second->getLineNo()) {
×
171
                return $first->getColNo() - $second->getColNo();
×
172
            }
173
            return $first->getLineNo() - $second->getLineNo();
×
174
        });
×
175
        return $result;
×
176
    }
177

178
    /**
179
     * Overrides all the rules of this set.
180
     *
181
     * @param array<array-key, Rule> $rules The rules to override with.
182
     */
NEW
183
    public function setRules(array $rules): void
×
184
    {
NEW
185
        $this->rules = [];
×
NEW
186
        foreach ($rules as $rule) {
×
UNCOV
187
            $this->addRule($rule);
×
188
        }
189
    }
×
190

191
    /**
192
     * Returns all rules matching the given pattern and returns them in an associative array with the rule’s name
193
     * as keys. This method exists mainly for backwards-compatibility and is really only partially useful.
194
     *
195
     * Note: This method loses some information: Calling this (with an argument of `background-`) on a declaration block
196
     * like `{ background-color: green; background-color; rgba(0, 127, 0, 0.7); }` will only yield an associative array
197
     * containing the rgba-valued rule while `getRules()` would yield an indexed array containing both.
198
     *
199
     * @param Rule|string|null $mRule $mRule
200
     *        Pattern to search for. If null, returns all rules. If the pattern ends with a dash,
201
     *        all rules starting with the pattern are returned as well as one matching the pattern with the dash
202
     *        excluded. Passing a Rule behaves like calling `getRules($mRule->getRule())`.
203
     *
204
     * @return array<string, Rule>
205
     */
206
    public function getRulesAssoc($mRule = null)
×
207
    {
208
        /** @var array<string, Rule> $result */
209
        $result = [];
×
210
        foreach ($this->getRules($mRule) as $rule) {
×
211
            $result[$rule->getRule()] = $rule;
×
212
        }
213
        return $result;
×
214
    }
215

216
    /**
217
     * Removes a rule from this RuleSet. This accepts all the possible values that `getRules()` accepts.
218
     *
219
     * If given a Rule, it will only remove this particular rule (by identity).
220
     * If given a name, it will remove all rules by that name.
221
     *
222
     * Note: this is different from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would
223
     * remove all rules with the same name. To get the old behaviour, use `removeRule($rule->getRule())`.
224
     *
225
     * @param Rule|string|null $mRule
226
     *        pattern to remove. If $mRule is null, all rules are removed. If the pattern ends in a dash,
227
     *        all rules starting with the pattern are removed as well as one matching the pattern with the dash
228
     *        excluded. Passing a Rule behaves matches by identity.
229
     */
230
    public function removeRule($mRule): void
×
231
    {
232
        if ($mRule instanceof Rule) {
×
233
            $sRule = $mRule->getRule();
×
NEW
234
            if (!isset($this->rules[$sRule])) {
×
235
                return;
×
236
            }
NEW
237
            foreach ($this->rules[$sRule] as $key => $rule) {
×
238
                if ($rule === $mRule) {
×
NEW
239
                    unset($this->rules[$sRule][$key]);
×
240
                }
241
            }
242
        } else {
NEW
243
            foreach ($this->rules as $sName => $rules) {
×
244
                // Either no search rule is given or the search rule matches the found rule exactly
245
                // or the search rule ends in “-” and the found rule starts with the search rule or equals it
246
                // (without the trailing dash).
247
                if (
248
                    !$mRule || $sName === $mRule
×
249
                    || (\strrpos($mRule, '-') === \strlen($mRule) - \strlen('-')
×
250
                        && (\strpos($sName, $mRule) === 0 || $sName === \substr($mRule, 0, -1)))
×
251
                ) {
NEW
252
                    unset($this->rules[$sName]);
×
253
                }
254
            }
255
        }
256
    }
×
257

258
    /**
259
     * @deprecated in V8.8.0, will be removed in V9.0.0. Use `render` instead.
260
     */
261
    public function __toString(): string
×
262
    {
263
        return $this->render(new OutputFormat());
×
264
    }
265

266
    /**
267
     * @return string
268
     */
269
    protected function renderRules(OutputFormat $outputFormat)
×
270
    {
271
        $result = '';
×
272
        $isFirst = true;
×
273
        $oNextLevel = $outputFormat->nextLevel();
×
NEW
274
        foreach ($this->rules as $rules) {
×
NEW
275
            foreach ($rules as $rule) {
×
276
                $sRendered = $oNextLevel->safely(static function () use ($rule, $oNextLevel): string {
277
                    return $rule->render($oNextLevel);
×
278
                });
×
279
                if ($sRendered === null) {
×
280
                    continue;
×
281
                }
282
                if ($isFirst) {
×
283
                    $isFirst = false;
×
284
                    $result .= $oNextLevel->spaceBeforeRules();
×
285
                } else {
286
                    $result .= $oNextLevel->spaceBetweenRules();
×
287
                }
288
                $result .= $sRendered;
×
289
            }
290
        }
291

292
        if (!$isFirst) {
×
293
            // Had some output
294
            $result .= $outputFormat->spaceAfterRules();
×
295
        }
296

297
        return $outputFormat->removeLastSemicolon($result);
×
298
    }
299

300
    /**
301
     * @param array<string, Comment> $comments
302
     */
303
    public function addComments(array $comments): void
×
304
    {
305
        $this->comments = \array_merge($this->comments, $comments);
×
306
    }
×
307

308
    /**
309
     * @return array<string, Comment>
310
     */
311
    public function getComments(): array
×
312
    {
313
        return $this->comments;
×
314
    }
315

316
    /**
317
     * @param array<string, Comment> $comments
318
     */
319
    public function setComments(array $comments): void
×
320
    {
321
        $this->comments = $comments;
×
322
    }
×
323
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc