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