Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.18% covered (success)
98.18%
54 / 55
92.86% covered (success)
92.86%
13 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
Less_Environment
98.18% covered (success)
98.18%
54 / 55
92.86% covered (success)
92.86%
13 / 14
26
0.00% covered (danger)
0.00%
0 / 1
 Init
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 addParsedFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clone
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 isFileParsed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 copyEvalEnv
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 isMathOn
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
6
 inParenthesis
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 outOfParenthesis
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isPathRelative
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 enterCalc
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 exitCalc
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 normalizePath
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
7
 unshiftFrame
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 shiftFrame
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * @private
4 */
5class Less_Environment {
6
7    /**
8     * Information about the current file - for error reporting and importing and making urls relative etc.
9     *
10     * - rootpath: rootpath to append to URLs
11     *
12     * @var array|null
13     */
14    public $currentFileInfo;
15
16    /** @var bool Whether we are currently importing multiple copies */
17    public $importMultiple = false;
18
19    /**
20     * @var array
21     */
22    public $frames = [];
23    /** @var array */
24    public $importantScope = [];
25    public $inCalc = false;
26    public $mathOn = true;
27
28    private $calcStack = [];
29
30    /** @var Less_Tree_Media[] */
31    public $mediaBlocks = [];
32    /** @var Less_Tree_Media[] */
33    public $mediaPath = [];
34
35    /** @var string[] */
36    public $imports = [];
37
38    /**
39     * This is the equivalent of `importVisitor.onceFileDetectionMap`
40     * as used by the dynamic `importNode.skip` function.
41     *
42     * @see less-2.5.3.js#ImportVisitor.prototype.onImported
43     * @var array<string,true>
44     */
45    public $importVisitorOnceMap = [];
46
47    public static $tabLevel = 0;
48
49    public static $lastRule = false;
50
51    public static $_noSpaceCombinators;
52
53    public static $mixin_stack = 0;
54
55    public $math = self::MATH_PARENS_DIVISION;
56
57    public $importCallback = null;
58
59    public $parensStack = [];
60
61    public const MATH_ALWAYS = 0;
62    public const MATH_PARENS_DIVISION = 1;
63    public const MATH_PARENS = 2;
64
65    /**
66     * @var array
67     */
68    public $functions = [];
69
70    public function Init() {
71        self::$tabLevel = 0;
72        self::$lastRule = false;
73        self::$mixin_stack = 0;
74
75        self::$_noSpaceCombinators = [
76            '' => true,
77            ' ' => true,
78            '|' => true
79        ];
80    }
81
82    /**
83     * @param string $file
84     * @return void
85     */
86    public function addParsedFile( $file ) {
87        $this->imports[] = $file;
88    }
89
90    public function clone() {
91        $new_env = clone $this;
92        // NOTE: Match JavaScript by-ref behaviour for arrays
93        $new_env->imports =& $this->imports;
94        $new_env->importVisitorOnceMap =& $this->importVisitorOnceMap;
95        return $new_env;
96    }
97
98    /**
99     * @param string $file
100     * @return bool
101     */
102    public function isFileParsed( $file ) {
103        return in_array( $file, $this->imports );
104    }
105
106    public function copyEvalEnv( $frames = [] ) {
107        $new_env = new self();
108        $new_env->frames = $frames;
109        $new_env->importantScope = $this->importantScope;
110        $new_env->math = $this->math;
111        return $new_env;
112    }
113
114    /**
115     * @return bool
116     * @see less-3.13.1.js#Eval.prototype.isMathOn
117     */
118    public function isMathOn( $op = "" ) {
119        if ( !$this->mathOn ) {
120            return false;
121        }
122        if ( $op === '/' && $this->math !== $this::MATH_ALWAYS && !$this->parensStack ) {
123            return false;
124        }
125
126        if ( $this->math > $this::MATH_PARENS_DIVISION ) {
127            return (bool)$this->parensStack;
128        }
129        return true;
130    }
131
132    /**
133     * @see less-3.13.1.js#Eval.prototype.inParenthesis
134     */
135    public function inParenthesis() {
136        // Optimization: We don't need undefined/null, always have an array
137        $this->parensStack[] = true;
138    }
139
140    /**
141     * @see less-3.13.1.js#Eval.prototype.inParenthesis
142     */
143    public function outOfParenthesis() {
144        array_pop( $this->parensStack );
145    }
146
147    /**
148     * @param string $path
149     * @return bool
150     * @see less-2.5.3.js#Eval.isPathRelative
151     */
152    public static function isPathRelative( $path ) {
153        return !preg_match( '/^(?:[a-z-]+:|\/|#)/', $path );
154    }
155
156    public function enterCalc() {
157        $this->calcStack[] = true;
158        $this->inCalc = true;
159    }
160
161    public function exitCalc() {
162        array_pop( $this->calcStack );
163        if ( !$this->calcStack ) {
164            $this->inCalc = false;
165        }
166    }
167
168    /**
169     * Canonicalize a path by resolving references to '/./', '/../'
170     * Does not remove leading "../"
171     * @param string $path or url
172     * @return string Canonicalized path
173     */
174    public static function normalizePath( $path ) {
175        $segments = explode( '/', $path );
176        $segments = array_reverse( $segments );
177
178        $path = [];
179        $path_len = 0;
180
181        while ( $segments ) {
182            $segment = array_pop( $segments );
183            switch ( $segment ) {
184
185                case '.':
186                    break;
187
188                case '..':
189                    // @phan-suppress-next-line PhanTypeInvalidDimOffset False positive
190                    if ( !$path_len || ( $path[$path_len - 1] === '..' ) ) {
191                        $path[] = $segment;
192                        $path_len++;
193                    } else {
194                        array_pop( $path );
195                        $path_len--;
196                    }
197                    break;
198
199                default:
200                    $path[] = $segment;
201                    $path_len++;
202                    break;
203            }
204        }
205
206        return implode( '/', $path );
207    }
208
209    public function unshiftFrame( $frame ) {
210        array_unshift( $this->frames, $frame );
211    }
212
213    public function shiftFrame() {
214        return array_shift( $this->frames );
215    }
216
217}