Skip to content

Commit d6c7420

Browse files
committed
[TASK] Use delegation for DeclarationBlock -> RuleSet
... rather than inheritance. This will allow `DeclarationBlock` to instead extend `CSSBlockList` in order to support [CSS nesting](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting). This is a slightly-breaking change, since now `CSSBlockList::getAllRuleSets()` will include the `RuleSet` property of the `DeclarationBlock` instead of the `DeclarationBlock` itself. Part of #1170.
1 parent ac31718 commit d6c7420

File tree

8 files changed

+179
-25
lines changed

8 files changed

+179
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Please also have a look at our
2121

2222
### Changed
2323

24+
- `DeclarationBlock` no longer extends `RuleSet` and instead has a `RuleSet` as
25+
a property; use `getRuleSet()` to access it directly (#1194)
2426
- The default line (and column) number is now `null` (not zero) (#1288)
2527
- `setPosition()` (in `Rule` and other classes) now has fluent interface,
2628
returning itself (#1259)

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,11 @@ classDiagram
753753
CSSFunction <|-- Color: inheritance
754754
Positionable <|.. Comment: realization
755755
Renderable <|.. Comment: realization
756-
RuleSet <|-- DeclarationBlock: inheritance
756+
CSSElement <|.. DeclarationBlock: realization
757+
CSSListItem <|.. DeclarationBlock: realization
758+
Positionable <|.. DeclarationBlock: realization
759+
RuleContainer <|.. DeclarationBlock: realization
760+
DeclarationBlock ..> RuleSet : dependency
757761
DeclarationBlock ..> Selector: dependency
758762
CSSBlockList <|-- Document: inheritance
759763
AtRule <|.. Import: realization

src/CSSList/CSSBlockList.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public function getAllRuleSets(): array
5656
$result[] = $item;
5757
} elseif ($item instanceof CSSBlockList) {
5858
$result = \array_merge($result, $item->getAllRuleSets());
59+
} elseif ($item instanceof DeclarationBlock) {
60+
$result[] = $item->getRuleSet();
5961
}
6062
}
6163

src/RuleSet/DeclarationBlock.php

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@
44

55
namespace Sabberworm\CSS\RuleSet;
66

7+
use Sabberworm\CSS\Comment\CommentContainer;
8+
use Sabberworm\CSS\CSSElement;
79
use Sabberworm\CSS\CSSList\CSSList;
10+
use Sabberworm\CSS\CSSList\CSSListItem;
811
use Sabberworm\CSS\CSSList\KeyFrame;
912
use Sabberworm\CSS\OutputFormat;
1013
use Sabberworm\CSS\Parsing\OutputException;
1114
use Sabberworm\CSS\Parsing\ParserState;
1215
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
1316
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
17+
use Sabberworm\CSS\Position\Position;
18+
use Sabberworm\CSS\Position\Positionable;
1419
use Sabberworm\CSS\Property\KeyframeSelector;
1520
use Sabberworm\CSS\Property\Selector;
21+
use Sabberworm\CSS\Rule\Rule;
1622

1723
/**
1824
* This class represents a `RuleSet` constrained by a `Selector`.
@@ -21,14 +27,33 @@
2127
* matching elements.
2228
*
2329
* Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
30+
*
31+
* Note that `CSSListItem` extends both `Commentable` and `Renderable`, so those interfaces must also be implemented.
2432
*/
25-
class DeclarationBlock extends RuleSet
33+
class DeclarationBlock implements CSSElement, CSSListItem, Positionable, RuleContainer
2634
{
35+
use CommentContainer;
36+
use Position;
37+
2738
/**
2839
* @var array<Selector|string>
2940
*/
3041
private $selectors = [];
3142

43+
/**
44+
* @var RuleSet
45+
*/
46+
private $ruleSet;
47+
48+
/**
49+
* @param int<1, max>|null $lineNumber
50+
*/
51+
public function __construct(?int $lineNumber = null)
52+
{
53+
$this->ruleSet = new RuleSet($lineNumber);
54+
$this->setPosition($lineNumber);
55+
}
56+
3257
/**
3358
* @throws UnexpectedTokenException
3459
* @throws UnexpectedEOFException
@@ -107,7 +132,9 @@ public static function parse(ParserState $parserState, ?CSSList $list = null): ?
107132
}
108133
}
109134
$result->setComments($comments);
110-
RuleSet::parseRuleSet($parserState, $result);
135+
136+
RuleSet::parseRuleSet($parserState, $result->ruleSet);
137+
111138
return $result;
112139
}
113140

@@ -175,6 +202,73 @@ public function getSelectors(): array
175202
return $this->selectors;
176203
}
177204

205+
public function getRuleSet(): RuleSet
206+
{
207+
return $this->ruleSet;
208+
}
209+
210+
/**
211+
* @see RuleSet::addRule()
212+
*/
213+
public function addRule(Rule $ruleToAdd, ?Rule $sibling = null): void
214+
{
215+
$this->ruleSet->addRule($ruleToAdd, $sibling);
216+
}
217+
218+
/**
219+
* @see RuleSet::getRules()
220+
*
221+
* @return array<int<0, max>, Rule>
222+
*/
223+
public function getRules(?string $searchPattern = null): array
224+
{
225+
return $this->ruleSet->getRules($searchPattern);
226+
}
227+
228+
/**
229+
* @see RuleSet::setRules()
230+
*
231+
* @param array<Rule> $rules
232+
*/
233+
public function setRules(array $rules): void
234+
{
235+
$this->ruleSet->setRules($rules);
236+
}
237+
238+
/**
239+
* @see RuleSet::getRulesAssoc()
240+
*
241+
* @return array<string, Rule>
242+
*/
243+
public function getRulesAssoc(?string $searchPattern = null): array
244+
{
245+
return $this->ruleSet->getRulesAssoc($searchPattern);
246+
}
247+
248+
/**
249+
* @see RuleSet::removeRule()
250+
*/
251+
public function removeRule(Rule $ruleToRemove): void
252+
{
253+
$this->ruleSet->removeRule($ruleToRemove);
254+
}
255+
256+
/**
257+
* @see RuleSet::removeMatchingRules()
258+
*/
259+
public function removeMatchingRules(string $searchPattern): void
260+
{
261+
$this->ruleSet->removeMatchingRules($searchPattern);
262+
}
263+
264+
/**
265+
* @see RuleSet::removeAllRules()
266+
*/
267+
public function removeAllRules(): void
268+
{
269+
$this->ruleSet->removeAllRules();
270+
}
271+
178272
/**
179273
* @return non-empty-string
180274
*
@@ -198,7 +292,7 @@ public function render(OutputFormat $outputFormat): string
198292
);
199293
$result .= $outputFormat->getContentAfterDeclarationBlockSelectors();
200294
$result .= $formatter->spaceBeforeOpeningBrace() . '{';
201-
$result .= $this->renderRules($outputFormat);
295+
$result .= $this->ruleSet->render($outputFormat);
202296
$result .= '}';
203297
$result .= $outputFormat->getContentAfterDeclarationBlock();
204298

tests/ParserTest.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ final class ParserTest extends TestCase
3838
/**
3939
* @test
4040
*/
41-
public function parseForOneRuleSetReturnsDocumentWithOneRuleSet(): void
41+
public function parseForOneDeclarationBlockReturnsDocumentWithOneDeclarationBlock(): void
4242
{
4343
$css = '.thing { left: 10px; }';
4444
$parser = new Parser($css);
@@ -49,7 +49,7 @@ public function parseForOneRuleSetReturnsDocumentWithOneRuleSet(): void
4949

5050
$cssList = $document->getContents();
5151
self::assertCount(1, $cssList);
52-
self::assertInstanceOf(RuleSet::class, $cssList[0]);
52+
self::assertInstanceOf(DeclarationBlock::class, $cssList[0]);
5353
}
5454

5555
/**
@@ -926,9 +926,9 @@ public function missingPropertyValueStrict(): void
926926
public function missingPropertyValueLenient(): void
927927
{
928928
$parsed = self::parsedStructureForFile('missing-property-value', Settings::create()->withLenientParsing(true));
929-
$rulesets = $parsed->getAllRuleSets();
930-
self::assertCount(1, $rulesets);
931-
$block = $rulesets[0];
929+
$declarationBlocks = $parsed->getAllDeclarationBlocks();
930+
self::assertCount(1, $declarationBlocks);
931+
$block = $declarationBlocks[0];
932932
self::assertInstanceOf(DeclarationBlock::class, $block);
933933
self::assertEquals([new Selector('div')], $block->getSelectors());
934934
$rules = $block->getRules();
@@ -1055,7 +1055,7 @@ public function commentExtracting(): void
10551055
// $this->assertSame("* Number 5 *", $fooBarBlockComments[1]->getComment());
10561056

10571057
// Declaration rules.
1058-
self::assertInstanceOf(RuleSet::class, $fooBarBlock);
1058+
self::assertInstanceOf(DeclarationBlock::class, $fooBarBlock);
10591059
$fooBarRules = $fooBarBlock->getRules();
10601060
$fooBarRule = $fooBarRules[0];
10611061
$fooBarRuleComments = $fooBarRule->getComments();
@@ -1076,7 +1076,7 @@ public function commentExtracting(): void
10761076
self::assertSame('* Number 10 *', $fooBarComments[0]->getComment());
10771077

10781078
// Media -> declaration -> rule.
1079-
self::assertInstanceOf(RuleSet::class, $mediaRules[0]);
1079+
self::assertInstanceOf(DeclarationBlock::class, $mediaRules[0]);
10801080
$fooBarRules = $mediaRules[0]->getRules();
10811081
$fooBarChildComments = $fooBarRules[0]->getComments();
10821082
self::assertCount(1, $fooBarChildComments);
@@ -1092,7 +1092,7 @@ public function flatCommentExtractingOneComment(): void
10921092
$document = $parser->parse();
10931093

10941094
$contents = $document->getContents();
1095-
self::assertInstanceOf(RuleSet::class, $contents[0]);
1095+
self::assertInstanceOf(DeclarationBlock::class, $contents[0]);
10961096
$divRules = $contents[0]->getRules();
10971097
$comments = $divRules[0]->getComments();
10981098

@@ -1109,7 +1109,7 @@ public function flatCommentExtractingTwoConjoinedCommentsForOneRule(): void
11091109
$document = $parser->parse();
11101110

11111111
$contents = $document->getContents();
1112-
self::assertInstanceOf(RuleSet::class, $contents[0]);
1112+
self::assertInstanceOf(DeclarationBlock::class, $contents[0]);
11131113
$divRules = $contents[0]->getRules();
11141114
$comments = $divRules[0]->getComments();
11151115

@@ -1127,7 +1127,7 @@ public function flatCommentExtractingTwoSpaceSeparatedCommentsForOneRule(): void
11271127
$document = $parser->parse();
11281128

11291129
$contents = $document->getContents();
1130-
self::assertInstanceOf(RuleSet::class, $contents[0]);
1130+
self::assertInstanceOf(DeclarationBlock::class, $contents[0]);
11311131
$divRules = $contents[0]->getRules();
11321132
$comments = $divRules[0]->getComments();
11331133

@@ -1145,7 +1145,7 @@ public function flatCommentExtractingCommentsForTwoRules(): void
11451145
$document = $parser->parse();
11461146

11471147
$contents = $document->getContents();
1148-
self::assertInstanceOf(RuleSet::class, $contents[0]);
1148+
self::assertInstanceOf(DeclarationBlock::class, $contents[0]);
11491149
$divRules = $contents[0]->getRules();
11501150
$rule1Comments = $divRules[0]->getComments();
11511151
$rule2Comments = $divRules[1]->getComments();

tests/RuleSet/DeclarationBlockTest.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88
use Sabberworm\CSS\OutputFormat;
99
use Sabberworm\CSS\Parser;
1010
use Sabberworm\CSS\Rule\Rule;
11-
use Sabberworm\CSS\RuleSet\RuleSet;
11+
use Sabberworm\CSS\RuleSet\DeclarationBlock;
1212
use Sabberworm\CSS\Settings as ParserSettings;
1313
use Sabberworm\CSS\Value\Size;
1414

1515
/**
1616
* @covers \Sabberworm\CSS\RuleSet\DeclarationBlock
17-
* @covers \Sabberworm\CSS\RuleSet\RuleSet
1817
*/
1918
final class DeclarationBlockTest extends TestCase
2019
{
@@ -31,7 +30,7 @@ public function overrideRules(): void
3130
$contents = $document->getContents();
3231
$wrapper = $contents[0];
3332

34-
self::assertInstanceOf(RuleSet::class, $wrapper);
33+
self::assertInstanceOf(DeclarationBlock::class, $wrapper);
3534
self::assertCount(2, $wrapper->getRules());
3635
$wrapper->setRules([$rule]);
3736

@@ -52,7 +51,7 @@ public function ruleInsertion(): void
5251
$contents = $document->getContents();
5352
$wrapper = $contents[0];
5453

55-
self::assertInstanceOf(RuleSet::class, $wrapper);
54+
self::assertInstanceOf(DeclarationBlock::class, $wrapper);
5655

5756
$leftRules = $wrapper->getRules('left');
5857
self::assertCount(1, $leftRules);

tests/Unit/CSSList/CSSBlockListTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public function getAllRuleSetsWhenNoContentSetReturnsEmptyArray(): void
157157
/**
158158
* @test
159159
*/
160-
public function getAllRuleSetsReturnsOneDeclarationBlockDirectlySetAsContent(): void
160+
public function getAllRuleSetsReturnsRuleSetFromOneDeclarationBlockDirectlySetAsContent(): void
161161
{
162162
$subject = new ConcreteCSSBlockList();
163163

@@ -166,7 +166,7 @@ public function getAllRuleSetsReturnsOneDeclarationBlockDirectlySetAsContent():
166166

167167
$result = $subject->getAllRuleSets();
168168

169-
self::assertSame([$declarationBlock], $result);
169+
self::assertSame([$declarationBlock->getRuleSet()], $result);
170170
}
171171

172172
/**
@@ -187,7 +187,7 @@ public function getAllRuleSetsReturnsOneAtRuleSetDirectlySetAsContent(): void
187187
/**
188188
* @test
189189
*/
190-
public function getAllRuleSetsReturnsMultipleDeclarationBlocksDirectlySetAsContents(): void
190+
public function getAllRuleSetsReturnsRuleSetsFromMultipleDeclarationBlocksDirectlySetAsContents(): void
191191
{
192192
$subject = new ConcreteCSSBlockList();
193193

@@ -197,7 +197,7 @@ public function getAllRuleSetsReturnsMultipleDeclarationBlocksDirectlySetAsConte
197197

198198
$result = $subject->getAllRuleSets();
199199

200-
self::assertSame([$declarationBlock1, $declarationBlock2], $result);
200+
self::assertSame([$declarationBlock1->getRuleSet(), $declarationBlock2->getRuleSet()], $result);
201201
}
202202

203203
/**
@@ -219,7 +219,7 @@ public function getAllRuleSetsReturnsMultipleAtRuleSetsDirectlySetAsContents():
219219
/**
220220
* @test
221221
*/
222-
public function getAllRuleSetsReturnsDeclarationBlocksWithinAtRuleBlockList(): void
222+
public function getAllRuleSetsReturnsRuleSetsFromDeclarationBlocksWithinAtRuleBlockList(): void
223223
{
224224
$subject = new ConcreteCSSBlockList();
225225

@@ -230,7 +230,7 @@ public function getAllRuleSetsReturnsDeclarationBlocksWithinAtRuleBlockList(): v
230230

231231
$result = $subject->getAllRuleSets();
232232

233-
self::assertSame([$declarationBlock], $result);
233+
self::assertSame([$declarationBlock->getRuleSet()], $result);
234234
}
235235

236236
/**

0 commit comments

Comments
 (0)