Completed
Push — master ( 2b6e28...3d0129 )
by
unknown
10:26
created

Less_Functions::fadein()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 13
Ratio 100 %
Metric Value
dl 13
loc 13
rs 8.8571
cc 5
eloc 9
nc 3
nop 2
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 12 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
require_once( dirname(__FILE__).'/Cache.php');
4
5
/**
6
 * Class for parsing and compiling less files into css
7
 *
8
 * @package Less
9
 * @subpackage parser
10
 *
11
 */
12
class Less_Parser{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
13
14
15
	/**
16
	 * Default parser options
17
	 */
18
	public static $default_options = array(
19
		'compress'				=> false,			// option - whether to compress
20
		'strictUnits'			=> false,			// whether units need to evaluate correctly
21
		'strictMath'			=> false,			// whether math has to be within parenthesis
22
		'relativeUrls'			=> true,			// option - whether to adjust URL's to be relative
23
		'urlArgs'				=> array(),			// whether to add args into url tokens
24
		'numPrecision'			=> 8,
25
26
		'import_dirs'			=> array(),
27
		'import_callback'		=> null,
28
		'cache_dir'				=> null,
29
		'cache_method'			=> 'php', 			// false, 'serialize', 'php', 'var_export', 'callback';
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
30
		'cache_callback_get'	=> null,
31
		'cache_callback_set'	=> null,
32
33
		'sourceMap'				=> false,			// whether to output a source map
34
		'sourceMapBasepath'		=> null,
35
		'sourceMapWriteTo'		=> null,
36
		'sourceMapURL'			=> null,
37
38
		'plugins'				=> array(),
39
40
	);
41
42
	public static $options = array();
43
44
45
	private $input;					// Less input string
46
	private $input_len;				// input string length
47
	private $pos;					// current index in `input`
48
	private $saveStack = array();	// holds state for backtracking
49
	private $furthest;
50
51
	/**
52
	 * @var Less_Environment
53
	 */
54
	private $env;
55
56
	protected $rules = array();
57
58
	private static $imports = array();
59
60
	public static $has_extends = false;
61
62
	public static $next_id = 0;
63
64
	/**
65
	 * Filename to contents of all parsed the files
66
	 *
67
	 * @var array
68
	 */
69
	public static $contentsMap = array();
70
71
72
	/**
73
	 * @param Less_Environment|array|null $env
74
	 */
75
	public function __construct( $env = null ){
76
77
		// Top parser on an import tree must be sure there is one "env"
78
		// which will then be passed around by reference.
79
		if( $env instanceof Less_Environment ){
80
			$this->env = $env;
81
		}else{
82
			$this->SetOptions(Less_Parser::$default_options);
83
			$this->Reset( $env );
84
		}
85
86
	}
87
88
89
	/**
90
	 * Reset the parser state completely
91
	 *
92
	 */
93
	public function Reset( $options = null ){
94
		$this->rules = array();
95
		self::$imports = array();
96
		self::$has_extends = false;
97
		self::$imports = array();
98
		self::$contentsMap = array();
99
100
		$this->env = new Less_Environment($options);
0 ignored issues
show
Unused Code introduced by
The call to Less_Environment::__construct() has too many arguments starting with $options.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
101
		$this->env->Init();
102
103
		//set new options
104
		if( is_array($options) ){
105
			$this->SetOptions(Less_Parser::$default_options);
106
			$this->SetOptions($options);
107
		}
108
	}
109
110
	/**
111
	 * Set one or more compiler options
112
	 *  options: import_dirs, cache_dir, cache_method
113
	 *
114
	 */
115
	public function SetOptions( $options ){
116
		foreach($options as $option => $value){
117
			$this->SetOption($option,$value);
118
		}
119
	}
120
121
	/**
122
	 * Set one compiler option
123
	 *
124
	 */
125
	public function SetOption($option,$value){
126
127
		switch($option){
128
129
			case 'import_dirs':
130
				$this->SetImportDirs($value);
131
			return;
132
133
			case 'cache_dir':
134
				if( is_string($value) ){
135
					Less_Cache::SetCacheDir($value);
136
					Less_Cache::CheckCacheDir();
137
				}
138
			return;
139
		}
140
141
		Less_Parser::$options[$option] = $value;
142
	}
143
144
	/**
145
	 * Registers a new custom function
146
	 *
147
	 * @param  string   $name     function name
148
	 * @param  callable $callback callback
149
	 */
150
	public function registerFunction($name, $callback) {
151
		$this->env->functions[$name] = $callback;
152
	}
153
154
	/**
155
	 * Removed an already registered function
156
	 *
157
	 * @param  string $name function name
158
	 */
159
	public function unregisterFunction($name) {
160
		if( isset($this->env->functions[$name]) )
161
			unset($this->env->functions[$name]);
162
	}
163
164
165
	/**
166
	 * Get the current css buffer
167
	 *
168
	 * @return string
169
	 */
170
	public function getCss(){
171
172
		$precision = ini_get('precision');
173
		@ini_set('precision',16);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
174
		$locale = setlocale(LC_NUMERIC, 0);
175
		setlocale(LC_NUMERIC, "C");
176
177
		try {
178
179
	 		$root = new Less_Tree_Ruleset(array(), $this->rules );
180
			$root->root = true;
181
			$root->firstRoot = true;
182
183
184
			$this->PreVisitors($root);
185
186
			self::$has_extends = false;
187
			$evaldRoot = $root->compile($this->env);
188
189
190
191
			$this->PostVisitors($evaldRoot);
192
193
			if( Less_Parser::$options['sourceMap'] ){
194
				$generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
195
				// will also save file
196
				// FIXME: should happen somewhere else?
197
				$css = $generator->generateCSS();
198
			}else{
199
				$css = $evaldRoot->toCSS();
200
			}
201
202
			if( Less_Parser::$options['compress'] ){
203
				$css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css);
204
			}
205
206
		} catch (Exception $exc) {
207
        	   // Intentional fall-through so we can reset environment
208
        	}
209
210
		//reset php settings
211
		@ini_set('precision',$precision);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
212
		setlocale(LC_NUMERIC, $locale);
213
214
		// Rethrow exception after we handled resetting the environment
215
		if (!empty($exc)) {
216
            		throw $exc;
217
        	}
218
219
		return $css;
0 ignored issues
show
Bug introduced by
The variable $css does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
220
	}
221
222
	/**
223
	 * Run pre-compile visitors
224
	 *
225
	 */
226
	private function PreVisitors($root){
227
228
		if( Less_Parser::$options['plugins'] ){
229
			foreach(Less_Parser::$options['plugins'] as $plugin){
230
				if( !empty($plugin->isPreEvalVisitor) ){
231
					$plugin->run($root);
232
				}
233
			}
234
		}
235
	}
236
237
238
	/**
239
	 * Run post-compile visitors
240
	 *
241
	 */
242
	private function PostVisitors($evaldRoot){
243
244
		$visitors = array();
245
		$visitors[] = new Less_Visitor_joinSelector();
246
		if( self::$has_extends ){
247
			$visitors[] = new Less_Visitor_processExtends();
248
		}
249
		$visitors[] = new Less_Visitor_toCSS();
250
251
252
		if( Less_Parser::$options['plugins'] ){
253
			foreach(Less_Parser::$options['plugins'] as $plugin){
254
				if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){
255
					continue;
256
				}
257
258
				if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){
259
					array_unshift( $visitors, $plugin);
260
				}else{
261
					$visitors[] = $plugin;
262
				}
263
			}
264
		}
265
266
267
		for($i = 0; $i < count($visitors); $i++ ){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
268
			$visitors[$i]->run($evaldRoot);
269
		}
270
271
	}
272
273
274
	/**
275
	 * Parse a Less string into css
276
	 *
277
	 * @param string $str The string to convert
278
	 * @param string $uri_root The url of the file
0 ignored issues
show
Bug introduced by
There is no parameter named $uri_root. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
279
	 * @return Less_Tree_Ruleset|Less_Parser
280
	 */
281
	public function parse( $str, $file_uri = null ){
282
283
		if( !$file_uri ){
284
			$uri_root = '';
285
			$filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
286
		}else{
287
			$file_uri = self::WinPath($file_uri);
288
			$filename = $file_uri;
289
			$uri_root = dirname($file_uri);
290
		}
291
292
		$previousFileInfo = $this->env->currentFileInfo;
293
		$uri_root = self::WinPath($uri_root);
294
		$this->SetFileInfo($filename, $uri_root);
295
296
		$this->input = $str;
297
		$this->_parse();
298
299
		if( $previousFileInfo ){
300
			$this->env->currentFileInfo = $previousFileInfo;
301
		}
302
303
		return $this;
304
	}
305
306
307
	/**
308
	 * Parse a Less string from a given file
309
	 *
310
	 * @throws Less_Exception_Parser
311
	 * @param string $filename The file to parse
312
	 * @param string $uri_root The url of the file
313
	 * @param bool $returnRoot Indicates whether the return value should be a css string a root node
314
	 * @return Less_Tree_Ruleset|Less_Parser
315
	 */
316
	public function parseFile( $filename, $uri_root = '', $returnRoot = false){
317
318
		if( !file_exists($filename) ){
319
			$this->Error(sprintf('File `%s` not found.', $filename));
320
		}
321
322
323
		// fix uri_root?
324
		// Instead of The mixture of file path for the first argument and directory path for the second argument has bee
325
		if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){
326
			$uri_root = dirname($uri_root);
327
		}
328
329
330
		$previousFileInfo = $this->env->currentFileInfo;
331
332
333
		if( $filename ){
334
			$filename = self::WinPath(realpath($filename));
335
		}
336
		$uri_root = self::WinPath($uri_root);
337
338
		$this->SetFileInfo($filename, $uri_root);
339
340
		self::AddParsedFile($filename);
341
342
		if( $returnRoot ){
343
			$rules = $this->GetRules( $filename );
344
			$return = new Less_Tree_Ruleset(array(), $rules );
345
		}else{
346
			$this->_parse( $filename );
347
			$return = $this;
348
		}
349
350
		if( $previousFileInfo ){
351
			$this->env->currentFileInfo = $previousFileInfo;
352
		}
353
354
		return $return;
355
	}
356
357
358
	/**
359
	 * Allows a user to set variables values
360
	 * @param array $vars
361
	 * @return Less_Parser
362
	 */
363
	public function ModifyVars( $vars ){
364
365
		$this->input = Less_Parser::serializeVars( $vars );
366
		$this->_parse();
367
368
		return $this;
369
	}
370
371
372
	/**
373
	 * @param string $filename
374
	 */
375
	public function SetFileInfo( $filename, $uri_root = ''){
376
377
		$filename = Less_Environment::normalizePath($filename);
378
		$dirname = preg_replace('/[^\/\\\\]*$/','',$filename);
379
380
		if( !empty($uri_root) ){
381
			$uri_root = rtrim($uri_root,'/').'/';
382
		}
383
384
		$currentFileInfo = array();
385
386
		//entry info
387
		if( isset($this->env->currentFileInfo) ){
388
			$currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
389
			$currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
390
			$currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
391
392
		}else{
393
			$currentFileInfo['entryPath'] = $dirname;
394
			$currentFileInfo['entryUri'] = $uri_root;
395
			$currentFileInfo['rootpath'] = $dirname;
396
		}
397
398
		$currentFileInfo['currentDirectory'] = $dirname;
399
		$currentFileInfo['currentUri'] = $uri_root.basename($filename);
400
		$currentFileInfo['filename'] = $filename;
401
		$currentFileInfo['uri_root'] = $uri_root;
402
403
404
		//inherit reference
405
		if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){
406
			$currentFileInfo['reference'] = true;
407
		}
408
409
		$this->env->currentFileInfo = $currentFileInfo;
410
	}
411
412
413
	/**
414
	 * @deprecated 1.5.1.2
415
	 *
416
	 */
417
	public function SetCacheDir( $dir ){
418
419
		if( !file_exists($dir) ){
420
			if( mkdir($dir) ){
421
				return true;
422
			}
423
			throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir);
424
425
		}elseif( !is_dir($dir) ){
426
			throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir);
427
428
		}elseif( !is_writable($dir) ){
429
			throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir);
430
431
		}else{
432
			$dir = self::WinPath($dir);
433
			Less_Cache::$cache_dir = rtrim($dir,'/').'/';
0 ignored issues
show
Documentation Bug introduced by
The property $cache_dir was declared of type boolean, but rtrim($dir, '/') . '/' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
434
			return true;
435
		}
436
	}
437
438
439
	/**
440
	 * Set a list of directories or callbacks the parser should use for determining import paths
441
	 *
442
	 * @param array $dirs
443
	 */
444
	public function SetImportDirs( $dirs ){
445
		Less_Parser::$options['import_dirs'] = array();
446
447
		foreach($dirs as $path => $uri_root){
448
449
			$path = self::WinPath($path);
450
			if( !empty($path) ){
451
				$path = rtrim($path,'/').'/';
452
			}
453
454
			if ( !is_callable($uri_root) ){
455
				$uri_root = self::WinPath($uri_root);
456
				if( !empty($uri_root) ){
457
					$uri_root = rtrim($uri_root,'/').'/';
458
				}
459
			}
460
461
			Less_Parser::$options['import_dirs'][$path] = $uri_root;
462
		}
463
	}
464
465
	/**
466
	 * @param string $file_path
467
	 */
468
	private function _parse( $file_path = null ){
469
		if (ini_get("mbstring.func_overload")) {
470
			$mb_internal_encoding = ini_get("mbstring.internal_encoding");
471
			@ini_set("mbstring.internal_encoding", "ascii");
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
472
		}
473
474
		$this->rules = array_merge($this->rules, $this->GetRules( $file_path ));
475
476
		//reset php settings
477
		if (isset($mb_internal_encoding)) {
478
			@ini_set("mbstring.internal_encoding", $mb_internal_encoding);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
479
		}
480
	}
481
482
483
	/**
484
	 * Return the results of parsePrimary for $file_path
485
	 * Use cache and save cached results if possible
486
	 *
487
	 * @param string|null $file_path
488
	 */
489
	private function GetRules( $file_path ){
490
491
		$this->SetInput($file_path);
492
493
		$cache_file = $this->CacheFile( $file_path );
494
		if( $cache_file ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $cache_file of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
495
			if( Less_Parser::$options['cache_method'] == 'callback' ){
496
				if( is_callable(Less_Parser::$options['cache_callback_get']) ){
497
					$cache = call_user_func_array(
498
						Less_Parser::$options['cache_callback_get'],
499
						array($this, $file_path, $cache_file)
500
					);
501
502
					if( $cache ){
503
						$this->UnsetInput();
504
						return $cache;
505
					}
506
				}
507
508
			}elseif( file_exists($cache_file) ){
509
				switch(Less_Parser::$options['cache_method']){
510
511
					// Using serialize
512
					// Faster but uses more memory
513
					case 'serialize':
514
						$cache = unserialize(file_get_contents($cache_file));
515
						if( $cache ){
516
							touch($cache_file);
517
							$this->UnsetInput();
518
							return $cache;
519
						}
520
					break;
521
522
523
					// Using generated php code
524
					case 'var_export':
525
					case 'php':
526
					$this->UnsetInput();
527
					return include($cache_file);
528
				}
529
			}
530
		}
531
532
		$rules = $this->parsePrimary();
533
534
		if( $this->pos < $this->input_len ){
535
			throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo);
0 ignored issues
show
Documentation introduced by
$this->env->currentFileInfo is of type array<string,?>, but the function expects a object<Less_FileInfo>|string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
536
		}
537
538
		$this->UnsetInput();
539
540
541
		//save the cache
542
		if( $cache_file ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $cache_file of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
543
			if( Less_Parser::$options['cache_method'] == 'callback' ){
544
				if( is_callable(Less_Parser::$options['cache_callback_set']) ){
545
					call_user_func_array(
546
						Less_Parser::$options['cache_callback_set'],
547
						array($this, $file_path, $cache_file, $rules)
548
					);
549
				}
550
551
			}else{
552
				//msg('write cache file');
553
				switch(Less_Parser::$options['cache_method']){
554
					case 'serialize':
555
						file_put_contents( $cache_file, serialize($rules) );
556
					break;
557
					case 'php':
558
						file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' );
559
					break;
560
					case 'var_export':
561
						//Requires __set_state()
562
						file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' );
563
					break;
564
				}
565
566
				Less_Cache::CleanCache();
567
			}
568
		}
569
570
		return $rules;
571
	}
572
573
574
	/**
575
	 * Set up the input buffer
576
	 *
577
	 */
578
	public function SetInput( $file_path ){
579
580
		if( $file_path ){
581
			$this->input = file_get_contents( $file_path );
582
		}
583
584
		$this->pos = $this->furthest = 0;
585
586
		// Remove potential UTF Byte Order Mark
587
		$this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input);
588
		$this->input_len = strlen($this->input);
589
590
591
		if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->env->currentFileInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
592
			$uri = $this->env->currentFileInfo['currentUri'];
593
			Less_Parser::$contentsMap[$uri] = $this->input;
594
		}
595
596
	}
597
598
599
	/**
600
	 * Free up some memory
601
	 *
602
	 */
603
	public function UnsetInput(){
604
		unset($this->input, $this->pos, $this->input_len, $this->furthest);
605
		$this->saveStack = array();
606
	}
607
608
609
	public function CacheFile( $file_path ){
610
611
		if( $file_path && $this->CacheEnabled() ){
612
613
			$env = get_object_vars($this->env);
614
			unset($env['frames']);
615
616
			$parts = array();
617
			$parts[] = $file_path;
618
			$parts[] = filesize( $file_path );
619
			$parts[] = filemtime( $file_path );
620
			$parts[] = $env;
621
			$parts[] = Less_Version::cache_version;
622
			$parts[] = Less_Parser::$options['cache_method'];
623
			return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache';
624
		}
625
	}
626
627
628
	static function AddParsedFile($file){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
629
		self::$imports[] = $file;
630
	}
631
632
	static function AllParsedFiles(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
633
		return self::$imports;
634
	}
635
636
	/**
637
	 * @param string $file
638
	 */
639
	static function FileParsed($file){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
640
		return in_array($file,self::$imports);
641
	}
642
643
644
	function save() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
645
		$this->saveStack[] = $this->pos;
646
	}
647
648
	private function restore() {
649
		$this->pos = array_pop($this->saveStack);
650
	}
651
652
	private function forget(){
653
		array_pop($this->saveStack);
654
	}
655
656
657
	private function isWhitespace($offset = 0) {
658
		return preg_match('/\s/',$this->input[ $this->pos + $offset]);
659
	}
660
661
	/**
662
	 * Parse from a token, regexp or string, and move forward if match
663
	 *
664
	 * @param array $toks
665
	 * @return array
666
	 */
667
	private function match($toks){
668
669
		// The match is confirmed, add the match length to `this::pos`,
670
		// and consume any extra white-space characters (' ' || '\n')
671
		// which come after that. The reason for this is that LeSS's
672
		// grammar is mostly white-space insensitive.
673
		//
674
675
		foreach($toks as $tok){
676
677
			$char = $tok[0];
678
679
			if( $char === '/' ){
680
				$match = $this->MatchReg($tok);
681
682
				if( $match ){
683
					return count($match) === 1 ? $match[0] : $match;
684
				}
685
686
			}elseif( $char === '#' ){
687
				$match = $this->MatchChar($tok[1]);
688
689
			}else{
690
				// Non-terminal, match using a function call
691
				$match = $this->$tok();
692
693
			}
694
695
			if( $match ){
696
				return $match;
697
			}
698
		}
699
	}
700
701
	/**
702
	 * @param string[] $toks
703
	 *
704
	 * @return string
705
	 */
706
	private function MatchFuncs($toks){
707
708
		if( $this->pos < $this->input_len ){
709
			foreach($toks as $tok){
710
				$match = $this->$tok();
711
				if( $match ){
712
					return $match;
713
				}
714
			}
715
		}
716
717
	}
718
719
	// Match a single character in the input,
720
	private function MatchChar($tok){
721
		if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){
722
			$this->skipWhitespace(1);
723
			return $tok;
724
		}
725
	}
726
727
	// Match a regexp from the current start point
728
	private function MatchReg($tok){
729
730
		if( preg_match($tok, $this->input, $match, 0, $this->pos) ){
731
			$this->skipWhitespace(strlen($match[0]));
732
			return $match;
733
		}
734
	}
735
736
737
	/**
738
	 * Same as match(), but don't change the state of the parser,
739
	 * just return the match.
740
	 *
741
	 * @param string $tok
742
	 * @return integer
743
	 */
744
	public function PeekReg($tok){
745
		return preg_match($tok, $this->input, $match, 0, $this->pos);
746
	}
747
748
	/**
749
	 * @param string $tok
750
	 */
751
	public function PeekChar($tok){
752
		//return ($this->input[$this->pos] === $tok );
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
753
		return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok );
754
	}
755
756
757
	/**
758
	 * @param integer $length
759
	 */
760
	public function skipWhitespace($length){
761
762
		$this->pos += $length;
763
764
		for(; $this->pos < $this->input_len; $this->pos++ ){
765
			$c = $this->input[$this->pos];
766
767
			if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){
768
				break;
769
			}
770
		}
771
	}
772
773
774
	/**
775
	 * @param string $tok
776
	 * @param string|null $msg
777
	 */
778 View Code Duplication
	public function expect($tok, $msg = NULL) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
779
		$result = $this->match( array($tok) );
780
		if (!$result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
781
			$this->Error( $msg	? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
782
		} else {
783
			return $result;
784
		}
785
	}
786
787
	/**
788
	 * @param string $tok
789
	 */
790 View Code Duplication
	public function expectChar($tok, $msg = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
791
		$result = $this->MatchChar($tok);
792
		if( !$result ){
793
			$this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
794
		}else{
795
			return $result;
796
		}
797
	}
798
799
	//
800
	// Here in, the parsing rules/functions
801
	//
802
	// The basic structure of the syntax tree generated is as follows:
803
	//
804
	//   Ruleset ->  Rule -> Value -> Expression -> Entity
805
	//
806
	// Here's some LESS code:
807
	//
808
	//	.class {
809
	//	  color: #fff;
810
	//	  border: 1px solid #000;
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
811
	//	  width: @w + 4px;
812
	//	  > .child {...}
813
	//	}
814
	//
815
	// And here's what the parse tree might look like:
816
	//
817
	//	 Ruleset (Selector '.class', [
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
818
	//		 Rule ("color",  Value ([Expression [Color #fff]]))
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
819
	//		 Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
820
	//		 Rule ("width",  Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
821
	//		 Ruleset (Selector [Element '>', '.child'], [...])
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
822
	//	 ])
823
	//
824
	//  In general, most rules will try to parse a token with the `$()` function, and if the return
825
	//  value is truly, will return a new node, of the relevant type. Sometimes, we need to check
826
	//  first, before parsing, that's when we use `peek()`.
827
	//
828
829
	//
830
	// The `primary` rule is the *entry* and *exit* point of the parser.
831
	// The rules here can appear at any level of the parse tree.
832
	//
833
	// The recursive nature of the grammar is an interplay between the `block`
834
	// rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
835
	// as represented by this simplified grammar:
836
	//
837
	//	 primary  →  (ruleset | rule)+
838
	//	 ruleset  →  selector+ block
839
	//	 block	→  '{' primary '}'
840
	//
841
	// Only at one point is the primary rule not called from the
842
	// block rule: at the root level.
843
	//
844
	private function parsePrimary(){
845
		$root = array();
846
847
		while( true ){
848
849
			if( $this->pos >= $this->input_len ){
850
				break;
851
			}
852
853
			$node = $this->parseExtend(true);
854
			if( $node ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $node of type object[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
855
				$root = array_merge($root,$node);
856
				continue;
857
			}
858
859
			//$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
860
			$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective'));
861
862
			if( $node ){
863
				$root[] = $node;
864
			}elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){
865
				break;
866
			}
867
868
            if( $this->PeekChar('}') ){
869
				break;
870
			}
871
		}
872
873
		return $root;
874
	}
875
876
877
878
	// We create a Comment node for CSS comments `/* */`,
879
	// but keep the LeSS comments `//` silent, by just skipping
880
	// over them.
881
	private function parseComment(){
882
883
		if( $this->input[$this->pos] !== '/' ){
884
			return;
885
		}
886
887
		if( $this->input[$this->pos+1] === '/' ){
888
			$match = $this->MatchReg('/\\G\/\/.*/');
889
			return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo));
890
		}
891
892
		//$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
893
		$comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors
894
		if( $comment ){
895
			return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo));
896
		}
897
	}
898
899
	private function parseComments(){
900
		$comments = array();
901
902
		while( $this->pos < $this->input_len ){
903
			$comment = $this->parseComment();
904
			if( !$comment ){
905
				break;
906
			}
907
908
			$comments[] = $comment;
909
		}
910
911
		return $comments;
912
	}
913
914
915
916
	//
917
	// A string, which supports escaping " and '
918
	//
919
	//	 "milky way" 'he\'s the one!'
920
	//
921
	private function parseEntitiesQuoted() {
922
		$j = $this->pos;
923
		$e = false;
924
		$index = $this->pos;
925
926
		if( $this->input[$this->pos] === '~' ){
927
			$j++;
928
			$e = true; // Escaped strings
929
		}
930
931
		if( $this->input[$j] != '"' && $this->input[$j] !== "'" ){
932
			return;
933
		}
934
935
		if ($e) {
936
			$this->MatchChar('~');
937
		}
938
939
                // Fix for #124: match escaped newlines
940
                //$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.)*)"|\'((?:[^\'\\\\\r\n]|\\\\.)*)\'/');
941
		$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"|\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/');
942
943
		if( $str ){
944
			$result = $str[0][0] == '"' ? $str[1] : $str[2];
945
			return $this->NewObj5('Less_Tree_Quoted',array($str[0], $result, $e, $index, $this->env->currentFileInfo) );
946
		}
947
		return;
948
	}
949
950
951
	//
952
	// A catch-all word, such as:
953
	//
954
	//	 black border-collapse
955
	//
956
	private function parseEntitiesKeyword(){
957
958
		//$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
959
		$k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/');
960
		if( $k ){
961
			$k = $k[0];
962
			$color = $this->fromKeyword($k);
963
			if( $color ){
964
				return $color;
965
			}
966
			return $this->NewObj1('Less_Tree_Keyword',$k);
967
		}
968
	}
969
970
	// duplicate of Less_Tree_Color::FromKeyword
971
	private function FromKeyword( $keyword ){
972
		$keyword = strtolower($keyword);
973
974
		if( Less_Colors::hasOwnProperty($keyword) ){
975
			// detect named color
976
			return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1));
977
		}
978
979
		if( $keyword === 'transparent' ){
980
			return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true));
981
		}
982
	}
983
984
	//
985
	// A function call
986
	//
987
	//	 rgb(255, 0, 255)
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
988
	//
989
	// We also try to catch IE's `alpha()`, but let the `alpha` parser
990
	// deal with the details.
991
	//
992
	// The arguments are parsed with the `entities.arguments` parser.
993
	//
994
	private function parseEntitiesCall(){
995
		$index = $this->pos;
996
997
		if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){
998
			return;
999
		}
1000
		$name = $name[1];
1001
		$nameLC = strtolower($name);
1002
1003
		if ($nameLC === 'url') {
1004
			return null;
1005
		}
1006
1007
		$this->pos += strlen($name);
1008
1009
		if( $nameLC === 'alpha' ){
1010
			$alpha_ret = $this->parseAlpha();
1011
			if( $alpha_ret ){
1012
				return $alpha_ret;
1013
			}
1014
		}
1015
1016
		$this->MatchChar('('); // Parse the '(' and consume whitespace.
1017
1018
		$args = $this->parseEntitiesArguments();
1019
1020
		if( !$this->MatchChar(')') ){
1021
			return;
1022
		}
1023
1024
		if ($name) {
1025
			return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) );
1026
		}
1027
	}
1028
1029
	/**
1030
	 * Parse a list of arguments
1031
	 *
1032
	 * @return array
1033
	 */
1034
	private function parseEntitiesArguments(){
1035
1036
		$args = array();
1037
		while( true ){
1038
			$arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') );
1039
			if( !$arg ){
1040
				break;
1041
			}
1042
1043
			$args[] = $arg;
1044
			if( !$this->MatchChar(',') ){
1045
				break;
1046
			}
1047
		}
1048
		return $args;
1049
	}
1050
1051
	private function parseEntitiesLiteral(){
1052
		return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') );
1053
	}
1054
1055
	// Assignments are argument entities for calls.
1056
	// They are present in ie filter properties as shown below.
1057
	//
1058
	//	 filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
1059
	//
1060
	private function parseEntitiesAssignment() {
1061
1062
		$key = $this->MatchReg('/\\G\w+(?=\s?=)/');
1063
		if( !$key ){
1064
			return;
1065
		}
1066
1067
		if( !$this->MatchChar('=') ){
1068
			return;
1069
		}
1070
1071
		$value = $this->parseEntity();
1072
		if( $value ){
1073
			return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value));
1074
		}
1075
	}
1076
1077
	//
1078
	// Parse url() tokens
1079
	//
1080
	// We use a specific rule for urls, because they don't really behave like
1081
	// standard function calls. The difference is that the argument doesn't have
1082
	// to be enclosed within a string, so it can't be parsed as an Expression.
1083
	//
1084
	private function parseEntitiesUrl(){
1085
1086
1087
		if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){
1088
			return;
1089
		}
1090
1091
		$value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') );
1092
		if( !$value ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1093
			$value = '';
1094
		}
1095
1096
1097
		$this->expectChar(')');
1098
1099
1100
		if( isset($value->value) || $value instanceof Less_Tree_Variable ){
1101
			return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo));
1102
		}
1103
1104
		return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) );
1105
	}
1106
1107
1108
	//
1109
	// A Variable entity, such as `@fink`, in
1110
	//
1111
	//	 width: @fink + 2px
1112
	//
1113
	// We use a different parser for variable definitions,
1114
	// see `parsers.variable`.
1115
	//
1116
	private function parseEntitiesVariable(){
1117
		$index = $this->pos;
1118
		if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) {
1119
			return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo));
1120
		}
1121
	}
1122
1123
1124
	// A variable entity useing the protective {} e.g. @{var}
1125
	private function parseEntitiesVariableCurly() {
1126
		$index = $this->pos;
1127
1128
		if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){
1129
			return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo));
1130
		}
1131
	}
1132
1133
	//
1134
	// A Hexadecimal color
1135
	//
1136
	//	 #4F3C2F
1137
	//
1138
	// `rgb` and `hsl` colors are parsed through the `entities.call` parser.
1139
	//
1140
	private function parseEntitiesColor(){
1141
		if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) {
1142
			return $this->NewObj1('Less_Tree_Color',$rgb[1]);
1143
		}
1144
	}
1145
1146
	//
1147
	// A Dimension, that is, a number and a unit
1148
	//
1149
	//	 0.5em 95%
1150
	//
1151
	private function parseEntitiesDimension(){
1152
1153
		$c = @ord($this->input[$this->pos]);
1154
1155
		//Is the first char of the dimension 0-9, '.', '+' or '-'
1156
		if (($c > 57 || $c < 43) || $c === 47 || $c == 44){
1157
			return;
1158
		}
1159
1160
		$value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/');
1161
		if( $value ){
1162
1163
			if( isset($value[2]) ){
1164
				return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2]));
1165
			}
1166
			return $this->NewObj1('Less_Tree_Dimension',$value[1]);
1167
		}
1168
	}
1169
1170
1171
	//
1172
	// A unicode descriptor, as is used in unicode-range
1173
	//
1174
	// U+0?? or U+00A1-00A9
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1175
	//
1176
	function parseUnicodeDescriptor() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1177
		$ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/');
1178
		if( $ud ){
1179
			return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]);
1180
		}
1181
	}
1182
1183
1184
	//
1185
	// JavaScript code to be evaluated
1186
	//
1187
	//	 `window.location.href`
1188
	//
1189
	private function parseEntitiesJavascript(){
1190
		$e = false;
1191
		$j = $this->pos;
1192
		if( $this->input[$j] === '~' ){
1193
			$j++;
1194
			$e = true;
1195
		}
1196
		if( $this->input[$j] !== '`' ){
1197
			return;
1198
		}
1199
		if( $e ){
1200
			$this->MatchChar('~');
1201
		}
1202
		$str = $this->MatchReg('/\\G`([^`]*)`/');
1203
		if( $str ){
1204
			return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e));
1205
		}
1206
	}
1207
1208
1209
	//
1210
	// The variable part of a variable definition. Used in the `rule` parser
1211
	//
1212
	//	 @fink:
1213
	//
1214
	private function parseVariable(){
1215
		if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) {
1216
			return $name[1];
1217
		}
1218
	}
1219
1220
1221
	//
1222
	// The variable part of a variable definition. Used in the `rule` parser
1223
	//
1224
	// @fink();
1225
	//
1226
	private function parseRulesetCall(){
1227
1228
		if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){
1229
			return $this->NewObj1('Less_Tree_RulesetCall', $name[1] );
1230
		}
1231
	}
1232
1233
1234
	//
1235
	// extend syntax - used to extend selectors
1236
	//
1237
	function parseExtend($isRule = false){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1238
1239
		$index = $this->pos;
1240
		$extendList = array();
1241
1242
1243
		if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; }
1244
1245
		do{
1246
			$option = null;
1247
			$elements = array();
1248
			while( true ){
1249
				$option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/');
1250
				if( $option ){ break; }
0 ignored issues
show
Bug Best Practice introduced by
The expression $option of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1251
				$e = $this->parseElement();
1252
				if( !$e ){ break; }
1253
				$elements[] = $e;
1254
			}
1255
1256
			if( $option ){
1257
				$option = $option[1];
1258
			}
1259
1260
			$extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index ));
1261
1262
		}while( $this->MatchChar(",") );
1263
1264
		$this->expect('/\\G\)/');
1265
1266
		if( $isRule ){
1267
			$this->expect('/\\G;/');
1268
		}
1269
1270
		return $extendList;
1271
	}
1272
1273
1274
	//
1275
	// A Mixin call, with an optional argument list
1276
	//
1277
	//	 #mixins > .square(#fff);
1278
	//	 .rounded(4px, black);
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1279
	//	 .button;
1280
	//
1281
	// The `while` loop is there because mixins can be
1282
	// namespaced, but we only support the child and descendant
1283
	// selector for now.
1284
	//
1285
	private function parseMixinCall(){
1286
1287
		$char = $this->input[$this->pos];
1288
		if( $char !== '.' && $char !== '#' ){
1289
			return;
1290
		}
1291
1292
		$index = $this->pos;
1293
		$this->save(); // stop us absorbing part of an invalid selector
1294
1295
		$elements = $this->parseMixinCallElements();
1296
1297
		if( $elements ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $elements of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1298
1299
			if( $this->MatchChar('(') ){
1300
				$returned = $this->parseMixinArgs(true);
1301
				$args = $returned['args'];
1302
				$this->expectChar(')');
1303
			}else{
1304
				$args = array();
1305
			}
1306
1307
			$important = $this->parseImportant();
1308
1309
			if( $this->parseEnd() ){
1310
				$this->forget();
1311
				return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important));
1312
			}
1313
		}
1314
1315
		$this->restore();
1316
	}
1317
1318
1319
	private function parseMixinCallElements(){
1320
		$elements = array();
1321
		$c = null;
1322
1323
		while( true ){
1324
			$elemIndex = $this->pos;
1325
			$e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/');
1326
			if( !$e ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $e of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1327
				break;
1328
			}
1329
			$elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo));
1330
			$c = $this->MatchChar('>');
1331
		}
1332
1333
		return $elements;
1334
	}
1335
1336
1337
1338
	/**
1339
	 * @param boolean $isCall
1340
	 */
1341
	private function parseMixinArgs( $isCall ){
1342
		$expressions = array();
1343
		$argsSemiColon = array();
1344
		$isSemiColonSeperated = null;
1345
		$argsComma = array();
1346
		$expressionContainsNamed = null;
1347
		$name = null;
1348
		$returner = array('args'=>array(), 'variadic'=> false);
1349
1350
		$this->save();
1351
1352
		while( true ){
1353
			if( $isCall ){
1354
				$arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
1355
			} else {
1356
				$this->parseComments();
1357
				if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){
1358
					$returner['variadic'] = true;
1359
					if( $this->MatchChar(";") && !$isSemiColonSeperated ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $isSemiColonSeperated of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1360
						$isSemiColonSeperated = true;
1361
					}
1362
1363
					if( $isSemiColonSeperated ){
1364
						$argsSemiColon[] = array('variadic'=>true);
1365
					}else{
1366
						$argsComma[] = array('variadic'=>true);
1367
					}
1368
					break;
1369
				}
1370
				$arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') );
1371
			}
1372
1373
			if( !$arg ){
1374
				break;
1375
			}
1376
1377
1378
			$nameLoop = null;
1379
			if( $arg instanceof Less_Tree_Expression ){
1380
				$arg->throwAwayComments();
1381
			}
1382
			$value = $arg;
1383
			$val = null;
1384
1385
			if( $isCall ){
1386
				// Variable
1387
				if( property_exists($arg,'value') && count($arg->value) == 1 ){
1388
					$val = $arg->value[0];
1389
				}
1390
			} else {
1391
				$val = $arg;
1392
			}
1393
1394
1395
			if( $val instanceof Less_Tree_Variable ){
1396
1397
				if( $this->MatchChar(':') ){
1398
					if( $expressions ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $expressions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1399
						if( $isSemiColonSeperated ){
1400
							$this->Error('Cannot mix ; and , as delimiter types');
1401
						}
1402
						$expressionContainsNamed = true;
1403
					}
1404
1405
					// we do not support setting a ruleset as a default variable - it doesn't make sense
1406
					// However if we do want to add it, there is nothing blocking it, just don't error
1407
					// and remove isCall dependency below
1408
					$value = null;
1409
					if( $isCall ){
1410
						$value = $this->parseDetachedRuleset();
1411
					}
1412
					if( !$value ){
1413
						$value = $this->parseExpression();
1414
					}
1415
1416
					if( !$value ){
1417
						if( $isCall ){
1418
							$this->Error('could not understand value for named argument');
1419
						} else {
1420
							$this->restore();
1421
							$returner['args'] = array();
1422
							return $returner;
1423
						}
1424
					}
1425
1426
					$nameLoop = ($name = $val->name);
1427
				}elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){
1428
					$returner['variadic'] = true;
1429
					if( $this->MatchChar(";") && !$isSemiColonSeperated ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $isSemiColonSeperated of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1430
						$isSemiColonSeperated = true;
1431
					}
1432
					if( $isSemiColonSeperated ){
1433
						$argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true);
1434
					}else{
1435
						$argsComma[] = array('name'=> $arg->name, 'variadic' => true);
1436
					}
1437
					break;
1438
				}elseif( !$isCall ){
1439
					$name = $nameLoop = $val->name;
1440
					$value = null;
1441
				}
1442
			}
1443
1444
			if( $value ){
1445
				$expressions[] = $value;
1446
			}
1447
1448
			$argsComma[] = array('name'=>$nameLoop, 'value'=>$value );
1449
1450
			if( $this->MatchChar(',') ){
1451
				continue;
1452
			}
1453
1454
			if( $this->MatchChar(';') || $isSemiColonSeperated ){
1455
1456
				if( $expressionContainsNamed ){
1457
					$this->Error('Cannot mix ; and , as delimiter types');
1458
				}
1459
1460
				$isSemiColonSeperated = true;
1461
1462
				if( count($expressions) > 1 ){
1463
					$value = $this->NewObj1('Less_Tree_Value', $expressions);
1464
				}
1465
				$argsSemiColon[] = array('name'=>$name, 'value'=>$value );
1466
1467
				$name = null;
1468
				$expressions = array();
1469
				$expressionContainsNamed = false;
1470
			}
1471
		}
1472
1473
		$this->forget();
1474
		$returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma);
1475
		return $returner;
1476
	}
1477
1478
1479
1480
	//
1481
	// A Mixin definition, with a list of parameters
1482
	//
1483
	//	 .rounded (@radius: 2px, @color) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1484
	//		...
1485
	//	 }
1486
	//
1487
	// Until we have a finer grained state-machine, we have to
1488
	// do a look-ahead, to make sure we don't have a mixin call.
1489
	// See the `rule` function for more information.
1490
	//
1491
	// We start by matching `.rounded (`, and then proceed on to
1492
	// the argument list, which has optional default values.
1493
	// We store the parameters in `params`, with a `value` key,
1494
	// if there is a value, such as in the case of `@radius`.
1495
	//
1496
	// Once we've got our params list, and a closing `)`, we parse
1497
	// the `{...}` block.
1498
	//
1499
	private function parseMixinDefinition(){
1500
		$cond = null;
1501
1502
		$char = $this->input[$this->pos];
1503
		if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){
1504
			return;
1505
		}
1506
1507
		$this->save();
1508
1509
		$match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/');
1510
		if( $match ){
1511
			$name = $match[1];
1512
1513
			$argInfo = $this->parseMixinArgs( false );
1514
			$params = $argInfo['args'];
1515
			$variadic = $argInfo['variadic'];
1516
1517
1518
			// .mixincall("@{a}");
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1519
			// looks a bit like a mixin definition..
1520
			// also
1521
			// .mixincall(@a: {rule: set;});
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1522
			// so we have to be nice and restore
1523
			if( !$this->MatchChar(')') ){
1524
				$this->furthest = $this->pos;
1525
				$this->restore();
1526
				return;
1527
			}
1528
1529
1530
			$this->parseComments();
1531
1532
			if ($this->MatchReg('/\\Gwhen/')) { // Guard
1533
				$cond = $this->expect('parseConditions', 'Expected conditions');
1534
			}
1535
1536
			$ruleset = $this->parseBlock();
1537
1538
			if( is_array($ruleset) ){
1539
				$this->forget();
1540
				return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic));
1541
			}
1542
1543
			$this->restore();
1544
		}else{
1545
			$this->forget();
1546
		}
1547
	}
1548
1549
	//
1550
	// Entities are the smallest recognized token,
1551
	// and can be found inside a rule's value.
1552
	//
1553
	private function parseEntity(){
1554
1555
		return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') );
1556
	}
1557
1558
	//
1559
	// A Rule terminator. Note that we use `peek()` to check for '}',
1560
	// because the `block` rule will be expecting it, but we still need to make sure
1561
	// it's there, if ';' was ommitted.
1562
	//
1563
	private function parseEnd(){
1564
		return $this->MatchChar(';') || $this->PeekChar('}');
1565
	}
1566
1567
	//
1568
	// IE's alpha function
1569
	//
1570
	//	 alpha(opacity=88)
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1571
	//
1572
	private function parseAlpha(){
1573
1574
		if ( ! $this->MatchReg('/\\G\(opacity=/i')) {
1575
			return;
1576
		}
1577
1578
		$value = $this->MatchReg('/\\G[0-9]+/');
1579
		if( $value ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1580
			$value = $value[0];
1581
		}else{
1582
			$value = $this->parseEntitiesVariable();
1583
			if( !$value ){
1584
				return;
1585
			}
1586
		}
1587
1588
		$this->expectChar(')');
1589
		return $this->NewObj1('Less_Tree_Alpha',$value);
1590
	}
1591
1592
1593
	//
1594
	// A Selector Element
1595
	//
1596
	//	 div
1597
	//	 + h1
1598
	//	 #socks
1599
	//	 input[type="text"]
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1600
	//
1601
	// Elements are the building blocks for Selectors,
1602
	// they are made out of a `Combinator` (see combinator rule),
1603
	// and an element name, such as a tag a class, or `*`.
1604
	//
1605
	private function parseElement(){
1606
		$c = $this->parseCombinator();
1607
		$index = $this->pos;
1608
1609
		$e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
1610
			'#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') );
1611
1612
		if( is_null($e) ){
1613
			$this->save();
1614
			if( $this->MatchChar('(') ){
1615
				if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){
1616
					$e = $this->NewObj1('Less_Tree_Paren',$v);
1617
					$this->forget();
1618
				}else{
1619
					$this->restore();
1620
				}
1621
			}else{
1622
				$this->forget();
1623
			}
1624
		}
1625
1626
		if( !is_null($e) ){
1627
			return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo));
1628
		}
1629
	}
1630
1631
	//
1632
	// Combinators combine elements together, in a Selector.
1633
	//
1634
	// Because our parser isn't white-space sensitive, special care
1635
	// has to be taken, when parsing the descendant combinator, ` `,
1636
	// as it's an empty space. We have to check the previous character
1637
	// in the input, to see if it's a ` ` character.
1638
	//
1639
	private function parseCombinator(){
1640
		if( $this->pos < $this->input_len ){
1641
			$c = $this->input[$this->pos];
1642
			if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){
1643
1644
				$this->pos++;
1645
				if( $this->input[$this->pos] === '^' ){
1646
					$c = '^^';
1647
					$this->pos++;
1648
				}
1649
1650
				$this->skipWhitespace(0);
1651
1652
				return $c;
1653
			}
1654
1655
			if( $this->pos > 0 && $this->isWhitespace(-1) ){
1656
				return ' ';
1657
			}
1658
		}
1659
	}
1660
1661
	//
1662
	// A CSS selector (see selector below)
1663
	// with less extensions e.g. the ability to extend and guard
1664
	//
1665
	private function parseLessSelector(){
1666
		return $this->parseSelector(true);
1667
	}
1668
1669
	//
1670
	// A CSS Selector
1671
	//
1672
	//	 .class > div + h1
1673
	//	 li a:hover
1674
	//
1675
	// Selectors are made out of one or more Elements, see above.
1676
	//
1677
	private function parseSelector( $isLess = false ){
1678
		$elements = array();
1679
		$extendList = array();
1680
		$condition = null;
1681
		$when = false;
1682
		$extend = false;
1683
		$e = null;
1684
		$c = null;
1685
		$index = $this->pos;
1686
1687
		while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){
1688
			if( $when ){
1689
				$condition = $this->expect('parseConditions', 'expected condition');
1690
			}elseif( $condition ){
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
1691
				//error("CSS guard can only be used at the end of selector");
1692
			}elseif( $extend ){
1693
				$extendList = array_merge($extendList,$extend);
1694
			}else{
1695
				//if( count($extendList) ){
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1696
					//error("Extend can only be used at the end of selector");
1697
				//}
1698
				if( $this->pos < $this->input_len ){
1699
					$c = $this->input[ $this->pos ];
1700
				}
1701
				$elements[] = $e;
1702
				$e = null;
1703
			}
1704
1705
			if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; }
1706
		}
1707
1708
		if( $elements ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $elements of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1709
			return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo));
1710
		}
1711
		if( $extendList ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extendList of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1712
			$this->Error('Extend must be used to extend a selector, it cannot be used on its own');
1713
		}
1714
	}
1715
1716
	private function parseTag(){
1717
		return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*');
1718
	}
1719
1720
	private function parseAttribute(){
1721
1722
		$val = null;
1723
1724
		if( !$this->MatchChar('[') ){
1725
			return;
1726
		}
1727
1728
		$key = $this->parseEntitiesVariableCurly();
1729
		if( !$key ){
1730
			$key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/');
1731
		}
1732
1733
		$op = $this->MatchReg('/\\G[|~*$^]?=/');
1734
		if( $op ){
1735
			$val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') );
1736
		}
1737
1738
		$this->expectChar(']');
1739
1740
		return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val));
1741
	}
1742
1743
	//
1744
	// The `block` rule is used by `ruleset` and `mixin.definition`.
1745
	// It's a wrapper around the `primary` rule, with added `{}`.
1746
	//
1747
	private function parseBlock(){
1748
		if( $this->MatchChar('{') ){
1749
			$content = $this->parsePrimary();
1750
			if( $this->MatchChar('}') ){
1751
				return $content;
1752
			}
1753
		}
1754
	}
1755
1756
	private function parseBlockRuleset(){
1757
		$block = $this->parseBlock();
1758
1759
		if( $block ){
1760
			$block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block));
1761
		}
1762
1763
		return $block;
1764
	}
1765
1766
	private function parseDetachedRuleset(){
1767
		$blockRuleset = $this->parseBlockRuleset();
1768
		if( $blockRuleset ){
1769
			return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset);
1770
		}
1771
	}
1772
1773
	//
1774
	// div, .class, body > p {...}
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1775
	//
1776
	private function parseRuleset(){
1777
		$selectors = array();
1778
1779
		$this->save();
1780
1781
		while( true ){
1782
			$s = $this->parseLessSelector();
1783
			if( !$s ){
1784
				break;
1785
			}
1786
			$selectors[] = $s;
1787
			$this->parseComments();
1788
1789
			if( $s->condition && count($selectors) > 1 ){
1790
				$this->Error('Guards are only currently allowed on a single selector.');
1791
			}
1792
1793
			if( !$this->MatchChar(',') ){
1794
				break;
1795
			}
1796
			if( $s->condition ){
1797
				$this->Error('Guards are only currently allowed on a single selector.');
1798
			}
1799
			$this->parseComments();
1800
		}
1801
1802
1803
		if( $selectors ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $selectors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1804
			$rules = $this->parseBlock();
1805
			if( is_array($rules) ){
1806
				$this->forget();
1807
				return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports
1808
			}
1809
		}
1810
1811
		// Backtrack
1812
		$this->furthest = $this->pos;
1813
		$this->restore();
1814
	}
1815
1816
	/**
1817
	 * Custom less.php parse function for finding simple name-value css pairs
1818
	 * ex: width:100px;
1819
	 *
1820
	 */
1821
	private function parseNameValue(){
1822
1823
		$index = $this->pos;
1824
		$this->save();
1825
1826
1827
		//$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
1828
		$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/');
1829
		if( $match ){
1830
1831
			if( $match[4] == '}' ){
1832
				$this->pos = $index + strlen($match[0])-1;
1833
			}
1834
1835
			if( $match[3] ){
1836
				$match[2] .= ' !important';
1837
			}
1838
1839
			return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo));
1840
		}
1841
1842
		$this->restore();
1843
	}
1844
1845
1846
	private function parseRule( $tryAnonymous = null ){
1847
1848
		$merge = false;
1849
		$startOfRule = $this->pos;
1850
1851
		$c = $this->input[$this->pos];
1852
		if( $c === '.' || $c === '#' || $c === '&' ){
1853
			return;
1854
		}
1855
1856
		$this->save();
1857
		$name = $this->MatchFuncs( array('parseVariable','parseRuleProperty'));
1858
1859
		if( $name ){
1860
1861
			$isVariable = is_string($name);
1862
1863
			$value = null;
1864
			if( $isVariable ){
1865
				$value = $this->parseDetachedRuleset();
1866
			}
1867
1868
			$important = null;
1869
			if( !$value ){
1870
1871
				// prefer to try to parse first if its a variable or we are compressing
1872
				// but always fallback on the other one
1873
				//if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1874
				if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){
1875
					$value = $this->MatchFuncs( array('parseValue','parseAnonymousValue'));
1876
				}else{
1877
					$value = $this->MatchFuncs( array('parseAnonymousValue','parseValue'));
1878
				}
1879
1880
				$important = $this->parseImportant();
1881
1882
				// a name returned by this.ruleProperty() is always an array of the form:
1883
				// [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1884
				// where each item is a tree.Keyword or tree.Variable
1885
				if( !$isVariable && is_array($name) ){
1886
					$nm = array_pop($name);
1887
					if( $nm->value ){
1888
						$merge = $nm->value;
1889
					}
1890
				}
1891
			}
1892
1893
1894
			if( $value && $this->parseEnd() ){
1895
				$this->forget();
1896
				return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo));
1897
			}else{
1898
				$this->furthest = $this->pos;
1899
				$this->restore();
1900
				if( $value && !$tryAnonymous ){
1901
					return $this->parseRule(true);
1902
				}
1903
			}
1904
		}else{
1905
			$this->forget();
1906
		}
1907
	}
1908
1909
	function parseAnonymousValue(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1910
1911
		if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){
1912
			$this->pos += strlen($match[1]);
1913
			return $this->NewObj1('Less_Tree_Anonymous',$match[1]);
1914
		}
1915
	}
1916
1917
	//
1918
	// An @import directive
1919
	//
1920
	//	 @import "lib";
1921
	//
1922
	// Depending on our environment, importing is done differently:
1923
	// In the browser, it's an XHR request, in Node, it would be a
1924
	// file-system operation. The function used for importing is
1925
	// stored in `import`, which we pass to the Import constructor.
1926
	//
1927
	private function parseImport(){
1928
1929
		$this->save();
1930
1931
		$dir = $this->MatchReg('/\\G@import?\s+/');
1932
1933
		if( $dir ){
1934
			$options = $this->parseImportOptions();
1935
			$path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl'));
1936
1937
			if( $path ){
1938
				$features = $this->parseMediaFeatures();
1939
				if( $this->MatchChar(';') ){
1940
					if( $features ){
1941
						$features = $this->NewObj1('Less_Tree_Value',$features);
1942
					}
1943
1944
					$this->forget();
1945
					return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo));
1946
				}
1947
			}
1948
		}
1949
1950
		$this->restore();
1951
	}
1952
1953
	private function parseImportOptions(){
1954
1955
		$options = array();
1956
1957
		// list of options, surrounded by parens
1958
		if( !$this->MatchChar('(') ){
1959
			return $options;
1960
		}
1961
		do{
1962
			$optionName = $this->parseImportOption();
1963
			if( $optionName ){
1964
				$value = true;
1965
				switch( $optionName ){
1966
					case "css":
1967
						$optionName = "less";
1968
						$value = false;
1969
					break;
1970
					case "once":
1971
						$optionName = "multiple";
1972
						$value = false;
1973
					break;
1974
				}
1975
				$options[$optionName] = $value;
1976
				if( !$this->MatchChar(',') ){ break; }
1977
			}
1978
		}while( $optionName );
1979
		$this->expectChar(')');
1980
		return $options;
1981
	}
1982
1983
	private function parseImportOption(){
1984
		$opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference)/');
1985
		if( $opt ){
1986
			return $opt[1];
1987
		}
1988
	}
1989
1990
	private function parseMediaFeature() {
1991
		$nodes = array();
1992
1993
		do{
1994
			$e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable'));
1995
			if( $e ){
1996
				$nodes[] = $e;
1997
			} elseif ($this->MatchChar('(')) {
1998
				$p = $this->parseProperty();
1999
				$e = $this->parseValue();
2000
				if ($this->MatchChar(')')) {
2001
					if ($p && $e) {
2002
						$r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true));
2003
						$nodes[] = $this->NewObj1('Less_Tree_Paren',$r);
2004
					} elseif ($e) {
2005
						$nodes[] = $this->NewObj1('Less_Tree_Paren',$e);
2006
					} else {
2007
						return null;
2008
					}
2009
				} else
2010
					return null;
2011
			}
2012
		} while ($e);
2013
2014
		if ($nodes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nodes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2015
			return $this->NewObj1('Less_Tree_Expression',$nodes);
2016
		}
2017
	}
2018
2019
	private function parseMediaFeatures() {
2020
		$features = array();
2021
2022
		do{
2023
			$e = $this->parseMediaFeature();
2024
			if( $e ){
2025
				$features[] = $e;
2026
				if (!$this->MatchChar(',')) break;
2027
			}else{
2028
				$e = $this->parseEntitiesVariable();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $e is correct as $this->parseEntitiesVariable() (which targets Less_Parser::parseEntitiesVariable()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2029
				if( $e ){
2030
					$features[] = $e;
2031
					if (!$this->MatchChar(',')) break;
2032
				}
2033
			}
2034
		} while ($e);
2035
2036
		return $features ? $features : null;
2037
	}
2038
2039
	private function parseMedia() {
2040
		if( $this->MatchReg('/\\G@media/') ){
2041
			$features = $this->parseMediaFeatures();
2042
			$rules = $this->parseBlock();
2043
2044
			if( is_array($rules) ){
2045
				return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo));
2046
			}
2047
		}
2048
	}
2049
2050
2051
	//
2052
	// A CSS Directive
2053
	//
2054
	// @charset "utf-8";
2055
	//
2056
	private function parseDirective(){
2057
2058
		if( !$this->PeekChar('@') ){
2059
			return;
2060
		}
2061
2062
		$rules = null;
2063
		$index = $this->pos;
2064
		$hasBlock = true;
2065
		$hasIdentifier = false;
2066
		$hasExpression = false;
2067
		$hasUnknown = false;
2068
2069
2070
		$value = $this->MatchFuncs(array('parseImport','parseMedia'));
2071
		if( $value ){
2072
			return $value;
2073
		}
2074
2075
		$this->save();
2076
2077
		$name = $this->MatchReg('/\\G@[a-z-]+/');
2078
2079
		if( !$name ) return;
2080
		$name = $name[0];
2081
2082
2083
		$nonVendorSpecificName = $name;
2084
		$pos = strpos($name,'-', 2);
2085
		if( $name[1] == '-' && $pos > 0 ){
2086
			$nonVendorSpecificName = "@" . substr($name, $pos + 1);
2087
		}
2088
2089
2090
		switch( $nonVendorSpecificName ){
2091
			/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2092
			case "@font-face":
2093
			case "@viewport":
2094
			case "@top-left":
2095
			case "@top-left-corner":
2096
			case "@top-center":
2097
			case "@top-right":
2098
			case "@top-right-corner":
2099
			case "@bottom-left":
2100
			case "@bottom-left-corner":
2101
			case "@bottom-center":
2102
			case "@bottom-right":
2103
			case "@bottom-right-corner":
2104
			case "@left-top":
2105
			case "@left-middle":
2106
			case "@left-bottom":
2107
			case "@right-top":
2108
			case "@right-middle":
2109
			case "@right-bottom":
2110
			hasBlock = true;
2111
			break;
2112
			*/
2113
			case "@charset":
2114
				$hasIdentifier = true;
2115
				$hasBlock = false;
2116
				break;
2117
			case "@namespace":
2118
				$hasExpression = true;
2119
				$hasBlock = false;
2120
				break;
2121
			case "@keyframes":
2122
				$hasIdentifier = true;
2123
				break;
2124
			case "@host":
2125
			case "@page":
2126
			case "@document":
2127
			case "@supports":
2128
				$hasUnknown = true;
2129
				break;
2130
		}
2131
2132
		if( $hasIdentifier ){
2133
			$value = $this->parseEntity();
2134
			if( !$value ){
2135
				$this->error("expected " . $name . " identifier");
2136
			}
2137
		} else if( $hasExpression ){
2138
			$value = $this->parseExpression();
2139
			if( !$value ){
2140
				$this->error("expected " . $name. " expression");
2141
			}
2142
		} else if ($hasUnknown) {
2143
2144
			$value = $this->MatchReg('/\\G[^{;]+/');
2145
			if( $value ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2146
				$value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0]));
2147
			}
2148
		}
2149
2150
		if( $hasBlock ){
2151
			$rules = $this->parseBlockRuleset();
2152
		}
2153
2154
		if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) {
2155
			$this->forget();
2156
			return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo));
2157
		}
2158
2159
		$this->restore();
2160
	}
2161
2162
2163
	//
2164
	// A Value is a comma-delimited list of Expressions
2165
	//
2166
	//	 font-family: Baskerville, Georgia, serif;
2167
	//
2168
	// In a Rule, a Value represents everything after the `:`,
2169
	// and before the `;`.
2170
	//
2171
	private function parseValue(){
2172
		$expressions = array();
2173
2174
		do{
2175
			$e = $this->parseExpression();
2176
			if( $e ){
2177
				$expressions[] = $e;
2178
				if (! $this->MatchChar(',')) {
2179
					break;
2180
				}
2181
			}
2182
		}while($e);
2183
2184
		if( $expressions ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $expressions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2185
			return $this->NewObj1('Less_Tree_Value',$expressions);
2186
		}
2187
	}
2188
2189
	private function parseImportant (){
2190
		if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){
2191
			return ' !important';
2192
		}
2193
	}
2194
2195
	private function parseSub (){
2196
2197
		if( $this->MatchChar('(') ){
2198
			$a = $this->parseAddition();
2199
			if( $a ){
2200
				$this->expectChar(')');
2201
				return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached
2202
			}
2203
		}
2204
	}
2205
2206
2207
	/**
2208
	 * Parses multiplication operation
2209
	 *
2210
	 * @return Less_Tree_Operation|null
2211
	 */
2212
	function parseMultiplication(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2213
2214
		$return = $m = $this->parseOperand();
2215
		if( $return ){
2216
			while( true ){
2217
2218
				$isSpaced = $this->isWhitespace( -1 );
2219
2220
				if( $this->PeekReg('/\\G\/[*\/]/') ){
2221
					break;
2222
				}
2223
2224
				$op = $this->MatchChar('/');
2225
				if( !$op ){
2226
					$op = $this->MatchChar('*');
2227
					if( !$op ){
2228
						break;
2229
					}
2230
				}
2231
2232
				$a = $this->parseOperand();
2233
2234
				if(!$a) { break; }
2235
2236
				$m->parensInOp = true;
2237
				$a->parensInOp = true;
2238
				$return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) );
2239
			}
2240
		}
2241
		return $return;
2242
2243
	}
2244
2245
2246
	/**
2247
	 * Parses an addition operation
2248
	 *
2249
	 * @return Less_Tree_Operation|null
2250
	 */
2251
	private function parseAddition (){
2252
2253
		$return = $m = $this->parseMultiplication();
2254
		if( $return ){
2255
			while( true ){
2256
2257
				$isSpaced = $this->isWhitespace( -1 );
2258
2259
				$op = $this->MatchReg('/\\G[-+]\s+/');
2260
				if( $op ){
2261
					$op = $op[0];
2262
				}else{
2263
					if( !$isSpaced ){
2264
						$op = $this->match(array('#+','#-'));
2265
					}
2266
					if( !$op ){
2267
						break;
2268
					}
2269
				}
2270
2271
				$a = $this->parseMultiplication();
2272
				if( !$a ){
2273
					break;
2274
				}
2275
2276
				$m->parensInOp = true;
2277
				$a->parensInOp = true;
2278
				$return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced));
2279
			}
2280
		}
2281
2282
		return $return;
2283
	}
2284
2285
2286
	/**
2287
	 * Parses the conditions
2288
	 *
2289
	 * @return Less_Tree_Condition|null
2290
	 */
2291
	private function parseConditions() {
2292
		$index = $this->pos;
2293
		$return = $a = $this->parseCondition();
2294
		if( $a ){
2295
			while( true ){
2296
				if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') ||  !$this->MatchChar(',') ){
2297
					break;
2298
				}
2299
				$b = $this->parseCondition();
2300
				if( !$b ){
2301
					break;
2302
				}
2303
2304
				$return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index));
2305
			}
2306
			return $return;
2307
		}
2308
	}
2309
2310
	private function parseCondition() {
2311
		$index = $this->pos;
2312
		$negate = false;
2313
		$c = null;
2314
2315
		if ($this->MatchReg('/\\Gnot/')) $negate = true;
2316
		$this->expectChar('(');
2317
		$a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
2318
2319
		if( $a ){
2320
			$op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/');
2321
			if( $op ){
2322
				$b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
2323 View Code Duplication
				if( $b ){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2324
					$c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate));
2325
				} else {
2326
					$this->Error('Unexpected expression');
2327
				}
2328 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2329
				$k = $this->NewObj1('Less_Tree_Keyword','true');
2330
				$c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate));
2331
			}
2332
			$this->expectChar(')');
2333
			return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c;
2334
		}
2335
	}
2336
2337
	/**
2338
	 * An operand is anything that can be part of an operation,
2339
	 * such as a Color, or a Variable
2340
	 *
2341
	 */
2342
	private function parseOperand (){
2343
2344
		$negate = false;
2345
		$offset = $this->pos+1;
2346
		if( $offset >= $this->input_len ){
2347
			return;
2348
		}
2349
		$char = $this->input[$offset];
2350
		if( $char === '@' || $char === '(' ){
2351
			$negate = $this->MatchChar('-');
2352
		}
2353
2354
		$o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall'));
2355
2356
		if( $negate ){
2357
			$o->parensInOp = true;
2358
			$o = $this->NewObj1('Less_Tree_Negative',$o);
2359
		}
2360
2361
		return $o;
2362
	}
2363
2364
2365
	/**
2366
	 * Expressions either represent mathematical operations,
2367
	 * or white-space delimited Entities.
2368
	 *
2369
	 *	 1px solid black
2370
	 *	 @var * 2
2371
	 *
2372
	 * @return Less_Tree_Expression|null
2373
	 */
2374
	private function parseExpression (){
2375
		$entities = array();
2376
2377
		do{
2378
			$e = $this->MatchFuncs(array('parseAddition','parseEntity'));
2379
			if( $e ){
2380
				$entities[] = $e;
2381
				// operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
2382
				if( !$this->PeekReg('/\\G\/[\/*]/') ){
2383
					$delim = $this->MatchChar('/');
2384
					if( $delim ){
2385
						$entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim);
2386
					}
2387
				}
2388
			}
2389
		}while($e);
2390
2391
		if( $entities ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $entities of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2392
			return $this->NewObj1('Less_Tree_Expression',$entities);
2393
		}
2394
	}
2395
2396
2397
	/**
2398
	 * Parse a property
2399
	 * eg: 'min-width', 'orientation', etc
2400
	 *
2401
	 * @return string
2402
	 */
2403
	private function parseProperty (){
2404
		$name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/');
2405
		if( $name ){
2406
			return $name[1];
2407
		}
2408
	}
2409
2410
2411
	/**
2412
	 * Parse a rule property
2413
	 * eg: 'color', 'width', 'height', etc
2414
	 *
2415
	 * @return string
2416
	 */
2417
	private function parseRuleProperty(){
2418
		$offset = $this->pos;
2419
		$name = array();
2420
		$index = array();
2421
		$length = 0;
2422
2423
2424
		$this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name );
2425
		while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // !
2426
2427
		if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){
2428
			// at last, we have the complete match now. move forward,
2429
			// convert name particles to tree objects and return:
2430
			$this->skipWhitespace($length);
2431
2432
			if( $name[0] === '' ){
2433
				array_shift($name);
2434
				array_shift($index);
2435
			}
2436
			foreach($name as $k => $s ){
2437
				if( !$s || $s[0] !== '@' ){
2438
					$name[$k] = $this->NewObj1('Less_Tree_Keyword',$s);
2439
				}else{
2440
					$name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo));
2441
				}
2442
			}
2443
			return $name;
2444
		}
2445
2446
2447
	}
2448
2449
	private function rulePropertyMatch( $re, &$offset, &$length,  &$index, &$name ){
2450
		preg_match($re, $this->input, $a, 0, $offset);
2451
		if( $a ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $a of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2452
			$index[] = $this->pos + $length;
2453
			$length += strlen($a[0]);
2454
			$offset += strlen($a[0]);
2455
			$name[] = $a[1];
2456
			return true;
2457
		}
2458
	}
2459
2460
	public static function serializeVars( $vars ){
2461
		$s = '';
2462
2463
		foreach($vars as $name => $value){
2464
			$s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';');
2465
		}
2466
2467
		return $s;
2468
	}
2469
2470
2471
	/**
2472
	 * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
2473
	 *
2474
	 * @param string $b
2475
	 */
2476
	public static function is_method($a,$b){
2477
		return is_object($a) && method_exists($a,$b);
2478
	}
2479
2480
2481
	/**
2482
	 * Round numbers similarly to javascript
2483
	 * eg: 1.499999 to 1 instead of 2
2484
	 *
2485
	 */
2486
	public static function round($i, $precision = 0){
2487
2488
		$precision = pow(10,$precision);
2489
		$i = $i*$precision;
2490
2491
		$ceil = ceil($i);
2492
		$floor = floor($i);
2493
		if( ($ceil - $i) <= ($i - $floor) ){
2494
			return $ceil/$precision;
2495
		}else{
2496
			return $floor/$precision;
2497
		}
2498
	}
2499
2500
2501
	/**
2502
	 * Create Less_Tree_* objects and optionally generate a cache string
2503
	 *
2504
	 * @return mixed
2505
	 */
2506
	public function NewObj0($class){
2507
		$obj = new $class();
2508
		if( $this->CacheEnabled() ){
2509
			$obj->cache_string = ' new '.$class.'()';
2510
		}
2511
		return $obj;
2512
	}
2513
2514
	public function NewObj1($class, $arg){
2515
		$obj = new $class( $arg );
2516
		if( $this->CacheEnabled() ){
2517
			$obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')';
2518
		}
2519
		return $obj;
2520
	}
2521
2522
	public function NewObj2($class, $args){
2523
		$obj = new $class( $args[0], $args[1] );
2524
		if( $this->CacheEnabled() ){
2525
			$this->ObjCache( $obj, $class, $args);
2526
		}
2527
		return $obj;
2528
	}
2529
2530
	public function NewObj3($class, $args){
2531
		$obj = new $class( $args[0], $args[1], $args[2] );
2532
		if( $this->CacheEnabled() ){
2533
			$this->ObjCache( $obj, $class, $args);
2534
		}
2535
		return $obj;
2536
	}
2537
2538
	public function NewObj4($class, $args){
2539
		$obj = new $class( $args[0], $args[1], $args[2], $args[3] );
2540
		if( $this->CacheEnabled() ){
2541
			$this->ObjCache( $obj, $class, $args);
2542
		}
2543
		return $obj;
2544
	}
2545
2546
	public function NewObj5($class, $args){
2547
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
2548
		if( $this->CacheEnabled() ){
2549
			$this->ObjCache( $obj, $class, $args);
2550
		}
2551
		return $obj;
2552
	}
2553
2554 View Code Duplication
	public function NewObj6($class, $args){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2555
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
2556
		if( $this->CacheEnabled() ){
2557
			$this->ObjCache( $obj, $class, $args);
2558
		}
2559
		return $obj;
2560
	}
2561
2562 View Code Duplication
	public function NewObj7($class, $args){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2563
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
2564
		if( $this->CacheEnabled() ){
2565
			$this->ObjCache( $obj, $class, $args);
2566
		}
2567
		return $obj;
2568
	}
2569
2570
	//caching
2571
	public function ObjCache($obj, $class, $args=array()){
2572
		$obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')';
2573
	}
2574
2575
	public function ArgCache($args){
2576
		return implode(',',array_map( array('Less_Parser','ArgString'),$args));
2577
	}
2578
2579
2580
	/**
2581
	 * Convert an argument to a string for use in the parser cache
2582
	 *
2583
	 * @return string
2584
	 */
2585
	public static function ArgString($arg){
2586
2587
		$type = gettype($arg);
2588
2589
		if( $type === 'object'){
2590
			$string = $arg->cache_string;
2591
			unset($arg->cache_string);
2592
			return $string;
2593
2594
		}elseif( $type === 'array' ){
2595
			$string = ' Array(';
2596
			foreach($arg as $k => $a){
2597
				$string .= var_export($k,true).' => '.self::ArgString($a).',';
2598
			}
2599
			return $string . ')';
2600
		}
2601
2602
		return var_export($arg,true);
2603
	}
2604
2605
	public function Error($msg){
2606
		throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo);
0 ignored issues
show
Documentation introduced by
$this->env->currentFileInfo is of type array<string,?>, but the function expects a object<Less_FileInfo>|string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2607
	}
2608
2609
	public static function WinPath($path){
2610
		return str_replace('\\', '/', $path);
2611
	}
2612
2613
	public function CacheEnabled(){
2614
		return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback')));
2615
	}
2616
2617
}
2618
2619
2620
 
2621
2622
/**
2623
 * Utility for css colors
2624
 *
2625
 * @package Less
2626
 * @subpackage color
2627
 */
2628
class Less_Colors {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
2629
2630
	public static $colors = array(
2631
			'aliceblue'=>'#f0f8ff',
2632
			'antiquewhite'=>'#faebd7',
2633
			'aqua'=>'#00ffff',
2634
			'aquamarine'=>'#7fffd4',
2635
			'azure'=>'#f0ffff',
2636
			'beige'=>'#f5f5dc',
2637
			'bisque'=>'#ffe4c4',
2638
			'black'=>'#000000',
2639
			'blanchedalmond'=>'#ffebcd',
2640
			'blue'=>'#0000ff',
2641
			'blueviolet'=>'#8a2be2',
2642
			'brown'=>'#a52a2a',
2643
			'burlywood'=>'#deb887',
2644
			'cadetblue'=>'#5f9ea0',
2645
			'chartreuse'=>'#7fff00',
2646
			'chocolate'=>'#d2691e',
2647
			'coral'=>'#ff7f50',
2648
			'cornflowerblue'=>'#6495ed',
2649
			'cornsilk'=>'#fff8dc',
2650
			'crimson'=>'#dc143c',
2651
			'cyan'=>'#00ffff',
2652
			'darkblue'=>'#00008b',
2653
			'darkcyan'=>'#008b8b',
2654
			'darkgoldenrod'=>'#b8860b',
2655
			'darkgray'=>'#a9a9a9',
2656
			'darkgrey'=>'#a9a9a9',
2657
			'darkgreen'=>'#006400',
2658
			'darkkhaki'=>'#bdb76b',
2659
			'darkmagenta'=>'#8b008b',
2660
			'darkolivegreen'=>'#556b2f',
2661
			'darkorange'=>'#ff8c00',
2662
			'darkorchid'=>'#9932cc',
2663
			'darkred'=>'#8b0000',
2664
			'darksalmon'=>'#e9967a',
2665
			'darkseagreen'=>'#8fbc8f',
2666
			'darkslateblue'=>'#483d8b',
2667
			'darkslategray'=>'#2f4f4f',
2668
			'darkslategrey'=>'#2f4f4f',
2669
			'darkturquoise'=>'#00ced1',
2670
			'darkviolet'=>'#9400d3',
2671
			'deeppink'=>'#ff1493',
2672
			'deepskyblue'=>'#00bfff',
2673
			'dimgray'=>'#696969',
2674
			'dimgrey'=>'#696969',
2675
			'dodgerblue'=>'#1e90ff',
2676
			'firebrick'=>'#b22222',
2677
			'floralwhite'=>'#fffaf0',
2678
			'forestgreen'=>'#228b22',
2679
			'fuchsia'=>'#ff00ff',
2680
			'gainsboro'=>'#dcdcdc',
2681
			'ghostwhite'=>'#f8f8ff',
2682
			'gold'=>'#ffd700',
2683
			'goldenrod'=>'#daa520',
2684
			'gray'=>'#808080',
2685
			'grey'=>'#808080',
2686
			'green'=>'#008000',
2687
			'greenyellow'=>'#adff2f',
2688
			'honeydew'=>'#f0fff0',
2689
			'hotpink'=>'#ff69b4',
2690
			'indianred'=>'#cd5c5c',
2691
			'indigo'=>'#4b0082',
2692
			'ivory'=>'#fffff0',
2693
			'khaki'=>'#f0e68c',
2694
			'lavender'=>'#e6e6fa',
2695
			'lavenderblush'=>'#fff0f5',
2696
			'lawngreen'=>'#7cfc00',
2697
			'lemonchiffon'=>'#fffacd',
2698
			'lightblue'=>'#add8e6',
2699
			'lightcoral'=>'#f08080',
2700
			'lightcyan'=>'#e0ffff',
2701
			'lightgoldenrodyellow'=>'#fafad2',
2702
			'lightgray'=>'#d3d3d3',
2703
			'lightgrey'=>'#d3d3d3',
2704
			'lightgreen'=>'#90ee90',
2705
			'lightpink'=>'#ffb6c1',
2706
			'lightsalmon'=>'#ffa07a',
2707
			'lightseagreen'=>'#20b2aa',
2708
			'lightskyblue'=>'#87cefa',
2709
			'lightslategray'=>'#778899',
2710
			'lightslategrey'=>'#778899',
2711
			'lightsteelblue'=>'#b0c4de',
2712
			'lightyellow'=>'#ffffe0',
2713
			'lime'=>'#00ff00',
2714
			'limegreen'=>'#32cd32',
2715
			'linen'=>'#faf0e6',
2716
			'magenta'=>'#ff00ff',
2717
			'maroon'=>'#800000',
2718
			'mediumaquamarine'=>'#66cdaa',
2719
			'mediumblue'=>'#0000cd',
2720
			'mediumorchid'=>'#ba55d3',
2721
			'mediumpurple'=>'#9370d8',
2722
			'mediumseagreen'=>'#3cb371',
2723
			'mediumslateblue'=>'#7b68ee',
2724
			'mediumspringgreen'=>'#00fa9a',
2725
			'mediumturquoise'=>'#48d1cc',
2726
			'mediumvioletred'=>'#c71585',
2727
			'midnightblue'=>'#191970',
2728
			'mintcream'=>'#f5fffa',
2729
			'mistyrose'=>'#ffe4e1',
2730
			'moccasin'=>'#ffe4b5',
2731
			'navajowhite'=>'#ffdead',
2732
			'navy'=>'#000080',
2733
			'oldlace'=>'#fdf5e6',
2734
			'olive'=>'#808000',
2735
			'olivedrab'=>'#6b8e23',
2736
			'orange'=>'#ffa500',
2737
			'orangered'=>'#ff4500',
2738
			'orchid'=>'#da70d6',
2739
			'palegoldenrod'=>'#eee8aa',
2740
			'palegreen'=>'#98fb98',
2741
			'paleturquoise'=>'#afeeee',
2742
			'palevioletred'=>'#d87093',
2743
			'papayawhip'=>'#ffefd5',
2744
			'peachpuff'=>'#ffdab9',
2745
			'peru'=>'#cd853f',
2746
			'pink'=>'#ffc0cb',
2747
			'plum'=>'#dda0dd',
2748
			'powderblue'=>'#b0e0e6',
2749
			'purple'=>'#800080',
2750
			'red'=>'#ff0000',
2751
			'rosybrown'=>'#bc8f8f',
2752
			'royalblue'=>'#4169e1',
2753
			'saddlebrown'=>'#8b4513',
2754
			'salmon'=>'#fa8072',
2755
			'sandybrown'=>'#f4a460',
2756
			'seagreen'=>'#2e8b57',
2757
			'seashell'=>'#fff5ee',
2758
			'sienna'=>'#a0522d',
2759
			'silver'=>'#c0c0c0',
2760
			'skyblue'=>'#87ceeb',
2761
			'slateblue'=>'#6a5acd',
2762
			'slategray'=>'#708090',
2763
			'slategrey'=>'#708090',
2764
			'snow'=>'#fffafa',
2765
			'springgreen'=>'#00ff7f',
2766
			'steelblue'=>'#4682b4',
2767
			'tan'=>'#d2b48c',
2768
			'teal'=>'#008080',
2769
			'thistle'=>'#d8bfd8',
2770
			'tomato'=>'#ff6347',
2771
			'turquoise'=>'#40e0d0',
2772
			'violet'=>'#ee82ee',
2773
			'wheat'=>'#f5deb3',
2774
			'white'=>'#ffffff',
2775
			'whitesmoke'=>'#f5f5f5',
2776
			'yellow'=>'#ffff00',
2777
			'yellowgreen'=>'#9acd32'
2778
		);
2779
2780
	public static function hasOwnProperty($color) {
2781
		return isset(self::$colors[$color]);
2782
	}
2783
2784
2785
	public static function color($color) {
2786
		return self::$colors[$color];
2787
	}
2788
2789
}
2790
 
2791
2792
2793
/**
2794
 * Environment
2795
 *
2796
 * @package Less
2797
 * @subpackage environment
2798
 */
2799
class Less_Environment{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
2800
2801
	//public $paths = array();				// option - unmodified - paths to search for imports on
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2802
	//public static $files = array();		// list of files that have been imported, used for import-once
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2803
	//public $rootpath;						// option - rootpath to append to URL's
2804
	//public static $strictImports = null;	// option -
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2805
	//public $insecure;						// option - whether to allow imports from insecure ssl hosts
2806
	//public $processImports;				// option - whether to process imports. if false then imports will not be imported
2807
	//public $javascriptEnabled;			// option - whether JavaScript is enabled. if undefined, defaults to true
2808
	//public $useFileCache;					// browser only - whether to use the per file session cache
2809
	public $currentFileInfo;				// information about the current file - for error reporting and importing and making urls relative etc.
2810
2811
	public $importMultiple = false; 		// whether we are currently importing multiple copies
2812
2813
2814
	/**
2815
	 * @var array
2816
	 */
2817
	public $frames = array();
2818
2819
	/**
2820
	 * @var array
2821
	 */
2822
	public $mediaBlocks = array();
2823
2824
	/**
2825
	 * @var array
2826
	 */
2827
	public $mediaPath = array();
2828
2829
	public static $parensStack = 0;
2830
2831
	public static $tabLevel = 0;
2832
2833
	public static $lastRule = false;
2834
2835
	public static $_outputMap;
2836
2837
	public static $mixin_stack = 0;
2838
2839
	/**
2840
	 * @var array
2841
	 */
2842
	public $functions = array();
2843
2844
2845
	public function Init(){
2846
2847
		self::$parensStack = 0;
2848
		self::$tabLevel = 0;
2849
		self::$lastRule = false;
2850
		self::$mixin_stack = 0;
2851
2852
		if( Less_Parser::$options['compress'] ){
2853
2854
			Less_Environment::$_outputMap = array(
2855
				','	=> ',',
2856
				': ' => ':',
2857
				''  => '',
2858
				' ' => ' ',
2859
				':' => ' :',
2860
				'+' => '+',
2861
				'~' => '~',
2862
				'>' => '>',
2863
				'|' => '|',
2864
		        '^' => '^',
2865
		        '^^' => '^^'
2866
			);
2867
2868
		}else{
2869
2870
			Less_Environment::$_outputMap = array(
2871
				','	=> ', ',
2872
				': ' => ': ',
2873
				''  => '',
2874
				' ' => ' ',
2875
				':' => ' :',
2876
				'+' => ' + ',
2877
				'~' => ' ~ ',
2878
				'>' => ' > ',
2879
				'|' => '|',
2880
		        '^' => ' ^ ',
2881
		        '^^' => ' ^^ '
2882
			);
2883
2884
		}
2885
	}
2886
2887
2888
	public function copyEvalEnv($frames = array() ){
2889
		$new_env = new Less_Environment();
2890
		$new_env->frames = $frames;
2891
		return $new_env;
2892
	}
2893
2894
2895
	public static function isMathOn(){
2896
		return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
2897
	}
2898
2899
	public static function isPathRelative($path){
2900
		return !preg_match('/^(?:[a-z-]+:|\/)/',$path);
2901
	}
2902
2903
2904
	/**
2905
	 * Canonicalize a path by resolving references to '/./', '/../'
2906
	 * Does not remove leading "../"
2907
	 * @param string path or url
2908
	 * @return string Canonicalized path
2909
	 *
2910
	 */
2911
	public static function normalizePath($path){
2912
2913
		$segments = explode('/',$path);
2914
		$segments = array_reverse($segments);
2915
2916
		$path = array();
2917
		$path_len = 0;
2918
2919
		while( $segments ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $segments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2920
			$segment = array_pop($segments);
2921
			switch( $segment ) {
2922
2923
				case '.':
2924
				break;
2925
2926
				case '..':
2927
					if( !$path_len || ( $path[$path_len-1] === '..') ){
2928
						$path[] = $segment;
2929
						$path_len++;
2930
					}else{
2931
						array_pop($path);
2932
						$path_len--;
2933
					}
2934
				break;
2935
2936
				default:
2937
					$path[] = $segment;
2938
					$path_len++;
2939
				break;
2940
			}
2941
		}
2942
2943
		return implode('/',$path);
2944
	}
2945
2946
2947
	public function unshiftFrame($frame){
2948
		array_unshift($this->frames, $frame);
2949
	}
2950
2951
	public function shiftFrame(){
2952
		return array_shift($this->frames);
2953
	}
2954
2955
}
2956
 
2957
2958
/**
2959
 * Builtin functions
2960
 *
2961
 * @package Less
2962
 * @subpackage function
2963
 * @see http://lesscss.org/functions/
2964
 */
2965
class Less_Functions{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
2966
2967
	public $env;
2968
	public $currentFileInfo;
2969
2970
	function __construct($env, $currentFileInfo = null ){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
2971
		$this->env = $env;
2972
		$this->currentFileInfo = $currentFileInfo;
2973
	}
2974
2975
	/**
2976
	 * @param string $op
2977
	 */
2978
    public static function operate( $op, $a, $b ){
2979
		switch ($op) {
2980
			case '+': return $a + $b;
2981
			case '-': return $a - $b;
2982
			case '*': return $a * $b;
2983
			case '/': return $a / $b;
2984
		}
2985
	}
2986
2987
	public static function clamp($val, $max = 1){
2988
		return min( max($val, 0), $max);
2989
	}
2990
2991
	public static function fround( $value ){
2992
2993
		if( $value === 0 ){
2994
			return $value;
2995
		}
2996
2997
		if( Less_Parser::$options['numPrecision'] ){
2998
			$p = pow(10, Less_Parser::$options['numPrecision']);
2999
			return round( $value * $p) / $p;
3000
		}
3001
		return $value;
3002
	}
3003
3004
    public static function number($n){
3005
3006
		if ($n instanceof Less_Tree_Dimension) {
3007
			return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value);
3008
		} else if (is_numeric($n)) {
3009
			return $n;
3010
		} else {
3011
			throw new Less_Exception_Compiler("color functions take numbers as parameters");
3012
		}
3013
	}
3014
3015
    public static function scaled($n, $size = 255 ){
3016
		if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){
3017
			return (float)$n->value * $size / 100;
3018
		} else {
3019
			return Less_Functions::number($n);
3020
		}
3021
	}
3022
3023
	public function rgb ($r = null, $g = null, $b = null){
3024
		if (is_null($r) || is_null($g) || is_null($b)) {
3025
			throw new Less_Exception_Compiler("rgb expects three parameters");
3026
		}
3027
		return $this->rgba($r, $g, $b, 1.0);
3028
	}
3029
3030
	public function rgba($r = null, $g = null, $b = null, $a = null){
3031
		$rgb = array($r, $g, $b);
3032
		$rgb = array_map(array('Less_Functions','scaled'),$rgb);
3033
3034
		$a = self::number($a);
3035
		return new Less_Tree_Color($rgb, $a);
3036
	}
3037
3038
	public function hsl($h, $s, $l){
3039
		return $this->hsla($h, $s, $l, 1.0);
3040
	}
3041
3042
	public function hsla($h, $s, $l, $a){
3043
3044
		$h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int
3045
		$s = self::clamp(self::number($s));
3046
		$l = self::clamp(self::number($l));
3047
		$a = self::clamp(self::number($a));
3048
3049
		$m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
3050
3051
		$m1 = $l * 2 - $m2;
3052
3053
		return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255,
3054
							self::hsla_hue($h, $m1, $m2) * 255,
3055
							self::hsla_hue($h - 1/3, $m1, $m2) * 255,
3056
							$a);
3057
	}
3058
3059
	/**
3060
	 * @param double $h
3061
	 */
3062
	public function hsla_hue($h, $m1, $m2){
3063
		$h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h);
3064
		if	  ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
3065
		else if ($h * 2 < 1) return $m2;
3066
		else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
3067
		else				 return $m1;
3068
	}
3069
3070
	public function hsv($h, $s, $v) {
3071
		return $this->hsva($h, $s, $v, 1.0);
3072
	}
3073
3074
	/**
3075
	 * @param double $a
3076
	 */
3077
	public function hsva($h, $s, $v, $a) {
3078
		$h = ((Less_Functions::number($h) % 360) / 360 ) * 360;
3079
		$s = Less_Functions::number($s);
3080
		$v = Less_Functions::number($v);
3081
		$a = Less_Functions::number($a);
3082
3083
		$i = floor(($h / 60) % 6);
3084
		$f = ($h / 60) - $i;
3085
3086
		$vs = array( $v,
3087
				  $v * (1 - $s),
3088
				  $v * (1 - $f * $s),
3089
				  $v * (1 - (1 - $f) * $s));
3090
3091
		$perm = array(array(0, 3, 1),
3092
					array(2, 0, 1),
3093
					array(1, 0, 3),
3094
					array(1, 2, 0),
3095
					array(3, 1, 0),
3096
					array(0, 1, 2));
3097
3098
		return $this->rgba($vs[$perm[$i][0]] * 255,
3099
						 $vs[$perm[$i][1]] * 255,
3100
						 $vs[$perm[$i][2]] * 255,
3101
						 $a);
3102
	}
3103
3104
	public function hue($color = null){
3105
		if (!$color instanceof Less_Tree_Color) {
3106
			throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3107
		}
3108
3109
		$c = $color->toHSL();
3110
		return new Less_Tree_Dimension(Less_Parser::round($c['h']));
3111
	}
3112
3113 View Code Duplication
	public function saturation($color = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3114
		if (!$color instanceof Less_Tree_Color) {
3115
			throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3116
		}
3117
3118
		$c = $color->toHSL();
3119
		return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
3120
	}
3121
3122 View Code Duplication
	public function lightness($color = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3123
		if (!$color instanceof Less_Tree_Color) {
3124
			throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3125
		}
3126
3127
		$c = $color->toHSL();
3128
		return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
3129
	}
3130
3131
	public function hsvhue( $color = null ){
3132
		if (!$color instanceof Less_Tree_Color) {
3133
			throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3134
		}
3135
3136
		$hsv = $color->toHSV();
3137
		return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) );
3138
	}
3139
3140
3141 View Code Duplication
	public function hsvsaturation( $color = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3142
		if (!$color instanceof Less_Tree_Color) {
3143
			throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3144
		}
3145
3146
		$hsv = $color->toHSV();
3147
		return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' );
3148
	}
3149
3150 View Code Duplication
	public function hsvvalue( $color = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3151
		if (!$color instanceof Less_Tree_Color) {
3152
			throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3153
		}
3154
3155
		$hsv = $color->toHSV();
3156
		return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' );
3157
	}
3158
3159 View Code Duplication
	public function red($color = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3160
		if (!$color instanceof Less_Tree_Color) {
3161
			throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3162
		}
3163
3164
		return new Less_Tree_Dimension( $color->rgb[0] );
3165
	}
3166
3167 View Code Duplication
	public function green($color = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3168
		if (!$color instanceof Less_Tree_Color) {
3169
			throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3170
		}
3171
3172
		return new Less_Tree_Dimension( $color->rgb[1] );
3173
	}
3174
3175 View Code Duplication
	public function blue($color = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3176
		if (!$color instanceof Less_Tree_Color) {
3177
			throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3178
		}
3179
3180
		return new Less_Tree_Dimension( $color->rgb[2] );
3181
	}
3182
3183 View Code Duplication
	public function alpha($color = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3184
		if (!$color instanceof Less_Tree_Color) {
3185
			throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3186
		}
3187
3188
		$c = $color->toHSL();
3189
		return new Less_Tree_Dimension($c['a']);
3190
	}
3191
3192 View Code Duplication
	public function luma ($color = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3193
		if (!$color instanceof Less_Tree_Color) {
3194
			throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3195
		}
3196
3197
		return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%');
3198
	}
3199
3200
	public function luminance( $color = null ){
3201
		if (!$color instanceof Less_Tree_Color) {
3202
			throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3203
		}
3204
3205
		$luminance =
3206
			(0.2126 * $color->rgb[0] / 255)
3207
		  + (0.7152 * $color->rgb[1] / 255)
3208
		  + (0.0722 * $color->rgb[2] / 255);
3209
3210
		return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%');
3211
	}
3212
3213
	public function saturate($color = null, $amount = null){
3214
		// filter: saturate(3.2);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3215
		// should be kept as is, so check for color
3216
		if ($color instanceof Less_Tree_Dimension) {
3217
			return null;
3218
		}
3219
3220
		if (!$color instanceof Less_Tree_Color) {
3221
			throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3222
		}
3223
		if (!$amount instanceof Less_Tree_Dimension) {
3224
			throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3225
		}
3226
3227
		$hsl = $color->toHSL();
3228
3229
		$hsl['s'] += $amount->value / 100;
3230
		$hsl['s'] = self::clamp($hsl['s']);
3231
3232
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3233
	}
3234
3235
	/**
3236
	 * @param Less_Tree_Dimension $amount
3237
	 */
3238 View Code Duplication
	public function desaturate($color = null, $amount = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3239
		if (!$color instanceof Less_Tree_Color) {
3240
			throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3241
		}
3242
		if (!$amount instanceof Less_Tree_Dimension) {
3243
			throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3244
		}
3245
3246
		$hsl = $color->toHSL();
3247
3248
		$hsl['s'] -= $amount->value / 100;
3249
		$hsl['s'] = self::clamp($hsl['s']);
3250
3251
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3252
	}
3253
3254
3255
3256 View Code Duplication
	public function lighten($color = null, $amount=null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3257
		if (!$color instanceof Less_Tree_Color) {
3258
			throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3259
		}
3260
		if (!$amount instanceof Less_Tree_Dimension) {
3261
			throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3262
		}
3263
3264
		$hsl = $color->toHSL();
3265
3266
		$hsl['l'] += $amount->value / 100;
3267
		$hsl['l'] = self::clamp($hsl['l']);
3268
3269
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3270
	}
3271
3272 View Code Duplication
	public function darken($color = null, $amount = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3273
		if (!$color instanceof Less_Tree_Color) {
3274
			throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3275
		}
3276
		if (!$amount instanceof Less_Tree_Dimension) {
3277
			throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3278
		}
3279
3280
		$hsl = $color->toHSL();
3281
		$hsl['l'] -= $amount->value / 100;
3282
		$hsl['l'] = self::clamp($hsl['l']);
3283
3284
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3285
	}
3286
3287 View Code Duplication
	public function fadein($color = null, $amount = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3288
		if (!$color instanceof Less_Tree_Color) {
3289
			throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3290
		}
3291
		if (!$amount instanceof Less_Tree_Dimension) {
3292
			throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3293
		}
3294
3295
		$hsl = $color->toHSL();
3296
		$hsl['a'] += $amount->value / 100;
3297
		$hsl['a'] = self::clamp($hsl['a']);
3298
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3299
	}
3300
3301 View Code Duplication
	public function fadeout($color = null, $amount = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3302
		if (!$color instanceof Less_Tree_Color) {
3303
			throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3304
		}
3305
		if (!$amount instanceof Less_Tree_Dimension) {
3306
			throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3307
		}
3308
3309
		$hsl = $color->toHSL();
3310
		$hsl['a'] -= $amount->value / 100;
3311
		$hsl['a'] = self::clamp($hsl['a']);
3312
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3313
	}
3314
3315 View Code Duplication
	public function fade($color = null, $amount = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3316
		if (!$color instanceof Less_Tree_Color) {
3317
			throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3318
		}
3319
		if (!$amount instanceof Less_Tree_Dimension) {
3320
			throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3321
		}
3322
3323
		$hsl = $color->toHSL();
3324
3325
		$hsl['a'] = $amount->value / 100;
3326
		$hsl['a'] = self::clamp($hsl['a']);
3327
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3328
	}
3329
3330
3331
3332
	public function spin($color = null, $amount = null){
3333
		if (!$color instanceof Less_Tree_Color) {
3334
			throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3335
		}
3336
		if (!$amount instanceof Less_Tree_Dimension) {
3337
			throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3338
		}
3339
3340
		$hsl = $color->toHSL();
3341
		$hue = fmod($hsl['h'] + $amount->value, 360);
3342
3343
		$hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
3344
3345
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3346
	}
3347
3348
	//
3349
	// Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
3350
	// http://sass-lang.com
3351
	//
3352
3353
	/**
3354
	 * @param Less_Tree_Color $color1
3355
	 */
3356
	public function mix($color1 = null, $color2 = null, $weight = null){
3357
		if (!$color1 instanceof Less_Tree_Color) {
3358
			throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3359
		}
3360
		if (!$color2 instanceof Less_Tree_Color) {
3361
			throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3362
		}
3363
		if (!$weight) {
3364
			$weight = new Less_Tree_Dimension('50', '%');
3365
		}
3366
		if (!$weight instanceof Less_Tree_Dimension) {
3367
			throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3368
		}
3369
3370
		$p = $weight->value / 100.0;
3371
		$w = $p * 2 - 1;
3372
		$hsl1 = $color1->toHSL();
3373
		$hsl2 = $color2->toHSL();
3374
		$a = $hsl1['a'] - $hsl2['a'];
3375
3376
		$w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
3377
		$w2 = 1 - $w1;
3378
3379
		$rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
3380
					 $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
3381
					 $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2);
3382
3383
		$alpha = $color1->alpha * $p + $color2->alpha * (1 - $p);
3384
3385
		return new Less_Tree_Color($rgb, $alpha);
3386
	}
3387
3388
	public function greyscale($color){
3389
		return $this->desaturate($color, new Less_Tree_Dimension(100,'%'));
3390
	}
3391
3392
3393
	public function contrast( $color, $dark = null, $light = null, $threshold = null){
3394
		// filter: contrast(3.2);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3395
		// should be kept as is, so check for color
3396
		if (!$color instanceof Less_Tree_Color) {
3397
			return null;
3398
		}
3399
		if( !$light ){
3400
			$light = $this->rgba(255, 255, 255, 1.0);
3401
		}
3402
		if( !$dark ){
3403
			$dark = $this->rgba(0, 0, 0, 1.0);
3404
		}
3405
3406 View Code Duplication
		if (!$dark instanceof Less_Tree_Color) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3407
			throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3408
		}
3409 View Code Duplication
		if (!$light instanceof Less_Tree_Color) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3410
			throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3411
		}
3412
3413
		//Figure out which is actually light and dark!
3414
		if( $dark->luma() > $light->luma() ){
3415
			$t = $light;
3416
			$light = $dark;
3417
			$dark = $t;
3418
		}
3419
		if( !$threshold ){
3420
			$threshold = 0.43;
3421
		} else {
3422
			$threshold = Less_Functions::number($threshold);
3423
		}
3424
3425
		if( $color->luma() < $threshold ){
3426
			return $light;
3427
		} else {
3428
			return $dark;
3429
		}
3430
	}
3431
3432
	public function e ($str){
3433
		if( is_string($str) ){
3434
			return new Less_Tree_Anonymous($str);
3435
		}
3436
		return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
3437
	}
3438
3439
	public function escape ($str){
3440
3441
		$revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$');
3442
3443
		return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
3444
	}
3445
3446
3447
	/**
3448
	 * todo: This function will need some additional work to make it work the same as less.js
3449
	 *
3450
	 */
3451
	public function replace( $string, $pattern, $replacement, $flags = null ){
3452
		$result = $string->value;
3453
3454
		$expr = '/'.str_replace('/','\\/',$pattern->value).'/';
3455
		if( $flags && $flags->value){
3456
			$expr .= self::replace_flags($flags->value);
3457
		}
3458
3459
		$result = preg_replace($expr,$replacement->value,$result);
3460
3461
3462
		if( property_exists($string,'quote') ){
3463
			return new Less_Tree_Quoted( $string->quote, $result, $string->escaped);
3464
		}
3465
		return new Less_Tree_Quoted( '', $result );
3466
	}
3467
3468
	public static function replace_flags($flags){
3469
		$flags = str_split($flags,1);
3470
		$new_flags = '';
3471
3472
		foreach($flags as $flag){
3473
			switch($flag){
3474
				case 'e':
3475
				case 'g':
3476
				break;
3477
3478
				default:
3479
				$new_flags .= $flag;
3480
				break;
3481
			}
3482
		}
3483
3484
		return $new_flags;
3485
	}
3486
3487
	public function _percent(){
3488
		$string = func_get_arg(0);
3489
3490
		$args = func_get_args();
3491
		array_shift($args);
3492
		$result = $string->value;
3493
3494
		foreach($args as $arg){
3495
			if( preg_match('/%[sda]/i',$result, $token) ){
3496
				$token = $token[0];
3497
				$value = stristr($token, 's') ? $arg->value : $arg->toCSS();
3498
				$value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
3499
				$result = preg_replace('/%[sda]/i',$value, $result, 1);
3500
			}
3501
		}
3502
		$result = str_replace('%%', '%', $result);
3503
3504
		return new Less_Tree_Quoted( $string->quote , $result, $string->escaped);
3505
	}
3506
3507
    public function unit( $val, $unit = null) {
3508
		if( !($val instanceof Less_Tree_Dimension) ){
3509
			throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') );
3510
		}
3511
3512
		if( $unit ){
3513
			if( $unit instanceof Less_Tree_Keyword ){
3514
				$unit = $unit->value;
3515
			} else {
3516
				$unit = $unit->toCSS();
3517
			}
3518
		} else {
3519
			$unit = "";
3520
		}
3521
		return new Less_Tree_Dimension($val->value, $unit );
3522
    }
3523
3524
	public function convert($val, $unit){
3525
		return $val->convertTo($unit->value);
3526
	}
3527
3528
	public function round($n, $f = false) {
3529
3530
		$fraction = 0;
3531
		if( $f !== false ){
3532
			$fraction = $f->value;
3533
		}
3534
3535
		return $this->_math('Less_Parser::round',null, $n, $fraction);
3536
	}
3537
3538
	public function pi(){
3539
		return new Less_Tree_Dimension(M_PI);
3540
	}
3541
3542
	public function mod($a, $b) {
3543
		return new Less_Tree_Dimension( $a->value % $b->value, $a->unit);
3544
	}
3545
3546
3547
3548
	public function pow($x, $y) {
3549
		if( is_numeric($x) && is_numeric($y) ){
3550
			$x = new Less_Tree_Dimension($x);
3551
			$y = new Less_Tree_Dimension($y);
3552
		}elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){
3553
			throw new Less_Exception_Compiler('Arguments must be numbers');
3554
		}
3555
3556
		return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit );
3557
	}
3558
3559
	// var mathFunctions = [{name:"ce ...
3560
	public function ceil( $n ){		return $this->_math('ceil', null, $n); }
3561
	public function floor( $n ){	return $this->_math('floor', null, $n); }
3562
	public function sqrt( $n ){		return $this->_math('sqrt', null, $n); }
3563
	public function abs( $n ){		return $this->_math('abs', null, $n); }
3564
3565
	public function tan( $n ){		return $this->_math('tan', '', $n);	}
3566
	public function sin( $n ){		return $this->_math('sin', '', $n);	}
3567
	public function cos( $n ){		return $this->_math('cos', '', $n);	}
3568
3569
	public function atan( $n ){		return $this->_math('atan', 'rad', $n);	}
3570
	public function asin( $n ){		return $this->_math('asin', 'rad', $n);	}
3571
	public function acos( $n ){		return $this->_math('acos', 'rad', $n);	}
3572
3573
	private function _math() {
3574
		$args = func_get_args();
3575
		$fn = array_shift($args);
3576
		$unit = array_shift($args);
3577
3578
		if ($args[0] instanceof Less_Tree_Dimension) {
3579
3580
			if( $unit === null ){
3581
				$unit = $args[0]->unit;
3582
			}else{
3583
				$args[0] = $args[0]->unify();
3584
			}
3585
			$args[0] = (float)$args[0]->value;
3586
			return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit);
3587
		} else if (is_numeric($args[0])) {
3588
			return call_user_func_array($fn,$args);
3589
		} else {
3590
			throw new Less_Exception_Compiler("math functions take numbers as parameters");
3591
		}
3592
	}
3593
3594
	/**
3595
	 * @param boolean $isMin
3596
	 */
3597
	private function _minmax( $isMin, $args ){
3598
3599
		$arg_count = count($args);
3600
3601
		if( $arg_count < 1 ){
3602
			throw new Less_Exception_Compiler( 'one or more arguments required');
3603
		}
3604
3605
		$j = null;
0 ignored issues
show
Unused Code introduced by
$j is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3606
		$unitClone = null;
3607
		$unitStatic = null;
3608
3609
3610
		$order = array();	// elems only contains original argument values.
3611
		$values = array();	// key is the unit.toString() for unified tree.Dimension values,
3612
							// value is the index into the order array.
3613
3614
3615
		for( $i = 0; $i < $arg_count; $i++ ){
3616
			$current = $args[$i];
3617
			if( !($current instanceof Less_Tree_Dimension) ){
3618
				if( is_array($args[$i]->value) ){
3619
					$args[] = $args[$i]->value;
3620
				}
3621
				continue;
3622
			}
3623
3624
			if( $current->unit->toString() === '' && !$unitClone ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $unitClone of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3625
				$temp = new Less_Tree_Dimension($current->value, $unitClone);
3626
				$currentUnified = $temp->unify();
3627
			}else{
3628
				$currentUnified = $current->unify();
3629
			}
3630
3631
			if( $currentUnified->unit->toString() === "" && !$unitStatic ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $unitStatic of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3632
				$unit = $unitStatic;
3633
			}else{
3634
				$unit = $currentUnified->unit->toString();
3635
			}
3636
3637
			if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $unitStatic of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3638
				$unitStatic = $unit;
3639
			}
3640
3641
			if( $unit != '' && !$unitClone ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $unitClone of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3642
				$unitClone = $current->unit->toString();
3643
			}
3644
3645
			if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){
3646
				$j = $values[''];
3647
			}elseif( isset($values[$unit]) ){
3648
				$j = $values[$unit];
3649
			}else{
3650
3651
				if( $unitStatic && $unit !== $unitStatic ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $unitStatic of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3652
					throw new Less_Exception_Compiler( 'incompatible types');
3653
				}
3654
				$values[$unit] = count($order);
3655
				$order[] = $current;
3656
				continue;
3657
			}
3658
3659
3660
			if( $order[$j]->unit->toString() === "" && $unitClone ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $unitClone of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
3661
				$temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone);
3662
				$referenceUnified = $temp->unify();
3663
			}else{
3664
				$referenceUnified = $order[$j]->unify();
3665
			}
3666
			if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){
3667
				$order[$j] = $current;
3668
			}
3669
		}
3670
3671
		if( count($order) == 1 ){
3672
			return $order[0];
3673
		}
3674
		$args = array();
3675
		foreach($order as $a){
3676
			$args[] = $a->toCSS($this->env);
3677
		}
3678
		return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')');
3679
	}
3680
3681
	public function min(){
3682
		$args = func_get_args();
3683
		return $this->_minmax( true, $args );
3684
	}
3685
3686
	public function max(){
3687
		$args = func_get_args();
3688
		return $this->_minmax( false, $args );
3689
	}
3690
3691
	public function getunit($n){
3692
		return new Less_Tree_Anonymous($n->unit);
3693
	}
3694
3695 View Code Duplication
	public function argb($color) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3696
		if (!$color instanceof Less_Tree_Color) {
3697
			throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3698
		}
3699
3700
		return new Less_Tree_Anonymous($color->toARGB());
3701
	}
3702
3703
	public function percentage($n) {
3704
		return new Less_Tree_Dimension($n->value * 100, '%');
3705
	}
3706
3707
	public function color($n) {
3708
3709
		if( $n instanceof Less_Tree_Quoted ){
3710
			$colorCandidate = $n->value;
3711
			$returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
3712
			if( $returnColor ){
3713
				return $returnColor;
3714
			}
3715
			if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){
3716
				return new Less_Tree_Color(substr($colorCandidate, 1));
3717
			}
3718
			throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
3719
		} else {
3720
			throw new Less_Exception_Compiler("argument must be a string");
3721
		}
3722
	}
3723
3724
3725
	public function iscolor($n) {
3726
		return $this->_isa($n, 'Less_Tree_Color');
3727
	}
3728
3729
	public function isnumber($n) {
3730
		return $this->_isa($n, 'Less_Tree_Dimension');
3731
	}
3732
3733
	public function isstring($n) {
3734
		return $this->_isa($n, 'Less_Tree_Quoted');
3735
	}
3736
3737
	public function iskeyword($n) {
3738
		return $this->_isa($n, 'Less_Tree_Keyword');
3739
	}
3740
3741
	public function isurl($n) {
3742
		return $this->_isa($n, 'Less_Tree_Url');
3743
	}
3744
3745
	public function ispixel($n) {
3746
		return $this->isunit($n, 'px');
3747
	}
3748
3749
	public function ispercentage($n) {
3750
		return $this->isunit($n, '%');
3751
	}
3752
3753
	public function isem($n) {
3754
		return $this->isunit($n, 'em');
3755
	}
3756
3757
	/**
3758
	 * @param string $unit
3759
	 */
3760
	public function isunit( $n, $unit ){
3761
		return ($n instanceof Less_Tree_Dimension) && $n->unit->is( ( property_exists($unit,'value') ? $unit->value : $unit) ) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
3762
	}
3763
3764
	/**
3765
	 * @param string $type
3766
	 */
3767
	private function _isa($n, $type) {
3768
		return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
3769
	}
3770
3771
	public function tint($color, $amount) {
3772
		return $this->mix( $this->rgb(255,255,255), $color, $amount);
3773
	}
3774
3775
	public function shade($color, $amount) {
3776
		return $this->mix($this->rgb(0, 0, 0), $color, $amount);
3777
	}
3778
3779
	public function extract($values, $index ){
3780
		$index = (int)$index->value - 1; // (1-based index)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3781
		// handle non-array values as an array of length 1
3782
		// return 'undefined' if index is invalid
3783
		if( property_exists($values,'value') && is_array($values->value) ){
3784
			if( isset($values->value[$index]) ){
3785
				return $values->value[$index];
3786
			}
3787
			return null;
3788
3789
		}elseif( (int)$index === 0 ){
3790
			return $values;
3791
		}
3792
3793
		return null;
3794
	}
3795
3796
	public function length($values){
3797
		$n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1;
3798
		return new Less_Tree_Dimension($n);
3799
	}
3800
3801
	public function datauri($mimetypeNode, $filePathNode = null ) {
3802
3803
		$filePath = ( $filePathNode ? $filePathNode->value : null );
3804
		$mimetype = $mimetypeNode->value;
3805
3806
		$args = 2;
3807
		if( !$filePath ){
3808
			$filePath = $mimetype;
3809
			$args = 1;
3810
		}
3811
3812
		$filePath = str_replace('\\','/',$filePath);
3813
		if( Less_Environment::isPathRelative($filePath) ){
3814
3815
			if( Less_Parser::$options['relativeUrls'] ){
3816
				$temp = $this->currentFileInfo['currentDirectory'];
3817
			} else {
3818
				$temp = $this->currentFileInfo['entryPath'];
3819
			}
3820
3821
			if( !empty($temp) ){
3822
				$filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath);
3823
			}
3824
3825
		}
3826
3827
3828
		// detect the mimetype if not given
3829
		if( $args < 2 ){
3830
3831
			/* incomplete
3832
			$mime = require('mime');
3833
			mimetype = mime.lookup(path);
3834
3835
			// use base 64 unless it's an ASCII or UTF-8 format
3836
			var charset = mime.charsets.lookup(mimetype);
3837
			useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
3838
			if (useBase64) mimetype += ';base64';
3839
			*/
3840
3841
			$mimetype = Less_Mime::lookup($filePath);
3842
3843
			$charset = Less_Mime::charsets_lookup($mimetype);
3844
			$useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8'));
3845
			if( $useBase64 ){ $mimetype .= ';base64'; }
3846
3847
		}else{
3848
			$useBase64 = preg_match('/;base64$/',$mimetype);
3849
		}
3850
3851
3852
		if( file_exists($filePath) ){
3853
			$buf = @file_get_contents($filePath);
3854
		}else{
3855
			$buf = false;
3856
		}
3857
3858
3859
		// IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
3860
		// and the --ieCompat flag is enabled, return a normal url() instead.
3861
		$DATA_URI_MAX_KB = 32;
3862
		$fileSizeInKB = round( strlen($buf) / 1024 );
3863
		if( $fileSizeInKB >= $DATA_URI_MAX_KB ){
3864
			$url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
3865
			return $url->compile($this);
3866
		}
3867
3868
		if( $buf ){
3869
			$buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
3870
			$filePath = '"data:' . $mimetype . ',' . $buf . '"';
3871
		}
3872
3873
		return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) );
3874
	}
3875
3876
	//svg-gradient
3877
	public function svggradient( $direction ){
3878
3879
		$throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
3880
		$arguments = func_get_args();
3881
3882
		if( count($arguments) < 3 ){
3883
			throw new Less_Exception_Compiler( $throw_message );
3884
		}
3885
3886
		$stops = array_slice($arguments,1);
3887
		$gradientType = 'linear';
3888
		$rectangleDimension = 'x="0" y="0" width="1" height="1"';
3889
		$useBase64 = true;
3890
		$directionValue = $direction->toCSS();
3891
3892
3893
		switch( $directionValue ){
3894
			case "to bottom":
3895
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
3896
				break;
3897
			case "to right":
3898
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
3899
				break;
3900
			case "to bottom right":
3901
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
3902
				break;
3903
			case "to top right":
3904
				$gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
3905
				break;
3906
			case "ellipse":
3907
			case "ellipse at center":
3908
				$gradientType = "radial";
3909
				$gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
3910
				$rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
3911
				break;
3912
			default:
3913
				throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" );
3914
		}
3915
3916
		$returner = '<?xml version="1.0" ?>' .
3917
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' .
3918
			'<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
3919
3920
		for( $i = 0; $i < count($stops); $i++ ){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3921
			if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){
3922
				$color = $stops[$i]->value[0];
3923
				$position = $stops[$i]->value[1];
3924
			}else{
3925
				$color = $stops[$i];
3926
				$position = null;
3927
			}
3928
3929
			if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){
3930
				throw new Less_Exception_Compiler( $throw_message );
3931
			}
3932
			if( $position ){
3933
				$positionValue = $position->toCSS();
3934
			}elseif( $i === 0 ){
3935
				$positionValue = '0%';
3936
			}else{
3937
				$positionValue = '100%';
3938
			}
3939
			$alpha = $color->alpha;
3940
			$returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>';
3941
		}
3942
3943
		$returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>';
3944
3945
3946
		if( $useBase64 ){
3947
			$returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'";
3948
		}else{
3949
			$returner = "'data:image/svg+xml,".$returner."'";
3950
		}
3951
3952
		return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
3953
	}
3954
3955
3956
	/**
3957
	 * Php version of javascript's `encodeURIComponent` function
3958
	 *
3959
	 * @param string $string The string to encode
3960
	 * @return string The encoded string
3961
	 */
3962
	public static function encodeURIComponent($string){
3963
		$revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
3964
		return strtr(rawurlencode($string), $revert);
3965
	}
3966
3967
3968
	// Color Blending
3969
	// ref: http://www.w3.org/TR/compositing-1
3970
3971
	public function colorBlend( $mode, $color1, $color2 ){
3972
		$ab = $color1->alpha;	// backdrop
3973
		$as = $color2->alpha;	// source
3974
		$r = array();			// result
3975
3976
		$ar = $as + $ab * (1 - $as);
3977
		for( $i = 0; $i < 3; $i++ ){
3978
			$cb = $color1->rgb[$i] / 255;
3979
			$cs = $color2->rgb[$i] / 255;
3980
			$cr = call_user_func( $mode, $cb, $cs );
3981
			if( $ar ){
3982
				$cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
3983
			}
3984
			$r[$i] = $cr * 255;
3985
		}
3986
3987
		return new Less_Tree_Color($r, $ar);
3988
	}
3989
3990 View Code Duplication
	public function multiply($color1 = null, $color2 = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3991
		if (!$color1 instanceof Less_Tree_Color) {
3992
			throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3993
		}
3994
		if (!$color2 instanceof Less_Tree_Color) {
3995
			throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3996
		}
3997
3998
		return $this->colorBlend( array($this,'colorBlendMultiply'),  $color1, $color2 );
3999
	}
4000
4001
	private function colorBlendMultiply($cb, $cs){
4002
		return $cb * $cs;
4003
	}
4004
4005 View Code Duplication
	public function screen($color1 = null, $color2 = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4006
		if (!$color1 instanceof Less_Tree_Color) {
4007
			throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4008
		}
4009
		if (!$color2 instanceof Less_Tree_Color) {
4010
			throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4011
		}
4012
4013
		return $this->colorBlend( array($this,'colorBlendScreen'),  $color1, $color2 );
4014
	}
4015
4016
	private function colorBlendScreen( $cb, $cs){
4017
		return $cb + $cs - $cb * $cs;
4018
	}
4019
4020 View Code Duplication
	public function overlay($color1 = null, $color2 = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4021
		if (!$color1 instanceof Less_Tree_Color) {
4022
			throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4023
		}
4024
		if (!$color2 instanceof Less_Tree_Color) {
4025
			throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4026
		}
4027
4028
		return $this->colorBlend( array($this,'colorBlendOverlay'),  $color1, $color2 );
4029
	}
4030
4031
	private function colorBlendOverlay($cb, $cs ){
4032
		$cb *= 2;
4033
		return ($cb <= 1)
4034
			? $this->colorBlendMultiply($cb, $cs)
4035
			: $this->colorBlendScreen($cb - 1, $cs);
4036
	}
4037
4038 View Code Duplication
	public function softlight($color1 = null, $color2 = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4039
		if (!$color1 instanceof Less_Tree_Color) {
4040
			throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4041
		}
4042
		if (!$color2 instanceof Less_Tree_Color) {
4043
			throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4044
		}
4045
4046
		return $this->colorBlend( array($this,'colorBlendSoftlight'),  $color1, $color2 );
4047
	}
4048
4049
	private function colorBlendSoftlight($cb, $cs ){
4050
		$d = 1;
4051
		$e = $cb;
4052
		if( $cs > 0.5 ){
4053
			$e = 1;
4054
			$d = ($cb > 0.25) ? sqrt($cb)
4055
				: ((16 * $cb - 12) * $cb + 4) * $cb;
4056
		}
4057
		return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
4058
	}
4059
4060 View Code Duplication
	public function hardlight($color1 = null, $color2 = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4061
		if (!$color1 instanceof Less_Tree_Color) {
4062
			throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4063
		}
4064
		if (!$color2 instanceof Less_Tree_Color) {
4065
			throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4066
		}
4067
4068
		return $this->colorBlend( array($this,'colorBlendHardlight'),  $color1, $color2 );
4069
	}
4070
4071
	private function colorBlendHardlight( $cb, $cs ){
4072
		return $this->colorBlendOverlay($cs, $cb);
4073
	}
4074
4075 View Code Duplication
	public function difference($color1 = null, $color2 = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4076
		if (!$color1 instanceof Less_Tree_Color) {
4077
			throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4078
		}
4079
		if (!$color2 instanceof Less_Tree_Color) {
4080
			throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4081
		}
4082
4083
		return $this->colorBlend( array($this,'colorBlendDifference'),  $color1, $color2 );
4084
	}
4085
4086
	private function colorBlendDifference( $cb, $cs ){
4087
		return abs($cb - $cs);
4088
	}
4089
4090 View Code Duplication
	public function exclusion( $color1 = null, $color2 = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4091
		if (!$color1 instanceof Less_Tree_Color) {
4092
			throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4093
		}
4094
		if (!$color2 instanceof Less_Tree_Color) {
4095
			throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4096
		}
4097
4098
		return $this->colorBlend( array($this,'colorBlendExclusion'),  $color1, $color2 );
4099
	}
4100
4101
	private function colorBlendExclusion( $cb, $cs ){
4102
		return $cb + $cs - 2 * $cb * $cs;
4103
	}
4104
4105 View Code Duplication
	public function average($color1 = null, $color2 = null){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4106
		if (!$color1 instanceof Less_Tree_Color) {
4107
			throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4108
		}
4109
		if (!$color2 instanceof Less_Tree_Color) {
4110
			throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4111
		}
4112
4113
		return $this->colorBlend( array($this,'colorBlendAverage'),  $color1, $color2 );
4114
	}
4115
4116
	// non-w3c functions:
4117
	public function colorBlendAverage($cb, $cs ){
4118
		return ($cb + $cs) / 2;
4119
	}
4120
4121 View Code Duplication
	public function negation($color1 = null, $color2 = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4122
		if (!$color1 instanceof Less_Tree_Color) {
4123
			throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4124
		}
4125
		if (!$color2 instanceof Less_Tree_Color) {
4126
			throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4127
		}
4128
4129
		return $this->colorBlend( array($this,'colorBlendNegation'),  $color1, $color2 );
4130
	}
4131
4132
	public function colorBlendNegation($cb, $cs){
4133
		return 1 - abs($cb + $cs - 1);
4134
	}
4135
4136
	// ~ End of Color Blending
4137
4138
}
4139
 
4140
4141
/**
4142
 * Mime lookup
4143
 *
4144
 * @package Less
4145
 * @subpackage node
4146
 */
4147
class Less_Mime{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4148
4149
	// this map is intentionally incomplete
4150
	// if you want more, install 'mime' dep
4151
	static $_types = array(
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $_types.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
4152
	        '.htm' => 'text/html',
4153
	        '.html'=> 'text/html',
4154
	        '.gif' => 'image/gif',
4155
	        '.jpg' => 'image/jpeg',
4156
	        '.jpeg'=> 'image/jpeg',
4157
	        '.png' => 'image/png',
4158
	        '.ttf' => 'application/x-font-ttf',
4159
	        '.otf' => 'application/x-font-otf',
4160
	        '.eot' => 'application/vnd.ms-fontobject',
4161
	        '.woff' => 'application/x-font-woff',
4162
	        '.svg' => 'image/svg+xml',
4163
	        );
4164
4165
	public static function lookup( $filepath ){
4166
		$parts = explode('.',$filepath);
4167
		$ext = '.'.strtolower(array_pop($parts));
4168
4169
		if( !isset(self::$_types[$ext]) ){
4170
			return null;
4171
		}
4172
		return self::$_types[$ext];
4173
	}
4174
4175
	public static function charsets_lookup( $type = null ){
4176
		// assumes all text types are UTF-8
4177
		return $type && preg_match('/^text\//',$type) ? 'UTF-8' : '';
4178
	}
4179
}
4180
 
4181
4182
/**
4183
 * Tree
4184
 *
4185
 * @package Less
4186
 * @subpackage tree
4187
 */
4188
class Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4189
4190
	public $cache_string;
4191
4192
	public function toCSS(){
4193
		$output = new Less_Output();
4194
		$this->genCSS($output);
4195
		return $output->toString();
4196
	}
4197
4198
4199
    /**
4200
     * Generate CSS by adding it to the output object
4201
     *
4202
     * @param Less_Output $output The output
4203
     * @return void
4204
     */
4205
    public function genCSS($output){}
4206
4207
4208
	/**
4209
	 * @param Less_Tree_Ruleset[] $rules
4210
	 */
4211
	public static function outputRuleset( $output, $rules ){
4212
4213
		$ruleCnt = count($rules);
4214
		Less_Environment::$tabLevel++;
4215
4216
4217
		// Compressed
4218
		if( Less_Parser::$options['compress'] ){
4219
			$output->add('{');
4220
			for( $i = 0; $i < $ruleCnt; $i++ ){
4221
				$rules[$i]->genCSS( $output );
4222
			}
4223
4224
			$output->add( '}' );
4225
			Less_Environment::$tabLevel--;
4226
			return;
4227
		}
4228
4229
4230
		// Non-compressed
4231
		$tabSetStr = "\n".str_repeat( '  ' , Less_Environment::$tabLevel-1 );
4232
		$tabRuleStr = $tabSetStr.'  ';
4233
4234
		$output->add( " {" );
4235
		for($i = 0; $i < $ruleCnt; $i++ ){
4236
			$output->add( $tabRuleStr );
4237
			$rules[$i]->genCSS( $output );
4238
		}
4239
		Less_Environment::$tabLevel--;
4240
		$output->add( $tabSetStr.'}' );
4241
4242
	}
4243
4244
	public function accept($visitor){}
4245
4246
4247
	public static function ReferencedArray($rules){
4248
		foreach($rules as $rule){
4249
			if( method_exists($rule, 'markReferenced') ){
4250
				$rule->markReferenced();
4251
			}
4252
		}
4253
	}
4254
4255
4256
	/**
4257
	 * Requires php 5.3+
4258
	 */
4259
	public static function __set_state($args){
4260
4261
		$class = get_called_class();
4262
		$obj = new $class(null,null,null,null);
4263
		foreach($args as $key => $val){
4264
			$obj->$key = $val;
4265
		}
4266
		return $obj;
4267
	}
4268
4269
} 
4270
4271
/**
4272
 * Parser output
4273
 *
4274
 * @package Less
4275
 * @subpackage output
4276
 */
4277
class Less_Output{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4278
4279
	/**
4280
	 * Output holder
4281
	 *
4282
	 * @var string
4283
	 */
4284
	protected $strs = array();
4285
4286
	/**
4287
	 * Adds a chunk to the stack
4288
	 *
4289
	 * @param string $chunk The chunk to output
4290
	 * @param Less_FileInfo $fileInfo The file information
4291
	 * @param integer $index The index
4292
	 * @param mixed $mapLines
4293
	 */
4294
	public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
4295
		$this->strs[] = $chunk;
4296
	}
4297
4298
	/**
4299
	 * Is the output empty?
4300
	 *
4301
	 * @return boolean
4302
	 */
4303
	public function isEmpty(){
4304
		return count($this->strs) === 0;
4305
	}
4306
4307
4308
	/**
4309
	 * Converts the output to string
4310
	 *
4311
	 * @return string
4312
	 */
4313
	public function toString(){
4314
		return implode('',$this->strs);
4315
	}
4316
4317
} 
4318
4319
/**
4320
 * Visitor
4321
 *
4322
 * @package Less
4323
 * @subpackage visitor
4324
 */
4325
class Less_Visitor{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4326
4327
	protected $methods = array();
4328
	protected $_visitFnCache = array();
4329
4330
	public function __construct(){
4331
		$this->_visitFnCache = get_class_methods(get_class($this));
4332
		$this->_visitFnCache = array_flip($this->_visitFnCache);
4333
	}
4334
4335
	public function visitObj( $node ){
4336
4337
		$funcName = 'visit'.$node->type;
4338
		if( isset($this->_visitFnCache[$funcName]) ){
4339
4340
			$visitDeeper = true;
4341
			$this->$funcName( $node, $visitDeeper );
4342
4343
			if( $visitDeeper ){
4344
				$node->accept($this);
4345
			}
4346
4347
			$funcName = $funcName . "Out";
4348
			if( isset($this->_visitFnCache[$funcName]) ){
4349
				$this->$funcName( $node );
4350
			}
4351
4352
		}else{
4353
			$node->accept($this);
4354
		}
4355
4356
		return $node;
4357
	}
4358
4359
	public function visitArray( $nodes ){
4360
4361
		array_map( array($this,'visitObj'), $nodes);
4362
		return $nodes;
4363
	}
4364
}
4365
4366
 
4367
4368
/**
4369
 * Replacing Visitor
4370
 *
4371
 * @package Less
4372
 * @subpackage visitor
4373
 */
4374
class Less_VisitorReplacing extends Less_Visitor{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4375
4376
	public function visitObj( $node ){
4377
4378
		$funcName = 'visit'.$node->type;
4379
		if( isset($this->_visitFnCache[$funcName]) ){
4380
4381
			$visitDeeper = true;
4382
			$node = $this->$funcName( $node, $visitDeeper );
4383
4384
			if( $node ){
4385
				if( $visitDeeper && is_object($node) ){
4386
					$node->accept($this);
4387
				}
4388
4389
				$funcName = $funcName . "Out";
4390
				if( isset($this->_visitFnCache[$funcName]) ){
4391
					$this->$funcName( $node );
4392
				}
4393
			}
4394
4395
		}else{
4396
			$node->accept($this);
4397
		}
4398
4399
		return $node;
4400
	}
4401
4402
	public function visitArray( $nodes ){
4403
4404
		$newNodes = array();
4405
		foreach($nodes as $node){
4406
			$evald = $this->visitObj($node);
4407
			if( $evald ){
4408
				if( is_array($evald) ){
4409
					self::flatten($evald,$newNodes);
4410
				}else{
4411
					$newNodes[] = $evald;
4412
				}
4413
			}
4414
		}
4415
		return $newNodes;
4416
	}
4417
4418
	public function flatten( $arr, &$out ){
4419
4420
		foreach($arr as $item){
4421
			if( !is_array($item) ){
4422
				$out[] = $item;
4423
				continue;
4424
			}
4425
4426
			foreach($item as $nestedItem){
4427
				if( is_array($nestedItem) ){
4428
					self::flatten( $nestedItem, $out);
4429
				}else{
4430
					$out[] = $nestedItem;
4431
				}
4432
			}
4433
		}
4434
4435
		return $out;
4436
	}
4437
4438
}
4439
4440
4441
 
4442
4443
/**
4444
 * Configurable
4445
 *
4446
 * @package Less
4447
 * @subpackage Core
4448
 */
4449
abstract class Less_Configurable {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4450
4451
	/**
4452
	 * Array of options
4453
	 *
4454
	 * @var array
4455
	 */
4456
	protected $options = array();
4457
4458
	/**
4459
	 * Array of default options
4460
	 *
4461
	 * @var array
4462
	 */
4463
	protected $defaultOptions = array();
4464
4465
4466
	/**
4467
	 * Set options
4468
	 *
4469
	 * If $options is an object it will be converted into an array by called
4470
	 * it's toArray method.
4471
	 *
4472
	 * @throws Exception
4473
	 * @param array|object $options
4474
	 *
4475
	 */
4476
	public function setOptions($options){
4477
		$options = array_intersect_key($options,$this->defaultOptions);
4478
		$this->options = array_merge($this->defaultOptions, $this->options, $options);
4479
	}
4480
4481
4482
	/**
4483
	 * Get an option value by name
4484
	 *
4485
	 * If the option is empty or not set a NULL value will be returned.
4486
	 *
4487
	 * @param string $name
4488
	 * @param mixed $default Default value if confiuration of $name is not present
4489
	 * @return mixed
4490
	 */
4491
	public function getOption($name, $default = null){
4492
		if(isset($this->options[$name])){
4493
			return $this->options[$name];
4494
		}
4495
		return $default;
4496
	}
4497
4498
4499
	/**
4500
	 * Set an option
4501
	 *
4502
	 * @param string $name
4503
	 * @param mixed $value
4504
	 */
4505
	public function setOption($name, $value){
4506
		$this->options[$name] = $value;
4507
	}
4508
4509
} 
4510
4511
/**
4512
 * Alpha
4513
 *
4514
 * @package Less
4515
 * @subpackage tree
4516
 */
4517
class Less_Tree_Alpha extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4518
	public $value;
4519
	public $type = 'Alpha';
4520
4521
	public function __construct($val){
4522
		$this->value = $val;
4523
	}
4524
4525
	//function accept( $visitor ){
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4526
	//	$this->value = $visitor->visit( $this->value );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4527
	//}
4528
4529
	public function compile($env){
4530
4531
		if( is_object($this->value) ){
4532
			$this->value = $this->value->compile($env);
4533
		}
4534
4535
		return $this;
4536
	}
4537
4538
    /**
4539
     * @see Less_Tree::genCSS
4540
     */
4541
	public function genCSS( $output ){
4542
4543
		$output->add( "alpha(opacity=" );
4544
4545
		if( is_string($this->value) ){
4546
			$output->add( $this->value );
4547
		}else{
4548
			$this->value->genCSS( $output);
4549
		}
4550
4551
		$output->add( ')' );
4552
	}
4553
4554
	public function toCSS(){
4555
		return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
4556
	}
4557
4558
4559
} 
4560
4561
/**
4562
 * Anonymous
4563
 *
4564
 * @package Less
4565
 * @subpackage tree
4566
 */
4567
class Less_Tree_Anonymous extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4568
	public $value;
4569
	public $quote;
4570
	public $index;
4571
	public $mapLines;
4572
	public $currentFileInfo;
4573
	public $type = 'Anonymous';
4574
4575
	/**
4576
	 * @param integer $index
4577
	 * @param boolean $mapLines
4578
	 */
4579
	public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null ){
4580
		$this->value = $value;
4581
		$this->index = $index;
4582
		$this->mapLines = $mapLines;
4583
		$this->currentFileInfo = $currentFileInfo;
4584
	}
4585
4586
	public function compile(){
4587
		return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines);
4588
	}
4589
4590 View Code Duplication
    public function compare($x){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4591
		if( !is_object($x) ){
4592
			return -1;
4593
		}
4594
4595
		$left = $this->toCSS();
4596
		$right = $x->toCSS();
4597
4598
		if( $left === $right ){
4599
			return 0;
4600
		}
4601
4602
		return $left < $right ? -1 : 1;
4603
	}
4604
4605
    /**
4606
     * @see Less_Tree::genCSS
4607
     */
4608
	public function genCSS( $output ){
4609
		$output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines );
4610
	}
4611
4612
	public function toCSS(){
4613
		return $this->value;
4614
	}
4615
4616
}
4617
 
4618
4619
/**
4620
 * Assignment
4621
 *
4622
 * @package Less
4623
 * @subpackage tree
4624
 */
4625
class Less_Tree_Assignment extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4626
4627
	public $key;
4628
	public $value;
4629
	public $type = 'Assignment';
4630
4631
    public function __construct($key, $val) {
4632
		$this->key = $key;
4633
		$this->value = $val;
4634
	}
4635
4636
    public function accept( $visitor ){
4637
		$this->value = $visitor->visitObj( $this->value );
4638
	}
4639
4640
	public function compile($env) {
4641
		return new Less_Tree_Assignment( $this->key, $this->value->compile($env));
4642
	}
4643
4644
    /**
4645
     * @see Less_Tree::genCSS
4646
     */
4647
	public function genCSS( $output ){
4648
		$output->add( $this->key . '=' );
4649
		$this->value->genCSS( $output );
4650
	}
4651
4652
	public function toCss(){
4653
		return $this->key . '=' . $this->value->toCSS();
4654
	}
4655
}
4656
 
4657
4658
/**
4659
 * Attribute
4660
 *
4661
 * @package Less
4662
 * @subpackage tree
4663
 */
4664
class Less_Tree_Attribute extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4665
4666
	public $key;
4667
	public $op;
4668
	public $value;
4669
	public $type = 'Attribute';
4670
4671
    public function __construct($key, $op, $value){
4672
		$this->key = $key;
4673
		$this->op = $op;
4674
		$this->value = $value;
4675
	}
4676
4677
    public function compile($env){
4678
4679
		$key_obj = is_object($this->key);
4680
		$val_obj = is_object($this->value);
4681
4682
		if( !$key_obj && !$val_obj ){
4683
			return $this;
4684
		}
4685
4686
		return new Less_Tree_Attribute(
4687
			$key_obj ? $this->key->compile($env) : $this->key ,
4688
			$this->op,
4689
			$val_obj ? $this->value->compile($env) : $this->value);
4690
	}
4691
4692
    /**
4693
     * @see Less_Tree::genCSS
4694
     */
4695
    public function genCSS( $output ){
4696
		$output->add( $this->toCSS() );
4697
	}
4698
4699
    public function toCSS(){
4700
		$value = $this->key;
4701
4702
		if( $this->op ){
4703
			$value .= $this->op;
4704
			$value .= (is_object($this->value) ? $this->value->toCSS() : $this->value);
4705
		}
4706
4707
		return '[' . $value . ']';
4708
	}
4709
} 
4710
4711
4712
/**
4713
 * Call
4714
 *
4715
 * @package Less
4716
 * @subpackage tree
4717
 */
4718
class Less_Tree_Call extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4719
    public $value;
4720
4721
    protected $name;
4722
    protected $args;
4723
    protected $index;
4724
    protected $currentFileInfo;
4725
    public $type = 'Call';
4726
4727 View Code Duplication
	public function __construct($name, $args, $index, $currentFileInfo = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4728
		$this->name = $name;
4729
		$this->args = $args;
4730
		$this->index = $index;
4731
		$this->currentFileInfo = $currentFileInfo;
4732
	}
4733
4734
    public function accept( $visitor ){
4735
		$this->args = $visitor->visitArray( $this->args );
4736
	}
4737
4738
    //
4739
    // When evaluating a function call,
4740
    // we either find the function in `tree.functions` [1],
4741
    // in which case we call it, passing the  evaluated arguments,
4742
    // or we simply print it out as it appeared originally [2].
4743
    //
4744
    // The *functions.js* file contains the built-in functions.
4745
    //
4746
    // The reason why we evaluate the arguments, is in the case where
4747
    // we try to pass a variable to a function, like: `saturate(@color)`.
4748
    // The function should receive the value, not the variable.
4749
    //
4750
    public function compile($env=null){
4751
		$args = array();
4752
		foreach($this->args as $a){
4753
			$args[] = $a->compile($env);
4754
		}
4755
4756
		$nameLC = strtolower($this->name);
4757
		switch($nameLC){
4758
			case '%':
4759
			$nameLC = '_percent';
4760
			break;
4761
4762
			case 'get-unit':
4763
			$nameLC = 'getunit';
4764
			break;
4765
4766
			case 'data-uri':
4767
			$nameLC = 'datauri';
4768
			break;
4769
4770
			case 'svg-gradient':
4771
			$nameLC = 'svggradient';
4772
			break;
4773
		}
4774
4775
		$result = null;
4776
		if( $nameLC === 'default' ){
4777
			$result = Less_Tree_DefaultFunc::compile();
4778
4779
		}else{
4780
4781
			if( method_exists('Less_Functions',$nameLC) ){ // 1.
4782
				try {
4783
4784
					$func = new Less_Functions($env, $this->currentFileInfo);
4785
					$result = call_user_func_array( array($func,$nameLC),$args);
4786
4787
				} catch (Exception $e) {
4788
					throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
4789
				}
4790
			} elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
4791
				try {
4792
					$result = call_user_func_array( $env->functions[$nameLC], $args );
4793
				} catch (Exception $e) {
4794
					throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
4795
				}
4796
			}
4797
		}
4798
4799
		if( $result !== null ){
4800
			return $result;
4801
		}
4802
4803
4804
		return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo );
4805
    }
4806
4807
    /**
4808
     * @see Less_Tree::genCSS
4809
     */
4810
	public function genCSS( $output ){
4811
4812
		$output->add( $this->name . '(', $this->currentFileInfo, $this->index );
4813
		$args_len = count($this->args);
4814
		for($i = 0; $i < $args_len; $i++ ){
4815
			$this->args[$i]->genCSS( $output );
4816
			if( $i + 1 < $args_len ){
4817
				$output->add( ', ' );
4818
			}
4819
		}
4820
4821
		$output->add( ')' );
4822
	}
4823
4824
4825
    //public function toCSS(){
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4826
    //    return $this->compile()->toCSS();
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
4827
    //}
4828
4829
}
4830
 
4831
4832
/**
4833
 * Color
4834
 *
4835
 * @package Less
4836
 * @subpackage tree
4837
 */
4838
class Less_Tree_Color extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4839
	public $rgb;
4840
	public $alpha;
4841
	public $isTransparentKeyword;
4842
	public $type = 'Color';
4843
4844
	public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){
4845
4846
		if( $isTransparentKeyword ){
4847
			$this->rgb = $rgb;
4848
			$this->alpha = $a;
4849
			$this->isTransparentKeyword = true;
4850
			return;
4851
		}
4852
4853
		$this->rgb = array();
4854
		if( is_array($rgb) ){
4855
			$this->rgb = $rgb;
4856
		}else if( strlen($rgb) == 6 ){
4857
			foreach(str_split($rgb, 2) as $c){
4858
				$this->rgb[] = hexdec($c);
4859
			}
4860
		}else{
4861
			foreach(str_split($rgb, 1) as $c){
4862
				$this->rgb[] = hexdec($c.$c);
4863
			}
4864
		}
4865
		$this->alpha = is_numeric($a) ? $a : 1;
4866
	}
4867
4868
	public function compile(){
4869
		return $this;
4870
	}
4871
4872
	public function luma(){
4873
		$r = $this->rgb[0] / 255;
4874
		$g = $this->rgb[1] / 255;
4875
		$b = $this->rgb[2] / 255;
4876
4877
		$r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
4878
		$g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
4879
		$b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
4880
4881
		return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
4882
	}
4883
4884
	/**
4885
	 * @see Less_Tree::genCSS
4886
	 */
4887
	public function genCSS( $output ){
4888
		$output->add( $this->toCSS() );
4889
	}
4890
4891
	public function toCSS( $doNotCompress = false ){
4892
		$compress = Less_Parser::$options['compress'] && !$doNotCompress;
4893
		$alpha = Less_Functions::fround( $this->alpha );
4894
4895
4896
		//
4897
		// If we have some transparency, the only way to represent it
4898
		// is via `rgba`. Otherwise, we use the hex representation,
4899
		// which has better compatibility with older browsers.
4900
		// Values are capped between `0` and `255`, rounded and zero-padded.
4901
		//
4902
		if( $alpha < 1 ){
4903
			if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){
4904
				return 'transparent';
4905
			}
4906
4907
			$values = array();
4908
			foreach($this->rgb as $c){
4909
				$values[] = Less_Functions::clamp( round($c), 255);
4910
			}
4911
			$values[] = $alpha;
4912
4913
			$glue = ($compress ? ',' : ', ');
4914
			return "rgba(" . implode($glue, $values) . ")";
4915
		}else{
4916
4917
			$color = $this->toRGB();
4918
4919
			if( $compress ){
4920
4921
				// Convert color to short format
4922
				if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
4923
					$color = '#'.$color[1] . $color[3] . $color[5];
4924
				}
4925
			}
4926
4927
			return $color;
4928
		}
4929
	}
4930
4931
	//
4932
	// Operations have to be done per-channel, if not,
4933
	// channels will spill onto each other. Once we have
4934
	// our result, in the form of an integer triplet,
4935
	// we create a new Color node to hold the result.
4936
	//
4937
4938
	/**
4939
	 * @param string $op
4940
	 */
4941
	public function operate( $op, $other) {
4942
		$rgb = array();
4943
		$alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
4944
		for ($c = 0; $c < 3; $c++) {
4945
			$rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]);
4946
		}
4947
		return new Less_Tree_Color($rgb, $alpha);
4948
	}
4949
4950
	public function toRGB(){
4951
		return $this->toHex($this->rgb);
4952
	}
4953
4954
	public function toHSL(){
4955
		$r = $this->rgb[0] / 255;
4956
		$g = $this->rgb[1] / 255;
4957
		$b = $this->rgb[2] / 255;
4958
		$a = $this->alpha;
4959
4960
		$max = max($r, $g, $b);
4961
		$min = min($r, $g, $b);
4962
		$l = ($max + $min) / 2;
4963
		$d = $max - $min;
4964
4965
		$h = $s = 0;
4966
		if( $max !== $min ){
4967
			$s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
4968
4969 View Code Duplication
			switch ($max) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4970
				case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
4971
				case $g: $h = ($b - $r) / $d + 2;				 break;
4972
				case $b: $h = ($r - $g) / $d + 4;				 break;
4973
			}
4974
			$h /= 6;
4975
		}
4976
		return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
4977
	}
4978
4979
	//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
4980
    public function toHSV() {
4981
		$r = $this->rgb[0] / 255;
4982
		$g = $this->rgb[1] / 255;
4983
		$b = $this->rgb[2] / 255;
4984
		$a = $this->alpha;
4985
4986
		$max = max($r, $g, $b);
4987
		$min = min($r, $g, $b);
4988
4989
		$v = $max;
4990
4991
		$d = $max - $min;
4992
		if ($max === 0) {
4993
			$s = 0;
4994
		} else {
4995
			$s = $d / $max;
4996
		}
4997
4998
		$h = 0;
4999
		if( $max !== $min ){
5000 View Code Duplication
			switch($max){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5001
				case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
5002
				case $g: $h = ($b - $r) / $d + 2; break;
5003
				case $b: $h = ($r - $g) / $d + 4; break;
5004
			}
5005
			$h /= 6;
5006
		}
5007
		return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a );
5008
	}
5009
5010
	public function toARGB(){
5011
		$argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb);
5012
		return $this->toHex( $argb );
5013
	}
5014
5015
	public function compare($x){
5016
5017
		if( !property_exists( $x, 'rgb' ) ){
5018
			return -1;
5019
		}
5020
5021
5022
		return ($x->rgb[0] === $this->rgb[0] &&
5023
			$x->rgb[1] === $this->rgb[1] &&
5024
			$x->rgb[2] === $this->rgb[2] &&
5025
			$x->alpha === $this->alpha) ? 0 : -1;
5026
	}
5027
5028
    public function toHex( $v ){
5029
5030
		$ret = '#';
5031
		foreach($v as $c){
5032
			$c = Less_Functions::clamp( Less_Parser::round($c), 255);
5033
			if( $c < 16 ){
5034
				$ret .= '0';
5035
			}
5036
			$ret .= dechex($c);
5037
		}
5038
5039
		return $ret;
5040
	}
5041
5042
5043
	/**
5044
	 * @param string $keyword
5045
	 */
5046
	public static function fromKeyword( $keyword ){
5047
		$keyword = strtolower($keyword);
5048
5049
		if( Less_Colors::hasOwnProperty($keyword) ){
5050
			// detect named color
5051
			return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
5052
		}
5053
5054
		if( $keyword === 'transparent' ){
5055
			return new Less_Tree_Color( array(0, 0, 0), 0, true);
5056
		}
5057
	}
5058
5059
}
5060
 
5061
5062
/**
5063
 * Comment
5064
 *
5065
 * @package Less
5066
 * @subpackage tree
5067
 */
5068
class Less_Tree_Comment extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5069
5070
	public $value;
5071
	public $silent;
5072
	public $isReferenced;
5073
	public $currentFileInfo;
5074
	public $type = 'Comment';
5075
5076
	public function __construct($value, $silent, $index = null, $currentFileInfo = null ){
0 ignored issues
show
Unused Code introduced by
The parameter $index is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
5077
		$this->value = $value;
5078
		$this->silent = !! $silent;
5079
		$this->currentFileInfo = $currentFileInfo;
5080
	}
5081
5082
    /**
5083
     * @see Less_Tree::genCSS
5084
     */
5085
	public function genCSS( $output ){
5086
		//if( $this->debugInfo ){
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5087
			//$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index);
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5088
		//}
5089
		$output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n
5090
	}
5091
5092
	public function toCSS(){
5093
		return Less_Parser::$options['compress'] ? '' : $this->value;
5094
	}
5095
5096
	public function isSilent(){
5097
		$isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) );
5098
		$isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value);
5099
		return $this->silent || $isReference || $isCompressed;
5100
	}
5101
5102
	public function compile(){
5103
		return $this;
5104
	}
5105
5106
	public function markReferenced(){
5107
		$this->isReferenced = true;
5108
	}
5109
5110
}
5111
 
5112
5113
/**
5114
 * Condition
5115
 *
5116
 * @package Less
5117
 * @subpackage tree
5118
 */
5119
class Less_Tree_Condition extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5120
5121
	public $op;
5122
	public $lvalue;
5123
	public $rvalue;
5124
	public $index;
5125
	public $negate;
5126
	public $type = 'Condition';
5127
5128
	public function __construct($op, $l, $r, $i = 0, $negate = false) {
5129
		$this->op = trim($op);
5130
		$this->lvalue = $l;
5131
		$this->rvalue = $r;
5132
		$this->index = $i;
5133
		$this->negate = $negate;
5134
	}
5135
5136
	public function accept($visitor){
5137
		$this->lvalue = $visitor->visitObj( $this->lvalue );
5138
		$this->rvalue = $visitor->visitObj( $this->rvalue );
5139
	}
5140
5141
    public function compile($env) {
5142
		$a = $this->lvalue->compile($env);
5143
		$b = $this->rvalue->compile($env);
5144
5145
		switch( $this->op ){
5146
			case 'and':
5147
				$result = $a && $b;
5148
			break;
5149
5150
			case 'or':
5151
				$result = $a || $b;
5152
			break;
5153
5154
			default:
5155
				if( Less_Parser::is_method($a, 'compare') ){
5156
					$result = $a->compare($b);
5157
				}elseif( Less_Parser::is_method($b, 'compare') ){
5158
					$result = $b->compare($a);
5159
				}else{
5160
					throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index);
5161
				}
5162
5163
				switch ($result) {
5164 View Code Duplication
					case -1:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5165
					$result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
5166
					break;
5167
5168 View Code Duplication
					case  0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5169
					$result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<=';
5170
					break;
5171
5172
					case  1:
0 ignored issues
show
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
5173
					$result = $this->op === '>' || $this->op === '>=';
5174
					break;
5175
				}
5176
			break;
5177
		}
5178
5179
		return $this->negate ? !$result : $result;
5180
    }
5181
5182
}
5183
 
5184
5185
/**
5186
 * DefaultFunc
5187
 *
5188
 * @package Less
5189
 * @subpackage tree
5190
 */
5191
class Less_Tree_DefaultFunc{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5192
5193
	static $error_;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $error_.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
5194
	static $value_;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $value_.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
5195
5196
    public static function compile(){
5197
		if( self::$error_ ){
5198
			throw new Exception(self::$error_);
5199
		}
5200
		if( self::$value_ !== null ){
5201
			return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
5202
		}
5203
	}
5204
5205
    public static function value( $v ){
5206
		self::$value_ = $v;
5207
	}
5208
5209
    public static function error( $e ){
5210
		self::$error_ = $e;
5211
	}
5212
5213
    public static function reset(){
5214
		self::$value_ = self::$error_ = null;
5215
	}
5216
} 
5217
5218
/**
5219
 * DetachedRuleset
5220
 *
5221
 * @package Less
5222
 * @subpackage tree
5223
 */
5224
class Less_Tree_DetachedRuleset extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5225
5226
	public $ruleset;
5227
	public $frames;
5228
	public $type = 'DetachedRuleset';
5229
5230
    public function __construct( $ruleset, $frames = null ){
5231
		$this->ruleset = $ruleset;
5232
		$this->frames = $frames;
5233
	}
5234
5235
    public function accept($visitor) {
5236
		$this->ruleset = $visitor->visitObj($this->ruleset);
5237
	}
5238
5239
    public function compile($env){
5240
		if( $this->frames ){
5241
			$frames = $this->frames;
5242
		}else{
5243
			$frames = $env->frames;
5244
		}
5245
		return new Less_Tree_DetachedRuleset($this->ruleset, $frames);
5246
	}
5247
5248
    public function callEval($env) {
5249
		if( $this->frames ){
5250
			return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) );
5251
		}
5252
		return $this->ruleset->compile( $env );
5253
	}
5254
}
5255
5256
 
5257
5258
/**
5259
 * Dimension
5260
 *
5261
 * @package Less
5262
 * @subpackage tree
5263
 */
5264
class Less_Tree_Dimension extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5265
5266
	public $value;
5267
	public $unit;
5268
	public $type = 'Dimension';
5269
5270
    public function __construct($value, $unit = null){
5271
        $this->value = floatval($value);
5272
5273
		if( $unit && ($unit instanceof Less_Tree_Unit) ){
5274
			$this->unit = $unit;
5275
		}elseif( $unit ){
5276
			$this->unit = new Less_Tree_Unit( array($unit) );
5277
		}else{
5278
			$this->unit = new Less_Tree_Unit( );
5279
		}
5280
    }
5281
5282
    public function accept( $visitor ){
5283
		$this->unit = $visitor->visitObj( $this->unit );
5284
	}
5285
5286
    public function compile(){
5287
        return $this;
5288
    }
5289
5290
    public function toColor() {
5291
        return new Less_Tree_Color(array($this->value, $this->value, $this->value));
5292
    }
5293
5294
    /**
5295
     * @see Less_Tree::genCSS
5296
     */
5297
	public function genCSS( $output ){
5298
5299
		if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){
5300
			throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString());
5301
		}
5302
5303
		$value = Less_Functions::fround( $this->value );
5304
		$strValue = (string)$value;
5305
5306
		if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){
5307
			// would be output 1e-6 etc.
5308
			$strValue = number_format($strValue,10);
5309
			$strValue = preg_replace('/\.?0+$/','', $strValue);
5310
		}
5311
5312
		if( Less_Parser::$options['compress'] ){
5313
			// Zero values doesn't need a unit
5314
			if( $value === 0 && $this->unit->isLength() ){
5315
				$output->add( $strValue );
5316
				return $strValue;
5317
			}
5318
5319
			// Float values doesn't need a leading zero
5320
			if( $value > 0 && $value < 1 && $strValue[0] === '0' ){
5321
				$strValue = substr($strValue,1);
5322
			}
5323
		}
5324
5325
		$output->add( $strValue );
5326
		$this->unit->genCSS( $output );
5327
	}
5328
5329
    public function __toString(){
5330
        return $this->toCSS();
5331
    }
5332
5333
    // In an operation between two Dimensions,
5334
    // we default to the first Dimension's unit,
5335
    // so `1px + 2em` will yield `3px`.
5336
5337
    /**
5338
     * @param string $op
5339
     */
5340
    public function operate( $op, $other){
5341
5342
		$value = Less_Functions::operate( $op, $this->value, $other->value);
5343
		$unit = clone $this->unit;
5344
5345
		if( $op === '+' || $op === '-' ){
5346
5347
			if( !$unit->numerator && !$unit->denominator ){
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
Bug Best Practice introduced by
The expression $unit->numerator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $unit->denominator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
5348
				$unit->numerator = $other->unit->numerator;
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5349
				$unit->denominator = $other->unit->denominator;
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5350
			}elseif( !$other->unit->numerator && !$other->unit->denominator ){
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
5351
				// do nothing
5352
			}else{
5353
				$other = $other->convertTo( $this->unit->usedUnits());
5354
5355
				if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){
5356
					throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '".$unit->toString() . "' and ".$other->unit->toString()+"'.");
5357
				}
5358
5359
				$value = Less_Functions::operate( $op, $this->value, $other->value);
5360
			}
5361
		}elseif( $op === '*' ){
5362
			$unit->numerator = array_merge($unit->numerator, $other->unit->numerator);
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5363
			$unit->denominator = array_merge($unit->denominator, $other->unit->denominator);
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5364
			sort($unit->numerator);
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5365
			sort($unit->denominator);
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5366
			$unit->cancel();
5367
		}elseif( $op === '/' ){
5368
			$unit->numerator = array_merge($unit->numerator, $other->unit->denominator);
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5369
			$unit->denominator = array_merge($unit->denominator, $other->unit->numerator);
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5370
			sort($unit->numerator);
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5371
			sort($unit->denominator);
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5372
			$unit->cancel();
5373
		}
5374
		return new Less_Tree_Dimension( $value, $unit);
5375
    }
5376
5377
	public function compare($other) {
5378
		if ($other instanceof Less_Tree_Dimension) {
5379
5380
			if( $this->unit->isEmpty() || $other->unit->isEmpty() ){
5381
				$a = $this;
5382
				$b = $other;
5383
			} else {
5384
				$a = $this->unify();
5385
				$b = $other->unify();
5386
				if( $a->unit->compare($b->unit) !== 0 ){
5387
					return -1;
5388
				}
5389
			}
5390
			$aValue = $a->value;
5391
			$bValue = $b->value;
5392
5393
			if ($bValue > $aValue) {
5394
				return -1;
5395
			} elseif ($bValue < $aValue) {
5396
				return 1;
5397
			} else {
5398
				return 0;
5399
			}
5400
		} else {
5401
			return -1;
5402
		}
5403
	}
5404
5405
    public function unify() {
5406
		return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' ));
5407
	}
5408
5409
    public function convertTo($conversions) {
5410
		$value = $this->value;
5411
		$unit = clone $this->unit;
5412
5413
		if( is_string($conversions) ){
5414
			$derivedConversions = array();
5415
			foreach( Less_Tree_UnitConversions::$groups as $i ){
5416
				if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){
5417
					$derivedConversions = array( $i => $conversions);
5418
				}
5419
			}
5420
			$conversions = $derivedConversions;
5421
		}
5422
5423
5424
		foreach($conversions as $groupName => $targetUnit){
5425
			$group = Less_Tree_UnitConversions::${$groupName};
5426
5427
			//numerator
5428 View Code Duplication
			foreach($unit->numerator as $i => $atomicUnit){
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5429
				$atomicUnit = $unit->numerator[$i];
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5430
				if( !isset($group[$atomicUnit]) ){
5431
					continue;
5432
				}
5433
5434
				$value = $value * ($group[$atomicUnit] / $group[$targetUnit]);
5435
5436
				$unit->numerator[$i] = $targetUnit;
0 ignored issues
show
Bug introduced by
The property numerator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5437
			}
5438
5439
			//denominator
5440 View Code Duplication
			foreach($unit->denominator as $i => $atomicUnit){
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5441
				$atomicUnit = $unit->denominator[$i];
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5442
				if( !isset($group[$atomicUnit]) ){
5443
					continue;
5444
				}
5445
5446
				$value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
5447
5448
				$unit->denominator[$i] = $targetUnit;
0 ignored issues
show
Bug introduced by
The property denominator cannot be accessed from this context as it is declared private in class Less_Tree_Unit.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
5449
			}
5450
		}
5451
5452
		$unit->cancel();
5453
5454
		return new Less_Tree_Dimension( $value, $unit);
5455
    }
5456
}
5457
 
5458
5459
/**
5460
 * Directive
5461
 *
5462
 * @package Less
5463
 * @subpackage tree
5464
 */
5465
class Less_Tree_Directive extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5466
5467
	public $name;
5468
	public $value;
5469
	public $rules;
5470
	public $index;
5471
	public $isReferenced;
5472
	public $currentFileInfo;
5473
	public $debugInfo;
5474
	public $type = 'Directive';
5475
5476
	public function __construct($name, $value = null, $rules, $index = null, $currentFileInfo = null, $debugInfo = null ){
5477
		$this->name = $name;
5478
		$this->value = $value;
5479
		if( $rules ){
5480
			$this->rules = $rules;
5481
			$this->rules->allowImports = true;
5482
		}
5483
5484
		$this->index = $index;
5485
		$this->currentFileInfo = $currentFileInfo;
5486
		$this->debugInfo = $debugInfo;
5487
	}
5488
5489
5490
    public function accept( $visitor ){
5491
		if( $this->rules ){
5492
			$this->rules = $visitor->visitObj( $this->rules );
5493
		}
5494
		if( $this->value ){
5495
			$this->value = $visitor->visitObj( $this->value );
5496
		}
5497
	}
5498
5499
5500
    /**
5501
     * @see Less_Tree::genCSS
5502
     */
5503
    public function genCSS( $output ){
5504
		$value = $this->value;
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
5505
		$rules = $this->rules;
0 ignored issues
show
Unused Code introduced by
$rules is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
5506
		$output->add( $this->name, $this->currentFileInfo, $this->index );
5507
		if( $this->value ){
5508
			$output->add(' ');
5509
			$this->value->genCSS($output);
5510
		}
5511
		if( $this->rules ){
5512
			Less_Tree::outputRuleset( $output, array($this->rules));
5513
		} else {
5514
			$output->add(';');
5515
		}
5516
	}
5517
5518
	public function compile($env){
5519
5520
		$value = $this->value;
5521
		$rules = $this->rules;
5522
		if( $value ){
5523
			$value = $value->compile($env);
5524
		}
5525
5526
		if( $rules ){
5527
			$rules = $rules->compile($env);
5528
			$rules->root = true;
5529
		}
5530
5531
		return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo );
5532
	}
5533
5534
5535
	public function variable($name){
5536
		if( $this->rules ){
5537
			return $this->rules->variable($name);
5538
		}
5539
	}
5540
5541
	public function find($selector){
5542
		if( $this->rules ){
5543
			return $this->rules->find($selector, $this);
5544
		}
5545
	}
5546
5547
	//rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5548
5549
	public function markReferenced(){
5550
		$this->isReferenced = true;
5551
		if( $this->rules ){
5552
			Less_Tree::ReferencedArray($this->rules->rules);
5553
		}
5554
	}
5555
5556
}
5557
 
5558
5559
/**
5560
 * Element
5561
 *
5562
 * @package Less
5563
 * @subpackage tree
5564
 */
5565
class Less_Tree_Element extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5566
5567
	public $combinator = '';
5568
	public $value = '';
5569
	public $index;
5570
	public $currentFileInfo;
5571
	public $type = 'Element';
5572
5573
	public $value_is_object = false;
5574
5575
	public function __construct($combinator, $value, $index = null, $currentFileInfo = null ){
5576
5577
		$this->value = $value;
5578
		$this->value_is_object = is_object($value);
5579
5580
		if( $combinator ){
5581
			$this->combinator = $combinator;
5582
		}
5583
5584
		$this->index = $index;
5585
		$this->currentFileInfo = $currentFileInfo;
5586
	}
5587
5588
    public function accept( $visitor ){
5589
		if( $this->value_is_object ){ //object or string
5590
			$this->value = $visitor->visitObj( $this->value );
5591
		}
5592
	}
5593
5594
	public function compile($env){
5595
5596
		if( Less_Environment::$mixin_stack ){
5597
			return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo );
0 ignored issues
show
Bug introduced by
The method compile cannot be called on $this->value (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
5598
		}
5599
5600
		if( $this->value_is_object ){
5601
			$this->value = $this->value->compile($env);
0 ignored issues
show
Bug introduced by
The method compile cannot be called on $this->value (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
5602
		}
5603
5604
		return $this;
5605
	}
5606
5607
    /**
5608
     * @see Less_Tree::genCSS
5609
     */
5610
	public function genCSS( $output ){
5611
		$output->add( $this->toCSS(), $this->currentFileInfo, $this->index );
5612
	}
5613
5614
	public function toCSS(){
5615
5616
		if( $this->value_is_object ){
5617
			$value = $this->value->toCSS();
0 ignored issues
show
Bug introduced by
The method toCSS cannot be called on $this->value (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
5618
		}else{
5619
			$value = $this->value;
5620
		}
5621
5622
5623
		if( $value === '' && $this->combinator && $this->combinator === '&' ){
5624
			return '';
5625
		}
5626
5627
5628
		return Less_Environment::$_outputMap[$this->combinator] . $value;
5629
	}
5630
5631
}
5632
 
5633
5634
/**
5635
 * Expression
5636
 *
5637
 * @package Less
5638
 * @subpackage tree
5639
 */
5640
class Less_Tree_Expression extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5641
5642
	public $value = array();
5643
	public $parens = false;
5644
	public $parensInOp = false;
5645
	public $type = 'Expression';
5646
5647
	public function __construct( $value, $parens = null ){
5648
		$this->value = $value;
5649
		$this->parens = $parens;
5650
	}
5651
5652
    public function accept( $visitor ){
5653
		$this->value = $visitor->visitArray( $this->value );
5654
	}
5655
5656
	public function compile($env) {
5657
5658
		$doubleParen = false;
5659
5660
		if( $this->parens && !$this->parensInOp ){
5661
			Less_Environment::$parensStack++;
5662
		}
5663
5664
		$returnValue = null;
0 ignored issues
show
Unused Code introduced by
$returnValue is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
5665
		if( $this->value ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->value of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
5666
5667
			$count = count($this->value);
5668
5669
			if( $count > 1 ){
5670
5671
				$ret = array();
5672
				foreach($this->value as $e){
5673
					$ret[] = $e->compile($env);
5674
				}
5675
				$returnValue = new Less_Tree_Expression($ret);
5676
5677
			}else{
5678
5679
				if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){
5680
					$doubleParen = true;
5681
				}
5682
5683
				$returnValue = $this->value[0]->compile($env);
5684
			}
5685
5686
		} else {
5687
			$returnValue = $this;
5688
		}
5689
5690
		if( $this->parens ){
5691
			if( !$this->parensInOp ){
5692
				Less_Environment::$parensStack--;
5693
5694
			}elseif( !Less_Environment::isMathOn() && !$doubleParen ){
5695
				$returnValue = new Less_Tree_Paren($returnValue);
5696
5697
			}
5698
		}
5699
		return $returnValue;
5700
	}
5701
5702
    /**
5703
     * @see Less_Tree::genCSS
5704
     */
5705 View Code Duplication
    public function genCSS( $output ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5706
		$val_len = count($this->value);
5707
		for( $i = 0; $i < $val_len; $i++ ){
5708
			$this->value[$i]->genCSS( $output );
5709
			if( $i + 1 < $val_len ){
5710
				$output->add( ' ' );
5711
			}
5712
		}
5713
	}
5714
5715
    public function throwAwayComments() {
5716
5717
		if( is_array($this->value) ){
5718
			$new_value = array();
5719
			foreach($this->value as $v){
5720
				if( $v instanceof Less_Tree_Comment ){
5721
					continue;
5722
				}
5723
				$new_value[] = $v;
5724
			}
5725
			$this->value = $new_value;
5726
		}
5727
	}
5728
}
5729
 
5730
5731
/**
5732
 * Extend
5733
 *
5734
 * @package Less
5735
 * @subpackage tree
5736
 */
5737
class Less_Tree_Extend extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5738
5739
	public $selector;
5740
	public $option;
5741
	public $index;
5742
	public $selfSelectors = array();
5743
	public $allowBefore;
5744
	public $allowAfter;
5745
	public $firstExtendOnThisSelectorPath;
5746
	public $type = 'Extend';
5747
	public $ruleset;
5748
5749
5750
	public $object_id;
5751
	public $parent_ids = array();
5752
5753
	/**
5754
	 * @param integer $index
5755
	 */
5756
    public function __construct($selector, $option, $index){
5757
		static $i = 0;
5758
		$this->selector = $selector;
5759
		$this->option = $option;
5760
		$this->index = $index;
5761
5762
		switch($option){
5763
			case "all":
5764
				$this->allowBefore = true;
5765
				$this->allowAfter = true;
5766
			break;
5767
			default:
5768
				$this->allowBefore = false;
5769
				$this->allowAfter = false;
5770
			break;
5771
		}
5772
5773
		$this->object_id = $i++;
5774
		$this->parent_ids = array($this->object_id);
5775
	}
5776
5777
    public function accept( $visitor ){
5778
		$this->selector = $visitor->visitObj( $this->selector );
5779
	}
5780
5781
    public function compile( $env ){
5782
		Less_Parser::$has_extends = true;
5783
		$this->selector = $this->selector->compile($env);
5784
		return $this;
5785
		//return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5786
	}
5787
5788
    public function findSelfSelectors( $selectors ){
5789
		$selfElements = array();
5790
5791
5792
		for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){
5793
			$selectorElements = $selectors[$i]->elements;
5794
			// duplicate the logic in genCSS function inside the selector node.
5795
			// future TODO - move both logics into the selector joiner visitor
5796
			if( $i && $selectorElements && $selectorElements[0]->combinator === "") {
5797
				$selectorElements[0]->combinator = ' ';
5798
			}
5799
			$selfElements = array_merge( $selfElements, $selectors[$i]->elements );
5800
		}
5801
5802
		$this->selfSelectors = array(new Less_Tree_Selector($selfElements));
5803
	}
5804
5805
} 
5806
5807
/**
5808
 * CSS @import node
5809
 *
5810
 * The general strategy here is that we don't want to wait
5811
 * for the parsing to be completed, before we start importing
5812
 * the file. That's because in the context of a browser,
5813
 * most of the time will be spent waiting for the server to respond.
5814
 *
5815
 * On creation, we push the import path to our import queue, though
5816
 * `import,push`, we also pass it a callback, which it'll call once
5817
 * the file has been fetched, and parsed.
5818
 *
5819
 * @package Less
5820
 * @subpackage tree
5821
 */
5822
class Less_Tree_Import extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
5823
5824
	public $options;
5825
	public $index;
5826
	public $path;
5827
	public $features;
5828
	public $currentFileInfo;
5829
	public $css;
5830
	public $skip;
5831
	public $root;
5832
	public $type = 'Import';
5833
5834
    public function __construct($path, $features, $options, $index, $currentFileInfo = null ){
5835
		$this->options = $options;
5836
		$this->index = $index;
5837
		$this->path = $path;
5838
		$this->features = $features;
5839
		$this->currentFileInfo = $currentFileInfo;
5840
5841
		if( is_array($options) ){
5842
			$this->options += array('inline'=>false);
5843
5844
			if( isset($this->options['less']) || $this->options['inline'] ){
5845
				$this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline'];
5846
			} else {
5847
				$pathValue = $this->getPath();
5848
				if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){
5849
					$this->css = true;
5850
				}
5851
			}
5852
		}
5853
	}
5854
5855
//
5856
// The actual import node doesn't return anything, when converted to CSS.
5857
// The reason is that it's used at the evaluation stage, so that the rules
5858
// it imports can be treated like any other rules.
5859
//
5860
// In `eval`, we make sure all Import nodes get evaluated, recursively, so
5861
// we end up with a flat structure, which can easily be imported in the parent
5862
// ruleset.
5863
//
5864
5865
    public function accept($visitor){
5866
5867
		if( $this->features ){
5868
			$this->features = $visitor->visitObj($this->features);
5869
		}
5870
		$this->path = $visitor->visitObj($this->path);
5871
5872
		if( !$this->options['inline'] && $this->root ){
5873
			$this->root = $visitor->visit($this->root);
5874
		}
5875
	}
5876
5877
    /**
5878
     * @see Less_Tree::genCSS
5879
     */
5880
    public function genCSS( $output ){
5881
		if( $this->css ){
5882
5883
			$output->add( '@import ', $this->currentFileInfo, $this->index );
5884
5885
			$this->path->genCSS( $output );
5886
			if( $this->features ){
5887
				$output->add( ' ' );
5888
				$this->features->genCSS( $output );
5889
			}
5890
			$output->add( ';' );
5891
		}
5892
	}
5893
5894
    public function toCSS(){
5895
		$features = $this->features ? ' ' . $this->features->toCSS() : '';
5896
5897
		if ($this->css) {
5898
			return "@import " . $this->path->toCSS() . $features . ";\n";
5899
		} else {
5900
			return "";
5901
		}
5902
	}
5903
5904
	/**
5905
	 * @return string
5906
	 */
5907
    public function getPath(){
5908
		if ($this->path instanceof Less_Tree_Quoted) {
5909
			$path = $this->path->value;
5910
			$path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less';
5911
		} else if ($this->path instanceof Less_Tree_URL) {
5912
			$path = $this->path->value->value;
5913
		}else{
5914
			return null;
5915
		}
5916
5917
		//remove query string and fragment
5918
		return preg_replace('/[\?#][^\?]*$/','',$path);
5919
	}
5920
5921
    public function compileForImport( $env ){
5922
		return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo);
5923
	}
5924
5925
    public function compilePath($env) {
5926
		$path = $this->path->compile($env);
5927
		$rootpath = '';
5928
		if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){
5929
			$rootpath = $this->currentFileInfo['rootpath'];
5930
		}
5931
5932
5933
		if( !($path instanceof Less_Tree_URL) ){
5934
			if( $rootpath ){
5935
				$pathValue = $path->value;
5936
				// Add the base path if the import is relative
5937
				if( $pathValue && Less_Environment::isPathRelative($pathValue) ){
5938
					$path->value = $this->currentFileInfo['uri_root'].$pathValue;
5939
				}
5940
			}
5941
			$path->value = Less_Environment::normalizePath($path->value);
5942
		}
5943
5944
5945
5946
		return $path;
5947
	}
5948
5949
    public function compile( $env ){
5950
5951
		$evald = $this->compileForImport($env);
5952
5953
		//get path & uri
5954
		$path_and_uri = null;
5955
		if( is_callable(Less_Parser::$options['import_callback']) ){
5956
			$path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald);
5957
		}
5958
5959
		if( !$path_and_uri ){
5960
			$path_and_uri = $evald->PathAndUri();
5961
		}
5962
5963
		if( $path_and_uri ){
5964
			list($full_path, $uri) = $path_and_uri;
5965
		}else{
5966
			$full_path = $uri = $evald->getPath();
5967
		}
5968
5969
5970
		//import once
5971
		if( $evald->skip( $full_path, $env) ){
5972
			return array();
5973
		}
5974
5975
		if( $this->options['inline'] ){
5976
			//todo needs to reference css file not import
5977
			//$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true );
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
5978
5979
			Less_Parser::AddParsedFile($full_path);
5980
			$contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
5981
5982
			if( $this->features ){
5983
				return new Less_Tree_Media( array($contents), $this->features->value );
5984
			}
5985
5986
			return array( $contents );
5987
		}
5988
5989
5990
		// css ?
5991
		if( $evald->css ){
5992
			$features = ( $evald->features ? $evald->features->compile($env) : null );
5993
			return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index);
5994
		}
5995
5996
5997
		return $this->ParseImport( $full_path, $uri, $env );
5998
	}
5999
6000
6001
	/**
6002
	 * Using the import directories, get the full absolute path and uri of the import
6003
	 *
6004
	 * @param Less_Tree_Import $evald
0 ignored issues
show
Bug introduced by
There is no parameter named $evald. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6005
	 */
6006
    public function PathAndUri(){
0 ignored issues
show
Coding Style introduced by
PathAndUri uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
6007
6008
		$evald_path = $this->getPath();
6009
6010
		if( $evald_path ){
6011
6012
			$import_dirs = array();
6013
6014
			if( Less_Environment::isPathRelative($evald_path) ){
6015
				//if the path is relative, the file should be in the current directory
6016
				$import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root'];
6017
6018
			}else{
6019
				//otherwise, the file should be relative to the server root
6020
				$import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri'];
6021
6022
				//if the user supplied entryPath isn't the actual root
6023
				$import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = '';
6024
6025
			}
6026
6027
			// always look in user supplied import directories
6028
			$import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] );
6029
6030
6031
			foreach( $import_dirs as $rootpath => $rooturi){
6032
				if( is_callable($rooturi) ){
6033
					list($path, $uri) = call_user_func($rooturi, $evald_path);
6034
					if( is_string($path) ){
6035
						$full_path = $path;
6036
						return array( $full_path, $uri );
6037
					}
6038
				}elseif( !empty($rootpath) ){
6039
6040
6041
					if( $rooturi ){
6042
						if( strpos($evald_path,$rooturi) === 0 ){
6043
							$evald_path = substr( $evald_path, strlen($rooturi) );
6044
						}
6045
					}
6046
6047
					$path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\');
6048
6049
					if( file_exists($path) ){
6050
						$full_path = Less_Environment::normalizePath($path);
6051
						$uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path));
6052
						return array( $full_path, $uri );
6053
					} elseif( file_exists($path.'.less') ){
6054
						$full_path = Less_Environment::normalizePath($path.'.less');
6055
						$uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less'));
6056
						return array( $full_path, $uri );
6057
					}
6058
				}
6059
			}
6060
		}
6061
	}
6062
6063
6064
	/**
6065
	 * Parse the import url and return the rules
6066
	 *
6067
	 * @return Less_Tree_Media|array
6068
	 */
6069
    public function ParseImport( $full_path, $uri, $env ){
6070
6071
		$import_env = clone $env;
6072
		if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){
6073
			$import_env->currentFileInfo['reference'] = true;
6074
		}
6075
6076
		if( (isset($this->options['multiple']) && $this->options['multiple']) ){
6077
			$import_env->importMultiple = true;
6078
		}
6079
6080
		$parser = new Less_Parser($import_env);
6081
		$root = $parser->parseFile($full_path, $uri, true);
6082
6083
6084
		$ruleset = new Less_Tree_Ruleset(array(), $root->rules );
6085
		$ruleset->evalImports($import_env);
6086
6087
		return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules;
6088
	}
6089
6090
6091
	/**
6092
	 * Should the import be skipped?
6093
	 *
6094
	 * @return boolean|null
6095
	 */
6096
	private function Skip($path, $env){
6097
6098
		$path = Less_Parser::winPath(realpath($path));
6099
6100
		if( $path && Less_Parser::FileParsed($path) ){
6101
6102
			if( isset($this->currentFileInfo['reference']) ){
6103
				return true;
6104
			}
6105
6106
			return !isset($this->options['multiple']) && !$env->importMultiple;
6107
		}
6108
6109
	}
6110
}
6111
6112
 
6113
6114
/**
6115
 * Javascript
6116
 *
6117
 * @package Less
6118
 * @subpackage tree
6119
 */
6120
class Less_Tree_Javascript extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6121
6122
	public $type = 'Javascript';
6123
	public $escaped;
6124
	public $expression;
6125
	public $index;
6126
6127
	/**
6128
	 * @param boolean $index
6129
	 * @param boolean $escaped
6130
	 */
6131
	public function __construct($string, $index, $escaped){
6132
		$this->escaped = $escaped;
6133
		$this->expression = $string;
6134
		$this->index = $index;
6135
	}
6136
6137
	public function compile(){
6138
		return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */');
6139
	}
6140
6141
}
6142
 
6143
6144
/**
6145
 * Keyword
6146
 *
6147
 * @package Less
6148
 * @subpackage tree
6149
 */
6150
class Less_Tree_Keyword extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6151
6152
	public $value;
6153
	public $type = 'Keyword';
6154
6155
	/**
6156
	 * @param string $value
6157
	 */
6158
	public function __construct($value){
6159
		$this->value = $value;
6160
	}
6161
6162
	public function compile(){
6163
		return $this;
6164
	}
6165
6166
    /**
6167
     * @see Less_Tree::genCSS
6168
     */
6169
	public function genCSS( $output ){
6170
6171
		if( $this->value === '%') {
6172
			throw new Less_Exception_Compiler("Invalid % without number");
6173
		}
6174
6175
		$output->add( $this->value );
6176
	}
6177
6178
	public function compare($other) {
6179
		if ($other instanceof Less_Tree_Keyword) {
6180
			return $other->value === $this->value ? 0 : 1;
6181
		} else {
6182
			return -1;
6183
		}
6184
	}
6185
}
6186
 
6187
6188
/**
6189
 * Media
6190
 *
6191
 * @package Less
6192
 * @subpackage tree
6193
 */
6194
class Less_Tree_Media extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6195
6196
	public $features;
6197
	public $rules;
6198
	public $index;
6199
	public $currentFileInfo;
6200
	public $isReferenced;
6201
	public $type = 'Media';
6202
6203
	public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null ){
6204
6205
		$this->index = $index;
6206
		$this->currentFileInfo = $currentFileInfo;
6207
6208
		$selectors = $this->emptySelectors();
6209
6210
		$this->features = new Less_Tree_Value($features);
6211
6212
		$this->rules = array(new Less_Tree_Ruleset($selectors, $value));
6213
		$this->rules[0]->allowImports = true;
6214
	}
6215
6216
    public function accept( $visitor ){
6217
		$this->features = $visitor->visitObj($this->features);
6218
		$this->rules = $visitor->visitArray($this->rules);
6219
	}
6220
6221
    /**
6222
     * @see Less_Tree::genCSS
6223
     */
6224
    public function genCSS( $output ){
6225
6226
		$output->add( '@media ', $this->currentFileInfo, $this->index );
6227
		$this->features->genCSS( $output );
6228
		Less_Tree::outputRuleset( $output, $this->rules);
6229
6230
	}
6231
6232
	public function compile($env) {
6233
6234
		$media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo );
6235
6236
		$strictMathBypass = false;
6237
		if( Less_Parser::$options['strictMath'] === false) {
6238
			$strictMathBypass = true;
6239
			Less_Parser::$options['strictMath'] = true;
6240
		}
6241
6242
		$media->features = $this->features->compile($env);
6243
6244
		if( $strictMathBypass ){
6245
			Less_Parser::$options['strictMath'] = false;
6246
		}
6247
6248
		$env->mediaPath[] = $media;
6249
		$env->mediaBlocks[] = $media;
6250
6251
		array_unshift($env->frames, $this->rules[0]);
6252
		$media->rules = array($this->rules[0]->compile($env));
6253
		array_shift($env->frames);
6254
6255
		array_pop($env->mediaPath);
6256
6257
		return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env);
6258
	}
6259
6260
	public function variable($name) {
6261
		return $this->rules[0]->variable($name);
6262
	}
6263
6264
	public function find($selector) {
6265
		return $this->rules[0]->find($selector, $this);
6266
	}
6267
6268
	public function emptySelectors(){
6269
		$el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo );
6270
		$sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) );
6271
		$sels[0]->mediaEmpty = true;
6272
        return $sels;
6273
	}
6274
6275
	public function markReferenced(){
6276
		$this->rules[0]->markReferenced();
6277
		$this->isReferenced = true;
6278
		Less_Tree::ReferencedArray($this->rules[0]->rules);
6279
	}
6280
6281
	// evaltop
6282
	public function compileTop($env) {
6283
		$result = $this;
6284
6285
		if (count($env->mediaBlocks) > 1) {
6286
			$selectors = $this->emptySelectors();
6287
			$result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks);
6288
			$result->multiMedia = true;
6289
		}
6290
6291
		$env->mediaBlocks = array();
6292
		$env->mediaPath = array();
6293
6294
		return $result;
6295
	}
6296
6297
	public function compileNested($env) {
6298
		$path = array_merge($env->mediaPath, array($this));
6299
6300
		// Extract the media-query conditions separated with `,` (OR).
6301
		foreach ($path as $key => $p) {
6302
			$value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
6303
			$path[$key] = is_array($value) ? $value : array($value);
6304
		}
6305
6306
		// Trace all permutations to generate the resulting media-query.
6307
		//
6308
		// (a, b and c) with nested (d, e) ->
6309
		//	a and d
6310
		//	a and e
6311
		//	b and c and d
6312
		//	b and c and e
6313
6314
		$permuted = $this->permute($path);
6315
		$expressions = array();
6316
		foreach($permuted as $path){
6317
6318
			for( $i=0, $len=count($path); $i < $len; $i++){
6319
				$path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]);
6320
			}
6321
6322
			for( $i = count($path) - 1; $i > 0; $i-- ){
6323
				array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and')));
6324
			}
6325
6326
			$expressions[] = new Less_Tree_Expression($path);
6327
		}
6328
		$this->features = new Less_Tree_Value($expressions);
6329
6330
6331
6332
		// Fake a tree-node that doesn't output anything.
6333
		return new Less_Tree_Ruleset(array(), array());
6334
	}
6335
6336
	public function permute($arr) {
6337
		if (!$arr)
6338
			return array();
6339
6340
		if (count($arr) == 1)
6341
			return $arr[0];
6342
6343
		$result = array();
6344
		$rest = $this->permute(array_slice($arr, 1));
6345
		foreach ($rest as $r) {
6346
			foreach ($arr[0] as $a) {
6347
				$result[] = array_merge(
6348
					is_array($a) ? $a : array($a),
6349
					is_array($r) ? $r : array($r)
6350
				);
6351
			}
6352
		}
6353
6354
		return $result;
6355
	}
6356
6357
    public function bubbleSelectors($selectors) {
6358
6359
		if( !$selectors) return;
6360
6361
		$this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0])));
6362
	}
6363
6364
}
6365
 
6366
6367
/**
6368
 * A simple css name-value pair
6369
 * ex: width:100px;
6370
 *
6371
 * In bootstrap, there are about 600-1,000 simple name-value pairs (depending on how forgiving the match is) -vs- 6,020 dynamic rules (Less_Tree_Rule)
6372
 * Using the name-value object can speed up bootstrap compilation slightly, but it breaks color keyword interpretation: color:red -> color:#FF0000;
6373
 *
6374
 * @package Less
6375
 * @subpackage tree
6376
 */
6377
class Less_Tree_NameValue extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6378
6379
	public $name;
6380
	public $value;
6381
	public $index;
6382
	public $currentFileInfo;
6383
	public $type = 'NameValue';
6384
6385 View Code Duplication
	public function __construct($name, $value = null, $index = null, $currentFileInfo = null ){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6386
		$this->name = $name;
6387
		$this->value = $value;
6388
		$this->index = $index;
6389
		$this->currentFileInfo = $currentFileInfo;
6390
	}
6391
6392
    public function genCSS( $output ){
6393
6394
		$output->add(
6395
			$this->name
6396
			. Less_Environment::$_outputMap[': ']
6397
			. $this->value
6398
			. (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";")
6399
			, $this->currentFileInfo, $this->index);
6400
	}
6401
6402
	public function compile ($env){
0 ignored issues
show
Unused Code introduced by
The parameter $env is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
6403
		return $this;
6404
	}
6405
}
6406
 
6407
6408
/**
6409
 * Negative
6410
 *
6411
 * @package Less
6412
 * @subpackage tree
6413
 */
6414
class Less_Tree_Negative extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6415
6416
	public $value;
6417
	public $type = 'Negative';
6418
6419
    public function __construct($node){
6420
		$this->value = $node;
6421
	}
6422
6423
	//function accept($visitor) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
6424
	//	$this->value = $visitor->visit($this->value);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
6425
	//}
6426
6427
    /**
6428
     * @see Less_Tree::genCSS
6429
     */
6430
    public function genCSS( $output ){
6431
		$output->add( '-' );
6432
		$this->value->genCSS( $output );
6433
	}
6434
6435
    public function compile($env) {
6436
		if( Less_Environment::isMathOn() ){
6437
			$ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) );
6438
			return $ret->compile($env);
6439
		}
6440
		return new Less_Tree_Negative( $this->value->compile($env) );
6441
	}
6442
} 
6443
6444
/**
6445
 * Operation
6446
 *
6447
 * @package Less
6448
 * @subpackage tree
6449
 */
6450
class Less_Tree_Operation extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6451
6452
	public $op;
6453
	public $operands;
6454
	public $isSpaced;
6455
	public $type = 'Operation';
6456
6457
	/**
6458
	 * @param string $op
6459
	 */
6460
	public function __construct($op, $operands, $isSpaced = false){
6461
		$this->op = trim($op);
6462
		$this->operands = $operands;
6463
		$this->isSpaced = $isSpaced;
6464
	}
6465
6466
    public function accept($visitor) {
6467
		$this->operands = $visitor->visitArray($this->operands);
6468
	}
6469
6470
	public function compile($env){
6471
		$a = $this->operands[0]->compile($env);
6472
		$b = $this->operands[1]->compile($env);
6473
6474
6475
		if( Less_Environment::isMathOn() ){
6476
6477
			if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){
6478
				$a = $a->toColor();
6479
6480
			}elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){
6481
				$b = $b->toColor();
6482
6483
			}
6484
6485
			if( !method_exists($a,'operate') ){
6486
				throw new Less_Exception_Compiler("Operation on an invalid type");
6487
			}
6488
6489
			return $a->operate( $this->op, $b);
6490
		}
6491
6492
		return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced );
6493
	}
6494
6495
6496
    /**
6497
     * @see Less_Tree::genCSS
6498
     */
6499
    public function genCSS( $output ){
6500
		$this->operands[0]->genCSS( $output );
6501
		if( $this->isSpaced ){
6502
			$output->add( " " );
6503
		}
6504
		$output->add( $this->op );
6505
		if( $this->isSpaced ){
6506
			$output->add( ' ' );
6507
		}
6508
		$this->operands[1]->genCSS( $output );
6509
	}
6510
6511
}
6512
 
6513
6514
/**
6515
 * Paren
6516
 *
6517
 * @package Less
6518
 * @subpackage tree
6519
 */
6520
class Less_Tree_Paren extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6521
6522
	public $value;
6523
	public $type = 'Paren';
6524
6525
	public function __construct($value) {
6526
		$this->value = $value;
6527
	}
6528
6529
    public function accept($visitor){
6530
		$this->value = $visitor->visitObj($this->value);
6531
	}
6532
6533
    /**
6534
     * @see Less_Tree::genCSS
6535
     */
6536
    public function genCSS( $output ){
6537
		$output->add( '(' );
6538
		$this->value->genCSS( $output );
6539
		$output->add( ')' );
6540
	}
6541
6542
	public function compile($env) {
6543
		return new Less_Tree_Paren($this->value->compile($env));
6544
	}
6545
6546
}
6547
 
6548
6549
/**
6550
 * Quoted
6551
 *
6552
 * @package Less
6553
 * @subpackage tree
6554
 */
6555
class Less_Tree_Quoted extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6556
	public $escaped;
6557
	public $value;
6558
	public $quote;
6559
	public $index;
6560
	public $currentFileInfo;
6561
	public $type = 'Quoted';
6562
6563
	/**
6564
	 * @param string $str
6565
	 */
6566
	public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ){
6567
		$this->escaped = $escaped;
6568
		$this->value = $content;
6569
		if( $str ){
6570
			$this->quote = $str[0];
6571
		}
6572
		$this->index = $index;
6573
		$this->currentFileInfo = $currentFileInfo;
6574
	}
6575
6576
    /**
6577
     * @see Less_Tree::genCSS
6578
     */
6579
    public function genCSS( $output ){
6580
		if( !$this->escaped ){
6581
			$output->add( $this->quote, $this->currentFileInfo, $this->index );
6582
        }
6583
        $output->add( $this->value );
6584
        if( !$this->escaped ){
6585
			$output->add( $this->quote );
6586
        }
6587
    }
6588
6589
	public function compile($env){
6590
6591
		$value = $this->value;
6592
		if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){
6593
			foreach($matches as $i => $match){
6594
				$js = new Less_Tree_JavaScript($matches[1], $this->index, true);
6595
				$js = $js->compile()->value;
6596
				$value = str_replace($matches[0][$i], $js, $value);
6597
			}
6598
		}
6599
6600
		if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){
6601
			foreach($matches[1] as $i => $match){
6602
				$v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo );
6603
				$v = $v->compile($env);
6604
				$v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS();
6605
				$value = str_replace($matches[0][$i], $v, $value);
6606
			}
6607
		}
6608
6609
		return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo);
6610
	}
6611
6612 View Code Duplication
    public function compare($x) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6613
6614
		if( !Less_Parser::is_method($x, 'toCSS') ){
6615
			return -1;
6616
		}
6617
6618
		$left = $this->toCSS();
6619
		$right = $x->toCSS();
6620
6621
		if ($left === $right) {
6622
			return 0;
6623
		}
6624
6625
		return $left < $right ? -1 : 1;
6626
	}
6627
}
6628
 
6629
6630
/**
6631
 * Rule
6632
 *
6633
 * @package Less
6634
 * @subpackage tree
6635
 */
6636
class Less_Tree_Rule extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6637
6638
	public $name;
6639
	public $value;
6640
	public $important;
6641
	public $merge;
6642
	public $index;
6643
	public $inline;
6644
	public $variable;
6645
	public $currentFileInfo;
6646
	public $type = 'Rule';
6647
6648
	/**
6649
	 * @param string $important
6650
	 */
6651
	public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null,  $inline = false){
6652
		$this->name = $name;
6653
		$this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value));
6654
		$this->important = $important ? ' ' . trim($important) : '';
6655
		$this->merge = $merge;
6656
		$this->index = $index;
6657
		$this->currentFileInfo = $currentFileInfo;
6658
		$this->inline = $inline;
6659
		$this->variable = ( is_string($name) && $name[0] === '@');
6660
	}
6661
6662
    public function accept($visitor) {
6663
		$this->value = $visitor->visitObj( $this->value );
6664
	}
6665
6666
    /**
6667
     * @see Less_Tree::genCSS
6668
     */
6669
    public function genCSS( $output ){
6670
6671
		$output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index);
6672
		try{
6673
			$this->value->genCSS( $output);
6674
6675
		}catch( Less_Exception_Parser $e ){
6676
			$e->index = $this->index;
6677
			$e->currentFile = $this->currentFileInfo;
6678
			throw $e;
6679
		}
6680
		$output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index);
6681
	}
6682
6683
	public function compile ($env){
6684
6685
		$name = $this->name;
6686
		if( is_array($name) ){
6687
			// expand 'primitive' name directly to get
6688
			// things faster (~10% for benchmark.less):
6689
			if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){
6690
				$name = $name[0]->value;
6691
			}else{
6692
				$name = $this->CompileName($env,$name);
6693
			}
6694
		}
6695
6696
		$strictMathBypass = Less_Parser::$options['strictMath'];
6697
		if( $name === "font" && !Less_Parser::$options['strictMath'] ){
6698
			Less_Parser::$options['strictMath'] = true;
6699
		}
6700
6701
		try {
6702
			$evaldValue = $this->value->compile($env);
6703
6704
			if( !$this->variable && $evaldValue->type === "DetachedRuleset") {
6705
				throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo);
6706
			}
6707
6708
			if( Less_Environment::$mixin_stack ){
6709
				$return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline);
6710
			}else{
6711
				$this->name = $name;
6712
				$this->value = $evaldValue;
6713
				$return = $this;
6714
			}
6715
6716
		}catch( Less_Exception_Parser $e ){
6717
			if( !is_numeric($e->index) ){
6718
				$e->index = $this->index;
6719
				$e->currentFile = $this->currentFileInfo;
6720
			}
6721
			throw $e;
6722
		}
6723
6724
		Less_Parser::$options['strictMath'] = $strictMathBypass;
6725
6726
		return $return;
6727
	}
6728
6729
6730
    public function CompileName( $env, $name ){
6731
		$output = new Less_Output();
6732
		foreach($name as $n){
6733
			$n->compile($env)->genCSS($output);
6734
		}
6735
		return $output->toString();
6736
	}
6737
6738
    public function makeImportant(){
6739
		return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline);
6740
	}
6741
6742
}
6743
 
6744
6745
/**
6746
 * Ruleset
6747
 *
6748
 * @package Less
6749
 * @subpackage tree
6750
 */
6751
class Less_Tree_Ruleset extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6752
6753
	protected $lookups;
6754
	public $_variables;
6755
	public $_rulesets;
6756
6757
	public $strictImports;
6758
6759
	public $selectors;
6760
	public $rules;
6761
	public $root;
6762
	public $allowImports;
6763
	public $paths;
6764
	public $firstRoot;
6765
	public $type = 'Ruleset';
6766
	public $multiMedia;
6767
	public $allExtends;
6768
6769
	public $ruleset_id;
6770
	public $originalRuleset;
6771
6772
	public $first_oelements;
6773
6774
	public function SetRulesetIndex(){
6775
		$this->ruleset_id = Less_Parser::$next_id++;
6776
		$this->originalRuleset = $this->ruleset_id;
6777
6778
		if( $this->selectors ){
6779
			foreach($this->selectors as $sel){
6780
				if( $sel->_oelements ){
6781
					$this->first_oelements[$sel->_oelements[0]] = true;
6782
				}
6783
			}
6784
		}
6785
	}
6786
6787
	public function __construct($selectors, $rules, $strictImports = null){
6788
		$this->selectors = $selectors;
6789
		$this->rules = $rules;
6790
		$this->lookups = array();
6791
		$this->strictImports = $strictImports;
6792
		$this->SetRulesetIndex();
6793
	}
6794
6795
	public function accept( $visitor ){
6796
		if( $this->paths ){
6797
			$paths_len = count($this->paths);
6798
			for($i = 0,$paths_len; $i < $paths_len; $i++ ){
6799
				$this->paths[$i] = $visitor->visitArray($this->paths[$i]);
6800
			}
6801
		}elseif( $this->selectors ){
6802
			$this->selectors = $visitor->visitArray($this->selectors);
6803
		}
6804
6805
		if( $this->rules ){
6806
			$this->rules = $visitor->visitArray($this->rules);
6807
		}
6808
	}
6809
6810
	public function compile($env){
6811
6812
		$ruleset = $this->PrepareRuleset($env);
6813
6814
6815
		// Store the frames around mixin definitions,
6816
		// so they can be evaluated like closures when the time comes.
6817
		$rsRuleCnt = count($ruleset->rules);
6818 View Code Duplication
		for( $i = 0; $i < $rsRuleCnt; $i++ ){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6819
			if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){
6820
				$ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
6821
			}
6822
		}
6823
6824
		$mediaBlockCount = 0;
6825
		if( $env instanceof Less_Environment ){
6826
			$mediaBlockCount = count($env->mediaBlocks);
6827
		}
6828
6829
		// Evaluate mixin calls.
6830
		$this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
6831
6832
6833
		// Evaluate everything else
6834 View Code Duplication
		for( $i=0; $i<$rsRuleCnt; $i++ ){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6835
			if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){
6836
				$ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
6837
			}
6838
		}
6839
6840
        // Evaluate everything else
6841
		for( $i=0; $i<$rsRuleCnt; $i++ ){
6842
			$rule = $ruleset->rules[$i];
6843
6844
            // for rulesets, check if it is a css guard and can be removed
6845
			if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){
6846
6847
                // check if it can be folded in (e.g. & where)
6848
				if( $rule->selectors[0]->isJustParentSelector() ){
6849
					array_splice($ruleset->rules,$i--,1);
6850
					$rsRuleCnt--;
6851
6852
					for($j = 0; $j < count($rule->rules); $j++ ){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
6853
						$subRule = $rule->rules[$j];
6854
						if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){
6855
							array_splice($ruleset->rules, ++$i, 0, array($subRule));
6856
							$rsRuleCnt++;
6857
						}
6858
					}
6859
6860
                }
6861
            }
6862
        }
6863
6864
6865
		// Pop the stack
6866
		$env->shiftFrame();
6867
6868
		if ($mediaBlockCount) {
6869
			$len = count($env->mediaBlocks);
6870
			for($i = $mediaBlockCount; $i < $len; $i++ ){
6871
				$env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors);
6872
			}
6873
		}
6874
6875
		return $ruleset;
6876
	}
6877
6878
	/**
6879
	 * Compile Less_Tree_Mixin_Call objects
6880
	 *
6881
	 * @param Less_Tree_Ruleset $ruleset
6882
	 * @param integer $rsRuleCnt
6883
	 */
6884
	private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){
6885
		for($i=0; $i < $rsRuleCnt; $i++){
6886
			$rule = $ruleset->rules[$i];
6887
6888
			if( $rule instanceof Less_Tree_Mixin_Call ){
6889
				$rule = $rule->compile($env);
6890
6891
				$temp = array();
6892
				foreach($rule as $r){
6893
					if( ($r instanceof Less_Tree_Rule) && $r->variable ){
6894
						// do not pollute the scope if the variable is
6895
						// already there. consider returning false here
6896
						// but we need a way to "return" variable from mixins
6897
						if( !$ruleset->variable($r->name) ){
6898
							$temp[] = $r;
6899
						}
6900
					}else{
6901
						$temp[] = $r;
6902
					}
6903
				}
6904
				$temp_count = count($temp)-1;
6905
				array_splice($ruleset->rules, $i, 1, $temp);
6906
				$rsRuleCnt += $temp_count;
6907
				$i += $temp_count;
6908
				$ruleset->resetCache();
6909
6910
			}elseif( $rule instanceof Less_Tree_RulesetCall ){
6911
6912
				$rule = $rule->compile($env);
6913
				$rules = array();
6914
				foreach($rule->rules as $r){
6915
					if( ($r instanceof Less_Tree_Rule) && $r->variable ){
6916
						continue;
6917
					}
6918
					$rules[] = $r;
6919
				}
6920
6921
				array_splice($ruleset->rules, $i, 1, $rules);
6922
				$temp_count = count($rules);
6923
				$rsRuleCnt += $temp_count - 1;
6924
				$i += $temp_count-1;
6925
				$ruleset->resetCache();
6926
			}
6927
6928
		}
6929
	}
6930
6931
6932
	/**
6933
	 * Compile the selectors and create a new ruleset object for the compile() method
6934
	 *
6935
	 */
6936
	private function PrepareRuleset($env){
6937
6938
		$hasOnePassingSelector = false;
6939
		$selectors = array();
6940
		if( $this->selectors ){
6941
			Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
6942
6943
			foreach($this->selectors as $s){
6944
				$selector = $s->compile($env);
6945
				$selectors[] = $selector;
6946
				if( $selector->evaldCondition ){
6947
					$hasOnePassingSelector = true;
6948
				}
6949
			}
6950
6951
			Less_Tree_DefaultFunc::reset();
6952
		} else {
6953
			$hasOnePassingSelector = true;
6954
		}
6955
6956
		if( $this->rules && $hasOnePassingSelector ){
6957
			$rules = $this->rules;
6958
		}else{
6959
			$rules = array();
6960
		}
6961
6962
		$ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
6963
6964
		$ruleset->originalRuleset = $this->ruleset_id;
6965
6966
		$ruleset->root = $this->root;
6967
		$ruleset->firstRoot = $this->firstRoot;
6968
		$ruleset->allowImports = $this->allowImports;
6969
6970
6971
		// push the current ruleset to the frames stack
6972
		$env->unshiftFrame($ruleset);
6973
6974
6975
		// Evaluate imports
6976
		if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){
6977
			$ruleset->evalImports($env);
6978
		}
6979
6980
		return $ruleset;
6981
	}
6982
6983
	function evalImports($env) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
6984
6985
		$rules_len = count($this->rules);
6986
		for($i=0; $i < $rules_len; $i++){
6987
			$rule = $this->rules[$i];
6988
6989
			if( $rule instanceof Less_Tree_Import ){
6990
				$rules = $rule->compile($env);
6991
				if( is_array($rules) ){
6992
					array_splice($this->rules, $i, 1, $rules);
6993
					$temp_count = count($rules)-1;
6994
					$i += $temp_count;
6995
					$rules_len += $temp_count;
6996
				}else{
6997
					array_splice($this->rules, $i, 1, array($rules));
6998
				}
6999
7000
				$this->resetCache();
7001
			}
7002
		}
7003
	}
7004
7005
	function makeImportant(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
7006
7007
		$important_rules = array();
7008
		foreach($this->rules as $rule){
7009
			if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset ){
7010
				$important_rules[] = $rule->makeImportant();
7011
			}else{
7012
				$important_rules[] = $rule;
7013
			}
7014
		}
7015
7016
		return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports );
7017
	}
7018
7019
	public function matchArgs($args){
7020
		return !$args;
7021
	}
7022
7023
	// lets you call a css selector with a guard
7024
	public function matchCondition( $args, $env ){
7025
		$lastSelector = end($this->selectors);
7026
7027
		if( !$lastSelector->evaldCondition ){
7028
			return false;
7029
		}
7030
		if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){
7031
			return false;
7032
		}
7033
		return true;
7034
	}
7035
7036
	function resetCache(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
7037
		$this->_rulesets = null;
7038
		$this->_variables = null;
7039
		$this->lookups = array();
7040
	}
7041
7042
	public function variables(){
7043
		$this->_variables = array();
7044
		foreach( $this->rules as $r){
7045
			if ($r instanceof Less_Tree_Rule && $r->variable === true) {
7046
				$this->_variables[$r->name] = $r;
7047
			}
7048
		}
7049
	}
7050
7051
	public function variable($name){
7052
7053
		if( is_null($this->_variables) ){
7054
			$this->variables();
7055
		}
7056
		return isset($this->_variables[$name]) ? $this->_variables[$name] : null;
7057
	}
7058
7059
	public function find( $selector, $self = null ){
7060
7061
		$key = implode(' ',$selector->_oelements);
7062
7063
		if( !isset($this->lookups[$key]) ){
7064
7065
			if( !$self ){
7066
				$self = $this->ruleset_id;
7067
			}
7068
7069
			$this->lookups[$key] = array();
7070
7071
			$first_oelement = $selector->_oelements[0];
7072
7073
			foreach($this->rules as $rule){
7074
				if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){
7075
7076
					if( isset($rule->first_oelements[$first_oelement]) ){
7077
7078
						foreach( $rule->selectors as $ruleSelector ){
7079
							$match = $selector->match($ruleSelector);
7080
							if( $match ){
7081
								if( $selector->elements_len > $match ){
7082
									$this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self));
7083
								} else {
7084
									$this->lookups[$key][] = $rule;
7085
								}
7086
								break;
7087
							}
7088
						}
7089
					}
7090
				}
7091
			}
7092
		}
7093
7094
		return $this->lookups[$key];
7095
	}
7096
7097
7098
	/**
7099
	 * @see Less_Tree::genCSS
7100
	 */
7101
	public function genCSS( $output ){
7102
7103
		if( !$this->root ){
7104
			Less_Environment::$tabLevel++;
7105
		}
7106
7107
		$tabRuleStr = $tabSetStr = '';
7108
		if( !Less_Parser::$options['compress'] ){
7109
			if( Less_Environment::$tabLevel ){
7110
				$tabRuleStr = "\n".str_repeat( '  ' , Less_Environment::$tabLevel );
7111
				$tabSetStr = "\n".str_repeat( '  ' , Less_Environment::$tabLevel-1 );
7112
			}else{
7113
				$tabSetStr = $tabRuleStr = "\n";
7114
			}
7115
		}
7116
7117
7118
		$ruleNodes = array();
7119
		$rulesetNodes = array();
7120
		foreach($this->rules as $rule){
7121
7122
			$class = get_class($rule);
7123
			if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){
7124
				$rulesetNodes[] = $rule;
7125
			}else{
7126
				$ruleNodes[] = $rule;
7127
			}
7128
		}
7129
7130
		// If this is the root node, we don't render
7131
		// a selector, or {}.
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7132
		if( !$this->root ){
7133
7134
			/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7135
			debugInfo = tree.debugInfo(env, this, tabSetStr);
7136
7137
			if (debugInfo) {
7138
				output.add(debugInfo);
7139
				output.add(tabSetStr);
7140
			}
7141
			*/
7142
7143
			$paths_len = count($this->paths);
7144
			for( $i = 0; $i < $paths_len; $i++ ){
7145
				$path = $this->paths[$i];
7146
				$firstSelector = true;
7147
7148
				foreach($path as $p){
7149
					$p->genCSS( $output, $firstSelector );
7150
					$firstSelector = false;
7151
				}
7152
7153
				if( $i + 1 < $paths_len ){
7154
					$output->add( ',' . $tabSetStr );
7155
				}
7156
			}
7157
7158
			$output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr );
7159
		}
7160
7161
		// Compile rules and rulesets
7162
		$ruleNodes_len = count($ruleNodes);
7163
		$rulesetNodes_len = count($rulesetNodes);
7164
		for( $i = 0; $i < $ruleNodes_len; $i++ ){
7165
			$rule = $ruleNodes[$i];
7166
7167
			// @page{ directive ends up with root elements inside it, a mix of rules and rulesets
7168
			// In this instance we do not know whether it is the last property
7169
			if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){
7170
				Less_Environment::$lastRule = true;
7171
			}
7172
7173
			$rule->genCSS( $output );
7174
7175
			if( !Less_Environment::$lastRule ){
7176
				$output->add( $tabRuleStr );
7177
			}else{
7178
				Less_Environment::$lastRule = false;
7179
			}
7180
		}
7181
7182
		if( !$this->root ){
7183
			$output->add( $tabSetStr . '}' );
7184
			Less_Environment::$tabLevel--;
7185
		}
7186
7187
		$firstRuleset = true;
7188
		$space = ($this->root ? $tabRuleStr : $tabSetStr);
7189
		for( $i = 0; $i < $rulesetNodes_len; $i++ ){
7190
7191
			if( $ruleNodes_len && $firstRuleset ){
7192
				$output->add( $space );
7193
			}elseif( !$firstRuleset ){
7194
				$output->add( $space );
7195
			}
7196
			$firstRuleset = false;
7197
			$rulesetNodes[$i]->genCSS( $output);
7198
		}
7199
7200
		if( !Less_Parser::$options['compress'] && $this->firstRoot ){
7201
			$output->add( "\n" );
7202
		}
7203
7204
	}
7205
7206
7207
	function markReferenced(){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
7208
		if( !$this->selectors ){
7209
			return;
7210
		}
7211
		foreach($this->selectors as $selector){
7212
			$selector->markReferenced();
7213
		}
7214
	}
7215
7216
	public function joinSelectors( $context, $selectors ){
7217
		$paths = array();
7218
		if( is_array($selectors) ){
7219
			foreach($selectors as $selector) {
7220
				$this->joinSelector( $paths, $context, $selector);
7221
			}
7222
		}
7223
		return $paths;
7224
	}
7225
7226
	public function joinSelector( &$paths, $context, $selector){
7227
7228
		$hasParentSelector = false;
7229
7230
		foreach($selector->elements as $el) {
7231
			if( $el->value === '&') {
7232
				$hasParentSelector = true;
7233
			}
7234
		}
7235
7236
		if( !$hasParentSelector ){
7237
			if( $context ){
7238
				foreach($context as $context_el){
7239
					$paths[] = array_merge($context_el, array($selector) );
7240
				}
7241
			}else {
7242
				$paths[] = array($selector);
7243
			}
7244
			return;
7245
		}
7246
7247
7248
		// The paths are [[Selector]]
7249
		// The first list is a list of comma seperated selectors
7250
		// The inner list is a list of inheritance seperated selectors
7251
		// e.g.
7252
		// .a, .b {
7253
		//   .c {
7254
		//   }
7255
		// }
7256
		// == [[.a] [.c]] [[.b] [.c]]
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7257
		//
7258
7259
		// the elements from the current selector so far
7260
		$currentElements = array();
7261
		// the current list of new selectors to add to the path.
7262
		// We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
7263
		// by the parents
7264
		$newSelectors = array(array());
7265
7266
7267
		foreach( $selector->elements as $el){
7268
7269
			// non parent reference elements just get added
7270
			if( $el->value !== '&' ){
7271
				$currentElements[] = $el;
7272
			} else {
7273
				// the new list of selectors to add
7274
				$selectorsMultiplied = array();
7275
7276
				// merge the current list of non parent selector elements
7277
				// on to the current list of selectors to add
7278
				if( $currentElements ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $currentElements of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7279
					$this->mergeElementsOnToSelectors( $currentElements, $newSelectors);
7280
				}
7281
7282
				// loop through our current selectors
7283
				foreach($newSelectors as $sel){
7284
7285
					// if we don't have any parent paths, the & might be in a mixin so that it can be used
7286
					// whether there are parents or not
7287
					if( !$context ){
7288
						// the combinator used on el should now be applied to the next element instead so that
7289
						// it is not lost
7290
						if( $sel ){
7291
							$sel[0]->elements = array_slice($sel[0]->elements,0);
7292
							$sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo );
7293
						}
7294
						$selectorsMultiplied[] = $sel;
7295
					}else {
7296
7297
						// and the parent selectors
7298
						foreach($context as $parentSel){
7299
							// We need to put the current selectors
7300
							// then join the last selector's elements on to the parents selectors
7301
7302
							// our new selector path
7303
							$newSelectorPath = array();
7304
							// selectors from the parent after the join
7305
							$afterParentJoin = array();
7306
							$newJoinedSelectorEmpty = true;
7307
7308
							//construct the joined selector - if & is the first thing this will be empty,
7309
							// if not newJoinedSelector will be the last set of elements in the selector
7310
							if( $sel ){
7311
								$newSelectorPath = $sel;
7312
								$lastSelector = array_pop($newSelectorPath);
7313
								$newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) );
7314
								$newJoinedSelectorEmpty = false;
7315
							}
7316
							else {
7317
								$newJoinedSelector = $selector->createDerived(array());
7318
							}
7319
7320
							//put together the parent selectors after the join
7321
							if ( count($parentSel) > 1) {
7322
								$afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) );
7323
							}
7324
7325
							if ( $parentSel ){
7326
								$newJoinedSelectorEmpty = false;
7327
7328
								// join the elements so far with the first part of the parent
7329
								$newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
7330
7331
								$newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) );
7332
							}
7333
7334
							if (!$newJoinedSelectorEmpty) {
7335
								// now add the joined selector
7336
								$newSelectorPath[] = $newJoinedSelector;
7337
							}
7338
7339
							// and the rest of the parent
7340
							$newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
7341
7342
							// add that to our new set of selectors
7343
							$selectorsMultiplied[] = $newSelectorPath;
7344
						}
7345
					}
7346
				}
7347
7348
				// our new selectors has been multiplied, so reset the state
7349
				$newSelectors = $selectorsMultiplied;
7350
				$currentElements = array();
7351
			}
7352
		}
7353
7354
		// if we have any elements left over (e.g. .a& .b == .b)
7355
		// add them on to all the current selectors
7356
		if( $currentElements ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $currentElements of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7357
			$this->mergeElementsOnToSelectors($currentElements, $newSelectors);
7358
		}
7359
		foreach( $newSelectors as $new_sel){
7360
			if( $new_sel ){
7361
				$paths[] = $new_sel;
7362
			}
7363
		}
7364
	}
7365
7366
	function mergeElementsOnToSelectors( $elements, &$selectors){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
7367
7368
		if( !$selectors ){
7369
			$selectors[] = array( new Less_Tree_Selector($elements) );
7370
			return;
7371
		}
7372
7373
7374
		foreach( $selectors as &$sel){
7375
7376
			// if the previous thing in sel is a parent this needs to join on to it
7377
			if( $sel ){
7378
				$last = count($sel)-1;
7379
				$sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) );
7380
			}else{
7381
				$sel[] = new Less_Tree_Selector( $elements );
7382
			}
7383
		}
7384
	}
7385
}
7386
 
7387
7388
/**
7389
 * RulesetCall
7390
 *
7391
 * @package Less
7392
 * @subpackage tree
7393
 */
7394
class Less_Tree_RulesetCall extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7395
7396
	public $variable;
7397
	public $type = "RulesetCall";
7398
7399
    public function __construct($variable){
7400
		$this->variable = $variable;
7401
	}
7402
7403
    public function accept($visitor) {}
7404
7405
    public function compile( $env ){
7406
		$variable = new Less_Tree_Variable($this->variable);
7407
		$detachedRuleset = $variable->compile($env);
7408
		return $detachedRuleset->callEval($env);
7409
	}
7410
}
7411
7412
 
7413
7414
/**
7415
 * Selector
7416
 *
7417
 * @package Less
7418
 * @subpackage tree
7419
 */
7420
class Less_Tree_Selector extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7421
7422
	public $elements;
7423
	public $condition;
7424
	public $extendList = array();
7425
	public $_css;
7426
	public $index;
7427
	public $evaldCondition = false;
7428
	public $type = 'Selector';
7429
	public $currentFileInfo = array();
7430
	public $isReferenced;
7431
	public $mediaEmpty;
7432
7433
	public $elements_len = 0;
7434
7435
	public $_oelements;
7436
	public $_oelements_len;
7437
	public $cacheable = true;
7438
7439
	/**
7440
	 * @param boolean $isReferenced
7441
	 */
7442
	public function __construct( $elements, $extendList = array() , $condition = null, $index=null, $currentFileInfo=null, $isReferenced=null ){
0 ignored issues
show
Unused Code introduced by
The parameter $index is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
7443
7444
		$this->elements = $elements;
7445
		$this->elements_len = count($elements);
7446
		$this->extendList = $extendList;
7447
		$this->condition = $condition;
7448
		if( $currentFileInfo ){
7449
			$this->currentFileInfo = $currentFileInfo;
7450
		}
7451
		$this->isReferenced = $isReferenced;
7452
		if( !$condition ){
7453
			$this->evaldCondition = true;
7454
		}
7455
7456
		$this->CacheElements();
7457
	}
7458
7459
    public function accept($visitor) {
7460
		$this->elements = $visitor->visitArray($this->elements);
7461
		$this->extendList = $visitor->visitArray($this->extendList);
7462
		if( $this->condition ){
7463
			$this->condition = $visitor->visitObj($this->condition);
7464
		}
7465
7466
		if( $visitor instanceof Less_Visitor_extendFinder ){
7467
			$this->CacheElements();
7468
		}
7469
	}
7470
7471
    public function createDerived( $elements, $extendList = null, $evaldCondition = null ){
7472
		$newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced);
7473
		$newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
7474
		return $newSelector;
7475
	}
7476
7477
7478
	public function match( $other ){
7479
7480
		if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){
7481
			return 0;
7482
		}
7483
7484
		for( $i = 0; $i < $other->_oelements_len; $i++ ){
7485
			if( $this->elements[$i]->value !== $other->_oelements[$i]) {
7486
				return 0;
7487
			}
7488
		}
7489
7490
		return $other->_oelements_len; // return number of matched elements
7491
	}
7492
7493
7494
	public function CacheElements(){
7495
7496
		$this->_oelements = array();
7497
		$css = '';
7498
7499
		foreach($this->elements as $v){
7500
7501
			$css .= $v->combinator;
7502
			if( !$v->value_is_object ){
7503
				$css .= $v->value;
7504
				continue;
7505
			}
7506
7507
			if( !property_exists($v->value,'value') || !is_string($v->value->value) ){
7508
				$this->cacheable = false;
7509
				return;
7510
			}
7511
			$css .= $v->value->value;
7512
		}
7513
7514
		$this->_oelements_len = preg_match_all('/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches);
7515
		if( $this->_oelements_len ){
7516
			$this->_oelements = $matches[0];
7517
7518
			if( $this->_oelements[0] === '&' ){
7519
				array_shift($this->_oelements);
7520
				$this->_oelements_len--;
7521
			}
7522
		}
7523
	}
7524
7525
	public function isJustParentSelector(){
7526
		return !$this->mediaEmpty &&
7527
			count($this->elements) === 1 &&
7528
			$this->elements[0]->value === '&' &&
7529
			($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '');
7530
	}
7531
7532
	public function compile($env) {
7533
7534
		$elements = array();
7535
		foreach($this->elements as $el){
7536
			$elements[] = $el->compile($env);
7537
		}
7538
7539
		$extendList = array();
7540
		foreach($this->extendList as $el){
7541
			$extendList[] = $el->compile($el);
7542
		}
7543
7544
		$evaldCondition = false;
7545
		if( $this->condition ){
7546
			$evaldCondition = $this->condition->compile($env);
7547
		}
7548
7549
		return $this->createDerived( $elements, $extendList, $evaldCondition );
7550
	}
7551
7552
7553
	/**
7554
	 * @see Less_Tree::genCSS
7555
	 */
7556
    public function genCSS( $output, $firstSelector = true ){
7557
7558
		if( !$firstSelector && $this->elements[0]->combinator === "" ){
7559
			$output->add(' ', $this->currentFileInfo, $this->index);
7560
		}
7561
7562
		foreach($this->elements as $element){
7563
			$element->genCSS( $output );
7564
		}
7565
	}
7566
7567
    public function markReferenced(){
7568
		$this->isReferenced = true;
7569
	}
7570
7571
    public function getIsReferenced(){
7572
		return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced;
7573
	}
7574
7575
    public function getIsOutput(){
7576
		return $this->evaldCondition;
7577
	}
7578
7579
}
7580
 
7581
7582
/**
7583
 * UnicodeDescriptor
7584
 *
7585
 * @package Less
7586
 * @subpackage tree
7587
 */
7588
class Less_Tree_UnicodeDescriptor extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7589
7590
	public $value;
7591
	public $type = 'UnicodeDescriptor';
7592
7593
	public function __construct($value){
7594
		$this->value = $value;
7595
	}
7596
7597
    /**
7598
     * @see Less_Tree::genCSS
7599
     */
7600
	public function genCSS( $output ){
7601
		$output->add( $this->value );
7602
	}
7603
7604
	public function compile(){
7605
		return $this;
7606
	}
7607
}
7608
7609
 
7610
7611
/**
7612
 * Unit
7613
 *
7614
 * @package Less
7615
 * @subpackage tree
7616
 */
7617
class Less_Tree_Unit extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7618
7619
	var $numerator = array();
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $numerator.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
7620
	var $denominator = array();
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $denominator.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
7621
	public $backupUnit;
7622
	public $type = 'Unit';
7623
7624
    public function __construct($numerator = array(), $denominator = array(), $backupUnit = null ){
7625
		$this->numerator = $numerator;
7626
		$this->denominator = $denominator;
7627
		$this->backupUnit = $backupUnit;
7628
	}
7629
7630
    public function __clone(){
7631
	}
7632
7633
    /**
7634
     * @see Less_Tree::genCSS
7635
     */
7636
    public function genCSS( $output ){
7637
7638
		if( $this->numerator ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->numerator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7639
			$output->add( $this->numerator[0] );
7640
		}elseif( $this->denominator ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->denominator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7641
			$output->add( $this->denominator[0] );
7642
		}elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){
7643
			$output->add( $this->backupUnit );
7644
			return ;
7645
		}
7646
	}
7647
7648
    public function toString(){
7649
		$returnStr = implode('*',$this->numerator);
7650
		foreach($this->denominator as $d){
7651
			$returnStr .= '/'.$d;
7652
		}
7653
		return $returnStr;
7654
	}
7655
7656
    public function __toString(){
7657
		return $this->toString();
7658
	}
7659
7660
7661
	/**
7662
	 * @param Less_Tree_Unit $other
7663
	 */
7664
    public function compare($other) {
7665
		return $this->is( $other->toString() ) ? 0 : -1;
7666
	}
7667
7668
    public function is($unitString){
7669
		return $this->toString() === $unitString;
7670
	}
7671
7672
    public function isLength(){
7673
		$css = $this->toCSS();
7674
		return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css);
7675
	}
7676
7677
    public function isAngle() {
7678
		return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] );
7679
	}
7680
7681
    public function isEmpty(){
7682
		return !$this->numerator && !$this->denominator;
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->numerator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $this->denominator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7683
	}
7684
7685
    public function isSingular() {
7686
		return count($this->numerator) <= 1 && !$this->denominator;
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->denominator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7687
	}
7688
7689
7690
    public function usedUnits(){
7691
		$result = array();
7692
7693
		foreach(Less_Tree_UnitConversions::$groups as $groupName){
7694
			$group = Less_Tree_UnitConversions::${$groupName};
7695
7696 View Code Duplication
			foreach($this->numerator as $atomicUnit){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7697
				if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
7698
					$result[$groupName] = $atomicUnit;
7699
				}
7700
			}
7701
7702 View Code Duplication
			foreach($this->denominator as $atomicUnit){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7703
				if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
7704
					$result[$groupName] = $atomicUnit;
7705
				}
7706
			}
7707
		}
7708
7709
		return $result;
7710
	}
7711
7712
    public function cancel(){
7713
		$counter = array();
7714
		$backup = null;
7715
7716 View Code Duplication
		foreach($this->numerator as $atomicUnit){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7717
			if( !$backup ){
7718
				$backup = $atomicUnit;
7719
			}
7720
			$counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1;
7721
		}
7722
7723 View Code Duplication
		foreach($this->denominator as $atomicUnit){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7724
			if( !$backup ){
7725
				$backup = $atomicUnit;
7726
			}
7727
			$counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1;
7728
		}
7729
7730
		$this->numerator = array();
7731
		$this->denominator = array();
7732
7733
		foreach($counter as $atomicUnit => $count){
7734
			if( $count > 0 ){
7735
				for( $i = 0; $i < $count; $i++ ){
7736
					$this->numerator[] = $atomicUnit;
7737
				}
7738
			}elseif( $count < 0 ){
7739
				for( $i = 0; $i < -$count; $i++ ){
7740
					$this->denominator[] = $atomicUnit;
7741
				}
7742
			}
7743
		}
7744
7745
		if( !$this->numerator && !$this->denominator && $backup ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->numerator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $this->denominator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7746
			$this->backupUnit = $backup;
7747
		}
7748
7749
		sort($this->numerator);
7750
		sort($this->denominator);
7751
	}
7752
7753
7754
}
7755
7756
 
7757
7758
/**
7759
 * UnitConversions
7760
 *
7761
 * @package Less
7762
 * @subpackage tree
7763
 */
7764
class Less_Tree_UnitConversions{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7765
7766
	public static $groups = array('length','duration','angle');
7767
7768
	public static $length = array(
7769
		'm'=> 1,
7770
		'cm'=> 0.01,
7771
		'mm'=> 0.001,
7772
		'in'=> 0.0254,
7773
		'px'=> 0.000264583, // 0.0254 / 96,
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7774
		'pt'=> 0.000352778, // 0.0254 / 72,
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7775
		'pc'=> 0.004233333, // 0.0254 / 72 * 12
7776
		);
7777
7778
	public static $duration = array(
7779
		's'=> 1,
7780
		'ms'=> 0.001
7781
		);
7782
7783
	public static $angle = array(
7784
		'rad' => 0.1591549430919,	// 1/(2*M_PI),
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7785
		'deg' => 0.002777778, 		// 1/360,
7786
		'grad'=> 0.0025,			// 1/400,
7787
		'turn'=> 1
7788
		);
7789
7790
} 
7791
7792
/**
7793
 * Url
7794
 *
7795
 * @package Less
7796
 * @subpackage tree
7797
 */
7798
class Less_Tree_Url extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7799
7800
	public $attrs;
7801
	public $value;
7802
	public $currentFileInfo;
7803
	public $isEvald;
7804
	public $type = 'Url';
7805
7806
	public function __construct($value, $currentFileInfo = null, $isEvald = null){
7807
		$this->value = $value;
7808
		$this->currentFileInfo = $currentFileInfo;
7809
		$this->isEvald = $isEvald;
7810
	}
7811
7812
    public function accept( $visitor ){
7813
		$this->value = $visitor->visitObj($this->value);
7814
	}
7815
7816
    /**
7817
     * @see Less_Tree::genCSS
7818
     */
7819
    public function genCSS( $output ){
7820
		$output->add( 'url(' );
7821
		$this->value->genCSS( $output );
7822
		$output->add( ')' );
7823
	}
7824
7825
	/**
7826
	 * @param Less_Functions $ctx
7827
	 */
7828
	public function compile($ctx){
7829
		$val = $this->value->compile($ctx);
7830
7831
		if( !$this->isEvald ){
7832
			// Add the base path if the URL is relative
7833
			if( Less_Parser::$options['relativeUrls']
7834
				&& $this->currentFileInfo
7835
				&& is_string($val->value)
7836
				&& Less_Environment::isPathRelative($val->value)
7837
			){
7838
				$rootpath = $this->currentFileInfo['uri_root'];
7839
				if ( !$val->quote ){
7840
					$rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath );
7841
				}
7842
				$val->value = $rootpath . $val->value;
7843
			}
7844
7845
			$val->value = Less_Environment::normalizePath( $val->value);
7846
		}
7847
7848
		// Add cache buster if enabled
7849
		if( Less_Parser::$options['urlArgs'] ){
7850
			if( !preg_match('/^\s*data:/',$val->value) ){
7851
				$delimiter = strpos($val->value,'?') === false ? '?' : '&';
7852
				$urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
7853
				$hash_pos = strpos($val->value,'#');
7854
				if( $hash_pos !== false ){
7855
					$val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0);
7856
				} else {
7857
					$val->value .= $urlArgs;
7858
				}
7859
			}
7860
		}
7861
7862
		return new Less_Tree_URL($val, $this->currentFileInfo, true);
7863
	}
7864
7865
}
7866
 
7867
7868
/**
7869
 * Value
7870
 *
7871
 * @package Less
7872
 * @subpackage tree
7873
 */
7874
class Less_Tree_Value extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7875
7876
	public $type = 'Value';
7877
	public $value;
7878
7879
	public function __construct($value){
7880
		$this->value = $value;
7881
	}
7882
7883
    public function accept($visitor) {
7884
		$this->value = $visitor->visitArray($this->value);
7885
	}
7886
7887
	public function compile($env){
7888
7889
		$ret = array();
7890
		$i = 0;
7891
		foreach($this->value as $i => $v){
7892
			$ret[] = $v->compile($env);
7893
		}
7894
		if( $i > 0 ){
7895
			return new Less_Tree_Value($ret);
7896
		}
7897
		return $ret[0];
7898
	}
7899
7900
    /**
7901
     * @see Less_Tree::genCSS
7902
     */
7903 View Code Duplication
	function genCSS( $output ){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7904
		$len = count($this->value);
7905
		for($i = 0; $i < $len; $i++ ){
7906
			$this->value[$i]->genCSS( $output );
7907
			if( $i+1 < $len ){
7908
				$output->add( Less_Environment::$_outputMap[','] );
7909
			}
7910
		}
7911
	}
7912
7913
}
7914
 
7915
7916
/**
7917
 * Variable
7918
 *
7919
 * @package Less
7920
 * @subpackage tree
7921
 */
7922
class Less_Tree_Variable extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7923
7924
	public $name;
7925
	public $index;
7926
	public $currentFileInfo;
7927
	public $evaluating = false;
7928
	public $type = 'Variable';
7929
7930
    /**
7931
     * @param string $name
7932
     */
7933
    public function __construct($name, $index = null, $currentFileInfo = null) {
7934
        $this->name = $name;
7935
        $this->index = $index;
7936
		$this->currentFileInfo = $currentFileInfo;
7937
    }
7938
7939
	public function compile($env) {
7940
7941
		if( $this->name[1] === '@' ){
7942
			$v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo);
7943
			$name = '@' . $v->compile($env)->value;
7944
		}else{
7945
			$name = $this->name;
7946
		}
7947
7948
		if ($this->evaluating) {
7949
			throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo);
7950
		}
7951
7952
		$this->evaluating = true;
7953
7954
		foreach($env->frames as $frame){
7955
			if( $v = $frame->variable($name) ){
7956
				$r = $v->value->compile($env);
7957
				$this->evaluating = false;
7958
				return $r;
7959
			}
7960
		}
7961
7962
		throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo);
7963
	}
7964
7965
}
7966
 
7967
7968
7969
class Less_Tree_Mixin_Call extends Less_Tree{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7970
7971
	public $selector;
7972
	public $arguments;
7973
	public $index;
7974
	public $currentFileInfo;
7975
7976
	public $important;
7977
	public $type = 'MixinCall';
7978
7979
	/**
7980
	 * less.js: tree.mixin.Call
7981
	 *
7982
	 */
7983
	public function __construct($elements, $args, $index, $currentFileInfo, $important = false){
7984
		$this->selector = new Less_Tree_Selector($elements);
7985
		$this->arguments = $args;
7986
		$this->index = $index;
7987
		$this->currentFileInfo = $currentFileInfo;
7988
		$this->important = $important;
7989
	}
7990
7991
	//function accept($visitor){
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7992
	//	$this->selector = $visitor->visit($this->selector);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7993
	//	$this->arguments = $visitor->visit($this->arguments);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
7994
	//}
7995
7996
7997
	public function compile($env){
7998
7999
		$rules = array();
8000
		$match = false;
8001
		$isOneFound = false;
8002
		$candidates = array();
8003
		$defaultUsed = false;
0 ignored issues
show
Unused Code introduced by
$defaultUsed is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
8004
		$conditionResult = array();
8005
8006
		$args = array();
8007
		foreach($this->arguments as $a){
8008
			$args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) );
8009
		}
8010
8011
		foreach($env->frames as $frame){
8012
8013
			$mixins = $frame->find($this->selector);
8014
8015
			if( !$mixins ){
8016
				continue;
8017
			}
8018
8019
			$isOneFound = true;
8020
			$defNone = 0;
8021
			$defTrue = 1;
8022
			$defFalse = 2;
8023
8024
			// To make `default()` function independent of definition order we have two "subpasses" here.
8025
			// At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
8026
			// and build candidate list with corresponding flags. Then, when we know all possible matches,
8027
			// we make a final decision.
8028
8029
			$mixins_len = count($mixins);
8030
			for( $m = 0; $m < $mixins_len; $m++ ){
8031
				$mixin = $mixins[$m];
8032
8033
				if( $this->IsRecursive( $env, $mixin ) ){
8034
					continue;
8035
				}
8036
8037
				if( $mixin->matchArgs($args, $env) ){
8038
8039
					$candidate = array('mixin' => $mixin, 'group' => $defNone);
8040
8041
					if( $mixin instanceof Less_Tree_Ruleset ){
8042
8043
						for( $f = 0; $f < 2; $f++ ){
8044
							Less_Tree_DefaultFunc::value($f);
8045
							$conditionResult[$f] = $mixin->matchCondition( $args, $env);
8046
						}
8047
						if( $conditionResult[0] || $conditionResult[1] ){
8048
							if( $conditionResult[0] != $conditionResult[1] ){
8049
								$candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
8050
							}
8051
8052
							$candidates[] = $candidate;
8053
						}
8054
					}else{
8055
						$candidates[] = $candidate;
8056
					}
8057
8058
					$match = true;
8059
				}
8060
			}
8061
8062
			Less_Tree_DefaultFunc::reset();
8063
8064
8065
			$count = array(0, 0, 0);
8066
			for( $m = 0; $m < count($candidates); $m++ ){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
8067
				$count[ $candidates[$m]['group'] ]++;
8068
			}
8069
8070
			if( $count[$defNone] > 0 ){
8071
				$defaultResult = $defFalse;
8072
			} else {
8073
				$defaultResult = $defTrue;
8074
				if( ($count[$defTrue] + $count[$defFalse]) > 1 ){
8075
					throw new Exception( 'Ambiguous use of `default()` found when matching for `'. $this->format($args) + '`' );
8076
				}
8077
			}
8078
8079
8080
			$candidates_length = count($candidates);
8081
			$length_1 = ($candidates_length == 1);
0 ignored issues
show
Unused Code introduced by
$length_1 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
8082
8083
			for( $m = 0; $m < $candidates_length; $m++){
8084
				$candidate = $candidates[$m]['group'];
8085
				if( ($candidate === $defNone) || ($candidate === $defaultResult) ){
8086
					try{
8087
						$mixin = $candidates[$m]['mixin'];
8088
						if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
8089
							$mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false);
8090
							$mixin->originalRuleset = $mixins[$m]->originalRuleset;
8091
						}
8092
						$rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules);
8093
					} catch (Exception $e) {
8094
						//throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8095
						throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo);
8096
					}
8097
				}
8098
			}
8099
8100
			if( $match ){
8101
				if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){
8102
					Less_Tree::ReferencedArray($rules);
8103
				}
8104
8105
				return $rules;
8106
			}
8107
		}
8108
8109
		if( $isOneFound ){
8110
			throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo);
8111
8112
		}else{
8113
			throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index);
8114
		}
8115
8116
	}
8117
8118
	/**
8119
	 * Format the args for use in exception messages
8120
	 *
8121
	 */
8122
	private function Format($args){
8123
		$message = array();
8124
		if( $args ){
8125
			foreach($args as $a){
8126
				$argValue = '';
8127
				if( $a['name'] ){
8128
					$argValue += $a['name']+':';
8129
				}
8130
				if( is_object($a['value']) ){
8131
					$argValue += $a['value']->toCSS();
8132
				}else{
8133
					$argValue += '???';
8134
				}
8135
				$message[] = $argValue;
8136
			}
8137
		}
8138
		return implode(', ',$message);
8139
	}
8140
8141
8142
	/**
8143
	 * Are we in a recursive mixin call?
8144
	 *
8145
	 * @return bool
8146
	 */
8147
	private function IsRecursive( $env, $mixin ){
8148
8149
		foreach($env->frames as $recur_frame){
8150
			if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
8151
8152
				if( $mixin === $recur_frame ){
8153
					return true;
8154
				}
8155
8156
				if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){
8157
					return true;
8158
				}
8159
			}
8160
		}
8161
8162
		return false;
8163
	}
8164
8165
}
8166
8167
8168
 
8169
8170
class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
8171
	public $name;
8172
	public $selectors;
8173
	public $params;
8174
	public $arity		= 0;
8175
	public $rules;
8176
	public $lookups		= array();
8177
	public $required	= 0;
8178
	public $frames		= array();
8179
	public $condition;
8180
	public $variadic;
8181
	public $type		= 'MixinDefinition';
8182
8183
8184
	// less.js : /lib/less/tree/mixin.js : tree.mixin.Definition
8185
	public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = array() ){
8186
		$this->name = $name;
8187
		$this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name))));
8188
8189
		$this->params = $params;
8190
		$this->condition = $condition;
8191
		$this->variadic = $variadic;
8192
		$this->rules = $rules;
8193
8194
		if( $params ){
8195
			$this->arity = count($params);
8196
			foreach( $params as $p ){
8197
				if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) {
8198
					$this->required++;
8199
				}
8200
			}
8201
		}
8202
8203
		$this->frames = $frames;
8204
		$this->SetRulesetIndex();
8205
	}
8206
8207
8208
8209
	//function accept( $visitor ){
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8210
	//	$this->params = $visitor->visit($this->params);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8211
	//	$this->rules = $visitor->visit($this->rules);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8212
	//	$this->condition = $visitor->visit($this->condition);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8213
	//}
8214
8215
8216
	public function toCSS(){
8217
		return '';
8218
	}
8219
8220
	// less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
8221
	public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){
8222
		$frame = new Less_Tree_Ruleset(null, array());
8223
		$params = $this->params;
8224
		$mixinEnv = null;
8225
		$argsLength = 0;
8226
8227
		if( $args ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $args of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8228
			$argsLength = count($args);
8229
			for($i = 0; $i < $argsLength; $i++ ){
8230
				$arg = $args[$i];
8231
8232
				if( $arg && $arg['name'] ){
8233
					$isNamedFound = false;
8234
8235
					foreach($params as $j => $param){
8236
						if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) {
8237
							$evaldArguments[$j] = $arg['value']->compile($env);
8238
							array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) );
8239
							$isNamedFound = true;
8240
							break;
8241
						}
8242
					}
8243
					if ($isNamedFound) {
8244
						array_splice($args, $i, 1);
8245
						$i--;
8246
						$argsLength--;
8247
						continue;
8248
					} else {
8249
						throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found');
8250
					}
8251
				}
8252
			}
8253
		}
8254
8255
		$argIndex = 0;
8256
		foreach($params as $i => $param){
8257
8258
			if ( isset($evaldArguments[$i]) ){ continue; }
8259
8260
			$arg = null;
8261
			if( isset($args[$argIndex]) ){
8262
				$arg = $args[$argIndex];
8263
			}
8264
8265
			if (isset($param['name']) && $param['name']) {
8266
8267
				if( isset($param['variadic']) ){
8268
					$varargs = array();
8269 View Code Duplication
					for ($j = $argIndex; $j < $argsLength; $j++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8270
						$varargs[] = $args[$j]['value']->compile($env);
8271
					}
8272
					$expression = new Less_Tree_Expression($varargs);
8273
					array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env)));
8274
				}else{
8275
					$val = ($arg && $arg['value']) ? $arg['value'] : false;
8276
8277
					if ($val) {
8278
						$val = $val->compile($env);
8279
					} else if ( isset($param['value']) ) {
8280
8281
						if( !$mixinEnv ){
8282
							$mixinEnv = new Less_Environment();
8283
							$mixinEnv->frames = array_merge( array($frame), $mixinFrames);
8284
						}
8285
8286
						$val = $param['value']->compile($mixinEnv);
8287
						$frame->resetCache();
8288
					} else {
8289
						throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")");
8290
					}
8291
8292
					array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val));
8293
					$evaldArguments[$i] = $val;
8294
				}
8295
			}
8296
8297
			if ( isset($param['variadic']) && $args) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $args of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8298 View Code Duplication
				for ($j = $argIndex; $j < $argsLength; $j++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8299
					$evaldArguments[$j] = $args[$j]['value']->compile($env);
8300
				}
8301
			}
8302
			$argIndex++;
8303
		}
8304
8305
		ksort($evaldArguments);
8306
		$evaldArguments = array_values($evaldArguments);
8307
8308
		return $frame;
8309
	}
8310
8311
	public function compile($env) {
8312
		if( $this->frames ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->frames of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8313
			return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames );
8314
		}
8315
		return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames );
8316
	}
8317
8318
	public function evalCall($env, $args = NULL, $important = NULL) {
8319
8320
		Less_Environment::$mixin_stack++;
8321
8322
		$_arguments = array();
8323
8324
		if( $this->frames ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->frames of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8325
			$mixinFrames = array_merge($this->frames, $env->frames);
8326
		}else{
8327
			$mixinFrames = $env->frames;
8328
		}
8329
8330
		$frame = $this->compileParams($env, $mixinFrames, $args, $_arguments);
8331
8332
		$ex = new Less_Tree_Expression($_arguments);
8333
		array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env)));
8334
8335
8336
		$ruleset = new Less_Tree_Ruleset(null, $this->rules);
8337
		$ruleset->originalRuleset = $this->ruleset_id;
8338
8339
8340
		$ruleSetEnv = new Less_Environment();
8341
		$ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames );
8342
		$ruleset = $ruleset->compile( $ruleSetEnv );
8343
8344
		if( $important ){
8345
			$ruleset = $ruleset->makeImportant();
8346
		}
8347
8348
		Less_Environment::$mixin_stack--;
8349
8350
		return $ruleset;
8351
	}
8352
8353
8354
	public function matchCondition($args, $env) {
8355
8356
		if( !$this->condition ){
8357
			return true;
8358
		}
8359
8360
		// set array to prevent error on array_merge
8361
		if(!is_array($this->frames)) {
8362
             $this->frames = array();
8363
        }
8364
8365
		$frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args );
8366
8367
		$compile_env = new Less_Environment();
8368
		$compile_env->frames = array_merge(
8369
				array($frame)		// the parameter variables
8370
				, $this->frames		// the parent namespace/mixin frames
8371
				, $env->frames		// the current environment frames
8372
			);
8373
8374
		$compile_env->functions = $env->functions;
8375
8376
		return (bool)$this->condition->compile($compile_env);
8377
	}
8378
8379
	public function matchArgs($args, $env = NULL){
8380
		$argsLength = count($args);
8381
8382
		if( !$this->variadic ){
8383
			if( $argsLength < $this->required ){
8384
				return false;
8385
			}
8386
			if( $argsLength > count($this->params) ){
8387
				return false;
8388
			}
8389
		}else{
8390
			if( $argsLength < ($this->required - 1)){
8391
				return false;
8392
			}
8393
		}
8394
8395
		$len = min($argsLength, $this->arity);
8396
8397
		for( $i = 0; $i < $len; $i++ ){
8398
			if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){
8399
				if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){
8400
					return false;
8401
				}
8402
			}
8403
		}
8404
8405
		return true;
8406
	}
8407
8408
}
8409
 
8410
8411
/**
8412
 * Extend Finder Visitor
8413
 *
8414
 * @package Less
8415
 * @subpackage visitor
8416
 */
8417
class Less_Visitor_extendFinder extends Less_Visitor{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
8418
8419
	public $contexts = array();
8420
	public $allExtendsStack;
8421
	public $foundExtends;
8422
8423
	public function __construct(){
8424
		$this->contexts = array();
8425
		$this->allExtendsStack = array(array());
8426
		parent::__construct();
8427
	}
8428
8429
	/**
8430
	 * @param Less_Tree_Ruleset $root
8431
	 */
8432
    public function run($root){
8433
		$root = $this->visitObj($root);
8434
		$root->allExtends =& $this->allExtendsStack[0];
8435
		return $root;
8436
	}
8437
8438
    public function visitRule($ruleNode, &$visitDeeper ){
0 ignored issues
show
Unused Code introduced by
The parameter $ruleNode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
8439
		$visitDeeper = false;
8440
	}
8441
8442
    public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
0 ignored issues
show
Unused Code introduced by
The parameter $mixinDefinitionNode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
8443
		$visitDeeper = false;
8444
	}
8445
8446
    public function visitRuleset($rulesetNode){
8447
8448
		if( $rulesetNode->root ){
8449
			return;
8450
		}
8451
8452
		$allSelectorsExtendList = array();
8453
8454
		// get &:extend(.a); rules which apply to all selectors in this ruleset
8455
		if( $rulesetNode->rules ){
8456
			foreach($rulesetNode->rules as $rule){
8457
				if( $rule instanceof Less_Tree_Extend ){
8458
					$allSelectorsExtendList[] = $rule;
8459
					$rulesetNode->extendOnEveryPath = true;
8460
				}
8461
			}
8462
		}
8463
8464
8465
		// now find every selector and apply the extends that apply to all extends
8466
		// and the ones which apply to an individual extend
8467
		foreach($rulesetNode->paths as $selectorPath){
8468
			$selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1];
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8469
8470
			$j = 0;
8471
			foreach($selector->extendList as $extend){
8472
				$this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
8473
			}
8474
			foreach($allSelectorsExtendList as $extend){
8475
				$this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
8476
			}
8477
		}
8478
8479
		$this->contexts[] = $rulesetNode->selectors;
8480
	}
8481
8482
    public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){
8483
		$this->foundExtends = true;
8484
		$extend = clone $extend;
8485
		$extend->findSelfSelectors( $selectorPath );
8486
		$extend->ruleset = $rulesetNode;
8487
		if( $j === 0 ){
8488
			$extend->firstExtendOnThisSelectorPath = true;
8489
		}
8490
8491
		$end_key = count($this->allExtendsStack)-1;
8492
		$this->allExtendsStack[$end_key][] = $extend;
8493
		$j++;
8494
	}
8495
8496
8497
    public function visitRulesetOut( $rulesetNode ){
8498
		if( !is_object($rulesetNode) || !$rulesetNode->root ){
8499
			array_pop($this->contexts);
8500
		}
8501
	}
8502
8503
    public function visitMedia( $mediaNode ){
8504
		$mediaNode->allExtends = array();
8505
		$this->allExtendsStack[] =& $mediaNode->allExtends;
8506
	}
8507
8508
    public function visitMediaOut(){
8509
		array_pop($this->allExtendsStack);
8510
	}
8511
8512
    public function visitDirective( $directiveNode ){
8513
		$directiveNode->allExtends = array();
8514
		$this->allExtendsStack[] =& $directiveNode->allExtends;
8515
	}
8516
8517
    public function visitDirectiveOut(){
8518
		array_pop($this->allExtendsStack);
8519
	}
8520
}
8521
8522
8523
 
8524
8525
/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8526
class Less_Visitor_import extends Less_VisitorReplacing{
8527
8528
	public $_visitor;
8529
	public $_importer;
8530
	public $importCount;
8531
8532
	function __construct( $evalEnv ){
8533
		$this->env = $evalEnv;
8534
		$this->importCount = 0;
8535
		parent::__construct();
8536
	}
8537
8538
8539
	function run( $root ){
8540
		$root = $this->visitObj($root);
8541
		$this->isFinished = true;
8542
8543
		//if( $this->importCount === 0) {
8544
		//	$this->_finish();
8545
		//}
8546
	}
8547
8548
	function visitImport($importNode, &$visitDeeper ){
8549
		$importVisitor = $this;
8550
		$inlineCSS = $importNode->options['inline'];
8551
8552
		if( !$importNode->css || $inlineCSS ){
8553
			$evaldImportNode = $importNode->compileForImport($this->env);
8554
8555
			if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){
8556
				$importNode = $evaldImportNode;
8557
				$this->importCount++;
8558
				$env = clone $this->env;
8559
8560
				if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){
8561
					$env->importMultiple = true;
8562
				}
8563
8564
				//get path & uri
8565
				$path_and_uri = null;
8566
				if( is_callable(Less_Parser::$options['import_callback']) ){
8567
					$path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode);
8568
				}
8569
8570
				if( !$path_and_uri ){
8571
					$path_and_uri = $importNode->PathAndUri();
8572
				}
8573
8574
				if( $path_and_uri ){
8575
					list($full_path, $uri) = $path_and_uri;
8576
				}else{
8577
					$full_path = $uri = $importNode->getPath();
8578
				}
8579
8580
8581
				//import once
8582
				if( $importNode->skip( $full_path, $env) ){
8583
					return array();
8584
				}
8585
8586
				if( $importNode->options['inline'] ){
8587
					//todo needs to reference css file not import
8588
					//$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true );
8589
8590
					Less_Parser::AddParsedFile($full_path);
8591
					$contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
8592
8593
					if( $importNode->features ){
8594
						return new Less_Tree_Media( array($contents), $importNode->features->value );
8595
					}
8596
8597
					return array( $contents );
8598
				}
8599
8600
8601
				// css ?
8602
				if( $importNode->css ){
8603
					$features = ( $importNode->features ? $importNode->features->compile($env) : null );
8604
					return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index);
8605
				}
8606
8607
				return $importNode->ParseImport( $full_path, $uri, $env );
8608
			}
8609
8610
		}
8611
8612
		$visitDeeper = false;
8613
		return $importNode;
8614
	}
8615
8616
8617
	function visitRule( $ruleNode, &$visitDeeper ){
8618
		$visitDeeper = false;
8619
		return $ruleNode;
8620
	}
8621
8622
	function visitDirective($directiveNode, $visitArgs){
8623
		array_unshift($this->env->frames,$directiveNode);
8624
		return $directiveNode;
8625
	}
8626
8627
	function visitDirectiveOut($directiveNode) {
8628
		array_shift($this->env->frames);
8629
	}
8630
8631
	function visitMixinDefinition($mixinDefinitionNode, $visitArgs) {
8632
		array_unshift($this->env->frames,$mixinDefinitionNode);
8633
		return $mixinDefinitionNode;
8634
	}
8635
8636
	function visitMixinDefinitionOut($mixinDefinitionNode) {
8637
		array_shift($this->env->frames);
8638
	}
8639
8640
	function visitRuleset($rulesetNode, $visitArgs) {
8641
		array_unshift($this->env->frames,$rulesetNode);
8642
		return $rulesetNode;
8643
	}
8644
8645
	function visitRulesetOut($rulesetNode) {
8646
		array_shift($this->env->frames);
8647
	}
8648
8649
	function visitMedia($mediaNode, $visitArgs) {
8650
		array_unshift($this->env->frames, $mediaNode->ruleset);
8651
		return $mediaNode;
8652
	}
8653
8654
	function visitMediaOut($mediaNode) {
8655
		array_shift($this->env->frames);
8656
	}
8657
8658
}
8659
*/
8660
8661
8662
 
8663
8664
/**
8665
 * Join Selector Visitor
8666
 *
8667
 * @package Less
8668
 * @subpackage visitor
8669
 */
8670
class Less_Visitor_joinSelector extends Less_Visitor{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
8671
8672
	public $contexts = array( array() );
8673
8674
	/**
8675
	 * @param Less_Tree_Ruleset $root
8676
	 */
8677
	public function run( $root ){
8678
		return $this->visitObj($root);
8679
	}
8680
8681
    public function visitRule( $ruleNode, &$visitDeeper ){
0 ignored issues
show
Unused Code introduced by
The parameter $ruleNode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
8682
		$visitDeeper = false;
8683
	}
8684
8685
    public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
0 ignored issues
show
Unused Code introduced by
The parameter $mixinDefinitionNode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
8686
		$visitDeeper = false;
8687
	}
8688
8689
    public function visitRuleset( $rulesetNode ){
8690
8691
		$paths = array();
8692
8693
		if( !$rulesetNode->root ){
8694
			$selectors = array();
8695
8696
			if( $rulesetNode->selectors && $rulesetNode->selectors ){
8697
				foreach($rulesetNode->selectors as $selector){
8698
					if( $selector->getIsOutput() ){
8699
						$selectors[] = $selector;
8700
					}
8701
				}
8702
			}
8703
8704
			if( !$selectors ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $selectors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8705
				$rulesetNode->selectors = null;
8706
				$rulesetNode->rules = null;
8707
			}else{
8708
				$context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8709
				$paths = $rulesetNode->joinSelectors( $context, $selectors);
8710
			}
8711
8712
			$rulesetNode->paths = $paths;
8713
		}
8714
8715
		$this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
8716
	}
8717
8718
    public function visitRulesetOut(){
8719
		array_pop($this->contexts);
8720
	}
8721
8722
    public function visitMedia($mediaNode) {
8723
		$context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8724
8725
		if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){
8726
			$mediaNode->rules[0]->root = true;
8727
		}
8728
	}
8729
8730
}
8731
8732
 
8733
8734
/**
8735
 * Process Extends Visitor
8736
 *
8737
 * @package Less
8738
 * @subpackage visitor
8739
 */
8740
class Less_Visitor_processExtends extends Less_Visitor{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
8741
8742
	public $allExtendsStack;
8743
8744
	/**
8745
	 * @param Less_Tree_Ruleset $root
8746
	 */
8747
	public function run( $root ){
8748
		$extendFinder = new Less_Visitor_extendFinder();
8749
		$extendFinder->run( $root );
8750
		if( !$extendFinder->foundExtends){
8751
			return $root;
8752
		}
8753
8754
		$root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends);
8755
8756
		$this->allExtendsStack = array();
8757
		$this->allExtendsStack[] = &$root->allExtends;
8758
8759
		return $this->visitObj( $root );
8760
	}
8761
8762
	private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){
8763
		//
8764
		// chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
8765
		// the selector we would do normally, but we are also adding an extend with the same target selector
8766
		// this means this new extend can then go and alter other extends
8767
		//
8768
		// this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
8769
		// this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
8770
		// we look at each selector at a time, as is done in visitRuleset
8771
8772
		$extendsToAdd = array();
8773
8774
8775
		//loop through comparing every extend with every target extend.
8776
		// a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
8777
		// e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
8778
		// and the second is the target.
8779
		// the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
8780
		// case when processing media queries
8781
		for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){
8782
			for( $targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++ ){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
8783
8784
				$extend = $extendsList[$extendIndex];
8785
				$targetExtend = $extendsListTarget[$targetExtendIndex];
8786
8787
				// look for circular references
8788
				if( in_array($targetExtend->object_id, $extend->parent_ids,true) ){
8789
					continue;
8790
				}
8791
8792
				// find a match in the target extends self selector (the bit before :extend)
8793
				$selectorPath = array( $targetExtend->selfSelectors[0] );
8794
				$matches = $this->findMatch( $extend, $selectorPath);
8795
8796
8797
				if( $matches ){
8798
8799
					// we found a match, so for each self selector..
8800
					foreach($extend->selfSelectors as $selfSelector ){
8801
8802
8803
						// process the extend as usual
8804
						$newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector);
8805
8806
						// but now we create a new extend from it
8807
						$newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0);
8808
						$newExtend->selfSelectors = $newSelector;
8809
8810
						// add the extend onto the list of extends for that selector
8811
						end($newSelector)->extendList = array($newExtend);
8812
						//$newSelector[ count($newSelector)-1]->extendList = array($newExtend);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
8813
8814
						// record that we need to add it.
8815
						$extendsToAdd[] = $newExtend;
8816
						$newExtend->ruleset = $targetExtend->ruleset;
8817
8818
						//remember its parents for circular references
8819
						$newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids);
8820
8821
						// only process the selector once.. if we have :extend(.a,.b) then multiple
8822
						// extends will look at the same selector path, so when extending
8823
						// we know that any others will be duplicates in terms of what is added to the css
8824
						if( $targetExtend->firstExtendOnThisSelectorPath ){
8825
							$newExtend->firstExtendOnThisSelectorPath = true;
8826
							$targetExtend->ruleset->paths[] = $newSelector;
8827
						}
8828
					}
8829
				}
8830
			}
8831
		}
8832
8833
		if( $extendsToAdd ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $extendsToAdd of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8834
			// try to detect circular references to stop a stack overflow.
8835
			// may no longer be needed.			$this->extendChainCount++;
8836
			if( $iterationCount > 100) {
8837
8838
				try{
8839
					$selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
8840
					$selectorTwo = $extendsToAdd[0]->selector->toCSS();
8841
				}catch(Exception $e){
8842
					$selectorOne = "{unable to calculate}";
8843
					$selectorTwo = "{unable to calculate}";
8844
				}
8845
8846
				throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:"+$selectorOne+":extend(" + $selectorTwo+")");
8847
			}
8848
8849
			// now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
8850
			$extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1);
8851
		}
8852
8853
		return array_merge($extendsList, $extendsToAdd);
8854
	}
8855
8856
8857
	protected function visitRule( $ruleNode, &$visitDeeper ){
0 ignored issues
show
Unused Code introduced by
The parameter $ruleNode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
8858
		$visitDeeper = false;
8859
	}
8860
8861
	protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
0 ignored issues
show
Unused Code introduced by
The parameter $mixinDefinitionNode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
8862
		$visitDeeper = false;
8863
	}
8864
8865
	protected function visitSelector( $selectorNode, &$visitDeeper ){
0 ignored issues
show
Unused Code introduced by
The parameter $selectorNode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
8866
		$visitDeeper = false;
8867
	}
8868
8869
	protected function visitRuleset($rulesetNode){
8870
8871
8872
		if( $rulesetNode->root ){
8873
			return;
8874
		}
8875
8876
		$allExtends	= end($this->allExtendsStack);
8877
		$paths_len = count($rulesetNode->paths);
8878
8879
		// look at each selector path in the ruleset, find any extend matches and then copy, find and replace
8880
		foreach($allExtends as $allExtend){
8881
			for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){
8882
8883
				// extending extends happens initially, before the main pass
8884
				if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){
8885
					continue;
8886
				}
8887
8888
				$selectorPath = $rulesetNode->paths[$pathIndex];
8889
8890
				if( end($selectorPath)->extendList ){
8891
					continue;
8892
				}
8893
8894
				$this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath);
8895
8896
			}
8897
		}
8898
	}
8899
8900
8901
	private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){
8902
		$matches = $this->findMatch($extend, $selectorPath);
8903
8904
		if( $matches ){
8905
			foreach($extend->selfSelectors as $selfSelector ){
8906
				$rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector);
8907
			}
8908
		}
8909
	}
8910
8911
8912
8913
	private function findMatch($extend, $haystackSelectorPath ){
8914
8915
8916
		if( !$this->HasMatches($extend, $haystackSelectorPath) ){
8917
			return false;
8918
		}
8919
8920
8921
		//
8922
		// look through the haystack selector path to try and find the needle - extend.selector
8923
		// returns an array of selector matches that can then be replaced
8924
		//
8925
		$needleElements = $extend->selector->elements;
8926
		$potentialMatches = array();
8927
		$potentialMatches_len = 0;
8928
		$potentialMatch = null;
8929
		$matches = array();
8930
8931
8932
8933
		// loop through the haystack elements
8934
		$haystack_path_len = count($haystackSelectorPath);
8935
		for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){
8936
			$hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
8937
8938
			$haystack_elements_len = count($hackstackSelector->elements);
8939
			for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){
8940
8941
				$haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
8942
8943
				// if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
8944
				if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){
8945
					$potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator);
8946
					$potentialMatches_len++;
8947
				}
8948
8949
				for($i = 0; $i < $potentialMatches_len; $i++ ){
8950
8951
					$potentialMatch = &$potentialMatches[$i];
8952
					$potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex );
8953
8954
8955
					// if we are still valid and have finished, test whether we have elements after and whether these are allowed
8956
					if( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $potentialMatch of type array<string,false> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
8957
						$potentialMatch['finished'] = true;
8958
8959
						if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){
8960
							$potentialMatch = null;
8961
						}
8962
					}
8963
8964
					// if null we remove, if not, we are still valid, so either push as a valid match or continue
8965
					if( $potentialMatch ){
8966
						if( $potentialMatch['finished'] ){
8967
							$potentialMatch['length'] = $extend->selector->elements_len;
8968
							$potentialMatch['endPathIndex'] = $haystackSelectorIndex;
8969
							$potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
8970
							$potentialMatches = array(); // we don't allow matches to overlap, so start matching again
8971
							$potentialMatches_len = 0;
8972
							$matches[] = $potentialMatch;
8973
						}
8974
						continue;
8975
					}
8976
8977
					array_splice($potentialMatches, $i, 1);
8978
					$potentialMatches_len--;
8979
					$i--;
8980
				}
8981
			}
8982
		}
8983
8984
		return $matches;
8985
	}
8986
8987
8988
	// Before going through all the nested loops, lets check to see if a match is possible
8989
	// Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
8990
	private function HasMatches($extend, $haystackSelectorPath){
8991
8992
		if( !$extend->selector->cacheable ){
8993
			return true;
8994
		}
8995
8996
		$first_el = $extend->selector->_oelements[0];
8997
8998
		foreach($haystackSelectorPath as $hackstackSelector){
8999
			if( !$hackstackSelector->cacheable ){
9000
				return true;
9001
			}
9002
9003
			if( in_array($first_el, $hackstackSelector->_oelements) ){
9004
				return true;
9005
			}
9006
		}
9007
9008
		return false;
9009
	}
9010
9011
9012
	/**
9013
	 * @param integer $hackstackElementIndex
9014
	 */
9015
	private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){
9016
9017
9018
		if( $potentialMatch['matched'] > 0 ){
9019
9020
			// selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
9021
			// then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
9022
			// what the resulting combinator will be
9023
			$targetCombinator = $haystackElement->combinator;
9024
			if( $targetCombinator === '' && $hackstackElementIndex === 0 ){
9025
				$targetCombinator = ' ';
9026
			}
9027
9028
			if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){
9029
				return null;
9030
			}
9031
		}
9032
9033
		// if we don't match, null our match to indicate failure
9034
		if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){
9035
			return null;
9036
		}
9037
9038
		$potentialMatch['finished'] = false;
9039
		$potentialMatch['matched']++;
9040
9041
		return $potentialMatch;
9042
	}
9043
9044
9045
	private function isElementValuesEqual( $elementValue1, $elementValue2 ){
9046
9047
		if( $elementValue1 === $elementValue2 ){
9048
			return true;
9049
		}
9050
9051
		if( is_string($elementValue1) || is_string($elementValue2) ) {
9052
			return false;
9053
		}
9054
9055
		if( $elementValue1 instanceof Less_Tree_Attribute ){
9056
			return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 );
9057
		}
9058
9059
		$elementValue1 = $elementValue1->value;
9060
		if( $elementValue1 instanceof Less_Tree_Selector ){
9061
			return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 );
9062
		}
9063
9064
		return false;
9065
	}
9066
9067
9068
	/**
9069
	 * @param Less_Tree_Selector $elementValue1
9070
	 */
9071
	private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){
9072
9073
		$elementValue2 = $elementValue2->value;
9074
		if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){
9075
			return false;
9076
		}
9077
9078
		for( $i = 0; $i < $elementValue1->elements_len; $i++ ){
9079
9080
			if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){
9081
				if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){
9082
					return false;
9083
				}
9084
			}
9085
9086
			if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){
9087
				return false;
9088
			}
9089
		}
9090
9091
		return true;
9092
	}
9093
9094
9095
	/**
9096
	 * @param Less_Tree_Attribute $elementValue1
9097
	 */
9098
	private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){
9099
9100
		if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){
9101
			return false;
9102
		}
9103
9104
		if( !$elementValue1->value || !$elementValue2->value ){
9105
			if( $elementValue1->value || $elementValue2->value ) {
9106
				return false;
9107
			}
9108
			return true;
9109
		}
9110
9111
		$elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value );
9112
		$elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value );
9113
9114
		return $elementValue1 === $elementValue2;
9115
	}
9116
9117
9118
	private function extendSelector($matches, $selectorPath, $replacementSelector){
9119
9120
		//for a set of matches, replace each match with the replacement selector
9121
9122
		$currentSelectorPathIndex = 0;
9123
		$currentSelectorPathElementIndex = 0;
9124
		$path = array();
9125
		$selectorPath_len = count($selectorPath);
9126
9127
		for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){
9128
9129
9130
			$match = $matches[$matchIndex];
9131
			$selector = $selectorPath[ $match['pathIndex'] ];
9132
9133
			$firstElement = new Less_Tree_Element(
9134
				$match['initialCombinator'],
9135
				$replacementSelector->elements[0]->value,
9136
				$replacementSelector->elements[0]->index,
9137
				$replacementSelector->elements[0]->currentFileInfo
9138
			);
9139
9140 View Code Duplication
			if( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9141
				$last_path = end($path);
9142
				$last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
9143
				$currentSelectorPathElementIndex = 0;
9144
				$currentSelectorPathIndex++;
9145
			}
9146
9147
			$newElements = array_merge(
9148
				array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice
9149
				, array($firstElement)
9150
				, array_slice($replacementSelector->elements,1)
9151
				);
9152
9153
			if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){
9154
				$last_key = count($path)-1;
9155
				$path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements);
9156
			}else{
9157
				$path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] ));
9158
				$path[] = new Less_Tree_Selector( $newElements );
9159
			}
9160
9161
			$currentSelectorPathIndex = $match['endPathIndex'];
9162
			$currentSelectorPathElementIndex = $match['endPathElementIndex'];
9163
			if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){
9164
				$currentSelectorPathElementIndex = 0;
9165
				$currentSelectorPathIndex++;
9166
			}
9167
		}
9168
9169 View Code Duplication
		if( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9170
			$last_path = end($path);
9171
			$last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
9172
			$currentSelectorPathIndex++;
9173
		}
9174
9175
		$slice_len = $selectorPath_len - $currentSelectorPathIndex;
9176
		$path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len));
9177
9178
		return $path;
9179
	}
9180
9181
9182
	protected function visitMedia( $mediaNode ){
9183
		$newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) );
9184
		$this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends);
9185
	}
9186
9187
	protected function visitMediaOut(){
9188
		array_pop( $this->allExtendsStack );
9189
	}
9190
9191
	protected function visitDirective( $directiveNode ){
9192
		$newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) );
9193
		$this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends);
9194
	}
9195
9196
	protected function visitDirectiveOut(){
9197
		array_pop($this->allExtendsStack);
9198
	}
9199
9200
} 
9201
9202
/**
9203
 * toCSS Visitor
9204
 *
9205
 * @package Less
9206
 * @subpackage visitor
9207
 */
9208
class Less_Visitor_toCSS extends Less_VisitorReplacing{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9209
9210
	private $charset;
9211
9212
	public function __construct(){
9213
		parent::__construct();
9214
	}
9215
9216
	/**
9217
	 * @param Less_Tree_Ruleset $root
9218
	 */
9219
	public function run( $root ){
9220
		return $this->visitObj($root);
9221
	}
9222
9223
	public function visitRule( $ruleNode ){
9224
		if( $ruleNode->variable ){
9225
			return array();
9226
		}
9227
		return $ruleNode;
9228
	}
9229
9230
	public function visitMixinDefinition($mixinNode){
9231
		// mixin definitions do not get eval'd - this means they keep state
9232
		// so we have to clear that state here so it isn't used if toCSS is called twice
9233
		$mixinNode->frames = array();
9234
		return array();
9235
	}
9236
9237
	public function visitExtend(){
9238
		return array();
9239
	}
9240
9241
	public function visitComment( $commentNode ){
9242
		if( $commentNode->isSilent() ){
9243
			return array();
9244
		}
9245
		return $commentNode;
9246
	}
9247
9248
	public function visitMedia( $mediaNode, &$visitDeeper ){
9249
		$mediaNode->accept($this);
9250
		$visitDeeper = false;
9251
9252
		if( !$mediaNode->rules ){
9253
			return array();
9254
		}
9255
		return $mediaNode;
9256
	}
9257
9258
	public function visitDirective( $directiveNode ){
9259
		if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){
9260
			return array();
9261
		}
9262
		if( $directiveNode->name === '@charset' ){
9263
			// Only output the debug info together with subsequent @charset definitions
9264
			// a comment (or @media statement) before the actual @charset directive would
9265
			// be considered illegal css as it has to be on the first line
9266
			if( isset($this->charset) && $this->charset ){
9267
9268
				//if( $directiveNode->debugInfo ){
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9269
				//	$comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9270
				//	$comment->debugInfo = $directiveNode->debugInfo;
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9271
				//	return $this->visit($comment);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9272
				//}
9273
9274
9275
				return array();
9276
			}
9277
			$this->charset = true;
9278
		}
9279
		return $directiveNode;
9280
	}
9281
9282
	public function checkPropertiesInRoot( $rulesetNode ){
9283
9284
		if( !$rulesetNode->firstRoot ){
9285
			return;
9286
		}
9287
9288
		foreach($rulesetNode->rules as $ruleNode){
9289
			if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){
9290
				$msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null);
9291
				throw new Less_Exception_Compiler($msg);
9292
			}
9293
		}
9294
	}
9295
9296
9297
	public function visitRuleset( $rulesetNode, &$visitDeeper ){
9298
9299
		$visitDeeper = false;
9300
9301
		$this->checkPropertiesInRoot( $rulesetNode );
9302
9303
		if( $rulesetNode->root ){
9304
			return $this->visitRulesetRoot( $rulesetNode );
9305
		}
9306
9307
		$rulesets = array();
9308
		$rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
9309
9310
9311
		// Compile rules and rulesets
9312
		$nodeRuleCnt = count($rulesetNode->rules);
9313
		for( $i = 0; $i < $nodeRuleCnt; ){
9314
			$rule = $rulesetNode->rules[$i];
9315
9316
			if( property_exists($rule,'rules') ){
9317
				// visit because we are moving them out from being a child
9318
				$rulesets[] = $this->visitObj($rule);
9319
				array_splice($rulesetNode->rules,$i,1);
9320
				$nodeRuleCnt--;
9321
				continue;
9322
			}
9323
			$i++;
9324
		}
9325
9326
9327
		// accept the visitor to remove rules and refactor itself
9328
		// then we can decide now whether we want it or not
9329
		if( $nodeRuleCnt > 0 ){
9330
			$rulesetNode->accept($this);
9331
9332
			if( $rulesetNode->rules ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $rulesetNode->rules of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
9333
9334
				if( count($rulesetNode->rules) >  1 ){
9335
					$this->_mergeRules( $rulesetNode->rules );
9336
					$this->_removeDuplicateRules( $rulesetNode->rules );
9337
				}
9338
9339
				// now decide whether we keep the ruleset
9340
				if( $rulesetNode->paths ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $rulesetNode->paths of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
9341
					//array_unshift($rulesets, $rulesetNode);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9342
					array_splice($rulesets,0,0,array($rulesetNode));
9343
				}
9344
			}
9345
9346
		}
9347
9348
9349
		if( count($rulesets) === 1 ){
9350
			return $rulesets[0];
9351
		}
9352
		return $rulesets;
9353
	}
9354
9355
9356
	/**
9357
	 * Helper function for visitiRuleset
9358
	 *
9359
	 * return array|Less_Tree_Ruleset
9360
	 */
9361
	private function visitRulesetRoot( $rulesetNode ){
9362
		$rulesetNode->accept( $this );
9363
		if( $rulesetNode->firstRoot || $rulesetNode->rules ){
9364
			return $rulesetNode;
9365
		}
9366
		return array();
9367
	}
9368
9369
9370
	/**
9371
	 * Helper function for visitRuleset()
9372
	 *
9373
	 * @return array
9374
	 */
9375
	private function visitRulesetPaths($rulesetNode){
9376
9377
		$paths = array();
9378
		foreach($rulesetNode->paths as $p){
9379
			if( $p[0]->elements[0]->combinator === ' ' ){
9380
				$p[0]->elements[0]->combinator = '';
9381
			}
9382
9383
			foreach($p as $pi){
9384
				if( $pi->getIsReferenced() && $pi->getIsOutput() ){
9385
					$paths[] = $p;
9386
					break;
9387
				}
9388
			}
9389
		}
9390
9391
		return $paths;
9392
	}
9393
9394
	protected function _removeDuplicateRules( &$rules ){
9395
		// remove duplicates
9396
		$ruleCache = array();
9397
		for( $i = count($rules)-1; $i >= 0 ; $i-- ){
9398
			$rule = $rules[$i];
9399
			if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){
9400
9401
				if( !isset($ruleCache[$rule->name]) ){
9402
					$ruleCache[$rule->name] = $rule;
9403
				}else{
9404
					$ruleList =& $ruleCache[$rule->name];
9405
9406
					if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){
9407
						$ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
9408
					}
9409
9410
					$ruleCSS = $rule->toCSS();
9411
					if( array_search($ruleCSS,$ruleList) !== false ){
9412
						array_splice($rules,$i,1);
9413
					}else{
9414
						$ruleList[] = $ruleCSS;
9415
					}
9416
				}
9417
			}
9418
		}
9419
	}
9420
9421
	protected function _mergeRules( &$rules ){
9422
		$groups = array();
9423
9424
		//obj($rules);
9425
9426
		$rules_len = count($rules);
9427
		for( $i = 0; $i < $rules_len; $i++ ){
9428
			$rule = $rules[$i];
9429
9430
			if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){
9431
9432
				$key = $rule->name;
9433
				if( $rule->important ){
9434
					$key .= ',!';
9435
				}
9436
9437
				if( !isset($groups[$key]) ){
9438
					$groups[$key] = array();
9439
				}else{
9440
					array_splice($rules, $i--, 1);
9441
					$rules_len--;
9442
				}
9443
9444
				$groups[$key][] = $rule;
9445
			}
9446
		}
9447
9448
9449
		foreach($groups as $parts){
9450
9451
			if( count($parts) > 1 ){
9452
				$rule = $parts[0];
9453
				$spacedGroups = array();
9454
				$lastSpacedGroup = array();
9455
				$parts_mapped = array();
0 ignored issues
show
Unused Code introduced by
$parts_mapped is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
9456
				foreach($parts as $p){
9457
					if( $p->merge === '+' ){
9458
						if( $lastSpacedGroup ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $lastSpacedGroup of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
9459
							$spacedGroups[] = self::toExpression($lastSpacedGroup);
9460
						}
9461
						$lastSpacedGroup = array();
9462
					}
9463
					$lastSpacedGroup[] = $p;
9464
				}
9465
9466
				$spacedGroups[] = self::toExpression($lastSpacedGroup);
9467
				$rule->value = self::toValue($spacedGroups);
9468
			}
9469
		}
9470
9471
	}
9472
9473
	public static function toExpression($values){
9474
		$mapped = array();
9475
		foreach($values as $p){
9476
			$mapped[] = $p->value;
9477
		}
9478
		return new Less_Tree_Expression( $mapped );
9479
	}
9480
9481
	public static function toValue($values){
9482
		//return new Less_Tree_Value($values); ??
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9483
9484
		$mapped = array();
9485
		foreach($values as $p){
9486
			$mapped[] = $p;
9487
		}
9488
		return new Less_Tree_Value($mapped);
9489
	}
9490
}
9491
9492
 
9493
9494
/**
9495
 * Parser Exception
9496
 *
9497
 * @package Less
9498
 * @subpackage exception
9499
 */
9500
class Less_Exception_Parser extends Exception{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9501
9502
	/**
9503
	 * The current file
9504
	 *
9505
	 * @var Less_ImportedFile
9506
	 */
9507
	public $currentFile;
9508
9509
	/**
9510
	 * The current parser index
9511
	 *
9512
	 * @var integer
9513
	 */
9514
	public $index;
9515
9516
	protected $input;
9517
9518
	protected $details = array();
9519
9520
9521
	/**
9522
	 * Constructor
9523
	 *
9524
	 * @param string $message
9525
	 * @param Exception $previous Previous exception
9526
	 * @param integer $index The current parser index
9527
	 * @param Less_FileInfo|string $currentFile The file
9528
	 * @param integer $code The exception code
9529
	 */
9530
	public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
9531
9532
		if (PHP_VERSION_ID < 50300) {
9533
			$this->previous = $previous;
0 ignored issues
show
Bug introduced by
The property previous cannot be accessed from this context as it is declared private in class Exception.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
9534
			parent::__construct($message, $code);
9535
		} else {
9536
			parent::__construct($message, $code, $previous);
9537
		}
9538
9539
		$this->currentFile = $currentFile;
0 ignored issues
show
Documentation Bug introduced by
It seems like $currentFile can also be of type object<Less_FileInfo> or string. However, the property $currentFile is declared as type object<Less_ImportedFile>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
9540
		$this->index = $index;
9541
9542
		$this->genMessage();
9543
	}
9544
9545
9546
	protected function getInput(){
9547
9548
		if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){
9549
			$this->input = file_get_contents( $this->currentFile['filename'] );
9550
		}
9551
	}
9552
9553
9554
9555
	/**
9556
	 * Converts the exception to string
9557
	 *
9558
	 * @return string
9559
	 */
9560
	public function genMessage(){
9561
9562
		if( $this->currentFile && $this->currentFile['filename'] ){
9563
			$this->message .= ' in '.basename($this->currentFile['filename']);
9564
		}
9565
9566
		if( $this->index !== null ){
9567
			$this->getInput();
9568
			if( $this->input ){
9569
				$line = self::getLineNumber();
9570
				$this->message .= ' on line '.$line.', column '.self::getColumn();
9571
9572
				$lines = explode("\n",$this->input);
9573
9574
				$count = count($lines);
9575
				$start_line = max(0, $line-3);
9576
				$last_line = min($count, $start_line+6);
9577
				$num_len = strlen($last_line);
9578
				for( $i = $start_line; $i < $last_line; $i++ ){
9579
					$this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i];
9580
				}
9581
			}
9582
		}
9583
9584
	}
9585
9586
	/**
9587
	 * Returns the line number the error was encountered
9588
	 *
9589
	 * @return integer
9590
	 */
9591
	public function getLineNumber(){
9592
		if( $this->index ){
9593
			// https://bugs.php.net/bug.php?id=49790
9594
			if (ini_get("mbstring.func_overload")) {
9595
				return substr_count(substr($this->input, 0, $this->index), "\n") + 1;
9596
			} else {
9597
				return substr_count($this->input, "\n", 0, $this->index) + 1;
9598
			}
9599
		}
9600
		return 1;
9601
	}
9602
9603
9604
	/**
9605
	 * Returns the column the error was encountered
9606
	 *
9607
	 * @return integer
9608
	 */
9609
	public function getColumn(){
9610
9611
		$part = substr($this->input, 0, $this->index);
9612
		$pos = strrpos($part,"\n");
9613
		return $this->index - $pos;
9614
	}
9615
9616
}
9617
 
9618
9619
/**
9620
 * Chunk Exception
9621
 *
9622
 * @package Less
9623
 * @subpackage exception
9624
 */
9625
class Less_Exception_Chunk extends Less_Exception_Parser{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9626
9627
9628
	protected $parserCurrentIndex = 0;
9629
9630
	protected $emitFrom = 0;
9631
9632
	protected $input_len;
9633
9634
9635
	/**
9636
	 * Constructor
9637
	 *
9638
	 * @param string $input
9639
	 * @param Exception $previous Previous exception
9640
	 * @param integer $index The current parser index
9641
	 * @param Less_FileInfo|string $currentFile The file
9642
	 * @param integer $code The exception code
9643
	 */
9644
	public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
9645
9646
		$this->message = 'ParseError: Unexpected input'; //default message
9647
9648
		$this->index = $index;
9649
9650
		$this->currentFile = $currentFile;
0 ignored issues
show
Documentation Bug introduced by
It seems like $currentFile can also be of type object<Less_FileInfo> or string. However, the property $currentFile is declared as type object<Less_ImportedFile>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
9651
9652
		$this->input = $input;
9653
		$this->input_len = strlen($input);
9654
9655
		$this->Chunks();
9656
		$this->genMessage();
9657
	}
9658
9659
9660
	/**
9661
	 * See less.js chunks()
9662
	 * We don't actually need the chunks
9663
	 *
9664
	 */
9665
	protected function Chunks(){
9666
		$level = 0;
9667
		$parenLevel = 0;
9668
		$lastMultiCommentEndBrace = null;
9669
		$lastOpening = null;
9670
		$lastMultiComment = null;
9671
		$lastParen = null;
9672
9673
		for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){
0 ignored issues
show
Comprehensibility Bug introduced by
Loop incrementor ($this) jumbling with inner loop
Loading history...
9674
			$cc = $this->CharCode($this->parserCurrentIndex);
9675
			if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
9676
				// a-z or whitespace
9677
				continue;
9678
			}
9679
9680
			switch ($cc) {
9681
9682
				// (
9683
				case 40:
9684
					$parenLevel++;
9685
					$lastParen = $this->parserCurrentIndex;
9686
					continue;
9687
9688
				// )
9689
				case 41:
9690
					$parenLevel--;
9691
					if( $parenLevel < 0 ){
9692
						return $this->fail("missing opening `(`");
9693
					}
9694
					continue;
9695
9696
				// ;
9697
				case 59:
9698
					//if (!$parenLevel) { $this->emitChunk();	}
0 ignored issues
show
Unused Code Comprehensibility introduced by
71% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9699
					continue;
9700
9701
				// {
9702
				case 123:
9703
					$level++;
9704
					$lastOpening = $this->parserCurrentIndex;
9705
					continue;
9706
9707
				// }
9708
				case 125:
9709
					$level--;
9710
					if( $level < 0 ){
9711
						return $this->fail("missing opening `{`");
9712
9713
					}
9714
					//if (!$level && !$parenLevel) { $this->emitChunk(); }
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9715
					continue;
9716
				// \
9717
				case 92:
9718
					if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; continue; }
9719
					return $this->fail("unescaped `\\`");
9720
9721
				// ", ' and `
9722
				case 34:
9723
				case 39:
9724
				case 96:
9725
					$matched = 0;
9726
					$currentChunkStartIndex = $this->parserCurrentIndex;
9727
					for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
9728
						$cc2 = $this->CharCode($this->parserCurrentIndex);
9729
						if ($cc2 > 96) { continue; }
9730
						if ($cc2 == $cc) { $matched = 1; break; }
9731
						if ($cc2 == 92) {        // \
9732
							if ($this->parserCurrentIndex == $this->input_len - 1) {
9733
								return $this->fail("unescaped `\\`");
9734
							}
9735
							$this->parserCurrentIndex++;
9736
						}
9737
					}
9738
					if ($matched) { continue; }
9739
					return $this->fail("unmatched `" + chr($cc) + "`", $currentChunkStartIndex);
9740
9741
				// /, check for comment
9742
				case 47:
9743
					if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { continue; }
9744
					$cc2 = $this->CharCode($this->parserCurrentIndex+1);
9745
					if ($cc2 == 47) {
9746
						// //, find lnfeed
9747
						for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
9748
							$cc2 = $this->CharCode($this->parserCurrentIndex);
9749
							if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }
9750
						}
9751
					} else if ($cc2 == 42) {
9752
						// /*, find */
9753
						$lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
9754
						for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
9755
							$cc2 = $this->CharCode($this->parserCurrentIndex);
9756
							if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }
9757
							if ($cc2 != 42) { continue; }
9758
							if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }
9759
						}
9760
						if ($this->parserCurrentIndex == $this->input_len - 1) {
9761
							return $this->fail("missing closing `*/`", $currentChunkStartIndex);
9762
						}
9763
					}
9764
					continue;
9765
9766
				// *, check for unmatched */
9767
				case 42:
9768
					if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {
9769
						return $this->fail("unmatched `/*`");
9770
					}
9771
					continue;
9772
			}
9773
		}
9774
9775
		if( $level !== 0 ){
9776
			if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){
9777
				return $this->fail("missing closing `}` or `*/`", $lastOpening);
9778
			} else {
9779
				return $this->fail("missing closing `}`", $lastOpening);
9780
			}
9781
		} else if ( $parenLevel !== 0 ){
9782
			return $this->fail("missing closing `)`", $lastParen);
9783
		}
9784
9785
9786
		//chunk didn't fail
9787
9788
9789
		//$this->emitChunk(true);
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9790
	}
9791
9792
	public function CharCode($pos){
9793
		return ord($this->input[$pos]);
9794
	}
9795
9796
9797
	public function fail( $msg, $index = null ){
9798
9799
		if( !$index ){
9800
			$this->index = $this->parserCurrentIndex;
9801
		}else{
9802
			$this->index = $index;
9803
		}
9804
		$this->message = 'ParseError: '.$msg;
9805
	}
9806
9807
9808
	/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9809
	function emitChunk( $force = false ){
9810
		$len = $this->parserCurrentIndex - $this->emitFrom;
9811
		if ((($len < 512) && !$force) || !$len) {
9812
			return;
9813
		}
9814
		$chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
9815
		$this->emitFrom = $this->parserCurrentIndex + 1;
9816
	}
9817
	*/
9818
9819
}
9820
 
9821
9822
/**
9823
 * Compiler Exception
9824
 *
9825
 * @package Less
9826
 * @subpackage exception
9827
 */
9828
class Less_Exception_Compiler extends Less_Exception_Parser{
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9829
9830
} 
9831
9832
/**
9833
 * Parser output with source map
9834
 *
9835
 * @package Less
9836
 * @subpackage Output
9837
 */
9838
class Less_Output_Mapped extends Less_Output {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9839
9840
	/**
9841
	 * The source map generator
9842
	 *
9843
	 * @var Less_SourceMap_Generator
9844
	 */
9845
	protected $generator;
9846
9847
	/**
9848
	 * Current line
9849
	 *
9850
	 * @var integer
9851
	 */
9852
	protected $lineNumber = 0;
9853
9854
	/**
9855
	 * Current column
9856
	 *
9857
	 * @var integer
9858
	 */
9859
	protected $column = 0;
9860
9861
	/**
9862
	 * Array of contents map (file and its content)
9863
	 *
9864
	 * @var array
9865
	 */
9866
	protected $contentsMap = array();
9867
9868
	/**
9869
	 * Constructor
9870
	 *
9871
	 * @param array $contentsMap Array of filename to contents map
9872
	 * @param Less_SourceMap_Generator $generator
9873
	 */
9874
	public function __construct(array $contentsMap, $generator){
9875
		$this->contentsMap = $contentsMap;
9876
		$this->generator = $generator;
9877
	}
9878
9879
	/**
9880
	 * Adds a chunk to the stack
9881
	 * The $index for less.php may be different from less.js since less.php does not chunkify inputs
9882
	 *
9883
	 * @param string $chunk
9884
	 * @param string $fileInfo
9885
	 * @param integer $index
9886
	 * @param mixed $mapLines
9887
	 */
9888
	public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
9889
9890
		//ignore adding empty strings
9891
		if( $chunk === '' ){
9892
			return;
9893
		}
9894
9895
9896
		$sourceLines = array();
9897
		$sourceColumns = ' ';
9898
9899
9900
		if( $fileInfo ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $fileInfo of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
9901
9902
			$url = $fileInfo['currentUri'];
9903
9904
			if( isset($this->contentsMap[$url]) ){
9905
				$inputSource = substr($this->contentsMap[$url], 0, $index);
9906
				$sourceLines = explode("\n", $inputSource);
9907
				$sourceColumns = end($sourceLines);
9908
			}else{
9909
				throw new Exception('Filename '.$url.' not in contentsMap');
9910
			}
9911
9912
		}
9913
9914
		$lines = explode("\n", $chunk);
9915
		$columns = end($lines);
9916
9917
		if($fileInfo){
0 ignored issues
show
Bug Best Practice introduced by
The expression $fileInfo of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
9918
9919
			if(!$mapLines){
9920
				$this->generator->addMapping(
9921
						$this->lineNumber + 1,					// generated_line
9922
						$this->column,							// generated_column
9923
						count($sourceLines),					// original_line
9924
						strlen($sourceColumns),					// original_column
9925
						$fileInfo
9926
				);
9927
			}else{
9928
				for($i = 0, $count = count($lines); $i < $count; $i++){
9929
					$this->generator->addMapping(
9930
						$this->lineNumber + $i + 1,				// generated_line
9931
						$i === 0 ? $this->column : 0,			// generated_column
9932
						count($sourceLines) + $i,				// original_line
9933
						$i === 0 ? strlen($sourceColumns) : 0, 	// original_column
9934
						$fileInfo
9935
					);
9936
				}
9937
			}
9938
		}
9939
9940
		if(count($lines) === 1){
9941
			$this->column += strlen($columns);
9942
		}else{
9943
			$this->lineNumber += count($lines) - 1;
9944
			$this->column = strlen($columns);
9945
		}
9946
9947
		// add only chunk
9948
		parent::add($chunk);
9949
	}
9950
9951
} 
9952
9953
/**
9954
 * Encode / Decode Base64 VLQ.
9955
 *
9956
 * @package Less
9957
 * @subpackage SourceMap
9958
 */
9959
class Less_SourceMap_Base64VLQ {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9960
9961
	/**
9962
	 * Shift
9963
	 *
9964
	 * @var integer
9965
	 */
9966
	private $shift = 5;
9967
9968
	/**
9969
	 * Mask
9970
	 *
9971
	 * @var integer
9972
	 */
9973
	private $mask = 0x1F; // == (1 << shift) == 0b00011111
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
9974
9975
	/**
9976
	 * Continuation bit
9977
	 *
9978
	 * @var integer
9979
	 */
9980
	private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
9981
9982
	/**
9983
	 * Char to integer map
9984
	 *
9985
	 * @var array
9986
	 */
9987
	private $charToIntMap = array(
9988
		'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6,
9989
		'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13,
9990
		'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20,
9991
		'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27,
9992
		'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34,
9993
		'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41,
9994
		'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48,
9995
		'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56,
9996
		5 => 57,	6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63,
9997
	);
9998
9999
	/**
10000
	 * Integer to char map
10001
	 *
10002
	 * @var array
10003
	 */
10004
	private $intToCharMap = array(
10005
		0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G',
10006
		7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N',
10007
		14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
10008
		21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b',
10009
		28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i',
10010
		35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p',
10011
		42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w',
10012
		49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
10013
		56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+',
10014
		63 => '/',
10015
	);
10016
10017
	/**
10018
	 * Constructor
10019
	 */
10020
	public function __construct(){
10021
		// I leave it here for future reference
10022
		// foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
10023
		// {
10024
		//	 $this->charToIntMap[$char] = $i;
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
10025
		//	 $this->intToCharMap[$i] = $char;
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
10026
		// }
10027
	}
10028
10029
	/**
10030
	 * Convert from a two-complement value to a value where the sign bit is
10031
	 * is placed in the least significant bit.	For example, as decimals:
10032
	 *	 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
10033
	 *	 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
10034
	 * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
10035
	 * even on a 64 bit machine.
10036
	 * @param string $aValue
10037
	 */
10038
	public function toVLQSigned($aValue){
10039
		return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
10040
	}
10041
10042
	/**
10043
	 * Convert to a two-complement value from a value where the sign bit is
10044
	 * is placed in the least significant bit. For example, as decimals:
10045
	 *	 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
10046
	 *	 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
10047
	 * We assume that the value was generated with a 32 bit machine in mind.
10048
	 * Hence
10049
	 *	 1 becomes -2147483648
10050
	 * even on a 64 bit machine.
10051
	 * @param integer $aValue
10052
	 */
10053
	public function fromVLQSigned($aValue){
10054
		return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
10055
	}
10056
10057
	/**
10058
	 * Return the base 64 VLQ encoded value.
10059
	 *
10060
	 * @param string $aValue The value to encode
10061
	 * @return string The encoded value
10062
	 */
10063
	public function encode($aValue){
10064
		$encoded = '';
10065
		$vlq = $this->toVLQSigned($aValue);
10066
		do
10067
		{
10068
			$digit = $vlq & $this->mask;
10069
			$vlq = $this->zeroFill($vlq, $this->shift);
10070
			if($vlq > 0){
10071
				$digit |= $this->continuationBit;
10072
			}
10073
			$encoded .= $this->base64Encode($digit);
10074
		} while($vlq > 0);
10075
10076
		return $encoded;
10077
	}
10078
10079
	/**
10080
	 * Return the value decoded from base 64 VLQ.
10081
	 *
10082
	 * @param string $encoded The encoded value to decode
10083
	 * @return integer The decoded value
10084
	 */
10085
	public function decode($encoded){
10086
		$vlq = 0;
10087
		$i = 0;
10088
		do
10089
		{
10090
			$digit = $this->base64Decode($encoded[$i]);
10091
			$vlq |= ($digit & $this->mask) << ($i * $this->shift);
10092
			$i++;
10093
		} while($digit & $this->continuationBit);
10094
10095
		return $this->fromVLQSigned($vlq);
10096
	}
10097
10098
	/**
10099
	 * Right shift with zero fill.
10100
	 *
10101
	 * @param integer $a number to shift
10102
	 * @param integer $b number of bits to shift
10103
	 * @return integer
10104
	 */
10105
	public function zeroFill($a, $b){
10106
		return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
10107
	}
10108
10109
	/**
10110
	 * Encode single 6-bit digit as base64.
10111
	 *
10112
	 * @param integer $number
10113
	 * @return string
10114
	 * @throws Exception If the number is invalid
10115
	 */
10116
	public function base64Encode($number){
10117
		if($number < 0 || $number > 63){
10118
			throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
10119
		}
10120
		return $this->intToCharMap[$number];
10121
	}
10122
10123
	/**
10124
	 * Decode single 6-bit digit from base64
10125
	 *
10126
	 * @param string $char
10127
	 * @return number
10128
	 * @throws Exception If the number is invalid
10129
	 */
10130
	public function base64Decode($char){
10131
		if(!array_key_exists($char, $this->charToIntMap)){
10132
			throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
10133
		}
10134
		return $this->charToIntMap[$char];
10135
	}
10136
10137
}
10138
 
10139
10140
/**
10141
 * Source map generator
10142
 *
10143
 * @package Less
10144
 * @subpackage Output
10145
 */
10146
class Less_SourceMap_Generator extends Less_Configurable {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
10147
10148
	/**
10149
	 * What version of source map does the generator generate?
10150
	 */
10151
	const VERSION = 3;
10152
10153
	/**
10154
	 * Array of default options
10155
	 *
10156
	 * @var array
10157
	 */
10158
	protected $defaultOptions = array(
10159
			// an optional source root, useful for relocating source files
10160
			// on a server or removing repeated values in the 'sources' entry.
10161
			// This value is prepended to the individual entries in the 'source' field.
10162
			'sourceRoot'			=> '',
10163
10164
			// an optional name of the generated code that this source map is associated with.
10165
			'sourceMapFilename'		=> null,
10166
10167
			// url of the map
10168
			'sourceMapURL'			=> null,
10169
10170
			// absolute path to a file to write the map to
10171
			'sourceMapWriteTo'		=> null,
10172
10173
			// output source contents?
10174
			'outputSourceFiles'		=> false,
10175
10176
			// base path for filename normalization
10177
			'sourceMapRootpath'		=> '',
10178
10179
			// base path for filename normalization
10180
			'sourceMapBasepath'   => ''
10181
	);
10182
10183
	/**
10184
	 * The base64 VLQ encoder
10185
	 *
10186
	 * @var Less_SourceMap_Base64VLQ
10187
	 */
10188
	protected $encoder;
10189
10190
	/**
10191
	 * Array of mappings
10192
	 *
10193
	 * @var array
10194
	 */
10195
	protected $mappings = array();
10196
10197
	/**
10198
	 * The root node
10199
	 *
10200
	 * @var Less_Tree_Ruleset
10201
	 */
10202
	protected $root;
10203
10204
	/**
10205
	 * Array of contents map
10206
	 *
10207
	 * @var array
10208
	 */
10209
	protected $contentsMap = array();
10210
10211
	/**
10212
	 * File to content map
10213
	 *
10214
	 * @var array
10215
	 */
10216
	protected $sources = array();
10217
	protected $source_keys = array();
10218
10219
	/**
10220
	 * Constructor
10221
	 *
10222
	 * @param Less_Tree_Ruleset $root The root node
10223
	 * @param array $options Array of options
10224
	 */
10225
	public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){
10226
		$this->root = $root;
10227
		$this->contentsMap = $contentsMap;
10228
		$this->encoder = new Less_SourceMap_Base64VLQ();
10229
10230
		$this->SetOptions($options);
10231
		
10232
		$this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true);
10233
		$this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true);
10234
	}
10235
10236
	/**
10237
	 * Generates the CSS
10238
	 *
10239
	 * @return string
10240
	 */
10241
	public function generateCSS(){
10242
		$output = new Less_Output_Mapped($this->contentsMap, $this);
10243
10244
		// catch the output
10245
		$this->root->genCSS($output);
10246
10247
10248
		$sourceMapUrl				= $this->getOption('sourceMapURL');
10249
		$sourceMapFilename			= $this->getOption('sourceMapFilename');
10250
		$sourceMapContent			= $this->generateJson();
10251
		$sourceMapWriteTo			= $this->getOption('sourceMapWriteTo');
10252
10253
		if( !$sourceMapUrl && $sourceMapFilename ){
10254
			$sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
10255
		}
10256
10257
		// write map to a file
10258
		if( $sourceMapWriteTo ){
10259
			$this->saveMap($sourceMapWriteTo, $sourceMapContent);
10260
		}
10261
10262
		// inline the map
10263
		if( !$sourceMapUrl ){
10264
			$sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
10265
		}
10266
10267
		if( $sourceMapUrl ){
10268
			$output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) );
10269
		}
10270
10271
		return $output->toString();
10272
	}
10273
10274
	/**
10275
	 * Saves the source map to a file
10276
	 *
10277
	 * @param string $file The absolute path to a file
10278
	 * @param string $content The content to write
10279
	 * @throws Exception If the file could not be saved
10280
	 */
10281
	protected function saveMap($file, $content){
10282
		$dir = dirname($file);
10283
		// directory does not exist
10284
		if( !is_dir($dir) ){
10285
			// FIXME: create the dir automatically?
10286
			throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
10287
		}
10288
		// FIXME: proper saving, with dir write check!
10289
		if(file_put_contents($file, $content) === false){
10290
			throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
10291
		}
10292
		return true;
10293
	}
10294
10295
	/**
10296
	 * Normalizes the filename
10297
	 *
10298
	 * @param string $filename
10299
	 * @return string
10300
	 */
10301
	protected function normalizeFilename($filename){
10302
10303
		$filename = $this->fixWindowsPath($filename);
10304
10305
		$rootpath = $this->getOption('sourceMapRootpath');
10306
		$basePath = $this->getOption('sourceMapBasepath');
10307
10308
		// "Trim" the 'sourceMapBasepath' from the output filename.
10309
		if (strpos($filename, $basePath) === 0) {
10310
			$filename = substr($filename, strlen($basePath));
10311
		}
10312
10313
		// Remove extra leading path separators.
10314
		if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){
10315
			$filename = substr($filename, 1);
10316
		}
10317
10318
		return $rootpath . $filename;
10319
	}
10320
10321
	/**
10322
	 * Adds a mapping
10323
	 *
10324
	 * @param integer $generatedLine The line number in generated file
10325
	 * @param integer $generatedColumn The column number in generated file
10326
	 * @param integer $originalLine The line number in original file
10327
	 * @param integer $originalColumn The column number in original file
10328
	 * @param string $sourceFile The original source file
0 ignored issues
show
Bug introduced by
There is no parameter named $sourceFile. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
10329
	 */
10330
	public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){
10331
10332
		$this->mappings[] = array(
10333
			'generated_line' => $generatedLine,
10334
			'generated_column' => $generatedColumn,
10335
			'original_line' => $originalLine,
10336
			'original_column' => $originalColumn,
10337
			'source_file' => $fileInfo['currentUri']
10338
		);
10339
10340
		$this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
10341
	}
10342
10343
10344
	/**
10345
	 * Generates the JSON source map
10346
	 *
10347
	 * @return string
10348
	 * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
10349
	 */
10350
	protected function generateJson(){
10351
10352
		$sourceMap = array();
10353
		$mappings = $this->generateMappings();
10354
10355
		// File version (always the first entry in the object) and must be a positive integer.
10356
		$sourceMap['version'] = self::VERSION;
10357
10358
10359
		// An optional name of the generated code that this source map is associated with.
10360
		$file = $this->getOption('sourceMapFilename');
10361
		if( $file ){
10362
			$sourceMap['file'] = $file;
10363
		}
10364
10365
10366
		// An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry.	This value is prepended to the individual entries in the 'source' field.
10367
		$root = $this->getOption('sourceRoot');
10368
		if( $root ){
10369
			$sourceMap['sourceRoot'] = $root;
10370
		}
10371
10372
10373
		// A list of original sources used by the 'mappings' entry.
10374
		$sourceMap['sources'] = array();
10375
		foreach($this->sources as $source_uri => $source_filename){
10376
			$sourceMap['sources'][] = $this->normalizeFilename($source_filename);
10377
		}
10378
10379
10380
		// A list of symbol names used by the 'mappings' entry.
10381
		$sourceMap['names'] = array();
10382
10383
		// A string with the encoded mapping data.
10384
		$sourceMap['mappings'] = $mappings;
10385
10386
		if( $this->getOption('outputSourceFiles') ){
10387
			// An optional list of source content, useful when the 'source' can't be hosted.
10388
			// The contents are listed in the same order as the sources above.
10389
			// 'null' may be used if some original sources should be retrieved by name.
10390
			$sourceMap['sourcesContent'] = $this->getSourcesContent();
10391
		}
10392
10393
		// less.js compat fixes
10394
		if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){
10395
			unset($sourceMap['sourceRoot']);
10396
		}
10397
10398
		return json_encode($sourceMap);
10399
	}
10400
10401
	/**
10402
	 * Returns the sources contents
10403
	 *
10404
	 * @return array|null
10405
	 */
10406
	protected function getSourcesContent(){
10407
		if(empty($this->sources)){
10408
			return;
10409
		}
10410
		$content = array();
10411
		foreach($this->sources as $sourceFile){
10412
			$content[] = file_get_contents($sourceFile);
10413
		}
10414
		return $content;
10415
	}
10416
10417
	/**
10418
	 * Generates the mappings string
10419
	 *
10420
	 * @return string
10421
	 */
10422
	public function generateMappings(){
10423
10424
		if( !count($this->mappings) ){
10425
			return '';
10426
		}
10427
10428
		$this->source_keys = array_flip(array_keys($this->sources));
10429
10430
10431
		// group mappings by generated line number.
10432
		$groupedMap = $groupedMapEncoded = array();
10433
		foreach($this->mappings as $m){
10434
			$groupedMap[$m['generated_line']][] = $m;
10435
		}
10436
		ksort($groupedMap);
10437
10438
		$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
10439
10440
		foreach($groupedMap as $lineNumber => $line_map){
10441
			while(++$lastGeneratedLine < $lineNumber){
10442
				$groupedMapEncoded[] = ';';
10443
			}
10444
10445
			$lineMapEncoded = array();
10446
			$lastGeneratedColumn = 0;
10447
10448
			foreach($line_map as $m){
10449
				$mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
10450
				$lastGeneratedColumn = $m['generated_column'];
10451
10452
				// find the index
10453
				if( $m['source_file'] ){
10454
					$index = $this->findFileIndex($m['source_file']);
10455
					if( $index !== false ){
10456
						$mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
10457
						$lastOriginalIndex = $index;
10458
10459
						// lines are stored 0-based in SourceMap spec version 3
10460
						$mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
10461
						$lastOriginalLine = $m['original_line'] - 1;
10462
10463
						$mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
10464
						$lastOriginalColumn = $m['original_column'];
10465
					}
10466
				}
10467
10468
				$lineMapEncoded[] = $mapEncoded;
10469
			}
10470
10471
			$groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
10472
		}
10473
10474
		return rtrim(implode($groupedMapEncoded), ';');
10475
	}
10476
10477
	/**
10478
	 * Finds the index for the filename
10479
	 *
10480
	 * @param string $filename
10481
	 * @return integer|false
10482
	 */
10483
	protected function findFileIndex($filename){
10484
		return $this->source_keys[$filename];
10485
	}
10486
10487
	/**
10488
	 * fix windows paths
10489
	 * @param  string $path
10490
	 * @return string      
10491
	 */
10492
	public function fixWindowsPath($path, $addEndSlash = false){
10493
		$slash = ($addEndSlash) ? '/' : '';
10494
		if( !empty($path) ){
10495
			$path = str_replace('\\', '/', $path);
10496
			$path = rtrim($path,'/') . $slash;
10497
		}
10498
10499
		return $path;
10500
	}
10501
10502
}