See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 3 namespace Sabberworm\CSS\Rule; 4 5 use Sabberworm\CSS\Comment\Comment; 6 use Sabberworm\CSS\Comment\Commentable; 7 use Sabberworm\CSS\OutputFormat; 8 use Sabberworm\CSS\Parsing\ParserState; 9 use Sabberworm\CSS\Parsing\UnexpectedEOFException; 10 use Sabberworm\CSS\Parsing\UnexpectedTokenException; 11 use Sabberworm\CSS\Renderable; 12 use Sabberworm\CSS\Value\RuleValueList; 13 use Sabberworm\CSS\Value\Value; 14 15 /** 16 * RuleSets contains Rule objects which always have a key and a value. 17 * In CSS, Rules are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];” 18 */ 19 class Rule implements Renderable, Commentable 20 { 21 /** 22 * @var string 23 */ 24 private $sRule; 25 26 /** 27 * @var RuleValueList|null 28 */ 29 private $mValue; 30 31 /** 32 * @var bool 33 */ 34 private $bIsImportant; 35 36 /** 37 * @var array<int, int> 38 */ 39 private $aIeHack; 40 41 /** 42 * @var int 43 */ 44 protected $iLineNo; 45 46 /** 47 * @var int 48 */ 49 protected $iColNo; 50 51 /** 52 * @var array<array-key, Comment> 53 */ 54 protected $aComments; 55 56 /** 57 * @param string $sRule 58 * @param int $iLineNo 59 * @param int $iColNo 60 */ 61 public function __construct($sRule, $iLineNo = 0, $iColNo = 0) 62 { 63 $this->sRule = $sRule; 64 $this->mValue = null; 65 $this->bIsImportant = false; 66 $this->aIeHack = []; 67 $this->iLineNo = $iLineNo; 68 $this->iColNo = $iColNo; 69 $this->aComments = []; 70 } 71 72 /** 73 * @return Rule 74 * 75 * @throws UnexpectedEOFException 76 * @throws UnexpectedTokenException 77 */ 78 public static function parse(ParserState $oParserState) 79 { 80 $aComments = $oParserState->consumeWhiteSpace(); 81 $oRule = new Rule( 82 $oParserState->parseIdentifier(!$oParserState->comes("--")), 83 $oParserState->currentLine(), 84 $oParserState->currentColumn() 85 ); 86 $oRule->setComments($aComments); 87 $oRule->addComments($oParserState->consumeWhiteSpace()); 88 $oParserState->consume(':'); 89 $oValue = Value::parseValue($oParserState, self::listDelimiterForRule($oRule->getRule())); 90 $oRule->setValue($oValue); 91 if ($oParserState->getSettings()->bLenientParsing) { 92 while ($oParserState->comes('\\')) { 93 $oParserState->consume('\\'); 94 $oRule->addIeHack($oParserState->consume()); 95 $oParserState->consumeWhiteSpace(); 96 } 97 } 98 $oParserState->consumeWhiteSpace(); 99 if ($oParserState->comes('!')) { 100 $oParserState->consume('!'); 101 $oParserState->consumeWhiteSpace(); 102 $oParserState->consume('important'); 103 $oRule->setIsImportant(true); 104 } 105 $oParserState->consumeWhiteSpace(); 106 while ($oParserState->comes(';')) { 107 $oParserState->consume(';'); 108 } 109 110 return $oRule; 111 } 112 113 /** 114 * @param string $sRule 115 * 116 * @return array<int, string> 117 */ 118 private static function listDelimiterForRule($sRule) 119 { 120 if (preg_match('/^font($|-)/', $sRule)) { 121 return [',', '/', ' ']; 122 } 123 return [',', ' ', '/']; 124 } 125 126 /** 127 * @return int 128 */ 129 public function getLineNo() 130 { 131 return $this->iLineNo; 132 } 133 134 /** 135 * @return int 136 */ 137 public function getColNo() 138 { 139 return $this->iColNo; 140 } 141 142 /** 143 * @param int $iLine 144 * @param int $iColumn 145 * 146 * @return void 147 */ 148 public function setPosition($iLine, $iColumn) 149 { 150 $this->iColNo = $iColumn; 151 $this->iLineNo = $iLine; 152 } 153 154 /** 155 * @param string $sRule 156 * 157 * @return void 158 */ 159 public function setRule($sRule) 160 { 161 $this->sRule = $sRule; 162 } 163 164 /** 165 * @return string 166 */ 167 public function getRule() 168 { 169 return $this->sRule; 170 } 171 172 /** 173 * @return RuleValueList|null 174 */ 175 public function getValue() 176 { 177 return $this->mValue; 178 } 179 180 /** 181 * @param RuleValueList|null $mValue 182 * 183 * @return void 184 */ 185 public function setValue($mValue) 186 { 187 $this->mValue = $mValue; 188 } 189 190 /** 191 * @param array<array-key, array<array-key, RuleValueList>> $aSpaceSeparatedValues 192 * 193 * @return RuleValueList 194 * 195 * @deprecated will be removed in version 9.0 196 * Old-Style 2-dimensional array given. Retained for (some) backwards-compatibility. 197 * Use `setValue()` instead and wrap the value inside a RuleValueList if necessary. 198 */ 199 public function setValues(array $aSpaceSeparatedValues) 200 { 201 $oSpaceSeparatedList = null; 202 if (count($aSpaceSeparatedValues) > 1) { 203 $oSpaceSeparatedList = new RuleValueList(' ', $this->iLineNo); 204 } 205 foreach ($aSpaceSeparatedValues as $aCommaSeparatedValues) { 206 $oCommaSeparatedList = null; 207 if (count($aCommaSeparatedValues) > 1) { 208 $oCommaSeparatedList = new RuleValueList(',', $this->iLineNo); 209 } 210 foreach ($aCommaSeparatedValues as $mValue) { 211 if (!$oSpaceSeparatedList && !$oCommaSeparatedList) { 212 $this->mValue = $mValue; 213 return $mValue; 214 } 215 if ($oCommaSeparatedList) { 216 $oCommaSeparatedList->addListComponent($mValue); 217 } else { 218 $oSpaceSeparatedList->addListComponent($mValue); 219 } 220 } 221 if (!$oSpaceSeparatedList) { 222 $this->mValue = $oCommaSeparatedList; 223 return $oCommaSeparatedList; 224 } else { 225 $oSpaceSeparatedList->addListComponent($oCommaSeparatedList); 226 } 227 } 228 $this->mValue = $oSpaceSeparatedList; 229 return $oSpaceSeparatedList; 230 } 231 232 /** 233 * @return array<int, array<int, RuleValueList>> 234 * 235 * @deprecated will be removed in version 9.0 236 * Old-Style 2-dimensional array returned. Retained for (some) backwards-compatibility. 237 * Use `getValue()` instead and check for the existence of a (nested set of) ValueList object(s). 238 */ 239 public function getValues() 240 { 241 if (!$this->mValue instanceof RuleValueList) { 242 return [[$this->mValue]]; 243 } 244 if ($this->mValue->getListSeparator() === ',') { 245 return [$this->mValue->getListComponents()]; 246 } 247 $aResult = []; 248 foreach ($this->mValue->getListComponents() as $mValue) { 249 if (!$mValue instanceof RuleValueList || $mValue->getListSeparator() !== ',') { 250 $aResult[] = [$mValue]; 251 continue; 252 } 253 if ($this->mValue->getListSeparator() === ' ' || count($aResult) === 0) { 254 $aResult[] = []; 255 } 256 foreach ($mValue->getListComponents() as $mValue) { 257 $aResult[count($aResult) - 1][] = $mValue; 258 } 259 } 260 return $aResult; 261 } 262 263 /** 264 * Adds a value to the existing value. Value will be appended if a `RuleValueList` exists of the given type. 265 * Otherwise, the existing value will be wrapped by one. 266 * 267 * @param RuleValueList|array<int, RuleValueList> $mValue 268 * @param string $sType 269 * 270 * @return void 271 */ 272 public function addValue($mValue, $sType = ' ') 273 { 274 if (!is_array($mValue)) { 275 $mValue = [$mValue]; 276 } 277 if (!$this->mValue instanceof RuleValueList || $this->mValue->getListSeparator() !== $sType) { 278 $mCurrentValue = $this->mValue; 279 $this->mValue = new RuleValueList($sType, $this->iLineNo); 280 if ($mCurrentValue) { 281 $this->mValue->addListComponent($mCurrentValue); 282 } 283 } 284 foreach ($mValue as $mValueItem) { 285 $this->mValue->addListComponent($mValueItem); 286 } 287 } 288 289 /** 290 * @param int $iModifier 291 * 292 * @return void 293 */ 294 public function addIeHack($iModifier) 295 { 296 $this->aIeHack[] = $iModifier; 297 } 298 299 /** 300 * @param array<int, int> $aModifiers 301 * 302 * @return void 303 */ 304 public function setIeHack(array $aModifiers) 305 { 306 $this->aIeHack = $aModifiers; 307 } 308 309 /** 310 * @return array<int, int> 311 */ 312 public function getIeHack() 313 { 314 return $this->aIeHack; 315 } 316 317 /** 318 * @param bool $bIsImportant 319 * 320 * @return void 321 */ 322 public function setIsImportant($bIsImportant) 323 { 324 $this->bIsImportant = $bIsImportant; 325 } 326 327 /** 328 * @return bool 329 */ 330 public function getIsImportant() 331 { 332 return $this->bIsImportant; 333 } 334 335 /** 336 * @return string 337 */ 338 public function __toString() 339 { 340 return $this->render(new OutputFormat()); 341 } 342 343 /** 344 * @return string 345 */ 346 public function render(OutputFormat $oOutputFormat) 347 { 348 $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}"; 349 if ($this->mValue instanceof Value) { //Can also be a ValueList 350 $sResult .= $this->mValue->render($oOutputFormat); 351 } else { 352 $sResult .= $this->mValue; 353 } 354 if (!empty($this->aIeHack)) { 355 $sResult .= ' \\' . implode('\\', $this->aIeHack); 356 } 357 if ($this->bIsImportant) { 358 $sResult .= ' !important'; 359 } 360 $sResult .= ';'; 361 return $sResult; 362 } 363 364 /** 365 * @param array<array-key, Comment> $aComments 366 * 367 * @return void 368 */ 369 public function addComments(array $aComments) 370 { 371 $this->aComments = array_merge($this->aComments, $aComments); 372 } 373 374 /** 375 * @return array<array-key, Comment> 376 */ 377 public function getComments() 378 { 379 return $this->aComments; 380 } 381 382 /** 383 * @param array<array-key, Comment> $aComments 384 * 385 * @return void 386 */ 387 public function setComments(array $aComments) 388 { 389 $this->aComments = $aComments; 390 } 391 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body