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

MyIntervals / PHP-CSS-Parser / 14025852631

24 Mar 2025 02:23AM UTC coverage: 51.611%. Remained the same
14025852631

Pull #1212

github

web-flow
Merge 090d8afa4 into 639366092
Pull Request #1212: [TASK] Add (and use) a `CSSListItem` type

6 of 10 new or added lines in 2 files covered. (60.0%)

41 existing lines in 3 files now uncovered.

945 of 1831 relevant lines covered (51.61%)

6.72 hits per line

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

56.72
/src/RuleSet/DeclarationBlock.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Sabberworm\CSS\RuleSet;
6

7
use Sabberworm\CSS\CSSList\CSSList;
8
use Sabberworm\CSS\CSSList\KeyFrame;
9
use Sabberworm\CSS\OutputFormat;
10
use Sabberworm\CSS\Parsing\OutputException;
11
use Sabberworm\CSS\Parsing\ParserState;
12
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
13
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
14
use Sabberworm\CSS\Property\KeyframeSelector;
15
use Sabberworm\CSS\Property\Selector;
16

17
/**
18
 * This class represents a `RuleSet` constrained by a `Selector`.
19
 *
20
 * It contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the
21
 * matching elements.
22
 *
23
 * Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
24
 */
25
class DeclarationBlock extends RuleSet
26
{
27
    /**
28
     * @var array<Selector|string>
29
     */
30
    private $selectors = [];
31

32
    /**
33
     * @throws UnexpectedTokenException
34
     * @throws UnexpectedEOFException
35
     *
36
     * @internal since V8.8.0
37
     */
38
    public static function parse(ParserState $parserState, ?CSSList $list = null): ?DeclarationBlock
6✔
39
    {
40
        $comments = [];
6✔
41
        $result = new DeclarationBlock($parserState->currentLine());
6✔
42
        try {
43
            $selectorParts = [];
6✔
44
            do {
45
                $selectorParts[] = $parserState->consume(1)
6✔
46
                    . $parserState->consumeUntil(['{', '}', '\'', '"'], false, false, $comments);
6✔
47
                if (\in_array($parserState->peek(), ['\'', '"'], true) && \substr(\end($selectorParts), -1) != '\\') {
6✔
UNCOV
48
                    if (!isset($stringWrapperCharacter)) {
×
UNCOV
49
                        $stringWrapperCharacter = $parserState->peek();
×
50
                    } elseif ($stringWrapperCharacter === $parserState->peek()) {
×
51
                        unset($stringWrapperCharacter);
×
52
                    }
53
                }
54
            } while (!\in_array($parserState->peek(), ['{', '}'], true) || isset($stringWrapperCharacter));
6✔
55
            $result->setSelectors(\implode('', $selectorParts), $list);
6✔
56
            if ($parserState->comes('{')) {
6✔
57
                $parserState->consume(1);
6✔
58
            }
UNCOV
59
        } catch (UnexpectedTokenException $e) {
×
UNCOV
60
            if ($parserState->getSettings()->usesLenientParsing()) {
×
61
                if (!$parserState->comes('}')) {
×
62
                    $parserState->consumeUntil('}', false, true);
×
63
                }
NEW
64
                return null;
×
65
            } else {
66
                throw $e;
×
67
            }
68
        }
69
        $result->setComments($comments);
6✔
70
        RuleSet::parseRuleSet($parserState, $result);
6✔
71
        return $result;
6✔
72
    }
73

74
    /**
75
     * @param array<Selector|string>|string $selectors
76
     *
77
     * @throws UnexpectedTokenException
78
     */
79
    public function setSelectors($selectors, ?CSSList $list = null): void
7✔
80
    {
81
        if (\is_array($selectors)) {
7✔
82
            $this->selectors = $selectors;
1✔
83
        } else {
84
            $this->selectors = \explode(',', $selectors);
6✔
85
        }
86
        foreach ($this->selectors as $key => $selector) {
7✔
87
            if (!($selector instanceof Selector)) {
7✔
88
                if ($list === null || !($list instanceof KeyFrame)) {
6✔
89
                    if (!Selector::isValid($selector)) {
6✔
UNCOV
90
                        throw new UnexpectedTokenException(
×
UNCOV
91
                            "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
×
92
                            $selectors,
93
                            'custom'
×
94
                        );
95
                    }
96
                    $this->selectors[$key] = new Selector($selector);
6✔
97
                } else {
UNCOV
98
                    if (!KeyframeSelector::isValid($selector)) {
×
UNCOV
99
                        throw new UnexpectedTokenException(
×
100
                            "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.",
×
101
                            $selector,
102
                            'custom'
×
103
                        );
104
                    }
UNCOV
105
                    $this->selectors[$key] = new KeyframeSelector($selector);
×
106
                }
107
            }
108
        }
109
    }
7✔
110

111
    /**
112
     * Remove one of the selectors of the block.
113
     *
114
     * @param Selector|string $selectorToRemove
115
     */
UNCOV
116
    public function removeSelector($selectorToRemove): bool
×
117
    {
118
        if ($selectorToRemove instanceof Selector) {
×
UNCOV
119
            $selectorToRemove = $selectorToRemove->getSelector();
×
120
        }
121
        foreach ($this->selectors as $key => $selector) {
×
UNCOV
122
            if ($selector->getSelector() === $selectorToRemove) {
×
123
                unset($this->selectors[$key]);
×
124
                return true;
×
125
            }
126
        }
UNCOV
127
        return false;
×
128
    }
129

130
    /**
131
     * @return array<Selector>
132
     */
UNCOV
133
    public function getSelectors(): array
×
134
    {
135
        return $this->selectors;
×
136
    }
137

138
    /**
139
     * @return non-empty-string
140
     *
141
     * @throws OutputException
142
     */
143
    public function render(OutputFormat $outputFormat): string
6✔
144
    {
145
        $formatter = $outputFormat->getFormatter();
6✔
146
        $result = $formatter->comments($this);
6✔
147
        if (\count($this->selectors) === 0) {
6✔
148
            // If all the selectors have been removed, this declaration block becomes invalid
UNCOV
149
            throw new OutputException('Attempt to print declaration block with missing selector', $this->lineNumber);
×
150
        }
151
        $result .= $outputFormat->getContentBeforeDeclarationBlock();
6✔
152
        $result .= $formatter->implode(
6✔
153
            $formatter->spaceBeforeSelectorSeparator() . ',' . $formatter->spaceAfterSelectorSeparator(),
6✔
154
            $this->selectors
6✔
155
        );
156
        $result .= $outputFormat->getContentAfterDeclarationBlockSelectors();
6✔
157
        $result .= $formatter->spaceBeforeOpeningBrace() . '{';
6✔
158
        $result .= $this->renderRules($outputFormat);
6✔
159
        $result .= '}';
6✔
160
        $result .= $outputFormat->getContentAfterDeclarationBlock();
6✔
161

162
        return $result;
6✔
163
    }
164
}
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