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