Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.80% covered (success)
96.80%
121 / 125
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Less_Tree_Mixin_Definition
96.80% covered (success)
96.80%
121 / 125
71.43% covered (warning)
71.43%
5 / 7
53
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 compileParams
94.44% covered (success)
94.44%
51 / 54
0.00% covered (danger)
0.00%
0 / 1
23.09
 compile
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 evalCall
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
3
 matchCondition
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
3.00
 makeImportant
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
 matchArgs
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
11
1<?php
2/**
3 * @private
4 */
5class 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}