Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.72% covered (success)
98.72%
77 / 78
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Less_Tree_Call
98.72% covered (success)
98.72%
77 / 78
83.33% covered (warning)
83.33%
5 / 6
31
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 accept
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 functionCaller
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
5.03
 exitCalc
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 compile
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
1 / 1
18
 genCSS
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * @private
4 * @see less-3.13.1.js#Call.prototype
5 */
6class Less_Tree_Call extends Less_Tree {
7    public $name;
8    public $args;
9    public $calc;
10    public $index;
11    public $currentFileInfo;
12
13    public function __construct( $name, $args, $index, $currentFileInfo = null ) {
14        $this->name = $name;
15        $this->args = $args;
16        $this->calc = $name === 'calc';
17        $this->index = $index;
18        $this->currentFileInfo = $currentFileInfo;
19    }
20
21    public function accept( $visitor ) {
22        $this->args = $visitor->visitArray( $this->args );
23    }
24
25    /**
26     * @see less-2.5.3.js#functionCaller.prototype.call
27     */
28    private function functionCaller( $function, array $arguments ) {
29        // This code is terrible and should be replaced as per this issue...
30        // https://github.com/less/less.js/issues/2477
31        $filtered = [];
32        foreach ( $arguments as $argument ) {
33            if ( $argument instanceof Less_Tree_Comment ) {
34                continue;
35            }
36            $filtered[] = $argument;
37        }
38        foreach ( $filtered as $index => $argument ) {
39            if ( $argument instanceof Less_Tree_Expression ) {
40                $filtered[$index] = $argument->mapToFunctionCallArgument();
41            }
42        }
43        return $function( ...$filtered );
44    }
45
46    /**
47     * @param Less_Environment $env
48     * @return void
49     */
50    private function exitCalc( $env, $currentMathContext ) {
51        if ( $this->calc || $env->inCalc ) {
52            $env->exitCalc();
53        }
54        $env->mathOn = $currentMathContext;
55    }
56
57    //
58    // When evaluating a function call,
59    // we either find the function in Less_Functions,
60    // in which case we call it, passing the evaluated arguments,
61    // or we simply print it out as it literal CSS.
62    //
63    // The reason why we compile the arguments, is in the case one
64    // of them is a LESS variable that only PHP knows the value of,
65    // like: `saturate(@mycolor)`.
66    // The function should receive the value, not the variable.
67    // TODO less.js#3.13.1 provide better parity with upstream.
68    public function compile( $env ) {
69        /**
70         * Turn off math for calc(), and switch back on for evaluating nested functions
71         */
72        $currentMathContext = $env->mathOn;
73        $env->mathOn = !$this->calc;
74
75        if ( $this->calc || $env->inCalc ) {
76            $env->enterCalc();
77        }
78
79        $args = [];
80        foreach ( $this->args as $a ) {
81            $args[] = $a->compile( $env );
82        }
83
84        $env->mathOn = $currentMathContext;
85
86        $nameLC = strtolower( $this->name );
87        switch ( $nameLC ) {
88            case '%':
89                $nameLC = '_percent';
90                break;
91
92            case 'get-unit':
93                $nameLC = 'getunit';
94                break;
95
96            case 'data-uri':
97                $nameLC = 'datauri';
98                break;
99
100            case 'svg-gradient':
101                $nameLC = 'svggradient';
102                break;
103            case 'image-size':
104                $nameLC = 'imagesize';
105                break;
106            case 'image-width':
107                $nameLC = 'imagewidth';
108                break;
109            case 'image-height':
110                $nameLC = 'imageheight';
111                break;
112        }
113
114        $result = null;
115        if ( $nameLC === 'default' ) {
116            $result = Less_Tree_DefaultFunc::compile();
117        } else {
118            $func = null;
119            $functions = new Less_Functions( $env, $this->currentFileInfo );
120            $funcBuiltin = [ $functions, $nameLC ];
121            // Avoid method_exists() as that considers private utility functions too
122            if ( is_callable( $funcBuiltin ) ) {
123                $func = $funcBuiltin;
124            } elseif ( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
125                $func = $env->functions[$nameLC];
126            }
127            // If the function name isn't known to LESS, output it unchanged as CSS.
128            if ( $func ) {
129                try {
130                    $result = $this->functionCaller( $func, $args );
131                    $this->exitCalc( $env, $currentMathContext );
132                } catch ( Exception $e ) {
133                    // Preserve original trace, especially from custom functions.
134                    // https://github.com/wikimedia/less.php/issues/38
135                    throw new Less_Exception_Compiler(
136                        'error evaluating function `' . $this->name . '` ' . $e->getMessage()
137                            . ' index: ' . $this->index,
138                        $e
139                    );
140                }
141            }
142        }
143
144        if ( $result !== null ) {
145            return $result;
146        }
147        $this->exitCalc( $env, $currentMathContext );
148        return new self( $this->name, $args, $this->index, $this->currentFileInfo );
149    }
150
151    /**
152     * @see Less_Tree::genCSS
153     */
154    public function genCSS( $output ) {
155        $output->add( $this->name . '(', $this->currentFileInfo, $this->index );
156        $args_len = count( $this->args );
157        for ( $i = 0; $i < $args_len; $i++ ) {
158            $this->args[$i]->genCSS( $output );
159            if ( $i + 1 < $args_len ) {
160                $output->add( ', ' );
161            }
162        }
163
164        $output->add( ')' );
165    }
166
167}