MDL-62294 lessphp: Fix for PHP 7.2 compatibility
[moodle.git] / lib / lessphp / Visitor / toCSS.php
CommitLineData
d433cf37
FM
1<?php
2
3/**
4 * toCSS Visitor
5 *
6 * @package Less
7 * @subpackage visitor
8 */
9class Less_Visitor_toCSS extends Less_VisitorReplacing{
10
11 private $charset;
12
657b1278 13 public function __construct(){
d433cf37
FM
14 parent::__construct();
15 }
16
17 /**
18 * @param Less_Tree_Ruleset $root
19 */
657b1278 20 public function run( $root ){
d433cf37
FM
21 return $this->visitObj($root);
22 }
23
657b1278 24 public function visitRule( $ruleNode ){
d433cf37
FM
25 if( $ruleNode->variable ){
26 return array();
27 }
28 return $ruleNode;
29 }
30
657b1278 31 public function visitMixinDefinition($mixinNode){
d433cf37
FM
32 // mixin definitions do not get eval'd - this means they keep state
33 // so we have to clear that state here so it isn't used if toCSS is called twice
34 $mixinNode->frames = array();
35 return array();
36 }
37
657b1278 38 public function visitExtend(){
d433cf37
FM
39 return array();
40 }
41
657b1278 42 public function visitComment( $commentNode ){
d433cf37
FM
43 if( $commentNode->isSilent() ){
44 return array();
45 }
46 return $commentNode;
47 }
48
657b1278 49 public function visitMedia( $mediaNode, &$visitDeeper ){
d433cf37
FM
50 $mediaNode->accept($this);
51 $visitDeeper = false;
52
53 if( !$mediaNode->rules ){
54 return array();
55 }
56 return $mediaNode;
57 }
58
657b1278 59 public function visitDirective( $directiveNode ){
d433cf37
FM
60 if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){
61 return array();
62 }
63 if( $directiveNode->name === '@charset' ){
64 // Only output the debug info together with subsequent @charset definitions
65 // a comment (or @media statement) before the actual @charset directive would
66 // be considered illegal css as it has to be on the first line
67 if( isset($this->charset) && $this->charset ){
68
69 //if( $directiveNode->debugInfo ){
70 // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
71 // $comment->debugInfo = $directiveNode->debugInfo;
72 // return $this->visit($comment);
73 //}
74
75
76 return array();
77 }
78 $this->charset = true;
79 }
80 return $directiveNode;
81 }
82
657b1278 83 public function checkPropertiesInRoot( $rulesetNode ){
d433cf37
FM
84
85 if( !$rulesetNode->firstRoot ){
86 return;
87 }
88
89 foreach($rulesetNode->rules as $ruleNode){
90 if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){
91 $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null);
92 throw new Less_Exception_Compiler($msg);
93 }
94 }
95 }
96
97
657b1278 98 public function visitRuleset( $rulesetNode, &$visitDeeper ){
d433cf37
FM
99
100 $visitDeeper = false;
101
102 $this->checkPropertiesInRoot( $rulesetNode );
103
104 if( $rulesetNode->root ){
105 return $this->visitRulesetRoot( $rulesetNode );
106 }
107
108 $rulesets = array();
109 $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
110
111
112 // Compile rules and rulesets
cde2530c 113 $nodeRuleCnt = $rulesetNode->rules?count($rulesetNode->rules):0;
d433cf37
FM
114 for( $i = 0; $i < $nodeRuleCnt; ){
115 $rule = $rulesetNode->rules[$i];
116
117 if( property_exists($rule,'rules') ){
118 // visit because we are moving them out from being a child
119 $rulesets[] = $this->visitObj($rule);
120 array_splice($rulesetNode->rules,$i,1);
121 $nodeRuleCnt--;
122 continue;
123 }
124 $i++;
125 }
126
127
128 // accept the visitor to remove rules and refactor itself
129 // then we can decide now whether we want it or not
130 if( $nodeRuleCnt > 0 ){
131 $rulesetNode->accept($this);
132
133 if( $rulesetNode->rules ){
134
135 if( count($rulesetNode->rules) > 1 ){
136 $this->_mergeRules( $rulesetNode->rules );
137 $this->_removeDuplicateRules( $rulesetNode->rules );
138 }
139
140 // now decide whether we keep the ruleset
141 if( $rulesetNode->paths ){
142 //array_unshift($rulesets, $rulesetNode);
143 array_splice($rulesets,0,0,array($rulesetNode));
144 }
145 }
146
147 }
148
149
150 if( count($rulesets) === 1 ){
151 return $rulesets[0];
152 }
153 return $rulesets;
154 }
155
156
157 /**
158 * Helper function for visitiRuleset
159 *
160 * return array|Less_Tree_Ruleset
161 */
162 private function visitRulesetRoot( $rulesetNode ){
163 $rulesetNode->accept( $this );
164 if( $rulesetNode->firstRoot || $rulesetNode->rules ){
165 return $rulesetNode;
166 }
167 return array();
168 }
169
170
171 /**
172 * Helper function for visitRuleset()
173 *
174 * @return array
175 */
176 private function visitRulesetPaths($rulesetNode){
177
178 $paths = array();
179 foreach($rulesetNode->paths as $p){
180 if( $p[0]->elements[0]->combinator === ' ' ){
181 $p[0]->elements[0]->combinator = '';
182 }
183
184 foreach($p as $pi){
185 if( $pi->getIsReferenced() && $pi->getIsOutput() ){
186 $paths[] = $p;
187 break;
188 }
189 }
190 }
191
192 return $paths;
193 }
194
657b1278 195 protected function _removeDuplicateRules( &$rules ){
d433cf37
FM
196 // remove duplicates
197 $ruleCache = array();
198 for( $i = count($rules)-1; $i >= 0 ; $i-- ){
199 $rule = $rules[$i];
200 if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){
201
202 if( !isset($ruleCache[$rule->name]) ){
203 $ruleCache[$rule->name] = $rule;
204 }else{
205 $ruleList =& $ruleCache[$rule->name];
206
207 if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){
208 $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
209 }
210
211 $ruleCSS = $rule->toCSS();
212 if( array_search($ruleCSS,$ruleList) !== false ){
213 array_splice($rules,$i,1);
214 }else{
215 $ruleList[] = $ruleCSS;
216 }
217 }
218 }
219 }
220 }
221
657b1278 222 protected function _mergeRules( &$rules ){
d433cf37
FM
223 $groups = array();
224
225 //obj($rules);
226
227 $rules_len = count($rules);
228 for( $i = 0; $i < $rules_len; $i++ ){
229 $rule = $rules[$i];
230
231 if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){
232
233 $key = $rule->name;
234 if( $rule->important ){
235 $key .= ',!';
236 }
237
238 if( !isset($groups[$key]) ){
239 $groups[$key] = array();
240 }else{
241 array_splice($rules, $i--, 1);
242 $rules_len--;
243 }
244
245 $groups[$key][] = $rule;
246 }
247 }
248
249
250 foreach($groups as $parts){
251
252 if( count($parts) > 1 ){
253 $rule = $parts[0];
254 $spacedGroups = array();
255 $lastSpacedGroup = array();
256 $parts_mapped = array();
257 foreach($parts as $p){
258 if( $p->merge === '+' ){
259 if( $lastSpacedGroup ){
260 $spacedGroups[] = self::toExpression($lastSpacedGroup);
261 }
262 $lastSpacedGroup = array();
263 }
264 $lastSpacedGroup[] = $p;
265 }
266
267 $spacedGroups[] = self::toExpression($lastSpacedGroup);
268 $rule->value = self::toValue($spacedGroups);
269 }
270 }
271
272 }
273
657b1278 274 public static function toExpression($values){
d433cf37
FM
275 $mapped = array();
276 foreach($values as $p){
277 $mapped[] = $p->value;
278 }
279 return new Less_Tree_Expression( $mapped );
280 }
281
657b1278 282 public static function toValue($values){
d433cf37
FM
283 //return new Less_Tree_Value($values); ??
284
285 $mapped = array();
286 foreach($values as $p){
287 $mapped[] = $p;
288 }
289 return new Less_Tree_Value($mapped);
290 }
291}
292