MDL-65759 library: Update php-css-parser to 8.3.0
[moodle.git] / lib / php-css-parser / Rule / Rule.php
1 <?php
3 namespace Sabberworm\CSS\Rule;
5 use Sabberworm\CSS\Comment\Commentable;
6 use Sabberworm\CSS\Parsing\ParserState;
7 use Sabberworm\CSS\Renderable;
8 use Sabberworm\CSS\Value\RuleValueList;
9 use Sabberworm\CSS\Value\Value;
11 /**
12  * RuleSets contains Rule objects which always have a key and a value.
13  * In CSS, Rules are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];”
14  */
15 class Rule implements Renderable, Commentable {
17         private $sRule;
18         private $mValue;
19         private $bIsImportant;
20         private $aIeHack;
21         protected $iLineNo;
22         protected $aComments;
24         public function __construct($sRule, $iLineNo = 0) {
25                 $this->sRule = $sRule;
26                 $this->mValue = null;
27                 $this->bIsImportant = false;
28                 $this->aIeHack = array();
29                 $this->iLineNo = $iLineNo;
30                 $this->aComments = array();
31         }
33         public static function parse(ParserState $oParserState) {
34                 $aComments = $oParserState->consumeWhiteSpace();
35                 $oRule = new Rule($oParserState->parseIdentifier(), $oParserState->currentLine());
36                 $oRule->setComments($aComments);
37                 $oRule->addComments($oParserState->consumeWhiteSpace());
38                 $oParserState->consume(':');
39                 $oValue = Value::parseValue($oParserState, self::listDelimiterForRule($oRule->getRule()));
40                 $oRule->setValue($oValue);
41                 if ($oParserState->getSettings()->bLenientParsing) {
42                         while ($oParserState->comes('\\')) {
43                                 $oParserState->consume('\\');
44                                 $oRule->addIeHack($oParserState->consume());
45                                 $oParserState->consumeWhiteSpace();
46                         }
47                 }
48                 $oParserState->consumeWhiteSpace();
49                 if ($oParserState->comes('!')) {
50                         $oParserState->consume('!');
51                         $oParserState->consumeWhiteSpace();
52                         $oParserState->consume('important');
53                         $oRule->setIsImportant(true);
54                 }
55                 $oParserState->consumeWhiteSpace();
56                 while ($oParserState->comes(';')) {
57                         $oParserState->consume(';');
58                 }
59                 $oParserState->consumeWhiteSpace();
61                 return $oRule;
62         }
64         private static function listDelimiterForRule($sRule) {
65                 if (preg_match('/^font($|-)/', $sRule)) {
66                         return array(',', '/', ' ');
67                 }
68                 return array(',', ' ', '/');
69         }
71         /**
72          * @return int
73          */
74         public function getLineNo() {
75                 return $this->iLineNo;
76         }
78         public function setRule($sRule) {
79                 $this->sRule = $sRule;
80         }
82         public function getRule() {
83                 return $this->sRule;
84         }
86         public function getValue() {
87                 return $this->mValue;
88         }
90         public function setValue($mValue) {
91                 $this->mValue = $mValue;
92         }
94         /**
95          *      @deprecated Old-Style 2-dimensional array given. Retained for (some) backwards-compatibility. Use setValue() instead and wrapp the value inside a RuleValueList if necessary.
96          */
97         public function setValues($aSpaceSeparatedValues) {
98                 $oSpaceSeparatedList = null;
99                 if (count($aSpaceSeparatedValues) > 1) {
100                         $oSpaceSeparatedList = new RuleValueList(' ', $this->iLineNo);
101                 }
102                 foreach ($aSpaceSeparatedValues as $aCommaSeparatedValues) {
103                         $oCommaSeparatedList = null;
104                         if (count($aCommaSeparatedValues) > 1) {
105                                 $oCommaSeparatedList = new RuleValueList(',', $this->iLineNo);
106                         }
107                         foreach ($aCommaSeparatedValues as $mValue) {
108                                 if (!$oSpaceSeparatedList && !$oCommaSeparatedList) {
109                                         $this->mValue = $mValue;
110                                         return $mValue;
111                                 }
112                                 if ($oCommaSeparatedList) {
113                                         $oCommaSeparatedList->addListComponent($mValue);
114                                 } else {
115                                         $oSpaceSeparatedList->addListComponent($mValue);
116                                 }
117                         }
118                         if (!$oSpaceSeparatedList) {
119                                 $this->mValue = $oCommaSeparatedList;
120                                 return $oCommaSeparatedList;
121                         } else {
122                                 $oSpaceSeparatedList->addListComponent($oCommaSeparatedList);
123                         }
124                 }
125                 $this->mValue = $oSpaceSeparatedList;
126                 return $oSpaceSeparatedList;
127         }
129         /**
130          *      @deprecated Old-Style 2-dimensional array returned. Retained for (some) backwards-compatibility. Use getValue() instead and check for the existance of a (nested set of) ValueList object(s).
131          */
132         public function getValues() {
133                 if (!$this->mValue instanceof RuleValueList) {
134                         return array(array($this->mValue));
135                 }
136                 if ($this->mValue->getListSeparator() === ',') {
137                         return array($this->mValue->getListComponents());
138                 }
139                 $aResult = array();
140                 foreach ($this->mValue->getListComponents() as $mValue) {
141                         if (!$mValue instanceof RuleValueList || $mValue->getListSeparator() !== ',') {
142                                 $aResult[] = array($mValue);
143                                 continue;
144                         }
145                         if ($this->mValue->getListSeparator() === ' ' || count($aResult) === 0) {
146                                 $aResult[] = array();
147                         }
148                         foreach ($mValue->getListComponents() as $mValue) {
149                                 $aResult[count($aResult) - 1][] = $mValue;
150                         }
151                 }
152                 return $aResult;
153         }
155         /**
156          * Adds a value to the existing value. Value will be appended if a RuleValueList exists of the given type. Otherwise, the existing value will be wrapped by one.
157          */
158         public function addValue($mValue, $sType = ' ') {
159                 if (!is_array($mValue)) {
160                         $mValue = array($mValue);
161                 }
162                 if (!$this->mValue instanceof RuleValueList || $this->mValue->getListSeparator() !== $sType) {
163                         $mCurrentValue = $this->mValue;
164                         $this->mValue = new RuleValueList($sType, $this->iLineNo);
165                         if ($mCurrentValue) {
166                                 $this->mValue->addListComponent($mCurrentValue);
167                         }
168                 }
169                 foreach ($mValue as $mValueItem) {
170                         $this->mValue->addListComponent($mValueItem);
171                 }
172         }
174         public function addIeHack($iModifier) {
175                 $this->aIeHack[] = $iModifier;
176         }
178         public function setIeHack(array $aModifiers) {
179                 $this->aIeHack = $aModifiers;
180         }
182         public function getIeHack() {
183                 return $this->aIeHack;
184         }
186         public function setIsImportant($bIsImportant) {
187                 $this->bIsImportant = $bIsImportant;
188         }
190         public function getIsImportant() {
191                 return $this->bIsImportant;
192         }
194         public function __toString() {
195                 return $this->render(new \Sabberworm\CSS\OutputFormat());
196         }
198         public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
199                 $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}";
200                 if ($this->mValue instanceof Value) { //Can also be a ValueList
201                         $sResult .= $this->mValue->render($oOutputFormat);
202                 } else {
203                         $sResult .= $this->mValue;
204                 }
205                 if (!empty($this->aIeHack)) {
206                         $sResult .= ' \\' . implode('\\', $this->aIeHack);
207                 }
208                 if ($this->bIsImportant) {
209                         $sResult .= ' !important';
210                 }
211                 $sResult .= ';';
212                 return $sResult;
213         }
215         /**
216          * @param array $aComments Array of comments.
217          */
218         public function addComments(array $aComments) {
219                 $this->aComments = array_merge($this->aComments, $aComments);
220         }
222         /**
223          * @return array
224          */
225         public function getComments() {
226                 return $this->aComments;
227         }
229         /**
230          * @param array $aComments Array containing Comment objects.
231          */
232         public function setComments(array $aComments) {
233                 $this->aComments = $aComments;
234         }