Skip to content

Commit 802cebe

Browse files
authored
[TASK] Rename Rule\Rule to Property\Declaration (#1508)
A property declaration is not a rule. Ref: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/CSS_Declaration Add a `class_alias` to avoid a breaking change, and deprecate the original class name.
1 parent 1ce3447 commit 802cebe

5 files changed

Lines changed: 266 additions & 224 deletions

File tree

config/phpstan-baseline.neon

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ parameters:
7979
path: ../tests/ParserTest.php
8080

8181
-
82-
message: '#^Parameter \#1 \$value of method Sabberworm\\CSS\\Rule\\Rule\:\:setValue\(\) expects Sabberworm\\CSS\\Value\\RuleValueList\|string\|null, Sabberworm\\CSS\\Value\\Size given\.$#'
82+
message: '#^Parameter \#1 \$value of method Sabberworm\\CSS\\Property\\Declaration\:\:setValue\(\) expects Sabberworm\\CSS\\Value\\RuleValueList\|string\|null, Sabberworm\\CSS\\Value\\Size given\.$#'
8383
identifier: argument.type
8484
count: 3
8585
path: ../tests/RuleSet/DeclarationBlockTest.php
@@ -91,13 +91,13 @@ parameters:
9191
path: ../tests/Unit/CSSList/AtRuleBlockListTest.php
9292

9393
-
94-
message: '#^Parameter \#1 \$value of method Sabberworm\\CSS\\Rule\\Rule\:\:setValue\(\) expects Sabberworm\\CSS\\Value\\RuleValueList\|string\|null, Sabberworm\\CSS\\Value\\CSSFunction given\.$#'
94+
message: '#^Parameter \#1 \$value of method Sabberworm\\CSS\\Property\\Declaration\:\:setValue\(\) expects Sabberworm\\CSS\\Value\\RuleValueList\|string\|null, Sabberworm\\CSS\\Value\\CSSFunction given\.$#'
9595
identifier: argument.type
9696
count: 2
9797
path: ../tests/Unit/CSSList/CSSBlockListTest.php
9898

9999
-
100-
message: '#^Parameter \#1 \$value of method Sabberworm\\CSS\\Rule\\Rule\:\:setValue\(\) expects Sabberworm\\CSS\\Value\\RuleValueList\|string\|null, Sabberworm\\CSS\\Value\\CSSString given\.$#'
100+
message: '#^Parameter \#1 \$value of method Sabberworm\\CSS\\Property\\Declaration\:\:setValue\(\) expects Sabberworm\\CSS\\Value\\RuleValueList\|string\|null, Sabberworm\\CSS\\Value\\CSSString given\.$#'
101101
identifier: argument.type
102102
count: 10
103103
path: ../tests/Unit/CSSList/CSSBlockListTest.php

config/phpstan.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ parameters:
1515
- %currentWorkingDirectory%/src/
1616
- %currentWorkingDirectory%/tests/
1717

18+
bootstrapFiles:
19+
- %currentWorkingDirectory%/src/Rule/Rule.php
20+
1821
type_perfect:
1922
no_mixed_property: true
2023
no_mixed_caller: true

src/Property/Declaration.php

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Property;
6+
7+
use Sabberworm\CSS\Comment\Comment;
8+
use Sabberworm\CSS\Comment\Commentable;
9+
use Sabberworm\CSS\Comment\CommentContainer;
10+
use Sabberworm\CSS\CSSElement;
11+
use Sabberworm\CSS\OutputFormat;
12+
use Sabberworm\CSS\Parsing\ParserState;
13+
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
14+
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
15+
use Sabberworm\CSS\Position\Position;
16+
use Sabberworm\CSS\Position\Positionable;
17+
use Sabberworm\CSS\Value\RuleValueList;
18+
use Sabberworm\CSS\Value\Value;
19+
20+
use function Safe\preg_match;
21+
22+
/**
23+
* `Declaration`s just have a string key (the property name) and a 'Value'.
24+
*
25+
* In CSS, `Declaration`s are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];”
26+
*/
27+
class Declaration implements Commentable, CSSElement, Positionable
28+
{
29+
use CommentContainer;
30+
use Position;
31+
32+
/**
33+
* @var non-empty-string
34+
*/
35+
private $propertyName;
36+
37+
/**
38+
* @var RuleValueList|string|null
39+
*/
40+
private $value;
41+
42+
/**
43+
* @var bool
44+
*/
45+
private $isImportant = false;
46+
47+
/**
48+
* @param non-empty-string $propertyName
49+
* @param int<1, max>|null $lineNumber
50+
* @param int<0, max>|null $columnNumber
51+
*/
52+
public function __construct(string $propertyName, ?int $lineNumber = null, ?int $columnNumber = null)
53+
{
54+
$this->propertyName = $propertyName;
55+
$this->setPosition($lineNumber, $columnNumber);
56+
}
57+
58+
/**
59+
* @param list<Comment> $commentsBefore
60+
*
61+
* @throws UnexpectedEOFException
62+
* @throws UnexpectedTokenException
63+
*
64+
* @internal since V8.8.0
65+
*/
66+
public static function parse(ParserState $parserState, array $commentsBefore = []): self
67+
{
68+
$comments = $commentsBefore;
69+
$parserState->consumeWhiteSpace($comments);
70+
$declaration = new self(
71+
$parserState->parseIdentifier(!$parserState->comes('--')),
72+
$parserState->currentLine(),
73+
$parserState->currentColumn()
74+
);
75+
$parserState->consumeWhiteSpace($comments);
76+
$declaration->setComments($comments);
77+
$parserState->consume(':');
78+
$value = Value::parseValue($parserState, self::getDelimitersForPropertyValue($declaration->getPropertyName()));
79+
$declaration->setValue($value);
80+
$parserState->consumeWhiteSpace();
81+
if ($parserState->comes('!')) {
82+
$parserState->consume('!');
83+
$parserState->consumeWhiteSpace();
84+
$parserState->consume('important');
85+
$declaration->setIsImportant(true);
86+
}
87+
$parserState->consumeWhiteSpace();
88+
while ($parserState->comes(';')) {
89+
$parserState->consume(';');
90+
}
91+
92+
return $declaration;
93+
}
94+
95+
/**
96+
* Returns a list of delimiters (or separators).
97+
* The first item is the innermost separator (or, put another way, the highest-precedence operator).
98+
* The sequence continues to the outermost separator (or lowest-precedence operator).
99+
*
100+
* @param non-empty-string $propertyName
101+
*
102+
* @return list<non-empty-string>
103+
*/
104+
private static function getDelimitersForPropertyValue(string $propertyName): array
105+
{
106+
if (preg_match('/^font($|-)/', $propertyName) === 1) {
107+
return [',', '/', ' '];
108+
}
109+
110+
switch ($propertyName) {
111+
case 'src':
112+
return [' ', ','];
113+
default:
114+
return [',', ' ', '/'];
115+
}
116+
}
117+
118+
/**
119+
* @param non-empty-string $propertyName
120+
*/
121+
public function setPropertyName(string $propertyName): void
122+
{
123+
$this->propertyName = $propertyName;
124+
}
125+
126+
/**
127+
* @return non-empty-string
128+
*/
129+
public function getPropertyName(): string
130+
{
131+
return $this->propertyName;
132+
}
133+
134+
/**
135+
* @param non-empty-string $propertyName
136+
*
137+
* @deprecated in v9.2, will be removed in v10.0; use `setPropertyName()` instead.
138+
*/
139+
public function setRule(string $propertyName): void
140+
{
141+
$this->propertyName = $propertyName;
142+
}
143+
144+
/**
145+
* @return non-empty-string
146+
*
147+
* @deprecated in v9.2, will be removed in v10.0; use `getPropertyName()` instead.
148+
*/
149+
public function getRule(): string
150+
{
151+
return $this->propertyName;
152+
}
153+
154+
/**
155+
* @return RuleValueList|string|null
156+
*/
157+
public function getValue()
158+
{
159+
return $this->value;
160+
}
161+
162+
/**
163+
* @param RuleValueList|string|null $value
164+
*/
165+
public function setValue($value): void
166+
{
167+
$this->value = $value;
168+
}
169+
170+
/**
171+
* Adds a value to the existing value. Value will be appended if a `RuleValueList` exists of the given type.
172+
* Otherwise, the existing value will be wrapped by one.
173+
*
174+
* @param RuleValueList|array<int, RuleValueList> $value
175+
*/
176+
public function addValue($value, string $type = ' '): void
177+
{
178+
if (!\is_array($value)) {
179+
$value = [$value];
180+
}
181+
if (!($this->value instanceof RuleValueList) || $this->value->getListSeparator() !== $type) {
182+
$currentValue = $this->value;
183+
$this->value = new RuleValueList($type, $this->getLineNumber());
184+
if ($currentValue !== null && $currentValue !== '') {
185+
$this->value->addListComponent($currentValue);
186+
}
187+
}
188+
foreach ($value as $valueItem) {
189+
$this->value->addListComponent($valueItem);
190+
}
191+
}
192+
193+
public function setIsImportant(bool $isImportant): void
194+
{
195+
$this->isImportant = $isImportant;
196+
}
197+
198+
public function getIsImportant(): bool
199+
{
200+
return $this->isImportant;
201+
}
202+
203+
/**
204+
* @return non-empty-string
205+
*/
206+
public function render(OutputFormat $outputFormat): string
207+
{
208+
$formatter = $outputFormat->getFormatter();
209+
$result = "{$formatter->comments($this)}{$this->propertyName}:{$formatter->spaceAfterRuleName()}";
210+
if ($this->value instanceof Value) { // Can also be a ValueList
211+
$result .= $this->value->render($outputFormat);
212+
} else {
213+
$result .= $this->value;
214+
}
215+
if ($this->isImportant) {
216+
$result .= ' !important';
217+
}
218+
$result .= ';';
219+
return $result;
220+
}
221+
222+
/**
223+
* @return array<string, bool|int|float|string|array<mixed>|null>
224+
*
225+
* @internal
226+
*/
227+
public function getArrayRepresentation(): array
228+
{
229+
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
230+
}
231+
}

0 commit comments

Comments
 (0)