Skip to content

Commit 759cb87

Browse files
committed
Allow multiple rules of the same name per RuleSet
This allows for constructs like these * { color: red; color: rgba(255, 0, 0, 0.9); width: 250%; width: -moz-device-width; //I don’t think there is a vendor-property like this but you get the point. height: 3px !important; height: 9px; } Closes MyIntervals#30 (for its most relevant change). The API had to be changed to accommodate this feature. RuleSet’s `getRules` method now returns indexed arrays. To get the old behaviour, use `getRulesAssoc`. `removeRule` changed as well but only for the case of passing a Rule instance directly. To get the old behaviour back in case you wanted it, use `removeRule($oRuleInstance->getRule())`.
1 parent 3ea17f3 commit 759cb87

File tree

4 files changed

+89
-42
lines changed

4 files changed

+89
-42
lines changed

lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public function expandBorderShorthand() {
9090
$aBorderSizes = array(
9191
'thin', 'medium', 'thick'
9292
);
93-
$aRules = $this->getRules();
93+
$aRules = $this->getRulesAssoc();
9494
foreach ($aBorderRules as $sBorderRule) {
9595
if (!isset($aRules[$sBorderRule]))
9696
continue;
@@ -141,7 +141,7 @@ public function expandDimensionsShorthand() {
141141
'border-style' => 'border-%s-style',
142142
'border-width' => 'border-%s-width'
143143
);
144-
$aRules = $this->getRules();
144+
$aRules = $this->getRulesAssoc();
145145
foreach ($aExpansions as $sProperty => $sExpanded) {
146146
if (!isset($aRules[$sProperty]))
147147
continue;
@@ -190,7 +190,7 @@ public function expandDimensionsShorthand() {
190190
* into their constituent parts.
191191
* */
192192
public function expandFontShorthand() {
193-
$aRules = $this->getRules();
193+
$aRules = $this->getRulesAssoc();
194194
if (!isset($aRules['font']))
195195
return;
196196
$oRule = $aRules['font'];
@@ -256,7 +256,7 @@ public function expandFontShorthand() {
256256
* */
257257

258258
public function expandBackgroundShorthand() {
259-
$aRules = $this->getRules();
259+
$aRules = $this->getRulesAssoc();
260260
if (!isset($aRules['background']))
261261
return;
262262
$oRule = $aRules['background'];
@@ -331,7 +331,7 @@ public function expandListStyleShorthand() {
331331
$aListStylePositions = array(
332332
'inside', 'outside'
333333
);
334-
$aRules = $this->getRules();
334+
$aRules = $this->getRulesAssoc();
335335
if (!isset($aRules['list-style']))
336336
return;
337337
$oRule = $aRules['list-style'];
@@ -374,7 +374,7 @@ public function expandListStyleShorthand() {
374374
}
375375

376376
public function createShorthandProperties(array $aProperties, $sShorthand) {
377-
$aRules = $this->getRules();
377+
$aRules = $this->getRulesAssoc();
378378
$aNewValues = array();
379379
foreach ($aProperties as $sProperty) {
380380
if (!isset($aRules[$sProperty]))
@@ -444,7 +444,7 @@ public function createDimensionsShorthand() {
444444
'border-style' => 'border-%s-style',
445445
'border-width' => 'border-%s-width'
446446
);
447-
$aRules = $this->getRules();
447+
$aRules = $this->getRulesAssoc();
448448
foreach ($aExpansions as $sProperty => $sExpanded) {
449449
$aFoldable = array();
450450
foreach ($aRules as $sRuleName => $oRule) {
@@ -509,7 +509,7 @@ public function createFontShorthand() {
509509
$aFontProperties = array(
510510
'font-style', 'font-variant', 'font-weight', 'font-size', 'line-height', 'font-family'
511511
);
512-
$aRules = $this->getRules();
512+
$aRules = $this->getRulesAssoc();
513513
if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) {
514514
return;
515515
}

lib/Sabberworm/CSS/RuleSet/RuleSet.php

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,56 +17,77 @@ public function __construct() {
1717
}
1818

1919
public function addRule(Rule $oRule) {
20-
$this->aRules[$oRule->getRule()] = $oRule;
20+
$sRule = $oRule->getRule();
21+
if(!isset($this->aRules[$sRule])) {
22+
$this->aRules[$sRule] = array();
23+
}
24+
$this->aRules[$sRule][] = $oRule;
2125
}
2226

2327
/**
24-
* Returns all rules matching the given pattern
28+
* Returns all rules matching the given rule name
2529
* @param (null|string|Rule) $mRule pattern to search for. If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are returned as well as one matching the pattern with the dash excluded. passing a Rule behaves like calling getRules($mRule->getRule()).
2630
* @example $oRuleSet->getRules('font-') //returns an array of all rules either beginning with font- or matching font.
2731
* @example $oRuleSet->getRules('font') //returns array('font' => $oRule) or array().
2832
*/
2933
public function getRules($mRule = null) {
30-
if ($mRule === null) {
31-
return $this->aRules;
32-
}
33-
$aResult = array();
3434
if ($mRule instanceof Rule) {
3535
$mRule = $mRule->getRule();
3636
}
37-
if (strrpos($mRule, '-') === strlen($mRule) - strlen('-')) {
38-
$sStart = substr($mRule, 0, -1);
39-
foreach ($this->aRules as $oRule) {
40-
if ($oRule->getRule() === $sStart || strpos($oRule->getRule(), $mRule) === 0) {
41-
$aResult[$oRule->getRule()] = $this->aRules[$oRule->getRule()];
42-
}
37+
$aResult = array();
38+
foreach($this->aRules as $sName => $aRules) {
39+
// Either no search rule is given or the search rule matches the found rule exactly or the search rule ends in “-” and the found rule starts with the search rule.
40+
if(!$mRule || $sName === $mRule || (strrpos($mRule, '-') === strlen($mRule) - strlen('-') && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1)))) {
41+
$aResult = array_merge($aResult, $aRules);
4342
}
44-
} else if (isset($this->aRules[$mRule])) {
45-
$aResult[$mRule] = $this->aRules[$mRule];
43+
}
44+
return $aResult;
45+
}
46+
47+
/**
48+
* Returns all rules matching the given pattern and returns them in an associative array with the rule’s name as keys. This method exists mainly for backwards-compatibility and is really only partially useful.
49+
* @param (string) $mRule pattern to search for. If null, returns all rules. if the pattern ends with a dash, all rules starting with the pattern are returned as well as one matching the pattern with the dash excluded. passing a Rule behaves like calling getRules($mRule->getRule()).
50+
* Note: This method loses some information: Calling this (with an argument of 'background-') on a declaration block like { background-color: green; background-color; rgba(0, 127, 0, 0.7); } will only yield an associative array containing the rgba-valued rule while @link{getRules()} would yield an indexed array containing both.
51+
*/
52+
public function getRulesAssoc($mRule = null) {
53+
$aResult = array();
54+
foreach($this->getRules($mRule) as $oRule) {
55+
$aResult[$oRule->getRule()] = $oRule;
4656
}
4757
return $aResult;
4858
}
4959

60+
/**
61+
* Remove a rule from this RuleSet. This accepts all the possible values that @link{getRules()} accepts. If given a Rule, it will only remove this particular rule (by identity). If given a name, it will remove all rules by that name. Note: this is previous from pre-v.2.0 behaviour of PHP-CSS-Parser, where passing a Rule instance would remove all rules with the same name. To get the old behvaiour, use removeRule($oRule->getRule()).
62+
* @param (null|string|Rule) $mRule pattern to remove. If $mRule is null, all rules are removed. If the pattern ends in a dash, all rules starting with the pattern are removed as well as one matching the pattern with the dash excluded. Passing a Rule behaves matches by identity.
63+
*/
5064
public function removeRule($mRule) {
51-
if ($mRule instanceof Rule) {
52-
$mRule = $mRule->getRule();
53-
}
54-
if (strrpos($mRule, '-') === strlen($mRule) - strlen('-')) {
55-
$sStart = substr($mRule, 0, -1);
56-
foreach ($this->aRules as $oRule) {
57-
if ($oRule->getRule() === $sStart || strpos($oRule->getRule(), $mRule) === 0) {
58-
unset($this->aRules[$oRule->getRule()]);
65+
if($mRule instanceof Rule) {
66+
$sRule = $mRule->getRule();
67+
if(!isset($this->aRules[$sRule])) {
68+
return;
69+
}
70+
foreach($this->aRules[$sRule] as $iKey => $oRule) {
71+
if($oRule === $mRule) {
72+
unset($this->aRules[$sRule][$iKey]);
73+
}
74+
}
75+
} else {
76+
foreach($this->aRules as $sName => $aRules) {
77+
// Either no search rule is given or the search rule matches the found rule exactly or the search rule ends in “-” and the found rule starts with the search rule or equals it (without the trailing dash).
78+
if(!$mRule || $sName === $mRule || (strrpos($mRule, '-') === strlen($mRule) - strlen('-') && (strpos($sName, $mRule) === 0 || $sName === substr($mRule, 0, -1)))) {
79+
unset($this->aRules[$sName]);
5980
}
6081
}
61-
} else if (isset($this->aRules[$mRule])) {
62-
unset($this->aRules[$mRule]);
6382
}
6483
}
6584

6685
public function __toString() {
6786
$sResult = '';
68-
foreach ($this->aRules as $oRule) {
69-
$sResult .= $oRule->__toString();
87+
foreach ($this->aRules as $aRules) {
88+
foreach($aRules as $oRule) {
89+
$sResult .= $oRule->__toString();
90+
}
7091
}
7192
return $sResult;
7293
}

tests/Sabberworm/CSS/ParserTest.php

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ function testColorParsing() {
4949
$sSelector = $sSelector[0]->getSelector();
5050
if ($sSelector == '#mine') {
5151
$aColorRule = $oRuleSet->getRules('color');
52-
$aValues = $aColorRule['color']->getValues();
52+
$aValues = $aColorRule[0]->getValues();
5353
$this->assertSame('red', $aValues[0][0]);
5454
$aColorRule = $oRuleSet->getRules('background-');
55-
$aValues = $aColorRule['background-color']->getValues();
55+
$aValues = $aColorRule[0]->getValues();
5656
$this->assertEquals(array('r' => new Size(35.0, null, true), 'g' => new Size(35.0, null, true), 'b' => new Size(35.0, null, true)), $aValues[0][0]->getColor());
5757
$aColorRule = $oRuleSet->getRules('border-color');
58-
$aValues = $aColorRule['border-color']->getValues();
58+
$aValues = $aColorRule[0]->getValues();
5959
$this->assertEquals(array('r' => new Size(10.0, null, true), 'g' => new Size(100.0, null, true), 'b' => new Size(230.0, null, true), 'a' => new Size(0.3, null, true)), $aValues[0][0]->getColor());
6060
$aColorRule = $oRuleSet->getRules('outline-color');
61-
$aValues = $aColorRule['outline-color']->getValues();
61+
$aValues = $aColorRule[0]->getValues();
6262
$this->assertEquals(array('r' => new Size(34.0, null, true), 'g' => new Size(34.0, null, true), 'b' => new Size(34.0, null, true)), $aValues[0][0]->getColor());
6363
}
6464
}
@@ -81,7 +81,7 @@ function testUnicodeParsing() {
8181
continue;
8282
}
8383
$aContentRules = $oRuleSet->getRules('content');
84-
$aContents = $aContentRules['content']->getValues();
84+
$aContents = $aContentRules[0]->getValues();
8585
$sString = $aContents[0][0]->__toString();
8686
if ($sSelector == '.test-1') {
8787
$this->assertSame('" "', $sString);
@@ -170,14 +170,38 @@ function testManipulation() {
170170
}', $oDoc->__toString());
171171

172172
$oDoc = $this->parsedStructureForFile('values');
173-
$this->assertSame('#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;font-size: 10px;color: red !important;}
173+
$this->assertSame('#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;font-size: 10px;color: red !important;background-color: green;background-color: rgba(0,128,0,0.7);}
174174
body {color: green;font: 75% "Lucida Grande","Trebuchet MS",Verdana,sans-serif;}' . "\n", $oDoc->__toString());
175175
foreach ($oDoc->getAllRuleSets() as $oRuleSet) {
176176
$oRuleSet->removeRule('font-');
177177
}
178+
$this->assertSame('#header {margin: 10px 2em 1cm 2%;color: red !important;background-color: green;background-color: rgba(0,128,0,0.7);}
179+
body {color: green;}' . "\n", $oDoc->__toString());
180+
foreach ($oDoc->getAllRuleSets() as $oRuleSet) {
181+
$oRuleSet->removeRule('background-');
182+
}
178183
$this->assertSame('#header {margin: 10px 2em 1cm 2%;color: red !important;}
179184
body {color: green;}' . "\n", $oDoc->__toString());
180185
}
186+
187+
function testRuleGetters() {
188+
$oDoc = $this->parsedStructureForFile('values');
189+
$aBlocks = $oDoc->getAllDeclarationBlocks();
190+
$oHeaderBlock = $aBlocks[0];
191+
$oBodyBlock = $aBlocks[1];
192+
$aHeaderRules = $oHeaderBlock->getRules('background-');
193+
$this->assertSame(2, count($aHeaderRules));
194+
$this->assertSame('background-color', $aHeaderRules[0]->getRule());
195+
$this->assertSame('background-color', $aHeaderRules[1]->getRule());
196+
$aHeaderRules = $oHeaderBlock->getRulesAssoc('background-');
197+
$this->assertSame(1, count($aHeaderRules));
198+
$this->assertSame(true, $aHeaderRules['background-color']->getValue() instanceof \Sabberworm\CSS\Value\Color);
199+
$this->assertSame('rgba', $aHeaderRules['background-color']->getValue()->getColorDescription());
200+
$oHeaderBlock->removeRule($aHeaderRules['background-color']);
201+
$aHeaderRules = $oHeaderBlock->getRules('background-');
202+
$this->assertSame(1, count($aHeaderRules));
203+
$this->assertSame('green', $aHeaderRules[0]->getValue());
204+
}
181205

182206
function testSlashedValues() {
183207
$oDoc = $this->parsedStructureForFile('slashed');
@@ -189,7 +213,7 @@ function testSlashedValues() {
189213
}
190214
foreach ($oDoc->getAllDeclarationBlocks() as $oBlock) {
191215
$oRule = $oBlock->getRules('font');
192-
$oRule = $oRule['font'];
216+
$oRule = $oRule[0];
193217
$oSpaceList = $oRule->getValue();
194218
$this->assertEquals(' ', $oSpaceList->getListSeparator());
195219
$oSlashList = $oSpaceList->getListComponents();
@@ -198,7 +222,7 @@ function testSlashedValues() {
198222
$this->assertEquals(',', $oCommaList->getListSeparator());
199223
$this->assertEquals('/', $oSlashList->getListSeparator());
200224
$oRule = $oBlock->getRules('border-radius');
201-
$oRule = $oRule['border-radius'];
225+
$oRule = $oRule[0];
202226
$oSlashList = $oRule->getValue();
203227
$this->assertEquals('/', $oSlashList->getListSeparator());
204228
$oSpaceList1 = $oSlashList->getListComponents();

tests/files/values.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
font-family: Verdana, Helvetica, "Gill Sans", sans-serif;
44
font-size: 10px;
55
color: red !important;
6+
background-color: green;
7+
background-color: rgba(0,128,0,0.7);
68
}
79

810
body {

0 commit comments

Comments
 (0)