Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
96.80% |
121 / 125 |
|
71.43% |
5 / 7 |
CRAP | |
0.00% |
0 / 1 |
Less_Tree_Mixin_Definition | |
96.80% |
121 / 125 |
|
71.43% |
5 / 7 |
53 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
6 | |||
compileParams | |
94.44% |
51 / 54 |
|
0.00% |
0 / 1 |
23.09 | |||
compile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
evalCall | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
3 | |||
matchCondition | |
92.86% |
13 / 14 |
|
0.00% |
0 / 1 |
3.00 | |||
makeImportant | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
5 | |||
matchArgs | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
11 |
1 | <?php |
2 | /** |
3 | * @private |
4 | */ |
5 | class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset { |
6 | public $name; |
7 | public $selectors; |
8 | public $params; |
9 | public $arity = 0; |
10 | public $rules; |
11 | public $lookups = []; |
12 | public $required = 0; |
13 | public $frames = []; |
14 | public $condition; |
15 | public $variadic; |
16 | public $optionalParameters = []; |
17 | |
18 | public function __construct( $name, $params, $rules, $condition, $variadic = false, $frames = [] ) { |
19 | $this->name = $name; |
20 | $this->selectors = [ new Less_Tree_Selector( [ new Less_Tree_Element( null, $name ) ] ) ]; |
21 | |
22 | $this->params = $params; |
23 | $this->condition = $condition; |
24 | $this->variadic = $variadic; |
25 | $this->rules = $rules; |
26 | |
27 | if ( $params ) { |
28 | $this->arity = count( $params ); |
29 | foreach ( $params as $p ) { |
30 | if ( !isset( $p['name'] ) || ( $p['name'] && !isset( $p['value'] ) ) ) { |
31 | $this->required++; |
32 | } else { |
33 | $this->optionalParameters[ (string)$p['name'] ] = true; |
34 | } |
35 | } |
36 | } |
37 | |
38 | $this->frames = $frames; |
39 | $this->SetRulesetIndex(); |
40 | } |
41 | |
42 | /** |
43 | * @param Less_Environment $env |
44 | * @see less-2.5.3.js#Definition.prototype.evalParams |
45 | */ |
46 | public function compileParams( $env, $mixinFrames, $args = [], &$evaldArguments = [] ) { |
47 | $frame = new Less_Tree_Ruleset( null, [] ); |
48 | $params = $this->params; |
49 | $mixinEnv = null; |
50 | $argsLength = 0; |
51 | |
52 | if ( $args ) { |
53 | $argsLength = count( $args ); |
54 | for ( $i = 0; $i < $argsLength; $i++ ) { |
55 | $arg = $args[$i]; |
56 | |
57 | if ( $arg && $arg['name'] ) { |
58 | $isNamedFound = false; |
59 | |
60 | foreach ( $params as $j => $param ) { |
61 | if ( !isset( $evaldArguments[$j] ) && $arg['name'] === $param['name'] ) { |
62 | $evaldArguments[$j] = $arg['value']->compile( $env ); |
63 | array_unshift( $frame->rules, new Less_Tree_Declaration( $arg['name'], $arg['value']->compile( $env ) ) ); |
64 | $isNamedFound = true; |
65 | break; |
66 | } |
67 | } |
68 | if ( $isNamedFound ) { |
69 | array_splice( $args, $i, 1 ); |
70 | $i--; |
71 | $argsLength--; |
72 | } else { |
73 | throw new Less_Exception_Compiler( "Named argument for " . $this->name . ' ' . $args[$i]['name'] . ' not found' ); |
74 | } |
75 | } |
76 | } |
77 | } |
78 | |
79 | $argIndex = 0; |
80 | foreach ( $params as $i => $param ) { |
81 | if ( isset( $evaldArguments[$i] ) ) { |
82 | continue; |
83 | } |
84 | |
85 | $arg = $args[$argIndex] ?? null; |
86 | |
87 | $name = $param['name'] ?? null; |
88 | if ( $name ) { |
89 | if ( isset( $param['variadic'] ) ) { |
90 | $varargs = []; |
91 | for ( $j = $argIndex; $j < $argsLength; $j++ ) { |
92 | $varargs[] = $args[$j]['value']->compile( $env ); |
93 | } |
94 | $expression = new Less_Tree_Expression( $varargs ); |
95 | array_unshift( $frame->rules, new Less_Tree_Declaration( $name, $expression->compile( $env ) ) ); |
96 | } else { |
97 | $val = ( $arg && $arg['value'] ) ? $arg['value'] : false; |
98 | |
99 | if ( $val ) { |
100 | // This was a mixin call, pass in a detached ruleset of it's eval'd rules |
101 | if ( is_array( $val ) ) { |
102 | $val = new Less_Tree_DetachedRuleset( new Less_Tree_Ruleset( null, $val ) ); |
103 | } else { |
104 | $val = $val->compile( $env ); |
105 | } |
106 | } elseif ( isset( $param['value'] ) ) { |
107 | |
108 | if ( !$mixinEnv ) { |
109 | $mixinEnv = $env->copyEvalEnv( array_merge( [ $frame ], $mixinFrames ) ); |
110 | } |
111 | |
112 | $val = $param['value']->compile( $mixinEnv ); |
113 | $frame->resetCache(); |
114 | } else { |
115 | throw new Less_Exception_Compiler( "Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")" ); |
116 | } |
117 | |
118 | array_unshift( $frame->rules, new Less_Tree_Declaration( $name, $val ) ); |
119 | $evaldArguments[$i] = $val; |
120 | } |
121 | } |
122 | |
123 | if ( isset( $param['variadic'] ) && $args ) { |
124 | for ( $j = $argIndex; $j < $argsLength; $j++ ) { |
125 | $evaldArguments[$j] = $args[$j]['value']->compile( $env ); |
126 | } |
127 | } |
128 | $argIndex++; |
129 | } |
130 | |
131 | ksort( $evaldArguments ); |
132 | $evaldArguments = array_values( $evaldArguments ); |
133 | |
134 | return $frame; |
135 | } |
136 | |
137 | public function compile( $env ) { |
138 | if ( $this->frames ) { |
139 | return new self( $this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames ); |
140 | } |
141 | return new self( $this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames ); |
142 | } |
143 | |
144 | /** |
145 | * @param Less_Environment $env |
146 | * @param array|null $args |
147 | * @param bool|null $important |
148 | * @return Less_Tree_Ruleset |
149 | */ |
150 | public function evalCall( $env, $args = null, $important = null ) { |
151 | Less_Environment::$mixin_stack++; |
152 | |
153 | $_arguments = []; |
154 | |
155 | if ( $this->frames ) { |
156 | $mixinFrames = array_merge( $this->frames, $env->frames ); |
157 | } else { |
158 | $mixinFrames = $env->frames; |
159 | } |
160 | |
161 | $frame = $this->compileParams( $env, $mixinFrames, $args, $_arguments ); |
162 | |
163 | $ex = new Less_Tree_Expression( $_arguments ); |
164 | array_unshift( $frame->rules, new Less_Tree_Declaration( '@arguments', $ex->compile( $env ) ) ); |
165 | |
166 | $ruleset = new Less_Tree_Ruleset( null, $this->rules ); |
167 | $ruleset->originalRuleset = $this->ruleset_id; |
168 | |
169 | $ruleSetEnv = $env->copyEvalEnv( array_merge( [ $this, $frame ], $mixinFrames ) ); |
170 | $ruleset = $ruleset->compile( $ruleSetEnv ); |
171 | |
172 | if ( $important ) { |
173 | $ruleset = $ruleset->makeImportant(); |
174 | } |
175 | |
176 | Less_Environment::$mixin_stack--; |
177 | |
178 | return $ruleset; |
179 | } |
180 | |
181 | /** |
182 | * @param array $args |
183 | * @param Less_Environment $env |
184 | * @return bool |
185 | */ |
186 | public function matchCondition( $args, $env ) { |
187 | if ( !$this->condition ) { |
188 | return true; |
189 | } |
190 | |
191 | // set array to prevent error on array_merge |
192 | if ( !is_array( $this->frames ) ) { |
193 | $this->frames = []; |
194 | } |
195 | |
196 | $frame = $this->compileParams( $env, array_merge( $this->frames, $env->frames ), $args ); |
197 | |
198 | $compile_env = $env->copyEvalEnv( |
199 | array_merge( |
200 | [ $frame ], // the parameter variables |
201 | $this->frames, // the parent namespace/mixin frames |
202 | $env->frames // the current environment frames |
203 | ) |
204 | ); |
205 | $compile_env->functions = $env->functions; |
206 | |
207 | return (bool)$this->condition->compile( $compile_env ); |
208 | } |
209 | |
210 | public function makeImportant() { |
211 | $important_rules = []; |
212 | foreach ( $this->rules as $rule ) { |
213 | if ( $rule instanceof Less_Tree_Declaration || $rule instanceof self || $rule instanceof Less_Tree_NameValue ) { |
214 | $important_rules[] = $rule->makeImportant(); |
215 | } else { |
216 | $important_rules[] = $rule; |
217 | } |
218 | } |
219 | return new self( $this->name, $this->params, $important_rules, $this->condition, $this->variadic, $this->frames ); |
220 | } |
221 | |
222 | /** |
223 | * @param array[] $args |
224 | * @param Less_Environment|null $env |
225 | * @see less-2.5.3.js#Definition.prototype.matchArgs |
226 | */ |
227 | public function matchArgs( $args, $env = null ) { |
228 | $allArgsCnt = count( $args ); |
229 | $requiredArgsCnt = 0; |
230 | foreach ( $args as $arg ) { |
231 | if ( !array_key_exists( $arg['name'], $this->optionalParameters ) ) { |
232 | $requiredArgsCnt++; |
233 | } |
234 | } |
235 | if ( !$this->variadic ) { |
236 | if ( $requiredArgsCnt < $this->required ) { |
237 | return false; |
238 | } |
239 | if ( $allArgsCnt > count( $this->params ) ) { |
240 | return false; |
241 | } |
242 | } else { |
243 | if ( $requiredArgsCnt < ( $this->required - 1 ) ) { |
244 | return false; |
245 | } |
246 | } |
247 | |
248 | $len = min( $requiredArgsCnt, $this->arity ); |
249 | |
250 | for ( $i = 0; $i < $len; $i++ ) { |
251 | if ( !isset( $this->params[$i]['name'] ) && !isset( $this->params[$i]['variadic'] ) ) { |
252 | if ( $args[$i]['value']->compile( $env )->toCSS() != $this->params[$i]['value']->compile( $env )->toCSS() ) { |
253 | return false; |
254 | } |
255 | } |
256 | } |
257 | |
258 | return true; |
259 | } |
260 | |
261 | } |