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

MyIntervals / PHP-CSS-Parser / 12998096795

27 Jan 2025 08:52PM UTC coverage: 51.041%. Remained the same
12998096795

Pull #835

github

web-flow
Merge 3657ed1c5 into 9f5c7dc3a
Pull Request #835: [CLEANUP] Avoid Hungarian notation for `oRule`

43 of 71 new or added lines in 4 files covered. (60.56%)

1 existing line in 1 file now uncovered.

1054 of 2065 relevant lines covered (51.04%)

10.55 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 $aRules;
31

32
    /**
33
     * @var int
34
     */
35
    protected $lineNumber;
36

37
    /**
38
     * @var array<array-key, Comment>
39
     */
40
    protected $comments;
41

42
    /**
43
     * @param int $lineNumber
44
     */
45
    public function __construct($lineNumber = 0)
×
46
    {
47
        $this->aRules = [];
×
48
        $this->lineNumber = $lineNumber;
×
49
        $this->comments = [];
×
50
    }
×
51

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

92
    /**
93
     * @return int
94
     */
95
    public function getLineNo()
×
96
    {
97
        return $this->lineNumber;
×
98
    }
99

100
    /**
101
     * @param Rule|null $oSibling
102
     */
NEW
103
    public function addRule(Rule $rule, ?Rule $oSibling = null): void
×
104
    {
NEW
105
        $sRule = $rule->getRule();
×
106
        if (!isset($this->aRules[$sRule])) {
×
107
            $this->aRules[$sRule] = [];
×
108
        }
109

110
        $iPosition = \count($this->aRules[$sRule]);
×
111

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

NEW
129
        \array_splice($this->aRules[$sRule], $iPosition, 0, [$rule]);
×
130
    }
×
131

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

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

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

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

257
    public function __toString(): string
×
258
    {
259
        return $this->render(new OutputFormat());
×
260
    }
261

262
    /**
263
     * @return string
264
     */
265
    protected function renderRules(OutputFormat $oOutputFormat)
×
266
    {
267
        $sResult = '';
×
268
        $bIsFirst = true;
×
269
        $oNextLevel = $oOutputFormat->nextLevel();
×
270
        foreach ($this->aRules as $aRules) {
×
NEW
271
            foreach ($aRules as $rule) {
×
272
                $sRendered = $oNextLevel->safely(function () use ($rule, $oNextLevel) {
NEW
273
                    return $rule->render($oNextLevel);
×
274
                });
×
275
                if ($sRendered === null) {
×
276
                    continue;
×
277
                }
278
                if ($bIsFirst) {
×
279
                    $bIsFirst = false;
×
280
                    $sResult .= $oNextLevel->spaceBeforeRules();
×
281
                } else {
282
                    $sResult .= $oNextLevel->spaceBetweenRules();
×
283
                }
284
                $sResult .= $sRendered;
×
285
            }
286
        }
287

288
        if (!$bIsFirst) {
×
289
            // Had some output
290
            $sResult .= $oOutputFormat->spaceAfterRules();
×
291
        }
292

293
        return $oOutputFormat->removeLastSemicolon($sResult);
×
294
    }
295

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

304
    /**
305
     * @return array<string, Comment>
306
     */
307
    public function getComments()
×
308
    {
309
        return $this->comments;
×
310
    }
311

312
    /**
313
     * @param array<string, Comment> $comments
314
     */
315
    public function setComments(array $comments): void
×
316
    {
317
        $this->comments = $comments;
×
318
    }
×
319
}
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

© 2025 Coveralls, Inc