Less_Tree_Mixin_Call::compile()   F
last analyzed

Complexity

Conditions 26
Paths 1384

Size

Total Lines 120
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 120
rs 2
cc 26
eloc 67
nc 1384
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
	private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding
51
52
	/**
53
	 * @var Less_Environment
54
	 */
55
	private $env;
56
57
	protected $rules = array();
58
59
	private static $imports = array();
60
61
	public static $has_extends = false;
62
63
	public static $next_id = 0;
64
65
	/**
66
	 * Filename to contents of all parsed the files
67
	 *
68
	 * @var array
69
	 */
70
	public static $contentsMap = array();
71
72
73
	/**
74
	 * @param Less_Environment|array|null $env
75
	 */
76
	public function __construct( $env = null ){
77
78
		// Top parser on an import tree must be sure there is one "env"
79
		// which will then be passed around by reference.
80
		if( $env instanceof Less_Environment ){
81
			$this->env = $env;
82
		}else{
83
			$this->SetOptions(Less_Parser::$default_options);
84
			$this->Reset( $env );
85
		}
86
87
		// mbstring.func_overload > 1 bugfix
88
		// The encoding value must be set for each source file,
89
		// therefore, to conserve resources and improve the speed of this design is taken here
90
		if (ini_get('mbstring.func_overload')) {			
91
			$this->mb_internal_encoding = ini_get('mbstring.internal_encoding');
92
			@ini_set('mbstring.internal_encoding', 'ascii');
0 ignored issues
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...
93
		}
94
95
	}
96
97
98
	/**
99
	 * Reset the parser state completely
100
	 *
101
	 */
102
	public function Reset( $options = null ){
103
		$this->rules = array();
104
		self::$imports = array();
105
		self::$has_extends = false;
106
		self::$imports = array();
107
		self::$contentsMap = array();
108
109
		$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...
110
		$this->env->Init();
111
112
		//set new options
113
		if( is_array($options) ){
114
			$this->SetOptions(Less_Parser::$default_options);
115
			$this->SetOptions($options);
116
		}
117
	}
118
119
	/**
120
	 * Set one or more compiler options
121
	 *  options: import_dirs, cache_dir, cache_method
122
	 *
123
	 */
124
	public function SetOptions( $options ){
125
		foreach($options as $option => $value){
126
			$this->SetOption($option,$value);
127
		}
128
	}
129
130
	/**
131
	 * Set one compiler option
132
	 *
133
	 */
134
	public function SetOption($option,$value){
135
136
		switch($option){
137
138
			case 'import_dirs':
139
				$this->SetImportDirs($value);
140
			return;
141
142
			case 'cache_dir':
143
				if( is_string($value) ){
144
					Less_Cache::SetCacheDir($value);
145
					Less_Cache::CheckCacheDir();
146
				}
147
			return;
148
		}
149
150
		Less_Parser::$options[$option] = $value;
151
	}
152
153
	/**
154
	 * Registers a new custom function
155
	 *
156
	 * @param  string   $name     function name
157
	 * @param  callable $callback callback
158
	 */
159
	public function registerFunction($name, $callback) {
160
		$this->env->functions[$name] = $callback;
161
	}
162
163
	/**
164
	 * Removed an already registered function
165
	 *
166
	 * @param  string $name function name
167
	 */
168
	public function unregisterFunction($name) {
169
		if( isset($this->env->functions[$name]) )
170
			unset($this->env->functions[$name]);
171
	}
172
173
174
	/**
175
	 * Get the current css buffer
176
	 *
177
	 * @return string
178
	 */
179
	public function getCss(){
180
181
		$precision = ini_get('precision');
182
		@ini_set('precision',16);
183
		$locale = setlocale(LC_NUMERIC, 0);
184
		setlocale(LC_NUMERIC, "C");
185
186
		try {
187
188
	 		$root = new Less_Tree_Ruleset(array(), $this->rules );
189
			$root->root = true;
190
			$root->firstRoot = true;
191
192
193
			$this->PreVisitors($root);
194
195
			self::$has_extends = false;
196
			$evaldRoot = $root->compile($this->env);
197
198
199
200
			$this->PostVisitors($evaldRoot);
201
202
			if( Less_Parser::$options['sourceMap'] ){
203
				$generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
204
				// will also save file
205
				// FIXME: should happen somewhere else?
206
				$css = $generator->generateCSS();
207
			}else{
208
				$css = $evaldRoot->toCSS();
209
			}
210
211
			if( Less_Parser::$options['compress'] ){
212
				$css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css);
213
			}
214
215
		} catch (Exception $exc) {
216
			// Intentional fall-through so we can reset environment
217
		}
218
219
		//reset php settings
220
		@ini_set('precision',$precision);
221
		setlocale(LC_NUMERIC, $locale);
222
223
		// If you previously defined $this->mb_internal_encoding 
224
		// is required to return the encoding as it was before
225
		if ($this->mb_internal_encoding != '') {
226
			@ini_set("mbstring.internal_encoding", $this->mb_internal_encoding);
0 ignored issues
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...
227
			$this->mb_internal_encoding = '';
228
		}
229
230
		// Rethrow exception after we handled resetting the environment
231
		if (!empty($exc)) {
232
			throw $exc;
233
		}
234
235
236
237
		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...
238
	}
239
240
	/**
241
	 * Run pre-compile visitors
242
	 *
243
	 */
244
	private function PreVisitors($root){
245
246
		if( Less_Parser::$options['plugins'] ){
247
			foreach(Less_Parser::$options['plugins'] as $plugin){
248
				if( !empty($plugin->isPreEvalVisitor) ){
249
					$plugin->run($root);
250
				}
251
			}
252
		}
253
	}
254
255
256
	/**
257
	 * Run post-compile visitors
258
	 *
259
	 */
260
	private function PostVisitors($evaldRoot){
261
262
		$visitors = array();
263
		$visitors[] = new Less_Visitor_joinSelector();
264
		if( self::$has_extends ){
265
			$visitors[] = new Less_Visitor_processExtends();
266
		}
267
		$visitors[] = new Less_Visitor_toCSS();
268
269
270
		if( Less_Parser::$options['plugins'] ){
271
			foreach(Less_Parser::$options['plugins'] as $plugin){
272
				if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){
273
					continue;
274
				}
275
276
				if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){
277
					array_unshift( $visitors, $plugin);
278
				}else{
279
					$visitors[] = $plugin;
280
				}
281
			}
282
		}
283
284
285
		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...
286
			$visitors[$i]->run($evaldRoot);
287
		}
288
289
	}
290
291
292
	/**
293
	 * Parse a Less string into css
294
	 *
295
	 * @param string $str The string to convert
296
	 * @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...
297
	 * @return Less_Tree_Ruleset|Less_Parser
298
	 */
299
	public function parse( $str, $file_uri = null ){
300
301
		if( !$file_uri ){
302
			$uri_root = '';
303
			$filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
304
		}else{
305
			$file_uri = self::WinPath($file_uri);
306
			$filename = $file_uri;
307
			$uri_root = dirname($file_uri);
308
		}
309
310
		$previousFileInfo = $this->env->currentFileInfo;
311
		$uri_root = self::WinPath($uri_root);
312
		$this->SetFileInfo($filename, $uri_root);
313
314
		$this->input = $str;
315
		$this->_parse();
316
317
		if( $previousFileInfo ){
318
			$this->env->currentFileInfo = $previousFileInfo;
319
		}
320
321
		return $this;
322
	}
323
324
325
	/**
326
	 * Parse a Less string from a given file
327
	 *
328
	 * @throws Less_Exception_Parser
329
	 * @param string $filename The file to parse
330
	 * @param string $uri_root The url of the file
331
	 * @param bool $returnRoot Indicates whether the return value should be a css string a root node
332
	 * @return Less_Tree_Ruleset|Less_Parser
333
	 */
334
	public function parseFile( $filename, $uri_root = '', $returnRoot = false){
335
336
		if( !file_exists($filename) ){
337
			$this->Error(sprintf('File `%s` not found.', $filename));
338
		}
339
340
341
		// fix uri_root?
342
		// Instead of The mixture of file path for the first argument and directory path for the second argument has bee
343
		if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){
344
			$uri_root = dirname($uri_root);
345
		}
346
347
348
		$previousFileInfo = $this->env->currentFileInfo;
349
350
351
		if( $filename ){
352
			$filename = self::WinPath(realpath($filename));
353
		}
354
		$uri_root = self::WinPath($uri_root);
355
356
		$this->SetFileInfo($filename, $uri_root);
357
358
		self::AddParsedFile($filename);
359
360
		if( $returnRoot ){
361
			$rules = $this->GetRules( $filename );
362
			$return = new Less_Tree_Ruleset(array(), $rules );
363
		}else{
364
			$this->_parse( $filename );
365
			$return = $this;
366
		}
367
368
		if( $previousFileInfo ){
369
			$this->env->currentFileInfo = $previousFileInfo;
370
		}
371
372
		return $return;
373
	}
374
375
376
	/**
377
	 * Allows a user to set variables values
378
	 * @param array $vars
379
	 * @return Less_Parser
380
	 */
381
	public function ModifyVars( $vars ){
382
383
		$this->input = Less_Parser::serializeVars( $vars );
384
		$this->_parse();
385
386
		return $this;
387
	}
388
389
390
	/**
391
	 * @param string $filename
392
	 */
393
	public function SetFileInfo( $filename, $uri_root = ''){
394
395
		$filename = Less_Environment::normalizePath($filename);
396
		$dirname = preg_replace('/[^\/\\\\]*$/','',$filename);
397
398
		if( !empty($uri_root) ){
399
			$uri_root = rtrim($uri_root,'/').'/';
400
		}
401
402
		$currentFileInfo = array();
403
404
		//entry info
405
		if( isset($this->env->currentFileInfo) ){
406
			$currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
407
			$currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
408
			$currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
409
410
		}else{
411
			$currentFileInfo['entryPath'] = $dirname;
412
			$currentFileInfo['entryUri'] = $uri_root;
413
			$currentFileInfo['rootpath'] = $dirname;
414
		}
415
416
		$currentFileInfo['currentDirectory'] = $dirname;
417
		$currentFileInfo['currentUri'] = $uri_root.basename($filename);
418
		$currentFileInfo['filename'] = $filename;
419
		$currentFileInfo['uri_root'] = $uri_root;
420
421
422
		//inherit reference
423
		if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){
424
			$currentFileInfo['reference'] = true;
425
		}
426
427
		$this->env->currentFileInfo = $currentFileInfo;
428
	}
429
430
431
	/**
432
	 * @deprecated 1.5.1.2
433
	 *
434
	 */
435
	public function SetCacheDir( $dir ){
436
437
		if( !file_exists($dir) ){
438
			if( mkdir($dir) ){
439
				return true;
440
			}
441
			throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir);
442
443
		}elseif( !is_dir($dir) ){
444
			throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir);
445
446
		}elseif( !is_writable($dir) ){
447
			throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir);
448
449
		}else{
450
			$dir = self::WinPath($dir);
451
			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...
452
			return true;
453
		}
454
	}
455
456
457
	/**
458
	 * Set a list of directories or callbacks the parser should use for determining import paths
459
	 *
460
	 * @param array $dirs
461
	 */
462
	public function SetImportDirs( $dirs ){
463
		Less_Parser::$options['import_dirs'] = array();
464
465
		foreach($dirs as $path => $uri_root){
466
467
			$path = self::WinPath($path);
468
			if( !empty($path) ){
469
				$path = rtrim($path,'/').'/';
470
			}
471
472
			if ( !is_callable($uri_root) ){
473
				$uri_root = self::WinPath($uri_root);
474
				if( !empty($uri_root) ){
475
					$uri_root = rtrim($uri_root,'/').'/';
476
				}
477
			}
478
479
			Less_Parser::$options['import_dirs'][$path] = $uri_root;
480
		}
481
	}
482
483
	/**
484
	 * @param string $file_path
485
	 */
486
	private function _parse( $file_path = null ){
487
		$this->rules = array_merge($this->rules, $this->GetRules( $file_path ));
488
	}
489
490
491
	/**
492
	 * Return the results of parsePrimary for $file_path
493
	 * Use cache and save cached results if possible
494
	 *
495
	 * @param string|null $file_path
496
	 */
497
	private function GetRules( $file_path ){
498
499
		$this->SetInput($file_path);
500
501
		$cache_file = $this->CacheFile( $file_path );
502
		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...
503
			if( Less_Parser::$options['cache_method'] == 'callback' ){
504
				if( is_callable(Less_Parser::$options['cache_callback_get']) ){
505
					$cache = call_user_func_array(
506
						Less_Parser::$options['cache_callback_get'],
507
						array($this, $file_path, $cache_file)
508
					);
509
510
					if( $cache ){
511
						$this->UnsetInput();
512
						return $cache;
513
					}
514
				}
515
516
			}elseif( file_exists($cache_file) ){
517
				switch(Less_Parser::$options['cache_method']){
518
519
					// Using serialize
520
					// Faster but uses more memory
521
					case 'serialize':
522
						$cache = unserialize(file_get_contents($cache_file));
523
						if( $cache ){
524
							touch($cache_file);
525
							$this->UnsetInput();
526
							return $cache;
527
						}
528
					break;
529
530
531
					// Using generated php code
532
					case 'var_export':
533
					case 'php':
534
					$this->UnsetInput();
535
					return include($cache_file);
536
				}
537
			}
538
		}
539
540
		$rules = $this->parsePrimary();
541
542
		if( $this->pos < $this->input_len ){
543
			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...
544
		}
545
546
		$this->UnsetInput();
547
548
549
		//save the cache
550
		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...
551
			if( Less_Parser::$options['cache_method'] == 'callback' ){
552
				if( is_callable(Less_Parser::$options['cache_callback_set']) ){
553
					call_user_func_array(
554
						Less_Parser::$options['cache_callback_set'],
555
						array($this, $file_path, $cache_file, $rules)
556
					);
557
				}
558
559
			}else{
560
				//msg('write cache file');
561
				switch(Less_Parser::$options['cache_method']){
562
					case 'serialize':
563
						file_put_contents( $cache_file, serialize($rules) );
564
					break;
565
					case 'php':
566
						file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' );
567
					break;
568
					case 'var_export':
569
						//Requires __set_state()
570
						file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' );
571
					break;
572
				}
573
574
				Less_Cache::CleanCache();
575
			}
576
		}
577
578
		return $rules;
579
	}
580
581
582
	/**
583
	 * Set up the input buffer
584
	 *
585
	 */
586
	public function SetInput( $file_path ){
587
588
		if( $file_path ){
589
			$this->input = file_get_contents( $file_path );
590
		}
591
592
		$this->pos = $this->furthest = 0;
593
594
		// Remove potential UTF Byte Order Mark
595
		$this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input);
596
		$this->input_len = strlen($this->input);
597
598
599
		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...
600
			$uri = $this->env->currentFileInfo['currentUri'];
601
			Less_Parser::$contentsMap[$uri] = $this->input;
602
		}
603
604
	}
605
606
607
	/**
608
	 * Free up some memory
609
	 *
610
	 */
611
	public function UnsetInput(){
612
		unset($this->input, $this->pos, $this->input_len, $this->furthest);
613
		$this->saveStack = array();
614
	}
615
616
617
	public function CacheFile( $file_path ){
618
619
		if( $file_path && $this->CacheEnabled() ){
620
621
			$env = get_object_vars($this->env);
622
			unset($env['frames']);
623
624
			$parts = array();
625
			$parts[] = $file_path;
626
			$parts[] = filesize( $file_path );
627
			$parts[] = filemtime( $file_path );
628
			$parts[] = $env;
629
			$parts[] = Less_Version::cache_version;
630
			$parts[] = Less_Parser::$options['cache_method'];
631
			return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache';
632
		}
633
	}
634
635
636
	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...
637
		self::$imports[] = $file;
638
	}
639
640
	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...
641
		return self::$imports;
642
	}
643
644
	/**
645
	 * @param string $file
646
	 */
647
	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...
648
		return in_array($file,self::$imports);
649
	}
650
651
652
	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...
653
		$this->saveStack[] = $this->pos;
654
	}
655
656
	private function restore() {
657
		$this->pos = array_pop($this->saveStack);
658
	}
659
660
	private function forget(){
661
		array_pop($this->saveStack);
662
	}
663
664
665
	private function isWhitespace($offset = 0) {
666
		return preg_match('/\s/',$this->input[ $this->pos + $offset]);
667
	}
668
669
	/**
670
	 * Parse from a token, regexp or string, and move forward if match
671
	 *
672
	 * @param array $toks
673
	 * @return array
674
	 */
675
	private function match($toks){
676
677
		// The match is confirmed, add the match length to `this::pos`,
678
		// and consume any extra white-space characters (' ' || '\n')
679
		// which come after that. The reason for this is that LeSS's
680
		// grammar is mostly white-space insensitive.
681
		//
682
683
		foreach($toks as $tok){
684
685
			$char = $tok[0];
686
687
			if( $char === '/' ){
688
				$match = $this->MatchReg($tok);
689
690
				if( $match ){
691
					return count($match) === 1 ? $match[0] : $match;
692
				}
693
694
			}elseif( $char === '#' ){
695
				$match = $this->MatchChar($tok[1]);
696
697
			}else{
698
				// Non-terminal, match using a function call
699
				$match = $this->$tok();
700
701
			}
702
703
			if( $match ){
704
				return $match;
705
			}
706
		}
707
	}
708
709
	/**
710
	 * @param string[] $toks
711
	 *
712
	 * @return string
713
	 */
714
	private function MatchFuncs($toks){
715
716
		if( $this->pos < $this->input_len ){
717
			foreach($toks as $tok){
718
				$match = $this->$tok();
719
				if( $match ){
720
					return $match;
721
				}
722
			}
723
		}
724
725
	}
726
727
	// Match a single character in the input,
728
	private function MatchChar($tok){
729
		if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){
730
			$this->skipWhitespace(1);
731
			return $tok;
732
		}
733
	}
734
735
	// Match a regexp from the current start point
736
	private function MatchReg($tok){
737
738
		if( preg_match($tok, $this->input, $match, 0, $this->pos) ){
739
			$this->skipWhitespace(strlen($match[0]));
740
			return $match;
741
		}
742
	}
743
744
745
	/**
746
	 * Same as match(), but don't change the state of the parser,
747
	 * just return the match.
748
	 *
749
	 * @param string $tok
750
	 * @return integer
751
	 */
752
	public function PeekReg($tok){
753
		return preg_match($tok, $this->input, $match, 0, $this->pos);
754
	}
755
756
	/**
757
	 * @param string $tok
758
	 */
759
	public function PeekChar($tok){
760
		//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...
761
		return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok );
762
	}
763
764
765
	/**
766
	 * @param integer $length
767
	 */
768
	public function skipWhitespace($length){
769
770
		$this->pos += $length;
771
772
		for(; $this->pos < $this->input_len; $this->pos++ ){
773
			$c = $this->input[$this->pos];
774
775
			if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){
776
				break;
777
			}
778
		}
779
	}
780
781
782
	/**
783
	 * @param string $tok
784
	 * @param string|null $msg
785
	 */
786 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...
787
		$result = $this->match( array($tok) );
788
		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...
789
			$this->Error( $msg	? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
790
		} else {
791
			return $result;
792
		}
793
	}
794
795
	/**
796
	 * @param string $tok
797
	 */
798 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...
799
		$result = $this->MatchChar($tok);
800
		if( !$result ){
801
			$this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
802
		}else{
803
			return $result;
804
		}
805
	}
806
807
	//
808
	// Here in, the parsing rules/functions
809
	//
810
	// The basic structure of the syntax tree generated is as follows:
811
	//
812
	//   Ruleset ->  Rule -> Value -> Expression -> Entity
813
	//
814
	// Here's some LESS code:
815
	//
816
	//	.class {
817
	//	  color: #fff;
818
	//	  border: 1px solid #000;
819
	//	  width: @w + 4px;
820
	//	  > .child {...}
821
	//	}
822
	//
823
	// And here's what the parse tree might look like:
824
	//
825
	//	 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...
826
	//		 Rule ("color",  Value ([Expression [Color #fff]]))
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...
827
	//		 Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
828
	//		 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...
829
	//		 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...
830
	//	 ])
831
	//
832
	//  In general, most rules will try to parse a token with the `$()` function, and if the return
833
	//  value is truly, will return a new node, of the relevant type. Sometimes, we need to check
834
	//  first, before parsing, that's when we use `peek()`.
835
	//
836
837
	//
838
	// The `primary` rule is the *entry* and *exit* point of the parser.
839
	// The rules here can appear at any level of the parse tree.
840
	//
841
	// The recursive nature of the grammar is an interplay between the `block`
842
	// rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
843
	// as represented by this simplified grammar:
844
	//
845
	//	 primary  →  (ruleset | rule)+
846
	//	 ruleset  →  selector+ block
847
	//	 block	→  '{' primary '}'
848
	//
849
	// Only at one point is the primary rule not called from the
850
	// block rule: at the root level.
851
	//
852
	private function parsePrimary(){
853
		$root = array();
854
855
		while( true ){
856
857
			if( $this->pos >= $this->input_len ){
858
				break;
859
			}
860
861
			$node = $this->parseExtend(true);
862
			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...
863
				$root = array_merge($root,$node);
864
				continue;
865
			}
866
867
			//$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...
868
			$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective'));
869
870
			if( $node ){
871
				$root[] = $node;
872
			}elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){
873
				break;
874
			}
875
876
            if( $this->PeekChar('}') ){
877
				break;
878
			}
879
		}
880
881
		return $root;
882
	}
883
884
885
886
	// We create a Comment node for CSS comments `/* */`,
887
	// but keep the LeSS comments `//` silent, by just skipping
888
	// over them.
889
	private function parseComment(){
890
891
		if( $this->input[$this->pos] !== '/' ){
892
			return;
893
		}
894
895
		if( $this->input[$this->pos+1] === '/' ){
896
			$match = $this->MatchReg('/\\G\/\/.*/');
897
			return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo));
898
		}
899
900
		//$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...
901
		$comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors
902
		if( $comment ){
903
			return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo));
904
		}
905
	}
906
907
	private function parseComments(){
908
		$comments = array();
909
910
		while( $this->pos < $this->input_len ){
911
			$comment = $this->parseComment();
912
			if( !$comment ){
913
				break;
914
			}
915
916
			$comments[] = $comment;
917
		}
918
919
		return $comments;
920
	}
921
922
923
924
	//
925
	// A string, which supports escaping " and '
926
	//
927
	//	 "milky way" 'he\'s the one!'
928
	//
929
	private function parseEntitiesQuoted() {
930
		$j = $this->pos;
931
		$e = false;
932
		$index = $this->pos;
933
934
		if( $this->input[$this->pos] === '~' ){
935
			$j++;
936
			$e = true; // Escaped strings
937
		}
938
939
		if( $this->input[$j] != '"' && $this->input[$j] !== "'" ){
940
			return;
941
		}
942
943
		if ($e) {
944
			$this->MatchChar('~');
945
		}
946
947
                // Fix for #124: match escaped newlines
948
                //$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.)*)"|\'((?:[^\'\\\\\r\n]|\\\\.)*)\'/');
949
		$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"|\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/');
950
951
		if( $str ){
952
			$result = $str[0][0] == '"' ? $str[1] : $str[2];
953
			return $this->NewObj5('Less_Tree_Quoted',array($str[0], $result, $e, $index, $this->env->currentFileInfo) );
954
		}
955
		return;
956
	}
957
958
959
	//
960
	// A catch-all word, such as:
961
	//
962
	//	 black border-collapse
963
	//
964
	private function parseEntitiesKeyword(){
965
966
		//$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...
967
		$k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/');
968
		if( $k ){
969
			$k = $k[0];
970
			$color = $this->fromKeyword($k);
971
			if( $color ){
972
				return $color;
973
			}
974
			return $this->NewObj1('Less_Tree_Keyword',$k);
975
		}
976
	}
977
978
	// duplicate of Less_Tree_Color::FromKeyword
979
	private function FromKeyword( $keyword ){
980
		$keyword = strtolower($keyword);
981
982
		if( Less_Colors::hasOwnProperty($keyword) ){
983
			// detect named color
984
			return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1));
985
		}
986
987
		if( $keyword === 'transparent' ){
988
			return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true));
989
		}
990
	}
991
992
	//
993
	// A function call
994
	//
995
	//	 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...
996
	//
997
	// We also try to catch IE's `alpha()`, but let the `alpha` parser
998
	// deal with the details.
999
	//
1000
	// The arguments are parsed with the `entities.arguments` parser.
1001
	//
1002
	private function parseEntitiesCall(){
1003
		$index = $this->pos;
1004
1005
		if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){
1006
			return;
1007
		}
1008
		$name = $name[1];
1009
		$nameLC = strtolower($name);
1010
1011
		if ($nameLC === 'url') {
1012
			return null;
1013
		}
1014
1015
		$this->pos += strlen($name);
1016
1017
		if( $nameLC === 'alpha' ){
1018
			$alpha_ret = $this->parseAlpha();
1019
			if( $alpha_ret ){
1020
				return $alpha_ret;
1021
			}
1022
		}
1023
1024
		$this->MatchChar('('); // Parse the '(' and consume whitespace.
1025
1026
		$args = $this->parseEntitiesArguments();
1027
1028
		if( !$this->MatchChar(')') ){
1029
			return;
1030
		}
1031
1032
		if ($name) {
1033
			return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) );
1034
		}
1035
	}
1036
1037
	/**
1038
	 * Parse a list of arguments
1039
	 *
1040
	 * @return array
1041
	 */
1042
	private function parseEntitiesArguments(){
1043
1044
		$args = array();
1045
		while( true ){
1046
			$arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') );
1047
			if( !$arg ){
1048
				break;
1049
			}
1050
1051
			$args[] = $arg;
1052
			if( !$this->MatchChar(',') ){
1053
				break;
1054
			}
1055
		}
1056
		return $args;
1057
	}
1058
1059
	private function parseEntitiesLiteral(){
1060
		return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') );
1061
	}
1062
1063
	// Assignments are argument entities for calls.
1064
	// They are present in ie filter properties as shown below.
1065
	//
1066
	//	 filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
1067
	//
1068
	private function parseEntitiesAssignment() {
1069
1070
		$key = $this->MatchReg('/\\G\w+(?=\s?=)/');
1071
		if( !$key ){
1072
			return;
1073
		}
1074
1075
		if( !$this->MatchChar('=') ){
1076
			return;
1077
		}
1078
1079
		$value = $this->parseEntity();
1080
		if( $value ){
1081
			return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value));
1082
		}
1083
	}
1084
1085
	//
1086
	// Parse url() tokens
1087
	//
1088
	// We use a specific rule for urls, because they don't really behave like
1089
	// standard function calls. The difference is that the argument doesn't have
1090
	// to be enclosed within a string, so it can't be parsed as an Expression.
1091
	//
1092
	private function parseEntitiesUrl(){
1093
1094
1095
		if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){
1096
			return;
1097
		}
1098
1099
		$value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') );
1100
		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...
1101
			$value = '';
1102
		}
1103
1104
1105
		$this->expectChar(')');
1106
1107
1108
		if( isset($value->value) || $value instanceof Less_Tree_Variable ){
1109
			return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo));
1110
		}
1111
1112
		return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) );
1113
	}
1114
1115
1116
	//
1117
	// A Variable entity, such as `@fink`, in
1118
	//
1119
	//	 width: @fink + 2px
1120
	//
1121
	// We use a different parser for variable definitions,
1122
	// see `parsers.variable`.
1123
	//
1124
	private function parseEntitiesVariable(){
1125
		$index = $this->pos;
1126
		if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) {
1127
			return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo));
1128
		}
1129
	}
1130
1131
1132
	// A variable entity using the protective {} e.g. @{var}
1133
	private function parseEntitiesVariableCurly() {
1134
		$index = $this->pos;
1135
1136
		if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){
1137
			return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo));
1138
		}
1139
	}
1140
1141
	//
1142
	// A Hexadecimal color
1143
	//
1144
	//	 #4F3C2F
1145
	//
1146
	// `rgb` and `hsl` colors are parsed through the `entities.call` parser.
1147
	//
1148
	private function parseEntitiesColor(){
1149
		if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) {
1150
			return $this->NewObj1('Less_Tree_Color',$rgb[1]);
1151
		}
1152
	}
1153
1154
	//
1155
	// A Dimension, that is, a number and a unit
1156
	//
1157
	//	 0.5em 95%
1158
	//
1159
	private function parseEntitiesDimension(){
1160
1161
		$c = @ord($this->input[$this->pos]);
1162
1163
		//Is the first char of the dimension 0-9, '.', '+' or '-'
1164
		if (($c > 57 || $c < 43) || $c === 47 || $c == 44){
1165
			return;
1166
		}
1167
1168
		$value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/');
1169
		if( $value ){
1170
1171
			if( isset($value[2]) ){
1172
				return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2]));
1173
			}
1174
			return $this->NewObj1('Less_Tree_Dimension',$value[1]);
1175
		}
1176
	}
1177
1178
1179
	//
1180
	// A unicode descriptor, as is used in unicode-range
1181
	//
1182
	// 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...
1183
	//
1184
	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...
1185
		$ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/');
1186
		if( $ud ){
1187
			return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]);
1188
		}
1189
	}
1190
1191
1192
	//
1193
	// JavaScript code to be evaluated
1194
	//
1195
	//	 `window.location.href`
1196
	//
1197
	private function parseEntitiesJavascript(){
1198
		$e = false;
1199
		$j = $this->pos;
1200
		if( $this->input[$j] === '~' ){
1201
			$j++;
1202
			$e = true;
1203
		}
1204
		if( $this->input[$j] !== '`' ){
1205
			return;
1206
		}
1207
		if( $e ){
1208
			$this->MatchChar('~');
1209
		}
1210
		$str = $this->MatchReg('/\\G`([^`]*)`/');
1211
		if( $str ){
1212
			return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e));
1213
		}
1214
	}
1215
1216
1217
	//
1218
	// The variable part of a variable definition. Used in the `rule` parser
1219
	//
1220
	//	 @fink:
1221
	//
1222
	private function parseVariable(){
1223
		if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) {
1224
			return $name[1];
1225
		}
1226
	}
1227
1228
1229
	//
1230
	// The variable part of a variable definition. Used in the `rule` parser
1231
	//
1232
	// @fink();
1233
	//
1234
	private function parseRulesetCall(){
1235
1236
		if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){
1237
			return $this->NewObj1('Less_Tree_RulesetCall', $name[1] );
1238
		}
1239
	}
1240
1241
1242
	//
1243
	// extend syntax - used to extend selectors
1244
	//
1245
	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...
1246
1247
		$index = $this->pos;
1248
		$extendList = array();
1249
1250
1251
		if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; }
1252
1253
		do{
1254
			$option = null;
1255
			$elements = array();
1256
			while( true ){
1257
				$option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/');
1258
				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...
1259
				$e = $this->parseElement();
1260
				if( !$e ){ break; }
1261
				$elements[] = $e;
1262
			}
1263
1264
			if( $option ){
1265
				$option = $option[1];
1266
			}
1267
1268
			$extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index ));
1269
1270
		}while( $this->MatchChar(",") );
1271
1272
		$this->expect('/\\G\)/');
1273
1274
		if( $isRule ){
1275
			$this->expect('/\\G;/');
1276
		}
1277
1278
		return $extendList;
1279
	}
1280
1281
1282
	//
1283
	// A Mixin call, with an optional argument list
1284
	//
1285
	//	 #mixins > .square(#fff);
1286
	//	 .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...
1287
	//	 .button;
1288
	//
1289
	// The `while` loop is there because mixins can be
1290
	// namespaced, but we only support the child and descendant
1291
	// selector for now.
1292
	//
1293
	private function parseMixinCall(){
1294
1295
		$char = $this->input[$this->pos];
1296
		if( $char !== '.' && $char !== '#' ){
1297
			return;
1298
		}
1299
1300
		$index = $this->pos;
1301
		$this->save(); // stop us absorbing part of an invalid selector
1302
1303
		$elements = $this->parseMixinCallElements();
1304
1305
		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...
1306
1307
			if( $this->MatchChar('(') ){
1308
				$returned = $this->parseMixinArgs(true);
1309
				$args = $returned['args'];
1310
				$this->expectChar(')');
1311
			}else{
1312
				$args = array();
1313
			}
1314
1315
			$important = $this->parseImportant();
1316
1317
			if( $this->parseEnd() ){
1318
				$this->forget();
1319
				return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important));
1320
			}
1321
		}
1322
1323
		$this->restore();
1324
	}
1325
1326
1327
	private function parseMixinCallElements(){
1328
		$elements = array();
1329
		$c = null;
1330
1331
		while( true ){
1332
			$elemIndex = $this->pos;
1333
			$e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/');
1334
			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...
1335
				break;
1336
			}
1337
			$elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo));
1338
			$c = $this->MatchChar('>');
1339
		}
1340
1341
		return $elements;
1342
	}
1343
1344
1345
1346
	/**
1347
	 * @param boolean $isCall
1348
	 */
1349
	private function parseMixinArgs( $isCall ){
1350
		$expressions = array();
1351
		$argsSemiColon = array();
1352
		$isSemiColonSeperated = null;
1353
		$argsComma = array();
1354
		$expressionContainsNamed = null;
1355
		$name = null;
1356
		$returner = array('args'=>array(), 'variadic'=> false);
1357
1358
		$this->save();
1359
1360
		while( true ){
1361
			if( $isCall ){
1362
				$arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
1363
			} else {
1364
				$this->parseComments();
1365
				if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){
1366
					$returner['variadic'] = true;
1367
					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...
1368
						$isSemiColonSeperated = true;
1369
					}
1370
1371
					if( $isSemiColonSeperated ){
1372
						$argsSemiColon[] = array('variadic'=>true);
1373
					}else{
1374
						$argsComma[] = array('variadic'=>true);
1375
					}
1376
					break;
1377
				}
1378
				$arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') );
1379
			}
1380
1381
			if( !$arg ){
1382
				break;
1383
			}
1384
1385
1386
			$nameLoop = null;
1387
			if( $arg instanceof Less_Tree_Expression ){
1388
				$arg->throwAwayComments();
1389
			}
1390
			$value = $arg;
1391
			$val = null;
1392
1393
			if( $isCall ){
1394
				// Variable
1395
				if( property_exists($arg,'value') && count($arg->value) == 1 ){
1396
					$val = $arg->value[0];
1397
				}
1398
			} else {
1399
				$val = $arg;
1400
			}
1401
1402
1403
			if( $val instanceof Less_Tree_Variable ){
1404
1405
				if( $this->MatchChar(':') ){
1406
					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...
1407
						if( $isSemiColonSeperated ){
1408
							$this->Error('Cannot mix ; and , as delimiter types');
1409
						}
1410
						$expressionContainsNamed = true;
1411
					}
1412
1413
					// we do not support setting a ruleset as a default variable - it doesn't make sense
1414
					// However if we do want to add it, there is nothing blocking it, just don't error
1415
					// and remove isCall dependency below
1416
					$value = null;
1417
					if( $isCall ){
1418
						$value = $this->parseDetachedRuleset();
1419
					}
1420
					if( !$value ){
1421
						$value = $this->parseExpression();
1422
					}
1423
1424
					if( !$value ){
1425
						if( $isCall ){
1426
							$this->Error('could not understand value for named argument');
1427
						} else {
1428
							$this->restore();
1429
							$returner['args'] = array();
1430
							return $returner;
1431
						}
1432
					}
1433
1434
					$nameLoop = ($name = $val->name);
1435
				}elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){
1436
					$returner['variadic'] = true;
1437
					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...
1438
						$isSemiColonSeperated = true;
1439
					}
1440
					if( $isSemiColonSeperated ){
1441
						$argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true);
1442
					}else{
1443
						$argsComma[] = array('name'=> $arg->name, 'variadic' => true);
1444
					}
1445
					break;
1446
				}elseif( !$isCall ){
1447
					$name = $nameLoop = $val->name;
1448
					$value = null;
1449
				}
1450
			}
1451
1452
			if( $value ){
1453
				$expressions[] = $value;
1454
			}
1455
1456
			$argsComma[] = array('name'=>$nameLoop, 'value'=>$value );
1457
1458
			if( $this->MatchChar(',') ){
1459
				continue;
1460
			}
1461
1462
			if( $this->MatchChar(';') || $isSemiColonSeperated ){
1463
1464
				if( $expressionContainsNamed ){
1465
					$this->Error('Cannot mix ; and , as delimiter types');
1466
				}
1467
1468
				$isSemiColonSeperated = true;
1469
1470
				if( count($expressions) > 1 ){
1471
					$value = $this->NewObj1('Less_Tree_Value', $expressions);
1472
				}
1473
				$argsSemiColon[] = array('name'=>$name, 'value'=>$value );
1474
1475
				$name = null;
1476
				$expressions = array();
1477
				$expressionContainsNamed = false;
1478
			}
1479
		}
1480
1481
		$this->forget();
1482
		$returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma);
1483
		return $returner;
1484
	}
1485
1486
1487
1488
	//
1489
	// A Mixin definition, with a list of parameters
1490
	//
1491
	//	 .rounded (@radius: 2px, @color) {
1492
	//		...
1493
	//	 }
1494
	//
1495
	// Until we have a finer grained state-machine, we have to
1496
	// do a look-ahead, to make sure we don't have a mixin call.
1497
	// See the `rule` function for more information.
1498
	//
1499
	// We start by matching `.rounded (`, and then proceed on to
1500
	// the argument list, which has optional default values.
1501
	// We store the parameters in `params`, with a `value` key,
1502
	// if there is a value, such as in the case of `@radius`.
1503
	//
1504
	// Once we've got our params list, and a closing `)`, we parse
1505
	// the `{...}` block.
1506
	//
1507
	private function parseMixinDefinition(){
1508
		$cond = null;
1509
1510
		$char = $this->input[$this->pos];
1511
		if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){
1512
			return;
1513
		}
1514
1515
		$this->save();
1516
1517
		$match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/');
1518
		if( $match ){
1519
			$name = $match[1];
1520
1521
			$argInfo = $this->parseMixinArgs( false );
1522
			$params = $argInfo['args'];
1523
			$variadic = $argInfo['variadic'];
1524
1525
1526
			// .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...
1527
			// looks a bit like a mixin definition..
1528
			// also
1529
			// .mixincall(@a: {rule: set;});
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...
1530
			// so we have to be nice and restore
1531
			if( !$this->MatchChar(')') ){
1532
				$this->furthest = $this->pos;
1533
				$this->restore();
1534
				return;
1535
			}
1536
1537
1538
			$this->parseComments();
1539
1540
			if ($this->MatchReg('/\\Gwhen/')) { // Guard
1541
				$cond = $this->expect('parseConditions', 'Expected conditions');
1542
			}
1543
1544
			$ruleset = $this->parseBlock();
1545
1546
			if( is_array($ruleset) ){
1547
				$this->forget();
1548
				return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic));
1549
			}
1550
1551
			$this->restore();
1552
		}else{
1553
			$this->forget();
1554
		}
1555
	}
1556
1557
	//
1558
	// Entities are the smallest recognized token,
1559
	// and can be found inside a rule's value.
1560
	//
1561
	private function parseEntity(){
1562
1563
		return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') );
1564
	}
1565
1566
	//
1567
	// A Rule terminator. Note that we use `peek()` to check for '}',
1568
	// because the `block` rule will be expecting it, but we still need to make sure
1569
	// it's there, if ';' was omitted.
1570
	//
1571
	private function parseEnd(){
1572
		return $this->MatchChar(';') || $this->PeekChar('}');
1573
	}
1574
1575
	//
1576
	// IE's alpha function
1577
	//
1578
	//	 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...
1579
	//
1580
	private function parseAlpha(){
1581
1582
		if ( ! $this->MatchReg('/\\G\(opacity=/i')) {
1583
			return;
1584
		}
1585
1586
		$value = $this->MatchReg('/\\G[0-9]+/');
1587
		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...
1588
			$value = $value[0];
1589
		}else{
1590
			$value = $this->parseEntitiesVariable();
1591
			if( !$value ){
1592
				return;
1593
			}
1594
		}
1595
1596
		$this->expectChar(')');
1597
		return $this->NewObj1('Less_Tree_Alpha',$value);
1598
	}
1599
1600
1601
	//
1602
	// A Selector Element
1603
	//
1604
	//	 div
1605
	//	 + h1
1606
	//	 #socks
1607
	//	 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...
1608
	//
1609
	// Elements are the building blocks for Selectors,
1610
	// they are made out of a `Combinator` (see combinator rule),
1611
	// and an element name, such as a tag a class, or `*`.
1612
	//
1613
	private function parseElement(){
1614
		$c = $this->parseCombinator();
1615
		$index = $this->pos;
1616
1617
		$e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
1618
			'#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') );
1619
1620
		if( is_null($e) ){
1621
			$this->save();
1622
			if( $this->MatchChar('(') ){
1623
				if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){
1624
					$e = $this->NewObj1('Less_Tree_Paren',$v);
1625
					$this->forget();
1626
				}else{
1627
					$this->restore();
1628
				}
1629
			}else{
1630
				$this->forget();
1631
			}
1632
		}
1633
1634
		if( !is_null($e) ){
1635
			return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo));
1636
		}
1637
	}
1638
1639
	//
1640
	// Combinators combine elements together, in a Selector.
1641
	//
1642
	// Because our parser isn't white-space sensitive, special care
1643
	// has to be taken, when parsing the descendant combinator, ` `,
1644
	// as it's an empty space. We have to check the previous character
1645
	// in the input, to see if it's a ` ` character.
1646
	//
1647
	private function parseCombinator(){
1648
		if( $this->pos < $this->input_len ){
1649
			$c = $this->input[$this->pos];
1650
			if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){
1651
1652
				$this->pos++;
1653
				if( $this->input[$this->pos] === '^' ){
1654
					$c = '^^';
1655
					$this->pos++;
1656
				}
1657
1658
				$this->skipWhitespace(0);
1659
1660
				return $c;
1661
			}
1662
1663
			if( $this->pos > 0 && $this->isWhitespace(-1) ){
1664
				return ' ';
1665
			}
1666
		}
1667
	}
1668
1669
	//
1670
	// A CSS selector (see selector below)
1671
	// with less extensions e.g. the ability to extend and guard
1672
	//
1673
	private function parseLessSelector(){
1674
		return $this->parseSelector(true);
1675
	}
1676
1677
	//
1678
	// A CSS Selector
1679
	//
1680
	//	 .class > div + h1
1681
	//	 li a:hover
1682
	//
1683
	// Selectors are made out of one or more Elements, see above.
1684
	//
1685
	private function parseSelector( $isLess = false ){
1686
		$elements = array();
1687
		$extendList = array();
1688
		$condition = null;
1689
		$when = false;
1690
		$extend = false;
1691
		$e = null;
1692
		$c = null;
1693
		$index = $this->pos;
1694
1695
		while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){
1696
			if( $when ){
1697
				$condition = $this->expect('parseConditions', 'expected condition');
1698
			}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...
1699
				//error("CSS guard can only be used at the end of selector");
1700
			}elseif( $extend ){
1701
				$extendList = array_merge($extendList,$extend);
1702
			}else{
1703
				//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...
1704
					//error("Extend can only be used at the end of selector");
1705
				//}
1706
				if( $this->pos < $this->input_len ){
1707
					$c = $this->input[ $this->pos ];
1708
				}
1709
				$elements[] = $e;
1710
				$e = null;
1711
			}
1712
1713
			if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; }
1714
		}
1715
1716
		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...
1717
			return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo));
1718
		}
1719
		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...
1720
			$this->Error('Extend must be used to extend a selector, it cannot be used on its own');
1721
		}
1722
	}
1723
1724
	private function parseTag(){
1725
		return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*');
1726
	}
1727
1728
	private function parseAttribute(){
1729
1730
		$val = null;
1731
1732
		if( !$this->MatchChar('[') ){
1733
			return;
1734
		}
1735
1736
		$key = $this->parseEntitiesVariableCurly();
1737
		if( !$key ){
1738
			$key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/');
1739
		}
1740
1741
		$op = $this->MatchReg('/\\G[|~*$^]?=/');
1742
		if( $op ){
1743
			$val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') );
1744
		}
1745
1746
		$this->expectChar(']');
1747
1748
		return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val));
1749
	}
1750
1751
	//
1752
	// The `block` rule is used by `ruleset` and `mixin.definition`.
1753
	// It's a wrapper around the `primary` rule, with added `{}`.
1754
	//
1755
	private function parseBlock(){
1756
		if( $this->MatchChar('{') ){
1757
			$content = $this->parsePrimary();
1758
			if( $this->MatchChar('}') ){
1759
				return $content;
1760
			}
1761
		}
1762
	}
1763
1764
	private function parseBlockRuleset(){
1765
		$block = $this->parseBlock();
1766
1767
		if( $block ){
1768
			$block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block));
1769
		}
1770
1771
		return $block;
1772
	}
1773
1774
	private function parseDetachedRuleset(){
1775
		$blockRuleset = $this->parseBlockRuleset();
1776
		if( $blockRuleset ){
1777
			return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset);
1778
		}
1779
	}
1780
1781
	//
1782
	// 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...
1783
	//
1784
	private function parseRuleset(){
1785
		$selectors = array();
1786
1787
		$this->save();
1788
1789
		while( true ){
1790
			$s = $this->parseLessSelector();
1791
			if( !$s ){
1792
				break;
1793
			}
1794
			$selectors[] = $s;
1795
			$this->parseComments();
1796
1797
			if( $s->condition && count($selectors) > 1 ){
1798
				$this->Error('Guards are only currently allowed on a single selector.');
1799
			}
1800
1801
			if( !$this->MatchChar(',') ){
1802
				break;
1803
			}
1804
			if( $s->condition ){
1805
				$this->Error('Guards are only currently allowed on a single selector.');
1806
			}
1807
			$this->parseComments();
1808
		}
1809
1810
1811
		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...
1812
			$rules = $this->parseBlock();
1813
			if( is_array($rules) ){
1814
				$this->forget();
1815
				return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports
1816
			}
1817
		}
1818
1819
		// Backtrack
1820
		$this->furthest = $this->pos;
1821
		$this->restore();
1822
	}
1823
1824
	/**
1825
	 * Custom less.php parse function for finding simple name-value css pairs
1826
	 * ex: width:100px;
1827
	 *
1828
	 */
1829
	private function parseNameValue(){
1830
1831
		$index = $this->pos;
1832
		$this->save();
1833
1834
1835
		//$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
1836
		$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/');
1837
		if( $match ){
1838
1839
			if( $match[4] == '}' ){
1840
				$this->pos = $index + strlen($match[0])-1;
1841
			}
1842
1843
			if( $match[3] ){
1844
				$match[2] .= ' !important';
1845
			}
1846
1847
			return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo));
1848
		}
1849
1850
		$this->restore();
1851
	}
1852
1853
1854
	private function parseRule( $tryAnonymous = null ){
1855
1856
		$merge = false;
1857
		$startOfRule = $this->pos;
1858
1859
		$c = $this->input[$this->pos];
1860
		if( $c === '.' || $c === '#' || $c === '&' ){
1861
			return;
1862
		}
1863
1864
		$this->save();
1865
		$name = $this->MatchFuncs( array('parseVariable','parseRuleProperty'));
1866
1867
		if( $name ){
1868
1869
			$isVariable = is_string($name);
1870
1871
			$value = null;
1872
			if( $isVariable ){
1873
				$value = $this->parseDetachedRuleset();
1874
			}
1875
1876
			$important = null;
1877
			if( !$value ){
1878
1879
				// prefer to try to parse first if its a variable or we are compressing
1880
				// but always fallback on the other one
1881
				//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...
1882
				if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){
1883
					$value = $this->MatchFuncs( array('parseValue','parseAnonymousValue'));
1884
				}else{
1885
					$value = $this->MatchFuncs( array('parseAnonymousValue','parseValue'));
1886
				}
1887
1888
				$important = $this->parseImportant();
1889
1890
				// a name returned by this.ruleProperty() is always an array of the form:
1891
				// [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...
1892
				// where each item is a tree.Keyword or tree.Variable
1893
				if( !$isVariable && is_array($name) ){
1894
					$nm = array_pop($name);
1895
					if( $nm->value ){
1896
						$merge = $nm->value;
1897
					}
1898
				}
1899
			}
1900
1901
1902
			if( $value && $this->parseEnd() ){
1903
				$this->forget();
1904
				return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo));
1905
			}else{
1906
				$this->furthest = $this->pos;
1907
				$this->restore();
1908
				if( $value && !$tryAnonymous ){
1909
					return $this->parseRule(true);
1910
				}
1911
			}
1912
		}else{
1913
			$this->forget();
1914
		}
1915
	}
1916
1917
	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...
1918
1919
		if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){
1920
			$this->pos += strlen($match[1]);
1921
			return $this->NewObj1('Less_Tree_Anonymous',$match[1]);
1922
		}
1923
	}
1924
1925
	//
1926
	// An @import directive
1927
	//
1928
	//	 @import "lib";
1929
	//
1930
	// Depending on our environment, importing is done differently:
1931
	// In the browser, it's an XHR request, in Node, it would be a
1932
	// file-system operation. The function used for importing is
1933
	// stored in `import`, which we pass to the Import constructor.
1934
	//
1935
	private function parseImport(){
1936
1937
		$this->save();
1938
1939
		$dir = $this->MatchReg('/\\G@import?\s+/');
1940
1941
		if( $dir ){
1942
			$options = $this->parseImportOptions();
1943
			$path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl'));
1944
1945
			if( $path ){
1946
				$features = $this->parseMediaFeatures();
1947
				if( $this->MatchChar(';') ){
1948
					if( $features ){
1949
						$features = $this->NewObj1('Less_Tree_Value',$features);
1950
					}
1951
1952
					$this->forget();
1953
					return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo));
1954
				}
1955
			}
1956
		}
1957
1958
		$this->restore();
1959
	}
1960
1961
	private function parseImportOptions(){
1962
1963
		$options = array();
1964
1965
		// list of options, surrounded by parens
1966
		if( !$this->MatchChar('(') ){
1967
			return $options;
1968
		}
1969
		do{
1970
			$optionName = $this->parseImportOption();
1971
			if( $optionName ){
1972
				$value = true;
1973
				switch( $optionName ){
1974
					case "css":
1975
						$optionName = "less";
1976
						$value = false;
1977
					break;
1978
					case "once":
1979
						$optionName = "multiple";
1980
						$value = false;
1981
					break;
1982
				}
1983
				$options[$optionName] = $value;
1984
				if( !$this->MatchChar(',') ){ break; }
1985
			}
1986
		}while( $optionName );
1987
		$this->expectChar(')');
1988
		return $options;
1989
	}
1990
1991
	private function parseImportOption(){
1992
		$opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference)/');
1993
		if( $opt ){
1994
			return $opt[1];
1995
		}
1996
	}
1997
1998
	private function parseMediaFeature() {
1999
		$nodes = array();
2000
2001
		do{
2002
			$e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable'));
2003
			if( $e ){
2004
				$nodes[] = $e;
2005
			} elseif ($this->MatchChar('(')) {
2006
				$p = $this->parseProperty();
2007
				$e = $this->parseValue();
2008
				if ($this->MatchChar(')')) {
2009
					if ($p && $e) {
2010
						$r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true));
2011
						$nodes[] = $this->NewObj1('Less_Tree_Paren',$r);
2012
					} elseif ($e) {
2013
						$nodes[] = $this->NewObj1('Less_Tree_Paren',$e);
2014
					} else {
2015
						return null;
2016
					}
2017
				} else
2018
					return null;
2019
			}
2020
		} while ($e);
2021
2022
		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...
2023
			return $this->NewObj1('Less_Tree_Expression',$nodes);
2024
		}
2025
	}
2026
2027
	private function parseMediaFeatures() {
2028
		$features = array();
2029
2030
		do{
2031
			$e = $this->parseMediaFeature();
2032
			if( $e ){
2033
				$features[] = $e;
2034
				if (!$this->MatchChar(',')) break;
2035
			}else{
2036
				$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...
2037
				if( $e ){
2038
					$features[] = $e;
2039
					if (!$this->MatchChar(',')) break;
2040
				}
2041
			}
2042
		} while ($e);
2043
2044
		return $features ? $features : null;
2045
	}
2046
2047
	private function parseMedia() {
2048
		if( $this->MatchReg('/\\G@media/') ){
2049
			$features = $this->parseMediaFeatures();
2050
			$rules = $this->parseBlock();
2051
2052
			if( is_array($rules) ){
2053
				return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo));
2054
			}
2055
		}
2056
	}
2057
2058
2059
	//
2060
	// A CSS Directive
2061
	//
2062
	// @charset "utf-8";
2063
	//
2064
	private function parseDirective(){
2065
2066
		if( !$this->PeekChar('@') ){
2067
			return;
2068
		}
2069
2070
		$rules = null;
2071
		$index = $this->pos;
2072
		$hasBlock = true;
2073
		$hasIdentifier = false;
2074
		$hasExpression = false;
2075
		$hasUnknown = false;
2076
2077
2078
		$value = $this->MatchFuncs(array('parseImport','parseMedia'));
2079
		if( $value ){
2080
			return $value;
2081
		}
2082
2083
		$this->save();
2084
2085
		$name = $this->MatchReg('/\\G@[a-z-]+/');
2086
2087
		if( !$name ) return;
2088
		$name = $name[0];
2089
2090
2091
		$nonVendorSpecificName = $name;
2092
		$pos = strpos($name,'-', 2);
2093
		if( $name[1] == '-' && $pos > 0 ){
2094
			$nonVendorSpecificName = "@" . substr($name, $pos + 1);
2095
		}
2096
2097
2098
		switch( $nonVendorSpecificName ){
2099
			/*
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...
2100
			case "@font-face":
2101
			case "@viewport":
2102
			case "@top-left":
2103
			case "@top-left-corner":
2104
			case "@top-center":
2105
			case "@top-right":
2106
			case "@top-right-corner":
2107
			case "@bottom-left":
2108
			case "@bottom-left-corner":
2109
			case "@bottom-center":
2110
			case "@bottom-right":
2111
			case "@bottom-right-corner":
2112
			case "@left-top":
2113
			case "@left-middle":
2114
			case "@left-bottom":
2115
			case "@right-top":
2116
			case "@right-middle":
2117
			case "@right-bottom":
2118
			hasBlock = true;
2119
			break;
2120
			*/
2121
			case "@charset":
2122
				$hasIdentifier = true;
2123
				$hasBlock = false;
2124
				break;
2125
			case "@namespace":
2126
				$hasExpression = true;
2127
				$hasBlock = false;
2128
				break;
2129
			case "@keyframes":
2130
				$hasIdentifier = true;
2131
				break;
2132
			case "@host":
2133
			case "@page":
2134
			case "@document":
2135
			case "@supports":
2136
				$hasUnknown = true;
2137
				break;
2138
		}
2139
2140
		if( $hasIdentifier ){
2141
			$value = $this->parseEntity();
2142
			if( !$value ){
2143
				$this->error("expected " . $name . " identifier");
2144
			}
2145
		} else if( $hasExpression ){
2146
			$value = $this->parseExpression();
2147
			if( !$value ){
2148
				$this->error("expected " . $name. " expression");
2149
			}
2150
		} else if ($hasUnknown) {
2151
2152
			$value = $this->MatchReg('/\\G[^{;]+/');
2153
			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...
2154
				$value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0]));
2155
			}
2156
		}
2157
2158
		if( $hasBlock ){
2159
			$rules = $this->parseBlockRuleset();
2160
		}
2161
2162
		if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) {
2163
			$this->forget();
2164
			return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo));
2165
		}
2166
2167
		$this->restore();
2168
	}
2169
2170
2171
	//
2172
	// A Value is a comma-delimited list of Expressions
2173
	//
2174
	//	 font-family: Baskerville, Georgia, serif;
2175
	//
2176
	// In a Rule, a Value represents everything after the `:`,
2177
	// and before the `;`.
2178
	//
2179
	private function parseValue(){
2180
		$expressions = array();
2181
2182
		do{
2183
			$e = $this->parseExpression();
2184
			if( $e ){
2185
				$expressions[] = $e;
2186
				if (! $this->MatchChar(',')) {
2187
					break;
2188
				}
2189
			}
2190
		}while($e);
2191
2192
		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...
2193
			return $this->NewObj1('Less_Tree_Value',$expressions);
2194
		}
2195
	}
2196
2197
	private function parseImportant (){
2198
		if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){
2199
			return ' !important';
2200
		}
2201
	}
2202
2203
	private function parseSub (){
2204
2205
		if( $this->MatchChar('(') ){
2206
			$a = $this->parseAddition();
2207
			if( $a ){
2208
				$this->expectChar(')');
2209
				return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached
2210
			}
2211
		}
2212
	}
2213
2214
2215
	/**
2216
	 * Parses multiplication operation
2217
	 *
2218
	 * @return Less_Tree_Operation|null
2219
	 */
2220
	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...
2221
2222
		$return = $m = $this->parseOperand();
2223
		if( $return ){
2224
			while( true ){
2225
2226
				$isSpaced = $this->isWhitespace( -1 );
2227
2228
				if( $this->PeekReg('/\\G\/[*\/]/') ){
2229
					break;
2230
				}
2231
2232
				$op = $this->MatchChar('/');
2233
				if( !$op ){
2234
					$op = $this->MatchChar('*');
2235
					if( !$op ){
2236
						break;
2237
					}
2238
				}
2239
2240
				$a = $this->parseOperand();
2241
2242
				if(!$a) { break; }
2243
2244
				$m->parensInOp = true;
2245
				$a->parensInOp = true;
2246
				$return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) );
2247
			}
2248
		}
2249
		return $return;
2250
2251
	}
2252
2253
2254
	/**
2255
	 * Parses an addition operation
2256
	 *
2257
	 * @return Less_Tree_Operation|null
2258
	 */
2259
	private function parseAddition (){
2260
2261
		$return = $m = $this->parseMultiplication();
2262
		if( $return ){
2263
			while( true ){
2264
2265
				$isSpaced = $this->isWhitespace( -1 );
2266
2267
				$op = $this->MatchReg('/\\G[-+]\s+/');
2268
				if( $op ){
2269
					$op = $op[0];
2270
				}else{
2271
					if( !$isSpaced ){
2272
						$op = $this->match(array('#+','#-'));
2273
					}
2274
					if( !$op ){
2275
						break;
2276
					}
2277
				}
2278
2279
				$a = $this->parseMultiplication();
2280
				if( !$a ){
2281
					break;
2282
				}
2283
2284
				$m->parensInOp = true;
2285
				$a->parensInOp = true;
2286
				$return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced));
2287
			}
2288
		}
2289
2290
		return $return;
2291
	}
2292
2293
2294
	/**
2295
	 * Parses the conditions
2296
	 *
2297
	 * @return Less_Tree_Condition|null
2298
	 */
2299
	private function parseConditions() {
2300
		$index = $this->pos;
2301
		$return = $a = $this->parseCondition();
2302
		if( $a ){
2303
			while( true ){
2304
				if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') ||  !$this->MatchChar(',') ){
2305
					break;
2306
				}
2307
				$b = $this->parseCondition();
2308
				if( !$b ){
2309
					break;
2310
				}
2311
2312
				$return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index));
2313
			}
2314
			return $return;
2315
		}
2316
	}
2317
2318
	private function parseCondition() {
2319
		$index = $this->pos;
2320
		$negate = false;
2321
		$c = null;
2322
2323
		if ($this->MatchReg('/\\Gnot/')) $negate = true;
2324
		$this->expectChar('(');
2325
		$a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
2326
2327
		if( $a ){
2328
			$op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/');
2329
			if( $op ){
2330
				$b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
2331 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...
2332
					$c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate));
2333
				} else {
2334
					$this->Error('Unexpected expression');
2335
				}
2336 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...
2337
				$k = $this->NewObj1('Less_Tree_Keyword','true');
2338
				$c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate));
2339
			}
2340
			$this->expectChar(')');
2341
			return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c;
2342
		}
2343
	}
2344
2345
	/**
2346
	 * An operand is anything that can be part of an operation,
2347
	 * such as a Color, or a Variable
2348
	 *
2349
	 */
2350
	private function parseOperand (){
2351
2352
		$negate = false;
2353
		$offset = $this->pos+1;
2354
		if( $offset >= $this->input_len ){
2355
			return;
2356
		}
2357
		$char = $this->input[$offset];
2358
		if( $char === '@' || $char === '(' ){
2359
			$negate = $this->MatchChar('-');
2360
		}
2361
2362
		$o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall'));
2363
2364
		if( $negate ){
2365
			$o->parensInOp = true;
2366
			$o = $this->NewObj1('Less_Tree_Negative',$o);
2367
		}
2368
2369
		return $o;
2370
	}
2371
2372
2373
	/**
2374
	 * Expressions either represent mathematical operations,
2375
	 * or white-space delimited Entities.
2376
	 *
2377
	 *	 1px solid black
2378
	 *	 @var * 2
2379
	 *
2380
	 * @return Less_Tree_Expression|null
2381
	 */
2382
	private function parseExpression (){
2383
		$entities = array();
2384
2385
		do{
2386
			$e = $this->MatchFuncs(array('parseAddition','parseEntity'));
2387
			if( $e ){
2388
				$entities[] = $e;
2389
				// operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
2390
				if( !$this->PeekReg('/\\G\/[\/*]/') ){
2391
					$delim = $this->MatchChar('/');
2392
					if( $delim ){
2393
						$entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim);
2394
					}
2395
				}
2396
			}
2397
		}while($e);
2398
2399
		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...
2400
			return $this->NewObj1('Less_Tree_Expression',$entities);
2401
		}
2402
	}
2403
2404
2405
	/**
2406
	 * Parse a property
2407
	 * eg: 'min-width', 'orientation', etc
2408
	 *
2409
	 * @return string
2410
	 */
2411
	private function parseProperty (){
2412
		$name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/');
2413
		if( $name ){
2414
			return $name[1];
2415
		}
2416
	}
2417
2418
2419
	/**
2420
	 * Parse a rule property
2421
	 * eg: 'color', 'width', 'height', etc
2422
	 *
2423
	 * @return string
2424
	 */
2425
	private function parseRuleProperty(){
2426
		$offset = $this->pos;
2427
		$name = array();
2428
		$index = array();
2429
		$length = 0;
2430
2431
2432
		$this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name );
2433
		while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // !
2434
2435
		if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){
2436
			// at last, we have the complete match now. move forward,
2437
			// convert name particles to tree objects and return:
2438
			$this->skipWhitespace($length);
2439
2440
			if( $name[0] === '' ){
2441
				array_shift($name);
2442
				array_shift($index);
2443
			}
2444
			foreach($name as $k => $s ){
2445
				if( !$s || $s[0] !== '@' ){
2446
					$name[$k] = $this->NewObj1('Less_Tree_Keyword',$s);
2447
				}else{
2448
					$name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo));
2449
				}
2450
			}
2451
			return $name;
2452
		}
2453
2454
2455
	}
2456
2457
	private function rulePropertyMatch( $re, &$offset, &$length,  &$index, &$name ){
2458
		preg_match($re, $this->input, $a, 0, $offset);
2459
		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...
2460
			$index[] = $this->pos + $length;
2461
			$length += strlen($a[0]);
2462
			$offset += strlen($a[0]);
2463
			$name[] = $a[1];
2464
			return true;
2465
		}
2466
	}
2467
2468
	public static function serializeVars( $vars ){
2469
		$s = '';
2470
2471
		foreach($vars as $name => $value){
2472
			$s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';');
2473
		}
2474
2475
		return $s;
2476
	}
2477
2478
2479
	/**
2480
	 * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
2481
	 *
2482
	 * @param string $b
2483
	 */
2484
	public static function is_method($a,$b){
2485
		return is_object($a) && method_exists($a,$b);
2486
	}
2487
2488
2489
	/**
2490
	 * Round numbers similarly to javascript
2491
	 * eg: 1.499999 to 1 instead of 2
2492
	 *
2493
	 */
2494
	public static function round($i, $precision = 0){
2495
2496
		$precision = pow(10,$precision);
2497
		$i = $i*$precision;
2498
2499
		$ceil = ceil($i);
2500
		$floor = floor($i);
2501
		if( ($ceil - $i) <= ($i - $floor) ){
2502
			return $ceil/$precision;
2503
		}else{
2504
			return $floor/$precision;
2505
		}
2506
	}
2507
2508
2509
	/**
2510
	 * Create Less_Tree_* objects and optionally generate a cache string
2511
	 *
2512
	 * @return mixed
2513
	 */
2514
	public function NewObj0($class){
2515
		$obj = new $class();
2516
		if( $this->CacheEnabled() ){
2517
			$obj->cache_string = ' new '.$class.'()';
2518
		}
2519
		return $obj;
2520
	}
2521
2522
	public function NewObj1($class, $arg){
2523
		$obj = new $class( $arg );
2524
		if( $this->CacheEnabled() ){
2525
			$obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')';
2526
		}
2527
		return $obj;
2528
	}
2529
2530
	public function NewObj2($class, $args){
2531
		$obj = new $class( $args[0], $args[1] );
2532
		if( $this->CacheEnabled() ){
2533
			$this->ObjCache( $obj, $class, $args);
2534
		}
2535
		return $obj;
2536
	}
2537
2538
	public function NewObj3($class, $args){
2539
		$obj = new $class( $args[0], $args[1], $args[2] );
2540
		if( $this->CacheEnabled() ){
2541
			$this->ObjCache( $obj, $class, $args);
2542
		}
2543
		return $obj;
2544
	}
2545
2546
	public function NewObj4($class, $args){
2547
		$obj = new $class( $args[0], $args[1], $args[2], $args[3] );
2548
		if( $this->CacheEnabled() ){
2549
			$this->ObjCache( $obj, $class, $args);
2550
		}
2551
		return $obj;
2552
	}
2553
2554
	public function NewObj5($class, $args){
2555
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
2556
		if( $this->CacheEnabled() ){
2557
			$this->ObjCache( $obj, $class, $args);
2558
		}
2559
		return $obj;
2560
	}
2561
2562 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...
2563
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
2564
		if( $this->CacheEnabled() ){
2565
			$this->ObjCache( $obj, $class, $args);
2566
		}
2567
		return $obj;
2568
	}
2569
2570 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...
2571
		$obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
2572
		if( $this->CacheEnabled() ){
2573
			$this->ObjCache( $obj, $class, $args);
2574
		}
2575
		return $obj;
2576
	}
2577
2578
	//caching
2579
	public function ObjCache($obj, $class, $args=array()){
2580
		$obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')';
2581
	}
2582
2583
	public function ArgCache($args){
2584
		return implode(',',array_map( array('Less_Parser','ArgString'),$args));
2585
	}
2586
2587
2588
	/**
2589
	 * Convert an argument to a string for use in the parser cache
2590
	 *
2591
	 * @return string
2592
	 */
2593
	public static function ArgString($arg){
2594
2595
		$type = gettype($arg);
2596
2597
		if( $type === 'object'){
2598
			$string = $arg->cache_string;
2599
			unset($arg->cache_string);
2600
			return $string;
2601
2602
		}elseif( $type === 'array' ){
2603
			$string = ' Array(';
2604
			foreach($arg as $k => $a){
2605
				$string .= var_export($k,true).' => '.self::ArgString($a).',';
2606
			}
2607
			return $string . ')';
2608
		}
2609
2610
		return var_export($arg,true);
2611
	}
2612
2613
	public function Error($msg){
2614
		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...
2615
	}
2616
2617
	public static function WinPath($path){
2618
		return str_replace('\\', '/', $path);
2619
	}
2620
2621
	public function CacheEnabled(){
2622
		return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback')));
2623
	}
2624
2625
}
2626
2627
2628
 
2629
2630
/**
2631
 * Utility for css colors
2632
 *
2633
 * @package Less
2634
 * @subpackage color
2635
 */
2636
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...
2637
2638
	public static $colors = array(
2639
			'aliceblue'=>'#f0f8ff',
2640
			'antiquewhite'=>'#faebd7',
2641
			'aqua'=>'#00ffff',
2642
			'aquamarine'=>'#7fffd4',
2643
			'azure'=>'#f0ffff',
2644
			'beige'=>'#f5f5dc',
2645
			'bisque'=>'#ffe4c4',
2646
			'black'=>'#000000',
2647
			'blanchedalmond'=>'#ffebcd',
2648
			'blue'=>'#0000ff',
2649
			'blueviolet'=>'#8a2be2',
2650
			'brown'=>'#a52a2a',
2651
			'burlywood'=>'#deb887',
2652
			'cadetblue'=>'#5f9ea0',
2653
			'chartreuse'=>'#7fff00',
2654
			'chocolate'=>'#d2691e',
2655
			'coral'=>'#ff7f50',
2656
			'cornflowerblue'=>'#6495ed',
2657
			'cornsilk'=>'#fff8dc',
2658
			'crimson'=>'#dc143c',
2659
			'cyan'=>'#00ffff',
2660
			'darkblue'=>'#00008b',
2661
			'darkcyan'=>'#008b8b',
2662
			'darkgoldenrod'=>'#b8860b',
2663
			'darkgray'=>'#a9a9a9',
2664
			'darkgrey'=>'#a9a9a9',
2665
			'darkgreen'=>'#006400',
2666
			'darkkhaki'=>'#bdb76b',
2667
			'darkmagenta'=>'#8b008b',
2668
			'darkolivegreen'=>'#556b2f',
2669
			'darkorange'=>'#ff8c00',
2670
			'darkorchid'=>'#9932cc',
2671
			'darkred'=>'#8b0000',
2672
			'darksalmon'=>'#e9967a',
2673
			'darkseagreen'=>'#8fbc8f',
2674
			'darkslateblue'=>'#483d8b',
2675
			'darkslategray'=>'#2f4f4f',
2676
			'darkslategrey'=>'#2f4f4f',
2677
			'darkturquoise'=>'#00ced1',
2678
			'darkviolet'=>'#9400d3',
2679
			'deeppink'=>'#ff1493',
2680
			'deepskyblue'=>'#00bfff',
2681
			'dimgray'=>'#696969',
2682
			'dimgrey'=>'#696969',
2683
			'dodgerblue'=>'#1e90ff',
2684
			'firebrick'=>'#b22222',
2685
			'floralwhite'=>'#fffaf0',
2686
			'forestgreen'=>'#228b22',
2687
			'fuchsia'=>'#ff00ff',
2688
			'gainsboro'=>'#dcdcdc',
2689
			'ghostwhite'=>'#f8f8ff',
2690
			'gold'=>'#ffd700',
2691
			'goldenrod'=>'#daa520',
2692
			'gray'=>'#808080',
2693
			'grey'=>'#808080',
2694
			'green'=>'#008000',
2695
			'greenyellow'=>'#adff2f',
2696
			'honeydew'=>'#f0fff0',
2697
			'hotpink'=>'#ff69b4',
2698
			'indianred'=>'#cd5c5c',
2699
			'indigo'=>'#4b0082',
2700
			'ivory'=>'#fffff0',
2701
			'khaki'=>'#f0e68c',
2702
			'lavender'=>'#e6e6fa',
2703
			'lavenderblush'=>'#fff0f5',
2704
			'lawngreen'=>'#7cfc00',
2705
			'lemonchiffon'=>'#fffacd',
2706
			'lightblue'=>'#add8e6',
2707
			'lightcoral'=>'#f08080',
2708
			'lightcyan'=>'#e0ffff',
2709
			'lightgoldenrodyellow'=>'#fafad2',
2710
			'lightgray'=>'#d3d3d3',
2711
			'lightgrey'=>'#d3d3d3',
2712
			'lightgreen'=>'#90ee90',
2713
			'lightpink'=>'#ffb6c1',
2714
			'lightsalmon'=>'#ffa07a',
2715
			'lightseagreen'=>'#20b2aa',
2716
			'lightskyblue'=>'#87cefa',
2717
			'lightslategray'=>'#778899',
2718
			'lightslategrey'=>'#778899',
2719
			'lightsteelblue'=>'#b0c4de',
2720
			'lightyellow'=>'#ffffe0',
2721
			'lime'=>'#00ff00',
2722
			'limegreen'=>'#32cd32',
2723
			'linen'=>'#faf0e6',
2724
			'magenta'=>'#ff00ff',
2725
			'maroon'=>'#800000',
2726
			'mediumaquamarine'=>'#66cdaa',
2727
			'mediumblue'=>'#0000cd',
2728
			'mediumorchid'=>'#ba55d3',
2729
			'mediumpurple'=>'#9370d8',
2730
			'mediumseagreen'=>'#3cb371',
2731
			'mediumslateblue'=>'#7b68ee',
2732
			'mediumspringgreen'=>'#00fa9a',
2733
			'mediumturquoise'=>'#48d1cc',
2734
			'mediumvioletred'=>'#c71585',
2735
			'midnightblue'=>'#191970',
2736
			'mintcream'=>'#f5fffa',
2737
			'mistyrose'=>'#ffe4e1',
2738
			'moccasin'=>'#ffe4b5',
2739
			'navajowhite'=>'#ffdead',
2740
			'navy'=>'#000080',
2741
			'oldlace'=>'#fdf5e6',
2742
			'olive'=>'#808000',
2743
			'olivedrab'=>'#6b8e23',
2744
			'orange'=>'#ffa500',
2745
			'orangered'=>'#ff4500',
2746
			'orchid'=>'#da70d6',
2747
			'palegoldenrod'=>'#eee8aa',
2748
			'palegreen'=>'#98fb98',
2749
			'paleturquoise'=>'#afeeee',
2750
			'palevioletred'=>'#d87093',
2751
			'papayawhip'=>'#ffefd5',
2752
			'peachpuff'=>'#ffdab9',
2753
			'peru'=>'#cd853f',
2754
			'pink'=>'#ffc0cb',
2755
			'plum'=>'#dda0dd',
2756
			'powderblue'=>'#b0e0e6',
2757
			'purple'=>'#800080',
2758
			'red'=>'#ff0000',
2759
			'rosybrown'=>'#bc8f8f',
2760
			'royalblue'=>'#4169e1',
2761
			'saddlebrown'=>'#8b4513',
2762
			'salmon'=>'#fa8072',
2763
			'sandybrown'=>'#f4a460',
2764
			'seagreen'=>'#2e8b57',
2765
			'seashell'=>'#fff5ee',
2766
			'sienna'=>'#a0522d',
2767
			'silver'=>'#c0c0c0',
2768
			'skyblue'=>'#87ceeb',
2769
			'slateblue'=>'#6a5acd',
2770
			'slategray'=>'#708090',
2771
			'slategrey'=>'#708090',
2772
			'snow'=>'#fffafa',
2773
			'springgreen'=>'#00ff7f',
2774
			'steelblue'=>'#4682b4',
2775
			'tan'=>'#d2b48c',
2776
			'teal'=>'#008080',
2777
			'thistle'=>'#d8bfd8',
2778
			'tomato'=>'#ff6347',
2779
			'turquoise'=>'#40e0d0',
2780
			'violet'=>'#ee82ee',
2781
			'wheat'=>'#f5deb3',
2782
			'white'=>'#ffffff',
2783
			'whitesmoke'=>'#f5f5f5',
2784
			'yellow'=>'#ffff00',
2785
			'yellowgreen'=>'#9acd32'
2786
		);
2787
2788
	public static function hasOwnProperty($color) {
2789
		return isset(self::$colors[$color]);
2790
	}
2791
2792
2793
	public static function color($color) {
2794
		return self::$colors[$color];
2795
	}
2796
2797
}
2798
 
2799
2800
2801
/**
2802
 * Environment
2803
 *
2804
 * @package Less
2805
 * @subpackage environment
2806
 */
2807
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...
2808
2809
	//public $paths = array();				// option - unmodified - paths to search for imports on
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...
2810
	//public static $files = array();		// list of files that have been imported, used for import-once
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
2811
	//public $rootpath;						// option - rootpath to append to URL's
2812
	//public static $strictImports = null;	// option -
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...
2813
	//public $insecure;						// option - whether to allow imports from insecure ssl hosts
2814
	//public $processImports;				// option - whether to process imports. if false then imports will not be imported
2815
	//public $javascriptEnabled;			// option - whether JavaScript is enabled. if undefined, defaults to true
2816
	//public $useFileCache;					// browser only - whether to use the per file session cache
2817
	public $currentFileInfo;				// information about the current file - for error reporting and importing and making urls relative etc.
2818
2819
	public $importMultiple = false; 		// whether we are currently importing multiple copies
2820
2821
2822
	/**
2823
	 * @var array
2824
	 */
2825
	public $frames = array();
2826
2827
	/**
2828
	 * @var array
2829
	 */
2830
	public $mediaBlocks = array();
2831
2832
	/**
2833
	 * @var array
2834
	 */
2835
	public $mediaPath = array();
2836
2837
	public static $parensStack = 0;
2838
2839
	public static $tabLevel = 0;
2840
2841
	public static $lastRule = false;
2842
2843
	public static $_outputMap;
2844
2845
	public static $mixin_stack = 0;
2846
2847
	/**
2848
	 * @var array
2849
	 */
2850
	public $functions = array();
2851
2852
2853
	public function Init(){
2854
2855
		self::$parensStack = 0;
2856
		self::$tabLevel = 0;
2857
		self::$lastRule = false;
2858
		self::$mixin_stack = 0;
2859
2860
		if( Less_Parser::$options['compress'] ){
2861
2862
			Less_Environment::$_outputMap = array(
2863
				','	=> ',',
2864
				': ' => ':',
2865
				''  => '',
2866
				' ' => ' ',
2867
				':' => ' :',
2868
				'+' => '+',
2869
				'~' => '~',
2870
				'>' => '>',
2871
				'|' => '|',
2872
		        '^' => '^',
2873
		        '^^' => '^^'
2874
			);
2875
2876
		}else{
2877
2878
			Less_Environment::$_outputMap = array(
2879
				','	=> ', ',
2880
				': ' => ': ',
2881
				''  => '',
2882
				' ' => ' ',
2883
				':' => ' :',
2884
				'+' => ' + ',
2885
				'~' => ' ~ ',
2886
				'>' => ' > ',
2887
				'|' => '|',
2888
		        '^' => ' ^ ',
2889
		        '^^' => ' ^^ '
2890
			);
2891
2892
		}
2893
	}
2894
2895
2896
	public function copyEvalEnv($frames = array() ){
2897
		$new_env = new Less_Environment();
2898
		$new_env->frames = $frames;
2899
		return $new_env;
2900
	}
2901
2902
2903
	public static function isMathOn(){
2904
		return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
2905
	}
2906
2907
	public static function isPathRelative($path){
2908
		return !preg_match('/^(?:[a-z-]+:|\/)/',$path);
2909
	}
2910
2911
2912
	/**
2913
	 * Canonicalize a path by resolving references to '/./', '/../'
2914
	 * Does not remove leading "../"
2915
	 * @param string path or url
2916
	 * @return string Canonicalized path
2917
	 *
2918
	 */
2919
	public static function normalizePath($path){
2920
2921
		$segments = explode('/',$path);
2922
		$segments = array_reverse($segments);
2923
2924
		$path = array();
2925
		$path_len = 0;
2926
2927
		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...
2928
			$segment = array_pop($segments);
2929
			switch( $segment ) {
2930
2931
				case '.':
2932
				break;
2933
2934
				case '..':
2935
					if( !$path_len || ( $path[$path_len-1] === '..') ){
2936
						$path[] = $segment;
2937
						$path_len++;
2938
					}else{
2939
						array_pop($path);
2940
						$path_len--;
2941
					}
2942
				break;
2943
2944
				default:
2945
					$path[] = $segment;
2946
					$path_len++;
2947
				break;
2948
			}
2949
		}
2950
2951
		return implode('/',$path);
2952
	}
2953
2954
2955
	public function unshiftFrame($frame){
2956
		array_unshift($this->frames, $frame);
2957
	}
2958
2959
	public function shiftFrame(){
2960
		return array_shift($this->frames);
2961
	}
2962
2963
}
2964
 
2965
2966
/**
2967
 * Builtin functions
2968
 *
2969
 * @package Less
2970
 * @subpackage function
2971
 * @see http://lesscss.org/functions/
2972
 */
2973
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...
2974
2975
	public $env;
2976
	public $currentFileInfo;
2977
2978
	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...
2979
		$this->env = $env;
2980
		$this->currentFileInfo = $currentFileInfo;
2981
	}
2982
2983
	/**
2984
	 * @param string $op
2985
	 */
2986
	public static function operate( $op, $a, $b ){
2987
		switch ($op) {
2988
			case '+': return $a + $b;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
2989
			case '-': return $a - $b;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
2990
			case '*': return $a * $b;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
2991
			case '/': return $a / $b;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
2992
		}
2993
	}
2994
2995
	public static function clamp($val, $max = 1){
2996
		return min( max($val, 0), $max);
2997
	}
2998
2999
	public static function fround( $value ){
3000
3001
		if( $value === 0 ){
3002
			return $value;
3003
		}
3004
3005
		if( Less_Parser::$options['numPrecision'] ){
3006
			$p = pow(10, Less_Parser::$options['numPrecision']);
3007
			return round( $value * $p) / $p;
3008
		}
3009
		return $value;
3010
	}
3011
3012
	public static function number($n){
3013
3014
		if ($n instanceof Less_Tree_Dimension) {
3015
			return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value);
3016
		} else if (is_numeric($n)) {
3017
			return $n;
3018
		} else {
3019
			throw new Less_Exception_Compiler("color functions take numbers as parameters");
3020
		}
3021
	}
3022
3023
	public static function scaled($n, $size = 255 ){
3024
		if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){
3025
			return (float)$n->value * $size / 100;
3026
		} else {
3027
			return Less_Functions::number($n);
3028
		}
3029
	}
3030
3031
	public function rgb ($r = null, $g = null, $b = null){
3032
		if (is_null($r) || is_null($g) || is_null($b)) {
3033
			throw new Less_Exception_Compiler("rgb expects three parameters");
3034
		}
3035
		return $this->rgba($r, $g, $b, 1.0);
3036
	}
3037
3038
	public function rgba($r = null, $g = null, $b = null, $a = null){
3039
		$rgb = array($r, $g, $b);
3040
		$rgb = array_map(array('Less_Functions','scaled'),$rgb);
3041
3042
		$a = self::number($a);
3043
		return new Less_Tree_Color($rgb, $a);
3044
	}
3045
3046
	public function hsl($h, $s, $l){
3047
		return $this->hsla($h, $s, $l, 1.0);
3048
	}
3049
3050
	public function hsla($h, $s, $l, $a){
3051
3052
		$h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int
3053
		$s = self::clamp(self::number($s));
3054
		$l = self::clamp(self::number($l));
3055
		$a = self::clamp(self::number($a));
3056
3057
		$m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
3058
3059
		$m1 = $l * 2 - $m2;
3060
3061
		return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255,
3062
							self::hsla_hue($h, $m1, $m2) * 255,
3063
							self::hsla_hue($h - 1/3, $m1, $m2) * 255,
3064
							$a);
3065
	}
3066
3067
	/**
3068
	 * @param double $h
3069
	 */
3070
	public function hsla_hue($h, $m1, $m2){
3071
		$h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h);
3072
		if	  ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
3073
		else if ($h * 2 < 1) return $m2;
3074
		else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
3075
		else				 return $m1;
3076
	}
3077
3078
	public function hsv($h, $s, $v) {
3079
		return $this->hsva($h, $s, $v, 1.0);
3080
	}
3081
3082
	/**
3083
	 * @param double $a
3084
	 */
3085
	public function hsva($h, $s, $v, $a) {
3086
		$h = ((Less_Functions::number($h) % 360) / 360 ) * 360;
3087
		$s = Less_Functions::number($s);
3088
		$v = Less_Functions::number($v);
3089
		$a = Less_Functions::number($a);
3090
3091
		$i = floor(($h / 60) % 6);
3092
		$f = ($h / 60) - $i;
3093
3094
		$vs = array( $v,
3095
				  $v * (1 - $s),
3096
				  $v * (1 - $f * $s),
3097
				  $v * (1 - (1 - $f) * $s));
3098
3099
		$perm = array(array(0, 3, 1),
3100
					array(2, 0, 1),
3101
					array(1, 0, 3),
3102
					array(1, 2, 0),
3103
					array(3, 1, 0),
3104
					array(0, 1, 2));
3105
3106
		return $this->rgba($vs[$perm[$i][0]] * 255,
3107
						 $vs[$perm[$i][1]] * 255,
3108
						 $vs[$perm[$i][2]] * 255,
3109
						 $a);
3110
	}
3111
3112
	public function hue($color = null){
3113
		if (!$color instanceof Less_Tree_Color) {
3114
			throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3115
		}
3116
3117
		$c = $color->toHSL();
3118
		return new Less_Tree_Dimension(Less_Parser::round($c['h']));
3119
	}
3120
3121 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...
3122
		if (!$color instanceof Less_Tree_Color) {
3123
			throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3124
		}
3125
3126
		$c = $color->toHSL();
3127
		return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
3128
	}
3129
3130 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...
3131
		if (!$color instanceof Less_Tree_Color) {
3132
			throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3133
		}
3134
3135
		$c = $color->toHSL();
3136
		return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
3137
	}
3138
3139
	public function hsvhue( $color = null ){
3140
		if (!$color instanceof Less_Tree_Color) {
3141
			throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3142
		}
3143
3144
		$hsv = $color->toHSV();
3145
		return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) );
3146
	}
3147
3148
3149 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...
3150
		if (!$color instanceof Less_Tree_Color) {
3151
			throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3152
		}
3153
3154
		$hsv = $color->toHSV();
3155
		return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' );
3156
	}
3157
3158 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...
3159
		if (!$color instanceof Less_Tree_Color) {
3160
			throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3161
		}
3162
3163
		$hsv = $color->toHSV();
3164
		return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' );
3165
	}
3166
3167 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...
3168
		if (!$color instanceof Less_Tree_Color) {
3169
			throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3170
		}
3171
3172
		return new Less_Tree_Dimension( $color->rgb[0] );
3173
	}
3174
3175 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...
3176
		if (!$color instanceof Less_Tree_Color) {
3177
			throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3178
		}
3179
3180
		return new Less_Tree_Dimension( $color->rgb[1] );
3181
	}
3182
3183 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...
3184
		if (!$color instanceof Less_Tree_Color) {
3185
			throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3186
		}
3187
3188
		return new Less_Tree_Dimension( $color->rgb[2] );
3189
	}
3190
3191 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...
3192
		if (!$color instanceof Less_Tree_Color) {
3193
			throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3194
		}
3195
3196
		$c = $color->toHSL();
3197
		return new Less_Tree_Dimension($c['a']);
3198
	}
3199
3200 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...
3201
		if (!$color instanceof Less_Tree_Color) {
3202
			throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3203
		}
3204
3205
		return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%');
3206
	}
3207
3208
	public function luminance( $color = null ){
3209
		if (!$color instanceof Less_Tree_Color) {
3210
			throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3211
		}
3212
3213
		$luminance =
3214
			(0.2126 * $color->rgb[0] / 255)
3215
		  + (0.7152 * $color->rgb[1] / 255)
3216
		  + (0.0722 * $color->rgb[2] / 255);
3217
3218
		return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%');
3219
	}
3220
3221
	public function saturate($color = null, $amount = null){
3222
		// filter: saturate(3.2);
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...
3223
		// should be kept as is, so check for color
3224
		if ($color instanceof Less_Tree_Dimension) {
3225
			return null;
3226
		}
3227
3228
		if (!$color instanceof Less_Tree_Color) {
3229
			throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3230
		}
3231
		if (!$amount instanceof Less_Tree_Dimension) {
3232
			throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3233
		}
3234
3235
		$hsl = $color->toHSL();
3236
3237
		$hsl['s'] += $amount->value / 100;
3238
		$hsl['s'] = self::clamp($hsl['s']);
3239
3240
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3241
	}
3242
3243
	/**
3244
	 * @param Less_Tree_Dimension $amount
3245
	 */
3246 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...
3247
		if (!$color instanceof Less_Tree_Color) {
3248
			throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3249
		}
3250
		if (!$amount instanceof Less_Tree_Dimension) {
3251
			throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3252
		}
3253
3254
		$hsl = $color->toHSL();
3255
3256
		$hsl['s'] -= $amount->value / 100;
3257
		$hsl['s'] = self::clamp($hsl['s']);
3258
3259
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3260
	}
3261
3262
3263
3264 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...
3265
		if (!$color instanceof Less_Tree_Color) {
3266
			throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3267
		}
3268
		if (!$amount instanceof Less_Tree_Dimension) {
3269
			throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3270
		}
3271
3272
		$hsl = $color->toHSL();
3273
3274
		$hsl['l'] += $amount->value / 100;
3275
		$hsl['l'] = self::clamp($hsl['l']);
3276
3277
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3278
	}
3279
3280 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...
3281
		if (!$color instanceof Less_Tree_Color) {
3282
			throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3283
		}
3284
		if (!$amount instanceof Less_Tree_Dimension) {
3285
			throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3286
		}
3287
3288
		$hsl = $color->toHSL();
3289
		$hsl['l'] -= $amount->value / 100;
3290
		$hsl['l'] = self::clamp($hsl['l']);
3291
3292
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3293
	}
3294
3295 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...
3296
		if (!$color instanceof Less_Tree_Color) {
3297
			throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3298
		}
3299
		if (!$amount instanceof Less_Tree_Dimension) {
3300
			throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3301
		}
3302
3303
		$hsl = $color->toHSL();
3304
		$hsl['a'] += $amount->value / 100;
3305
		$hsl['a'] = self::clamp($hsl['a']);
3306
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3307
	}
3308
3309 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...
3310
		if (!$color instanceof Less_Tree_Color) {
3311
			throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3312
		}
3313
		if (!$amount instanceof Less_Tree_Dimension) {
3314
			throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3315
		}
3316
3317
		$hsl = $color->toHSL();
3318
		$hsl['a'] -= $amount->value / 100;
3319
		$hsl['a'] = self::clamp($hsl['a']);
3320
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3321
	}
3322
3323 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...
3324
		if (!$color instanceof Less_Tree_Color) {
3325
			throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3326
		}
3327
		if (!$amount instanceof Less_Tree_Dimension) {
3328
			throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3329
		}
3330
3331
		$hsl = $color->toHSL();
3332
3333
		$hsl['a'] = $amount->value / 100;
3334
		$hsl['a'] = self::clamp($hsl['a']);
3335
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3336
	}
3337
3338
3339
3340
	public function spin($color = null, $amount = null){
3341
		if (!$color instanceof Less_Tree_Color) {
3342
			throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3343
		}
3344
		if (!$amount instanceof Less_Tree_Dimension) {
3345
			throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3346
		}
3347
3348
		$hsl = $color->toHSL();
3349
		$hue = fmod($hsl['h'] + $amount->value, 360);
3350
3351
		$hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
3352
3353
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
3354
	}
3355
3356
	//
3357
	// Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
3358
	// http://sass-lang.com
3359
	//
3360
3361
	/**
3362
	 * @param Less_Tree_Color $color1
3363
	 */
3364
	public function mix($color1 = null, $color2 = null, $weight = null){
3365
		if (!$color1 instanceof Less_Tree_Color) {
3366
			throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3367
		}
3368
		if (!$color2 instanceof Less_Tree_Color) {
3369
			throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3370
		}
3371
		if (!$weight) {
3372
			$weight = new Less_Tree_Dimension('50', '%');
3373
		}
3374
		if (!$weight instanceof Less_Tree_Dimension) {
3375
			throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3376
		}
3377
3378
		$p = $weight->value / 100.0;
3379
		$w = $p * 2 - 1;
3380
		$hsl1 = $color1->toHSL();
3381
		$hsl2 = $color2->toHSL();
3382
		$a = $hsl1['a'] - $hsl2['a'];
3383
3384
		$w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
3385
		$w2 = 1 - $w1;
3386
3387
		$rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
3388
					 $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
3389
					 $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2);
3390
3391
		$alpha = $color1->alpha * $p + $color2->alpha * (1 - $p);
3392
3393
		return new Less_Tree_Color($rgb, $alpha);
3394
	}
3395
3396
	public function greyscale($color){
3397
		return $this->desaturate($color, new Less_Tree_Dimension(100,'%'));
3398
	}
3399
3400
3401
	public function contrast( $color, $dark = null, $light = null, $threshold = null){
3402
		// filter: contrast(3.2);
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...
3403
		// should be kept as is, so check for color
3404
		if (!$color instanceof Less_Tree_Color) {
3405
			return null;
3406
		}
3407
		if( !$light ){
3408
			$light = $this->rgba(255, 255, 255, 1.0);
3409
		}
3410
		if( !$dark ){
3411
			$dark = $this->rgba(0, 0, 0, 1.0);
3412
		}
3413
3414 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...
3415
			throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3416
		}
3417 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...
3418
			throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3419
		}
3420
3421
		//Figure out which is actually light and dark!
3422
		if( $dark->luma() > $light->luma() ){
3423
			$t = $light;
3424
			$light = $dark;
3425
			$dark = $t;
3426
		}
3427
		if( !$threshold ){
3428
			$threshold = 0.43;
3429
		} else {
3430
			$threshold = Less_Functions::number($threshold);
3431
		}
3432
3433
		if( $color->luma() < $threshold ){
3434
			return $light;
3435
		} else {
3436
			return $dark;
3437
		}
3438
	}
3439
3440
	public function e ($str){
3441
		if( is_string($str) ){
3442
			return new Less_Tree_Anonymous($str);
3443
		}
3444
		return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
3445
	}
3446
3447
	public function escape ($str){
3448
3449
		$revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$');
3450
3451
		return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
3452
	}
3453
3454
3455
	/**
3456
	 * todo: This function will need some additional work to make it work the same as less.js
3457
	 *
3458
	 */
3459
	public function replace( $string, $pattern, $replacement, $flags = null ){
3460
		$result = $string->value;
3461
3462
		$expr = '/'.str_replace('/','\\/',$pattern->value).'/';
3463
		if( $flags && $flags->value){
3464
			$expr .= self::replace_flags($flags->value);
3465
		}
3466
3467
		$result = preg_replace($expr,$replacement->value,$result);
3468
3469
3470
		if( property_exists($string,'quote') ){
3471
			return new Less_Tree_Quoted( $string->quote, $result, $string->escaped);
3472
		}
3473
		return new Less_Tree_Quoted( '', $result );
3474
	}
3475
3476
	public static function replace_flags($flags){
3477
		$flags = str_split($flags,1);
3478
		$new_flags = '';
3479
3480
		foreach($flags as $flag){
3481
			switch($flag){
3482
				case 'e':
3483
				case 'g':
3484
				break;
3485
3486
				default:
3487
				$new_flags .= $flag;
3488
				break;
3489
			}
3490
		}
3491
3492
		return $new_flags;
3493
	}
3494
3495
	public function _percent(){
3496
		$string = func_get_arg(0);
3497
3498
		$args = func_get_args();
3499
		array_shift($args);
3500
		$result = $string->value;
3501
3502
		foreach($args as $arg){
3503
			if( preg_match('/%[sda]/i',$result, $token) ){
3504
				$token = $token[0];
3505
				$value = stristr($token, 's') ? $arg->value : $arg->toCSS();
3506
				$value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
3507
				$result = preg_replace('/%[sda]/i',$value, $result, 1);
3508
			}
3509
		}
3510
		$result = str_replace('%%', '%', $result);
3511
3512
		return new Less_Tree_Quoted( $string->quote , $result, $string->escaped);
3513
	}
3514
3515
	public function unit( $val, $unit = null) {
3516
		if( !($val instanceof Less_Tree_Dimension) ){
3517
			throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') );
3518
		}
3519
3520
		if( $unit ){
3521
			if( $unit instanceof Less_Tree_Keyword ){
3522
				$unit = $unit->value;
3523
			} else {
3524
				$unit = $unit->toCSS();
3525
			}
3526
		} else {
3527
			$unit = "";
3528
		}
3529
		return new Less_Tree_Dimension($val->value, $unit );
3530
	}
3531
3532
	public function convert($val, $unit){
3533
		return $val->convertTo($unit->value);
3534
	}
3535
3536
	public function round($n, $f = false) {
3537
3538
		$fraction = 0;
3539
		if( $f !== false ){
3540
			$fraction = $f->value;
3541
		}
3542
3543
		return $this->_math('Less_Parser::round',null, $n, $fraction);
3544
	}
3545
3546
	public function pi(){
3547
		return new Less_Tree_Dimension(M_PI);
3548
	}
3549
3550
	public function mod($a, $b) {
3551
		return new Less_Tree_Dimension( $a->value % $b->value, $a->unit);
3552
	}
3553
3554
3555
3556
	public function pow($x, $y) {
3557
		if( is_numeric($x) && is_numeric($y) ){
3558
			$x = new Less_Tree_Dimension($x);
3559
			$y = new Less_Tree_Dimension($y);
3560
		}elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){
3561
			throw new Less_Exception_Compiler('Arguments must be numbers');
3562
		}
3563
3564
		return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit );
3565
	}
3566
3567
	// var mathFunctions = [{name:"ce ...
3568
	public function ceil( $n ){		return $this->_math('ceil', null, $n); }
3569
	public function floor( $n ){	return $this->_math('floor', null, $n); }
3570
	public function sqrt( $n ){		return $this->_math('sqrt', null, $n); }
3571
	public function abs( $n ){		return $this->_math('abs', null, $n); }
3572
3573
	public function tan( $n ){		return $this->_math('tan', '', $n);	}
3574
	public function sin( $n ){		return $this->_math('sin', '', $n);	}
3575
	public function cos( $n ){		return $this->_math('cos', '', $n);	}
3576
3577
	public function atan( $n ){		return $this->_math('atan', 'rad', $n);	}
3578
	public function asin( $n ){		return $this->_math('asin', 'rad', $n);	}
3579
	public function acos( $n ){		return $this->_math('acos', 'rad', $n);	}
3580
3581
	private function _math() {
3582
		$args = func_get_args();
3583
		$fn = array_shift($args);
3584
		$unit = array_shift($args);
3585
3586
		if ($args[0] instanceof Less_Tree_Dimension) {
3587
3588
			if( $unit === null ){
3589
				$unit = $args[0]->unit;
3590
			}else{
3591
				$args[0] = $args[0]->unify();
3592
			}
3593
			$args[0] = (float)$args[0]->value;
3594
			return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit);
3595
		} else if (is_numeric($args[0])) {
3596
			return call_user_func_array($fn,$args);
3597
		} else {
3598
			throw new Less_Exception_Compiler("math functions take numbers as parameters");
3599
		}
3600
	}
3601
3602
	/**
3603
	 * @param boolean $isMin
3604
	 */
3605
	private function _minmax( $isMin, $args ){
3606
3607
		$arg_count = count($args);
3608
3609
		if( $arg_count < 1 ){
3610
			throw new Less_Exception_Compiler( 'one or more arguments required');
3611
		}
3612
3613
		$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...
3614
		$unitClone = null;
3615
		$unitStatic = null;
3616
3617
3618
		$order = array();	// elems only contains original argument values.
3619
		$values = array();	// key is the unit.toString() for unified tree.Dimension values,
3620
							// value is the index into the order array.
3621
3622
3623
		for( $i = 0; $i < $arg_count; $i++ ){
3624
			$current = $args[$i];
3625
			if( !($current instanceof Less_Tree_Dimension) ){
3626
				if( is_array($args[$i]->value) ){
3627
					$args[] = $args[$i]->value;
3628
				}
3629
				continue;
3630
			}
3631
3632
			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...
3633
				$temp = new Less_Tree_Dimension($current->value, $unitClone);
3634
				$currentUnified = $temp->unify();
3635
			}else{
3636
				$currentUnified = $current->unify();
3637
			}
3638
3639
			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...
3640
				$unit = $unitStatic;
3641
			}else{
3642
				$unit = $currentUnified->unit->toString();
3643
			}
3644
3645
			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...
3646
				$unitStatic = $unit;
3647
			}
3648
3649
			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...
3650
				$unitClone = $current->unit->toString();
3651
			}
3652
3653
			if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){
3654
				$j = $values[''];
3655
			}elseif( isset($values[$unit]) ){
3656
				$j = $values[$unit];
3657
			}else{
3658
3659
				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...
3660
					throw new Less_Exception_Compiler( 'incompatible types');
3661
				}
3662
				$values[$unit] = count($order);
3663
				$order[] = $current;
3664
				continue;
3665
			}
3666
3667
3668
			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...
3669
				$temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone);
3670
				$referenceUnified = $temp->unify();
3671
			}else{
3672
				$referenceUnified = $order[$j]->unify();
3673
			}
3674
			if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){
3675
				$order[$j] = $current;
3676
			}
3677
		}
3678
3679
		if( count($order) == 1 ){
3680
			return $order[0];
3681
		}
3682
		$args = array();
3683
		foreach($order as $a){
3684
			$args[] = $a->toCSS($this->env);
3685
		}
3686
		return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')');
3687
	}
3688
3689
	public function min(){
3690
		$args = func_get_args();
3691
		return $this->_minmax( true, $args );
3692
	}
3693
3694
	public function max(){
3695
		$args = func_get_args();
3696
		return $this->_minmax( false, $args );
3697
	}
3698
3699
	public function getunit($n){
3700
		return new Less_Tree_Anonymous($n->unit);
3701
	}
3702
3703 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...
3704
		if (!$color instanceof Less_Tree_Color) {
3705
			throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
3706
		}
3707
3708
		return new Less_Tree_Anonymous($color->toARGB());
3709
	}
3710
3711
	public function percentage($n) {
3712
		return new Less_Tree_Dimension($n->value * 100, '%');
3713
	}
3714
3715
	public function color($n) {
3716
3717
		if( $n instanceof Less_Tree_Quoted ){
3718
			$colorCandidate = $n->value;
3719
			$returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
3720
			if( $returnColor ){
3721
				return $returnColor;
3722
			}
3723
			if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){
3724
				return new Less_Tree_Color(substr($colorCandidate, 1));
3725
			}
3726
			throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
3727
		} else {
3728
			throw new Less_Exception_Compiler("argument must be a string");
3729
		}
3730
	}
3731
3732
3733
	public function iscolor($n) {
3734
		return $this->_isa($n, 'Less_Tree_Color');
3735
	}
3736
3737
	public function isnumber($n) {
3738
		return $this->_isa($n, 'Less_Tree_Dimension');
3739
	}
3740
3741
	public function isstring($n) {
3742
		return $this->_isa($n, 'Less_Tree_Quoted');
3743
	}
3744
3745
	public function iskeyword($n) {
3746
		return $this->_isa($n, 'Less_Tree_Keyword');
3747
	}
3748
3749
	public function isurl($n) {
3750
		return $this->_isa($n, 'Less_Tree_Url');
3751
	}
3752
3753
	public function ispixel($n) {
3754
		return $this->isunit($n, 'px');
3755
	}
3756
3757
	public function ispercentage($n) {
3758
		return $this->isunit($n, '%');
3759
	}
3760
3761
	public function isem($n) {
3762
		return $this->isunit($n, 'em');
3763
	}
3764
3765
	/**
3766
	 * @param string $unit
3767
	 */
3768
	public function isunit( $n, $unit ){
3769
		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');
3770
	}
3771
3772
	/**
3773
	 * @param string $type
3774
	 */
3775
	private function _isa($n, $type) {
3776
		return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
3777
	}
3778
3779
	public function tint($color, $amount) {
3780
		return $this->mix( $this->rgb(255,255,255), $color, $amount);
3781
	}
3782
3783
	public function shade($color, $amount) {
3784
		return $this->mix($this->rgb(0, 0, 0), $color, $amount);
3785
	}
3786
3787
	public function extract($values, $index ){
3788
		$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...
3789
		// handle non-array values as an array of length 1
3790
		// return 'undefined' if index is invalid
3791
		if( property_exists($values,'value') && is_array($values->value) ){
3792
			if( isset($values->value[$index]) ){
3793
				return $values->value[$index];
3794
			}
3795
			return null;
3796
3797
		}elseif( (int)$index === 0 ){
3798
			return $values;
3799
		}
3800
3801
		return null;
3802
	}
3803
3804
	public function length($values){
3805
		$n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1;
3806
		return new Less_Tree_Dimension($n);
3807
	}
3808
3809
	public function datauri($mimetypeNode, $filePathNode = null ) {
3810
3811
		$filePath = ( $filePathNode ? $filePathNode->value : null );
3812
		$mimetype = $mimetypeNode->value;
3813
3814
		$args = 2;
3815
		if( !$filePath ){
3816
			$filePath = $mimetype;
3817
			$args = 1;
3818
		}
3819
3820
		$filePath = str_replace('\\','/',$filePath);
3821
		if( Less_Environment::isPathRelative($filePath) ){
3822
3823
			if( Less_Parser::$options['relativeUrls'] ){
3824
				$temp = $this->currentFileInfo['currentDirectory'];
3825
			} else {
3826
				$temp = $this->currentFileInfo['entryPath'];
3827
			}
3828
3829
			if( !empty($temp) ){
3830
				$filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath);
3831
			}
3832
3833
		}
3834
3835
3836
		// detect the mimetype if not given
3837
		if( $args < 2 ){
3838
3839
			/* incomplete
3840
			$mime = require('mime');
3841
			mimetype = mime.lookup(path);
3842
3843
			// use base 64 unless it's an ASCII or UTF-8 format
3844
			var charset = mime.charsets.lookup(mimetype);
3845
			useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
3846
			if (useBase64) mimetype += ';base64';
3847
			*/
3848
3849
			$mimetype = Less_Mime::lookup($filePath);
3850
3851
			$charset = Less_Mime::charsets_lookup($mimetype);
3852
			$useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8'));
3853
			if( $useBase64 ){ $mimetype .= ';base64'; }
3854
3855
		}else{
3856
			$useBase64 = preg_match('/;base64$/',$mimetype);
3857
		}
3858
3859
3860
		if( file_exists($filePath) ){
3861
			$buf = @file_get_contents($filePath);
3862
		}else{
3863
			$buf = false;
3864
		}
3865
3866
3867
		// IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
3868
		// and the --ieCompat flag is enabled, return a normal url() instead.
3869
		$DATA_URI_MAX_KB = 32;
3870
		$fileSizeInKB = round( strlen($buf) / 1024 );
3871
		if( $fileSizeInKB >= $DATA_URI_MAX_KB ){
3872
			$url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
3873
			return $url->compile($this);
3874
		}
3875
3876
		if( $buf ){
3877
			$buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
3878
			$filePath = '"data:' . $mimetype . ',' . $buf . '"';
3879
		}
3880
3881
		return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) );
3882
	}
3883
3884
	//svg-gradient
3885
	public function svggradient( $direction ){
3886
3887
		$throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
3888
		$arguments = func_get_args();
3889
3890
		if( count($arguments) < 3 ){
3891
			throw new Less_Exception_Compiler( $throw_message );
3892
		}
3893
3894
		$stops = array_slice($arguments,1);
3895
		$gradientType = 'linear';
3896
		$rectangleDimension = 'x="0" y="0" width="1" height="1"';
3897
		$useBase64 = true;
3898
		$directionValue = $direction->toCSS();
3899
3900
3901
		switch( $directionValue ){
3902
			case "to bottom":
3903
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
3904
				break;
3905
			case "to right":
3906
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
3907
				break;
3908
			case "to bottom right":
3909
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
3910
				break;
3911
			case "to top right":
3912
				$gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
3913
				break;
3914
			case "ellipse":
3915
			case "ellipse at center":
3916
				$gradientType = "radial";
3917
				$gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
3918
				$rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
3919
				break;
3920
			default:
3921
				throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" );
3922
		}
3923
3924
		$returner = '<?xml version="1.0" ?>' .
3925
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' .
3926
			'<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
3927
3928
		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...
3929
			if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){
3930
				$color = $stops[$i]->value[0];
3931
				$position = $stops[$i]->value[1];
3932
			}else{
3933
				$color = $stops[$i];
3934
				$position = null;
3935
			}
3936
3937
			if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){
3938
				throw new Less_Exception_Compiler( $throw_message );
3939
			}
3940
			if( $position ){
3941
				$positionValue = $position->toCSS();
3942
			}elseif( $i === 0 ){
3943
				$positionValue = '0%';
3944
			}else{
3945
				$positionValue = '100%';
3946
			}
3947
			$alpha = $color->alpha;
3948
			$returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>';
3949
		}
3950
3951
		$returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>';
3952
3953
3954
		if( $useBase64 ){
3955
			$returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'";
3956
		}else{
3957
			$returner = "'data:image/svg+xml,".$returner."'";
3958
		}
3959
3960
		return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
3961
	}
3962
3963
3964
	/**
3965
	 * Php version of javascript's `encodeURIComponent` function
3966
	 *
3967
	 * @param string $string The string to encode
3968
	 * @return string The encoded string
3969
	 */
3970
	public static function encodeURIComponent($string){
3971
		$revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
3972
		return strtr(rawurlencode($string), $revert);
3973
	}
3974
3975
3976
	// Color Blending
3977
	// ref: http://www.w3.org/TR/compositing-1
3978
3979
	public function colorBlend( $mode, $color1, $color2 ){
3980
		$ab = $color1->alpha;	// backdrop
3981
		$as = $color2->alpha;	// source
3982
		$r = array();			// result
3983
3984
		$ar = $as + $ab * (1 - $as);
3985
		for( $i = 0; $i < 3; $i++ ){
3986
			$cb = $color1->rgb[$i] / 255;
3987
			$cs = $color2->rgb[$i] / 255;
3988
			$cr = call_user_func( $mode, $cb, $cs );
3989
			if( $ar ){
3990
				$cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
3991
			}
3992
			$r[$i] = $cr * 255;
3993
		}
3994
3995
		return new Less_Tree_Color($r, $ar);
3996
	}
3997
3998 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...
3999
		if (!$color1 instanceof Less_Tree_Color) {
4000
			throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4001
		}
4002
		if (!$color2 instanceof Less_Tree_Color) {
4003
			throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4004
		}
4005
4006
		return $this->colorBlend( array($this,'colorBlendMultiply'),  $color1, $color2 );
4007
	}
4008
4009
	private function colorBlendMultiply($cb, $cs){
4010
		return $cb * $cs;
4011
	}
4012
4013 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...
4014
		if (!$color1 instanceof Less_Tree_Color) {
4015
			throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4016
		}
4017
		if (!$color2 instanceof Less_Tree_Color) {
4018
			throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4019
		}
4020
4021
		return $this->colorBlend( array($this,'colorBlendScreen'),  $color1, $color2 );
4022
	}
4023
4024
	private function colorBlendScreen( $cb, $cs){
4025
		return $cb + $cs - $cb * $cs;
4026
	}
4027
4028 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...
4029
		if (!$color1 instanceof Less_Tree_Color) {
4030
			throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4031
		}
4032
		if (!$color2 instanceof Less_Tree_Color) {
4033
			throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4034
		}
4035
4036
		return $this->colorBlend( array($this,'colorBlendOverlay'),  $color1, $color2 );
4037
	}
4038
4039
	private function colorBlendOverlay($cb, $cs ){
4040
		$cb *= 2;
4041
		return ($cb <= 1)
4042
			? $this->colorBlendMultiply($cb, $cs)
4043
			: $this->colorBlendScreen($cb - 1, $cs);
4044
	}
4045
4046 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...
4047
		if (!$color1 instanceof Less_Tree_Color) {
4048
			throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4049
		}
4050
		if (!$color2 instanceof Less_Tree_Color) {
4051
			throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4052
		}
4053
4054
		return $this->colorBlend( array($this,'colorBlendSoftlight'),  $color1, $color2 );
4055
	}
4056
4057
	private function colorBlendSoftlight($cb, $cs ){
4058
		$d = 1;
4059
		$e = $cb;
4060
		if( $cs > 0.5 ){
4061
			$e = 1;
4062
			$d = ($cb > 0.25) ? sqrt($cb)
4063
				: ((16 * $cb - 12) * $cb + 4) * $cb;
4064
		}
4065
		return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
4066
	}
4067
4068 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...
4069
		if (!$color1 instanceof Less_Tree_Color) {
4070
			throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4071
		}
4072
		if (!$color2 instanceof Less_Tree_Color) {
4073
			throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4074
		}
4075
4076
		return $this->colorBlend( array($this,'colorBlendHardlight'),  $color1, $color2 );
4077
	}
4078
4079
	private function colorBlendHardlight( $cb, $cs ){
4080
		return $this->colorBlendOverlay($cs, $cb);
4081
	}
4082
4083 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...
4084
		if (!$color1 instanceof Less_Tree_Color) {
4085
			throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4086
		}
4087
		if (!$color2 instanceof Less_Tree_Color) {
4088
			throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4089
		}
4090
4091
		return $this->colorBlend( array($this,'colorBlendDifference'),  $color1, $color2 );
4092
	}
4093
4094
	private function colorBlendDifference( $cb, $cs ){
4095
		return abs($cb - $cs);
4096
	}
4097
4098 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...
4099
		if (!$color1 instanceof Less_Tree_Color) {
4100
			throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4101
		}
4102
		if (!$color2 instanceof Less_Tree_Color) {
4103
			throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4104
		}
4105
4106
		return $this->colorBlend( array($this,'colorBlendExclusion'),  $color1, $color2 );
4107
	}
4108
4109
	private function colorBlendExclusion( $cb, $cs ){
4110
		return $cb + $cs - 2 * $cb * $cs;
4111
	}
4112
4113 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...
4114
		if (!$color1 instanceof Less_Tree_Color) {
4115
			throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4116
		}
4117
		if (!$color2 instanceof Less_Tree_Color) {
4118
			throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4119
		}
4120
4121
		return $this->colorBlend( array($this,'colorBlendAverage'),  $color1, $color2 );
4122
	}
4123
4124
	// non-w3c functions:
4125
	public function colorBlendAverage($cb, $cs ){
4126
		return ($cb + $cs) / 2;
4127
	}
4128
4129 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...
4130
		if (!$color1 instanceof Less_Tree_Color) {
4131
			throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4132
		}
4133
		if (!$color2 instanceof Less_Tree_Color) {
4134
			throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
4135
		}
4136
4137
		return $this->colorBlend( array($this,'colorBlendNegation'),  $color1, $color2 );
4138
	}
4139
4140
	public function colorBlendNegation($cb, $cs){
4141
		return 1 - abs($cb + $cs - 1);
4142
	}
4143
4144
	// ~ End of Color Blending
4145
4146
}
4147
 
4148
4149
/**
4150
 * Mime lookup
4151
 *
4152
 * @package Less
4153
 * @subpackage node
4154
 */
4155
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...
4156
4157
	// this map is intentionally incomplete
4158
	// if you want more, install 'mime' dep
4159
	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...
4160
	        '.htm' => 'text/html',
4161
	        '.html'=> 'text/html',
4162
	        '.gif' => 'image/gif',
4163
	        '.jpg' => 'image/jpeg',
4164
	        '.jpeg'=> 'image/jpeg',
4165
	        '.png' => 'image/png',
4166
	        '.ttf' => 'application/x-font-ttf',
4167
	        '.otf' => 'application/x-font-otf',
4168
	        '.eot' => 'application/vnd.ms-fontobject',
4169
	        '.woff' => 'application/x-font-woff',
4170
	        '.svg' => 'image/svg+xml',
4171
	        );
4172
4173
	public static function lookup( $filepath ){
4174
		$parts = explode('.',$filepath);
4175
		$ext = '.'.strtolower(array_pop($parts));
4176
4177
		if( !isset(self::$_types[$ext]) ){
4178
			return null;
4179
		}
4180
		return self::$_types[$ext];
4181
	}
4182
4183
	public static function charsets_lookup( $type = null ){
4184
		// assumes all text types are UTF-8
4185
		return $type && preg_match('/^text\//',$type) ? 'UTF-8' : '';
4186
	}
4187
}
4188
 
4189
4190
/**
4191
 * Tree
4192
 *
4193
 * @package Less
4194
 * @subpackage tree
4195
 */
4196
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...
4197
4198
	public $cache_string;
4199
4200
	public function toCSS(){
4201
		$output = new Less_Output();
4202
		$this->genCSS($output);
4203
		return $output->toString();
4204
	}
4205
4206
4207
    /**
4208
     * Generate CSS by adding it to the output object
4209
     *
4210
     * @param Less_Output $output The output
4211
     * @return void
4212
     */
4213
    public function genCSS($output){}
4214
4215
4216
	/**
4217
	 * @param Less_Tree_Ruleset[] $rules
4218
	 */
4219
	public static function outputRuleset( $output, $rules ){
4220
4221
		$ruleCnt = count($rules);
4222
		Less_Environment::$tabLevel++;
4223
4224
4225
		// Compressed
4226
		if( Less_Parser::$options['compress'] ){
4227
			$output->add('{');
4228
			for( $i = 0; $i < $ruleCnt; $i++ ){
4229
				$rules[$i]->genCSS( $output );
4230
			}
4231
4232
			$output->add( '}' );
4233
			Less_Environment::$tabLevel--;
4234
			return;
4235
		}
4236
4237
4238
		// Non-compressed
4239
		$tabSetStr = "\n".str_repeat( '  ' , Less_Environment::$tabLevel-1 );
4240
		$tabRuleStr = $tabSetStr.'  ';
4241
4242
		$output->add( " {" );
4243
		for($i = 0; $i < $ruleCnt; $i++ ){
4244
			$output->add( $tabRuleStr );
4245
			$rules[$i]->genCSS( $output );
4246
		}
4247
		Less_Environment::$tabLevel--;
4248
		$output->add( $tabSetStr.'}' );
4249
4250
	}
4251
4252
	public function accept($visitor){}
4253
4254
4255
	public static function ReferencedArray($rules){
4256
		foreach($rules as $rule){
4257
			if( method_exists($rule, 'markReferenced') ){
4258
				$rule->markReferenced();
4259
			}
4260
		}
4261
	}
4262
4263
4264
	/**
4265
	 * Requires php 5.3+
4266
	 */
4267
	public static function __set_state($args){
4268
4269
		$class = get_called_class();
4270
		$obj = new $class(null,null,null,null);
4271
		foreach($args as $key => $val){
4272
			$obj->$key = $val;
4273
		}
4274
		return $obj;
4275
	}
4276
4277
} 
4278
4279
/**
4280
 * Parser output
4281
 *
4282
 * @package Less
4283
 * @subpackage output
4284
 */
4285
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...
4286
4287
	/**
4288
	 * Output holder
4289
	 *
4290
	 * @var string
4291
	 */
4292
	protected $strs = array();
4293
4294
	/**
4295
	 * Adds a chunk to the stack
4296
	 *
4297
	 * @param string $chunk The chunk to output
4298
	 * @param Less_FileInfo $fileInfo The file information
4299
	 * @param integer $index The index
4300
	 * @param mixed $mapLines
4301
	 */
4302
	public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
4303
		$this->strs[] = $chunk;
4304
	}
4305
4306
	/**
4307
	 * Is the output empty?
4308
	 *
4309
	 * @return boolean
4310
	 */
4311
	public function isEmpty(){
4312
		return count($this->strs) === 0;
4313
	}
4314
4315
4316
	/**
4317
	 * Converts the output to string
4318
	 *
4319
	 * @return string
4320
	 */
4321
	public function toString(){
4322
		return implode('',$this->strs);
4323
	}
4324
4325
} 
4326
4327
/**
4328
 * Visitor
4329
 *
4330
 * @package Less
4331
 * @subpackage visitor
4332
 */
4333
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...
4334
4335
	protected $methods = array();
4336
	protected $_visitFnCache = array();
4337
4338
	public function __construct(){
4339
		$this->_visitFnCache = get_class_methods(get_class($this));
4340
		$this->_visitFnCache = array_flip($this->_visitFnCache);
4341
	}
4342
4343
	public function visitObj( $node ){
4344
4345
		$funcName = 'visit'.$node->type;
4346
		if( isset($this->_visitFnCache[$funcName]) ){
4347
4348
			$visitDeeper = true;
4349
			$this->$funcName( $node, $visitDeeper );
4350
4351
			if( $visitDeeper ){
4352
				$node->accept($this);
4353
			}
4354
4355
			$funcName = $funcName . "Out";
4356
			if( isset($this->_visitFnCache[$funcName]) ){
4357
				$this->$funcName( $node );
4358
			}
4359
4360
		}else{
4361
			$node->accept($this);
4362
		}
4363
4364
		return $node;
4365
	}
4366
4367
	public function visitArray( $nodes ){
4368
4369
		array_map( array($this,'visitObj'), $nodes);
4370
		return $nodes;
4371
	}
4372
}
4373
4374
 
4375
4376
/**
4377
 * Replacing Visitor
4378
 *
4379
 * @package Less
4380
 * @subpackage visitor
4381
 */
4382
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...
4383
4384
	public function visitObj( $node ){
4385
4386
		$funcName = 'visit'.$node->type;
4387
		if( isset($this->_visitFnCache[$funcName]) ){
4388
4389
			$visitDeeper = true;
4390
			$node = $this->$funcName( $node, $visitDeeper );
4391
4392
			if( $node ){
4393
				if( $visitDeeper && is_object($node) ){
4394
					$node->accept($this);
4395
				}
4396
4397
				$funcName = $funcName . "Out";
4398
				if( isset($this->_visitFnCache[$funcName]) ){
4399
					$this->$funcName( $node );
4400
				}
4401
			}
4402
4403
		}else{
4404
			$node->accept($this);
4405
		}
4406
4407
		return $node;
4408
	}
4409
4410
	public function visitArray( $nodes ){
4411
4412
		$newNodes = array();
4413
		foreach($nodes as $node){
4414
			$evald = $this->visitObj($node);
4415
			if( $evald ){
4416
				if( is_array($evald) ){
4417
					self::flatten($evald,$newNodes);
4418
				}else{
4419
					$newNodes[] = $evald;
4420
				}
4421
			}
4422
		}
4423
		return $newNodes;
4424
	}
4425
4426
	public function flatten( $arr, &$out ){
4427
4428
		foreach($arr as $item){
4429
			if( !is_array($item) ){
4430
				$out[] = $item;
4431
				continue;
4432
			}
4433
4434
			foreach($item as $nestedItem){
4435
				if( is_array($nestedItem) ){
4436
					self::flatten( $nestedItem, $out);
4437
				}else{
4438
					$out[] = $nestedItem;
4439
				}
4440
			}
4441
		}
4442
4443
		return $out;
4444
	}
4445
4446
}
4447
4448
4449
 
4450
4451
/**
4452
 * Configurable
4453
 *
4454
 * @package Less
4455
 * @subpackage Core
4456
 */
4457
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...
4458
4459
	/**
4460
	 * Array of options
4461
	 *
4462
	 * @var array
4463
	 */
4464
	protected $options = array();
4465
4466
	/**
4467
	 * Array of default options
4468
	 *
4469
	 * @var array
4470
	 */
4471
	protected $defaultOptions = array();
4472
4473
4474
	/**
4475
	 * Set options
4476
	 *
4477
	 * If $options is an object it will be converted into an array by called
4478
	 * it's toArray method.
4479
	 *
4480
	 * @throws Exception
4481
	 * @param array|object $options
4482
	 *
4483
	 */
4484
	public function setOptions($options){
4485
		$options = array_intersect_key($options,$this->defaultOptions);
4486
		$this->options = array_merge($this->defaultOptions, $this->options, $options);
4487
	}
4488
4489
4490
	/**
4491
	 * Get an option value by name
4492
	 *
4493
	 * If the option is empty or not set a NULL value will be returned.
4494
	 *
4495
	 * @param string $name
4496
	 * @param mixed $default Default value if confiuration of $name is not present
4497
	 * @return mixed
4498
	 */
4499
	public function getOption($name, $default = null){
4500
		if(isset($this->options[$name])){
4501
			return $this->options[$name];
4502
		}
4503
		return $default;
4504
	}
4505
4506
4507
	/**
4508
	 * Set an option
4509
	 *
4510
	 * @param string $name
4511
	 * @param mixed $value
4512
	 */
4513
	public function setOption($name, $value){
4514
		$this->options[$name] = $value;
4515
	}
4516
4517
} 
4518
4519
/**
4520
 * Alpha
4521
 *
4522
 * @package Less
4523
 * @subpackage tree
4524
 */
4525
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...
4526
	public $value;
4527
	public $type = 'Alpha';
4528
4529
	public function __construct($val){
4530
		$this->value = $val;
4531
	}
4532
4533
	//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...
4534
	//	$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...
4535
	//}
4536
4537
	public function compile($env){
4538
4539
		if( is_object($this->value) ){
4540
			$this->value = $this->value->compile($env);
4541
		}
4542
4543
		return $this;
4544
	}
4545
4546
    /**
4547
     * @see Less_Tree::genCSS
4548
     */
4549
	public function genCSS( $output ){
4550
4551
		$output->add( "alpha(opacity=" );
4552
4553
		if( is_string($this->value) ){
4554
			$output->add( $this->value );
4555
		}else{
4556
			$this->value->genCSS( $output);
4557
		}
4558
4559
		$output->add( ')' );
4560
	}
4561
4562
	public function toCSS(){
4563
		return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
4564
	}
4565
4566
4567
} 
4568
4569
/**
4570
 * Anonymous
4571
 *
4572
 * @package Less
4573
 * @subpackage tree
4574
 */
4575
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...
4576
	public $value;
4577
	public $quote;
4578
	public $index;
4579
	public $mapLines;
4580
	public $currentFileInfo;
4581
	public $type = 'Anonymous';
4582
4583
	/**
4584
	 * @param integer $index
4585
	 * @param boolean $mapLines
4586
	 */
4587
	public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null ){
4588
		$this->value = $value;
4589
		$this->index = $index;
4590
		$this->mapLines = $mapLines;
4591
		$this->currentFileInfo = $currentFileInfo;
4592
	}
4593
4594
	public function compile(){
4595
		return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines);
4596
	}
4597
4598 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...
4599
		if( !is_object($x) ){
4600
			return -1;
4601
		}
4602
4603
		$left = $this->toCSS();
4604
		$right = $x->toCSS();
4605
4606
		if( $left === $right ){
4607
			return 0;
4608
		}
4609
4610
		return $left < $right ? -1 : 1;
4611
	}
4612
4613
    /**
4614
     * @see Less_Tree::genCSS
4615
     */
4616
	public function genCSS( $output ){
4617
		$output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines );
4618
	}
4619
4620
	public function toCSS(){
4621
		return $this->value;
4622
	}
4623
4624
}
4625
 
4626
4627
/**
4628
 * Assignment
4629
 *
4630
 * @package Less
4631
 * @subpackage tree
4632
 */
4633
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...
4634
4635
	public $key;
4636
	public $value;
4637
	public $type = 'Assignment';
4638
4639
    public function __construct($key, $val) {
4640
		$this->key = $key;
4641
		$this->value = $val;
4642
	}
4643
4644
    public function accept( $visitor ){
4645
		$this->value = $visitor->visitObj( $this->value );
4646
	}
4647
4648
	public function compile($env) {
4649
		return new Less_Tree_Assignment( $this->key, $this->value->compile($env));
4650
	}
4651
4652
    /**
4653
     * @see Less_Tree::genCSS
4654
     */
4655
	public function genCSS( $output ){
4656
		$output->add( $this->key . '=' );
4657
		$this->value->genCSS( $output );
4658
	}
4659
4660
	public function toCss(){
4661
		return $this->key . '=' . $this->value->toCSS();
4662
	}
4663
}
4664
 
4665
4666
/**
4667
 * Attribute
4668
 *
4669
 * @package Less
4670
 * @subpackage tree
4671
 */
4672
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...
4673
4674
	public $key;
4675
	public $op;
4676
	public $value;
4677
	public $type = 'Attribute';
4678
4679
    public function __construct($key, $op, $value){
4680
		$this->key = $key;
4681
		$this->op = $op;
4682
		$this->value = $value;
4683
	}
4684
4685
    public function compile($env){
4686
4687
		$key_obj = is_object($this->key);
4688
		$val_obj = is_object($this->value);
4689
4690
		if( !$key_obj && !$val_obj ){
4691
			return $this;
4692
		}
4693
4694
		return new Less_Tree_Attribute(
4695
			$key_obj ? $this->key->compile($env) : $this->key ,
4696
			$this->op,
4697
			$val_obj ? $this->value->compile($env) : $this->value);
4698
	}
4699
4700
    /**
4701
     * @see Less_Tree::genCSS
4702
     */
4703
    public function genCSS( $output ){
4704
		$output->add( $this->toCSS() );
4705
	}
4706
4707
    public function toCSS(){
4708
		$value = $this->key;
4709
4710
		if( $this->op ){
4711
			$value .= $this->op;
4712
			$value .= (is_object($this->value) ? $this->value->toCSS() : $this->value);
4713
		}
4714
4715
		return '[' . $value . ']';
4716
	}
4717
} 
4718
4719
4720
/**
4721
 * Call
4722
 *
4723
 * @package Less
4724
 * @subpackage tree
4725
 */
4726
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...
4727
    public $value;
4728
4729
    protected $name;
4730
    protected $args;
4731
    protected $index;
4732
    protected $currentFileInfo;
4733
    public $type = 'Call';
4734
4735 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...
4736
		$this->name = $name;
4737
		$this->args = $args;
4738
		$this->index = $index;
4739
		$this->currentFileInfo = $currentFileInfo;
4740
	}
4741
4742
    public function accept( $visitor ){
4743
		$this->args = $visitor->visitArray( $this->args );
4744
	}
4745
4746
    //
4747
    // When evaluating a function call,
4748
    // we either find the function in `tree.functions` [1],
4749
    // in which case we call it, passing the  evaluated arguments,
4750
    // or we simply print it out as it appeared originally [2].
4751
    //
4752
    // The *functions.js* file contains the built-in functions.
4753
    //
4754
    // The reason why we evaluate the arguments, is in the case where
4755
    // we try to pass a variable to a function, like: `saturate(@color)`.
4756
    // The function should receive the value, not the variable.
4757
    //
4758
    public function compile($env=null){
4759
		$args = array();
4760
		foreach($this->args as $a){
4761
			$args[] = $a->compile($env);
4762
		}
4763
4764
		$nameLC = strtolower($this->name);
4765
		switch($nameLC){
4766
			case '%':
4767
			$nameLC = '_percent';
4768
			break;
4769
4770
			case 'get-unit':
4771
			$nameLC = 'getunit';
4772
			break;
4773
4774
			case 'data-uri':
4775
			$nameLC = 'datauri';
4776
			break;
4777
4778
			case 'svg-gradient':
4779
			$nameLC = 'svggradient';
4780
			break;
4781
		}
4782
4783
		$result = null;
4784
		if( $nameLC === 'default' ){
4785
			$result = Less_Tree_DefaultFunc::compile();
4786
4787
		}else{
4788
4789
			if( method_exists('Less_Functions',$nameLC) ){ // 1.
4790
				try {
4791
4792
					$func = new Less_Functions($env, $this->currentFileInfo);
4793
					$result = call_user_func_array( array($func,$nameLC),$args);
4794
4795
				} catch (Exception $e) {
4796
					throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
4797
				}
4798
			} elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
4799
				try {
4800
					$result = call_user_func_array( $env->functions[$nameLC], $args );
4801
				} catch (Exception $e) {
4802
					throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
4803
				}
4804
			}
4805
		}
4806
4807
		if( $result !== null ){
4808
			return $result;
4809
		}
4810
4811
4812
		return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo );
4813
    }
4814
4815
    /**
4816
     * @see Less_Tree::genCSS
4817
     */
4818
	public function genCSS( $output ){
4819
4820
		$output->add( $this->name . '(', $this->currentFileInfo, $this->index );
4821
		$args_len = count($this->args);
4822
		for($i = 0; $i < $args_len; $i++ ){
4823
			$this->args[$i]->genCSS( $output );
4824
			if( $i + 1 < $args_len ){
4825
				$output->add( ', ' );
4826
			}
4827
		}
4828
4829
		$output->add( ')' );
4830
	}
4831
4832
4833
    //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...
4834
    //    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...
4835
    //}
4836
4837
}
4838
 
4839
4840
/**
4841
 * Color
4842
 *
4843
 * @package Less
4844
 * @subpackage tree
4845
 */
4846
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...
4847
	public $rgb;
4848
	public $alpha;
4849
	public $isTransparentKeyword;
4850
	public $type = 'Color';
4851
4852
	public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){
4853
4854
		if( $isTransparentKeyword ){
4855
			$this->rgb = $rgb;
4856
			$this->alpha = $a;
4857
			$this->isTransparentKeyword = true;
4858
			return;
4859
		}
4860
4861
		$this->rgb = array();
4862
		if( is_array($rgb) ){
4863
			$this->rgb = $rgb;
4864
		}else if( strlen($rgb) == 6 ){
4865
			foreach(str_split($rgb, 2) as $c){
4866
				$this->rgb[] = hexdec($c);
4867
			}
4868
		}else{
4869
			foreach(str_split($rgb, 1) as $c){
4870
				$this->rgb[] = hexdec($c.$c);
4871
			}
4872
		}
4873
		$this->alpha = is_numeric($a) ? $a : 1;
4874
	}
4875
4876
	public function compile(){
4877
		return $this;
4878
	}
4879
4880
	public function luma(){
4881
		$r = $this->rgb[0] / 255;
4882
		$g = $this->rgb[1] / 255;
4883
		$b = $this->rgb[2] / 255;
4884
4885
		$r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
4886
		$g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
4887
		$b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
4888
4889
		return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
4890
	}
4891
4892
	/**
4893
	 * @see Less_Tree::genCSS
4894
	 */
4895
	public function genCSS( $output ){
4896
		$output->add( $this->toCSS() );
4897
	}
4898
4899
	public function toCSS( $doNotCompress = false ){
4900
		$compress = Less_Parser::$options['compress'] && !$doNotCompress;
4901
		$alpha = Less_Functions::fround( $this->alpha );
4902
4903
4904
		//
4905
		// If we have some transparency, the only way to represent it
4906
		// is via `rgba`. Otherwise, we use the hex representation,
4907
		// which has better compatibility with older browsers.
4908
		// Values are capped between `0` and `255`, rounded and zero-padded.
4909
		//
4910
		if( $alpha < 1 ){
4911
			if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){
4912
				return 'transparent';
4913
			}
4914
4915
			$values = array();
4916
			foreach($this->rgb as $c){
4917
				$values[] = Less_Functions::clamp( round($c), 255);
4918
			}
4919
			$values[] = $alpha;
4920
4921
			$glue = ($compress ? ',' : ', ');
4922
			return "rgba(" . implode($glue, $values) . ")";
4923
		}else{
4924
4925
			$color = $this->toRGB();
4926
4927
			if( $compress ){
4928
4929
				// Convert color to short format
4930
				if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
4931
					$color = '#'.$color[1] . $color[3] . $color[5];
4932
				}
4933
			}
4934
4935
			return $color;
4936
		}
4937
	}
4938
4939
	//
4940
	// Operations have to be done per-channel, if not,
4941
	// channels will spill onto each other. Once we have
4942
	// our result, in the form of an integer triplet,
4943
	// we create a new Color node to hold the result.
4944
	//
4945
4946
	/**
4947
	 * @param string $op
4948
	 */
4949
	public function operate( $op, $other) {
4950
		$rgb = array();
4951
		$alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
4952
		for ($c = 0; $c < 3; $c++) {
4953
			$rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]);
4954
		}
4955
		return new Less_Tree_Color($rgb, $alpha);
4956
	}
4957
4958
	public function toRGB(){
4959
		return $this->toHex($this->rgb);
4960
	}
4961
4962
	public function toHSL(){
4963
		$r = $this->rgb[0] / 255;
4964
		$g = $this->rgb[1] / 255;
4965
		$b = $this->rgb[2] / 255;
4966
		$a = $this->alpha;
4967
4968
		$max = max($r, $g, $b);
4969
		$min = min($r, $g, $b);
4970
		$l = ($max + $min) / 2;
4971
		$d = $max - $min;
4972
4973
		$h = $s = 0;
4974
		if( $max !== $min ){
4975
			$s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
4976
4977 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...
4978
				case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
4979
				case $g: $h = ($b - $r) / $d + 2;				 break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
4980
				case $b: $h = ($r - $g) / $d + 4;				 break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
4981
			}
4982
			$h /= 6;
4983
		}
4984
		return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
4985
	}
4986
4987
	//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
4988
    public function toHSV() {
4989
		$r = $this->rgb[0] / 255;
4990
		$g = $this->rgb[1] / 255;
4991
		$b = $this->rgb[2] / 255;
4992
		$a = $this->alpha;
4993
4994
		$max = max($r, $g, $b);
4995
		$min = min($r, $g, $b);
4996
4997
		$v = $max;
4998
4999
		$d = $max - $min;
5000
		if ($max === 0) {
5001
			$s = 0;
5002
		} else {
5003
			$s = $d / $max;
5004
		}
5005
5006
		$h = 0;
5007
		if( $max !== $min ){
5008 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...
5009
				case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
5010
				case $g: $h = ($b - $r) / $d + 2; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
5011
				case $b: $h = ($r - $g) / $d + 4; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
5012
			}
5013
			$h /= 6;
5014
		}
5015
		return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a );
5016
	}
5017
5018
	public function toARGB(){
5019
		$argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb);
5020
		return $this->toHex( $argb );
5021
	}
5022
5023
	public function compare($x){
5024
5025
		if( !property_exists( $x, 'rgb' ) ){
5026
			return -1;
5027
		}
5028
5029
5030
		return ($x->rgb[0] === $this->rgb[0] &&
5031
			$x->rgb[1] === $this->rgb[1] &&
5032
			$x->rgb[2] === $this->rgb[2] &&
5033
			$x->alpha === $this->alpha) ? 0 : -1;
5034
	}
5035
5036
    public function toHex( $v ){
5037
5038
		$ret = '#';
5039
		foreach($v as $c){
5040
			$c = Less_Functions::clamp( Less_Parser::round($c), 255);
5041
			if( $c < 16 ){
5042
				$ret .= '0';
5043
			}
5044
			$ret .= dechex($c);
5045
		}
5046
5047
		return $ret;
5048
	}
5049
5050
5051
	/**
5052
	 * @param string $keyword
5053
	 */
5054
	public static function fromKeyword( $keyword ){
5055
		$keyword = strtolower($keyword);
5056
5057
		if( Less_Colors::hasOwnProperty($keyword) ){
5058
			// detect named color
5059
			return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
5060
		}
5061
5062
		if( $keyword === 'transparent' ){
5063
			return new Less_Tree_Color( array(0, 0, 0), 0, true);
5064
		}
5065
	}
5066
5067
}
5068
 
5069
5070
/**
5071
 * Comment
5072
 *
5073
 * @package Less
5074
 * @subpackage tree
5075
 */
5076
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...
5077
5078
	public $value;
5079
	public $silent;
5080
	public $isReferenced;
5081
	public $currentFileInfo;
5082
	public $type = 'Comment';
5083
5084
	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...
5085
		$this->value = $value;
5086
		$this->silent = !! $silent;
5087
		$this->currentFileInfo = $currentFileInfo;
5088
	}
5089
5090
    /**
5091
     * @see Less_Tree::genCSS
5092
     */
5093
	public function genCSS( $output ){
5094
		//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...
5095
			//$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...
5096
		//}
5097
		$output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n
5098
	}
5099
5100
	public function toCSS(){
5101
		return Less_Parser::$options['compress'] ? '' : $this->value;
5102
	}
5103
5104
	public function isSilent(){
5105
		$isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) );
5106
		$isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value);
5107
		return $this->silent || $isReference || $isCompressed;
5108
	}
5109
5110
	public function compile(){
5111
		return $this;
5112
	}
5113
5114
	public function markReferenced(){
5115
		$this->isReferenced = true;
5116
	}
5117
5118
}
5119
 
5120
5121
/**
5122
 * Condition
5123
 *
5124
 * @package Less
5125
 * @subpackage tree
5126
 */
5127
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...
5128
5129
	public $op;
5130
	public $lvalue;
5131
	public $rvalue;
5132
	public $index;
5133
	public $negate;
5134
	public $type = 'Condition';
5135
5136
	public function __construct($op, $l, $r, $i = 0, $negate = false) {
5137
		$this->op = trim($op);
5138
		$this->lvalue = $l;
5139
		$this->rvalue = $r;
5140
		$this->index = $i;
5141
		$this->negate = $negate;
5142
	}
5143
5144
	public function accept($visitor){
5145
		$this->lvalue = $visitor->visitObj( $this->lvalue );
5146
		$this->rvalue = $visitor->visitObj( $this->rvalue );
5147
	}
5148
5149
    public function compile($env) {
5150
		$a = $this->lvalue->compile($env);
5151
		$b = $this->rvalue->compile($env);
5152
5153
		switch( $this->op ){
5154
			case 'and':
5155
				$result = $a && $b;
5156
			break;
5157
5158
			case 'or':
5159
				$result = $a || $b;
5160
			break;
5161
5162
			default:
5163
				if( Less_Parser::is_method($a, 'compare') ){
5164
					$result = $a->compare($b);
5165
				}elseif( Less_Parser::is_method($b, 'compare') ){
5166
					$result = $b->compare($a);
5167
				}else{
5168
					throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index);
5169
				}
5170
5171
				switch ($result) {
5172 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...
5173
					$result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
5174
					break;
5175
5176 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...
5177
					$result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<=';
5178
					break;
5179
5180
					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...
5181
					$result = $this->op === '>' || $this->op === '>=';
5182
					break;
5183
				}
5184
			break;
5185
		}
5186
5187
		return $this->negate ? !$result : $result;
5188
    }
5189
5190
}
5191
 
5192
5193
/**
5194
 * DefaultFunc
5195
 *
5196
 * @package Less
5197
 * @subpackage tree
5198
 */
5199
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...
5200
5201
	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...
5202
	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...
5203
5204
    public static function compile(){
5205
		if( self::$error_ ){
5206
			throw new Exception(self::$error_);
5207
		}
5208
		if( self::$value_ !== null ){
5209
			return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
5210
		}
5211
	}
5212
5213
    public static function value( $v ){
5214
		self::$value_ = $v;
5215
	}
5216
5217
    public static function error( $e ){
5218
		self::$error_ = $e;
5219
	}
5220
5221
    public static function reset(){
5222
		self::$value_ = self::$error_ = null;
5223
	}
5224
} 
5225
5226
/**
5227
 * DetachedRuleset
5228
 *
5229
 * @package Less
5230
 * @subpackage tree
5231
 */
5232
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...
5233
5234
	public $ruleset;
5235
	public $frames;
5236
	public $type = 'DetachedRuleset';
5237
5238
    public function __construct( $ruleset, $frames = null ){
5239
		$this->ruleset = $ruleset;
5240
		$this->frames = $frames;
5241
	}
5242
5243
    public function accept($visitor) {
5244
		$this->ruleset = $visitor->visitObj($this->ruleset);
5245
	}
5246
5247
    public function compile($env){
5248
		if( $this->frames ){
5249
			$frames = $this->frames;
5250
		}else{
5251
			$frames = $env->frames;
5252
		}
5253
		return new Less_Tree_DetachedRuleset($this->ruleset, $frames);
5254
	}
5255
5256
    public function callEval($env) {
5257
		if( $this->frames ){
5258
			return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) );
5259
		}
5260
		return $this->ruleset->compile( $env );
5261
	}
5262
}
5263
5264
 
5265
5266
/**
5267
 * Dimension
5268
 *
5269
 * @package Less
5270
 * @subpackage tree
5271
 */
5272
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...
5273
5274
	public $value;
5275
	public $unit;
5276
	public $type = 'Dimension';
5277
5278
    public function __construct($value, $unit = null){
5279
        $this->value = floatval($value);
5280
5281
		if( $unit && ($unit instanceof Less_Tree_Unit) ){
5282
			$this->unit = $unit;
5283
		}elseif( $unit ){
5284
			$this->unit = new Less_Tree_Unit( array($unit) );
5285
		}else{
5286
			$this->unit = new Less_Tree_Unit( );
5287
		}
5288
    }
5289
5290
    public function accept( $visitor ){
5291
		$this->unit = $visitor->visitObj( $this->unit );
5292
	}
5293
5294
    public function compile(){
5295
        return $this;
5296
    }
5297
5298
    public function toColor() {
5299
        return new Less_Tree_Color(array($this->value, $this->value, $this->value));
5300
    }
5301
5302
    /**
5303
     * @see Less_Tree::genCSS
5304
     */
5305
	public function genCSS( $output ){
5306
5307
		if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){
5308
			throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString());
5309
		}
5310
5311
		$value = Less_Functions::fround( $this->value );
5312
		$strValue = (string)$value;
5313
5314
		if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){
5315
			// would be output 1e-6 etc.
5316
			$strValue = number_format($strValue,10);
5317
			$strValue = preg_replace('/\.?0+$/','', $strValue);
5318
		}
5319
5320
		if( Less_Parser::$options['compress'] ){
5321
			// Zero values doesn't need a unit
5322
			if( $value === 0 && $this->unit->isLength() ){
5323
				$output->add( $strValue );
5324
				return $strValue;
5325
			}
5326
5327
			// Float values doesn't need a leading zero
5328
			if( $value > 0 && $value < 1 && $strValue[0] === '0' ){
5329
				$strValue = substr($strValue,1);
5330
			}
5331
		}
5332
5333
		$output->add( $strValue );
5334
		$this->unit->genCSS( $output );
5335
	}
5336
5337
    public function __toString(){
5338
        return $this->toCSS();
5339
    }
5340
5341
    // In an operation between two Dimensions,
5342
    // we default to the first Dimension's unit,
5343
    // so `1px + 2em` will yield `3px`.
5344
5345
    /**
5346
     * @param string $op
5347
     */
5348
    public function operate( $op, $other){
5349
5350
		$value = Less_Functions::operate( $op, $this->value, $other->value);
5351
		$unit = clone $this->unit;
5352
5353
		if( $op === '+' || $op === '-' ){
5354
5355
			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...
5356
				$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...
5357
				$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...
5358
			}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...
5359
				// do nothing
5360
			}else{
5361
				$other = $other->convertTo( $this->unit->usedUnits());
5362
5363
				if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){
5364
					throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '".$unit->toString() . "' and ".$other->unit->toString()+"'.");
5365
				}
5366
5367
				$value = Less_Functions::operate( $op, $this->value, $other->value);
5368
			}
5369
		}elseif( $op === '*' ){
5370
			$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...
5371
			$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...
5372
			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...
5373
			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...
5374
			$unit->cancel();
5375
		}elseif( $op === '/' ){
5376
			$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...
5377
			$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...
5378
			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...
5379
			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...
5380
			$unit->cancel();
5381
		}
5382
		return new Less_Tree_Dimension( $value, $unit);
5383
    }
5384
5385
	public function compare($other) {
5386
		if ($other instanceof Less_Tree_Dimension) {
5387
5388
			if( $this->unit->isEmpty() || $other->unit->isEmpty() ){
5389
				$a = $this;
5390
				$b = $other;
5391
			} else {
5392
				$a = $this->unify();
5393
				$b = $other->unify();
5394
				if( $a->unit->compare($b->unit) !== 0 ){
5395
					return -1;
5396
				}
5397
			}
5398
			$aValue = $a->value;
5399
			$bValue = $b->value;
5400
5401
			if ($bValue > $aValue) {
5402
				return -1;
5403
			} elseif ($bValue < $aValue) {
5404
				return 1;
5405
			} else {
5406
				return 0;
5407
			}
5408
		} else {
5409
			return -1;
5410
		}
5411
	}
5412
5413
    public function unify() {
5414
		return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' ));
5415
	}
5416
5417
    public function convertTo($conversions) {
5418
		$value = $this->value;
5419
		$unit = clone $this->unit;
5420
5421
		if( is_string($conversions) ){
5422
			$derivedConversions = array();
5423
			foreach( Less_Tree_UnitConversions::$groups as $i ){
5424
				if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){
5425
					$derivedConversions = array( $i => $conversions);
5426
				}
5427
			}
5428
			$conversions = $derivedConversions;
5429
		}
5430
5431
5432
		foreach($conversions as $groupName => $targetUnit){
5433
			$group = Less_Tree_UnitConversions::${$groupName};
5434
5435
			//numerator
5436 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...
5437
				$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...
5438
				if( !isset($group[$atomicUnit]) ){
5439
					continue;
5440
				}
5441
5442
				$value = $value * ($group[$atomicUnit] / $group[$targetUnit]);
5443
5444
				$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...
5445
			}
5446
5447
			//denominator
5448 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...
5449
				$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...
5450
				if( !isset($group[$atomicUnit]) ){
5451
					continue;
5452
				}
5453
5454
				$value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
5455
5456
				$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...
5457
			}
5458
		}
5459
5460
		$unit->cancel();
5461
5462
		return new Less_Tree_Dimension( $value, $unit);
5463
    }
5464
}
5465
 
5466
5467
/**
5468
 * Directive
5469
 *
5470
 * @package Less
5471
 * @subpackage tree
5472
 */
5473
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...
5474
5475
	public $name;
5476
	public $value;
5477
	public $rules;
5478
	public $index;
5479
	public $isReferenced;
5480
	public $currentFileInfo;
5481
	public $debugInfo;
5482
	public $type = 'Directive';
5483
5484
	public function __construct($name, $value = null, $rules, $index = null, $currentFileInfo = null, $debugInfo = null ){
5485
		$this->name = $name;
5486
		$this->value = $value;
5487
		if( $rules ){
5488
			$this->rules = $rules;
5489
			$this->rules->allowImports = true;
5490
		}
5491
5492
		$this->index = $index;
5493
		$this->currentFileInfo = $currentFileInfo;
5494
		$this->debugInfo = $debugInfo;
5495
	}
5496
5497
5498
    public function accept( $visitor ){
5499
		if( $this->rules ){
5500
			$this->rules = $visitor->visitObj( $this->rules );
5501
		}
5502
		if( $this->value ){
5503
			$this->value = $visitor->visitObj( $this->value );
5504
		}
5505
	}
5506
5507
5508
    /**
5509
     * @see Less_Tree::genCSS
5510
     */
5511
    public function genCSS( $output ){
5512
		$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...
5513
		$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...
5514
		$output->add( $this->name, $this->currentFileInfo, $this->index );
5515
		if( $this->value ){
5516
			$output->add(' ');
5517
			$this->value->genCSS($output);
5518
		}
5519
		if( $this->rules ){
5520
			Less_Tree::outputRuleset( $output, array($this->rules));
5521
		} else {
5522
			$output->add(';');
5523
		}
5524
	}
5525
5526
	public function compile($env){
5527
5528
		$value = $this->value;
5529
		$rules = $this->rules;
5530
		if( $value ){
5531
			$value = $value->compile($env);
5532
		}
5533
5534
		if( $rules ){
5535
			$rules = $rules->compile($env);
5536
			$rules->root = true;
5537
		}
5538
5539
		return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo );
5540
	}
5541
5542
5543
	public function variable($name){
5544
		if( $this->rules ){
5545
			return $this->rules->variable($name);
5546
		}
5547
	}
5548
5549
	public function find($selector){
5550
		if( $this->rules ){
5551
			return $this->rules->find($selector, $this);
5552
		}
5553
	}
5554
5555
	//rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
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...
5556
5557
	public function markReferenced(){
5558
		$this->isReferenced = true;
5559
		if( $this->rules ){
5560
			Less_Tree::ReferencedArray($this->rules->rules);
5561
		}
5562
	}
5563
5564
}
5565
 
5566
5567
/**
5568
 * Element
5569
 *
5570
 * @package Less
5571
 * @subpackage tree
5572
 */
5573
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...
5574
5575
	public $combinator = '';
5576
	public $value = '';
5577
	public $index;
5578
	public $currentFileInfo;
5579
	public $type = 'Element';
5580
5581
	public $value_is_object = false;
5582
5583
	public function __construct($combinator, $value, $index = null, $currentFileInfo = null ){
5584
5585
		$this->value = $value;
5586
		$this->value_is_object = is_object($value);
5587
5588
		if( $combinator ){
5589
			$this->combinator = $combinator;
5590
		}
5591
5592
		$this->index = $index;
5593
		$this->currentFileInfo = $currentFileInfo;
5594
	}
5595
5596
    public function accept( $visitor ){
5597
		if( $this->value_is_object ){ //object or string
5598
			$this->value = $visitor->visitObj( $this->value );
5599
		}
5600
	}
5601
5602
	public function compile($env){
5603
5604
		if( Less_Environment::$mixin_stack ){
5605
			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...
5606
		}
5607
5608
		if( $this->value_is_object ){
5609
			$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...
5610
		}
5611
5612
		return $this;
5613
	}
5614
5615
    /**
5616
     * @see Less_Tree::genCSS
5617
     */
5618
	public function genCSS( $output ){
5619
		$output->add( $this->toCSS(), $this->currentFileInfo, $this->index );
5620
	}
5621
5622
	public function toCSS(){
5623
5624
		if( $this->value_is_object ){
5625
			$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...
5626
		}else{
5627
			$value = $this->value;
5628
		}
5629
5630
5631
		if( $value === '' && $this->combinator && $this->combinator === '&' ){
5632
			return '';
5633
		}
5634
5635
5636
		return Less_Environment::$_outputMap[$this->combinator] . $value;
5637
	}
5638
5639
}
5640
 
5641
5642
/**
5643
 * Expression
5644
 *
5645
 * @package Less
5646
 * @subpackage tree
5647
 */
5648
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...
5649
5650
	public $value = array();
5651
	public $parens = false;
5652
	public $parensInOp = false;
5653
	public $type = 'Expression';
5654
5655
	public function __construct( $value, $parens = null ){
5656
		$this->value = $value;
5657
		$this->parens = $parens;
5658
	}
5659
5660
    public function accept( $visitor ){
5661
		$this->value = $visitor->visitArray( $this->value );
5662
	}
5663
5664
	public function compile($env) {
5665
5666
		$doubleParen = false;
5667
5668
		if( $this->parens && !$this->parensInOp ){
5669
			Less_Environment::$parensStack++;
5670
		}
5671
5672
		$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...
5673
		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...
5674
5675
			$count = count($this->value);
5676
5677
			if( $count > 1 ){
5678
5679
				$ret = array();
5680
				foreach($this->value as $e){
5681
					$ret[] = $e->compile($env);
5682
				}
5683
				$returnValue = new Less_Tree_Expression($ret);
5684
5685
			}else{
5686
5687
				if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){
5688
					$doubleParen = true;
5689
				}
5690
5691
				$returnValue = $this->value[0]->compile($env);
5692
			}
5693
5694
		} else {
5695
			$returnValue = $this;
5696
		}
5697
5698
		if( $this->parens ){
5699
			if( !$this->parensInOp ){
5700
				Less_Environment::$parensStack--;
5701
5702
			}elseif( !Less_Environment::isMathOn() && !$doubleParen ){
5703
				$returnValue = new Less_Tree_Paren($returnValue);
5704
5705
			}
5706
		}
5707
		return $returnValue;
5708
	}
5709
5710
    /**
5711
     * @see Less_Tree::genCSS
5712
     */
5713 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...
5714
		$val_len = count($this->value);
5715
		for( $i = 0; $i < $val_len; $i++ ){
5716
			$this->value[$i]->genCSS( $output );
5717
			if( $i + 1 < $val_len ){
5718
				$output->add( ' ' );
5719
			}
5720
		}
5721
	}
5722
5723
    public function throwAwayComments() {
5724
5725
		if( is_array($this->value) ){
5726
			$new_value = array();
5727
			foreach($this->value as $v){
5728
				if( $v instanceof Less_Tree_Comment ){
5729
					continue;
5730
				}
5731
				$new_value[] = $v;
5732
			}
5733
			$this->value = $new_value;
5734
		}
5735
	}
5736
}
5737
 
5738
5739
/**
5740
 * Extend
5741
 *
5742
 * @package Less
5743
 * @subpackage tree
5744
 */
5745
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...
5746
5747
	public $selector;
5748
	public $option;
5749
	public $index;
5750
	public $selfSelectors = array();
5751
	public $allowBefore;
5752
	public $allowAfter;
5753
	public $firstExtendOnThisSelectorPath;
5754
	public $type = 'Extend';
5755
	public $ruleset;
5756
5757
5758
	public $object_id;
5759
	public $parent_ids = array();
5760
5761
	/**
5762
	 * @param integer $index
5763
	 */
5764
    public function __construct($selector, $option, $index){
5765
		static $i = 0;
5766
		$this->selector = $selector;
5767
		$this->option = $option;
5768
		$this->index = $index;
5769
5770
		switch($option){
5771
			case "all":
5772
				$this->allowBefore = true;
5773
				$this->allowAfter = true;
5774
			break;
5775
			default:
5776
				$this->allowBefore = false;
5777
				$this->allowAfter = false;
5778
			break;
5779
		}
5780
5781
		$this->object_id = $i++;
5782
		$this->parent_ids = array($this->object_id);
5783
	}
5784
5785
    public function accept( $visitor ){
5786
		$this->selector = $visitor->visitObj( $this->selector );
5787
	}
5788
5789
    public function compile( $env ){
5790
		Less_Parser::$has_extends = true;
5791
		$this->selector = $this->selector->compile($env);
5792
		return $this;
5793
		//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...
5794
	}
5795
5796
    public function findSelfSelectors( $selectors ){
5797
		$selfElements = array();
5798
5799
5800
		for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){
5801
			$selectorElements = $selectors[$i]->elements;
5802
			// duplicate the logic in genCSS function inside the selector node.
5803
			// future TODO - move both logics into the selector joiner visitor
5804
			if( $i && $selectorElements && $selectorElements[0]->combinator === "") {
5805
				$selectorElements[0]->combinator = ' ';
5806
			}
5807
			$selfElements = array_merge( $selfElements, $selectors[$i]->elements );
5808
		}
5809
5810
		$this->selfSelectors = array(new Less_Tree_Selector($selfElements));
5811
	}
5812
5813
} 
5814
5815
/**
5816
 * CSS @import node
5817
 *
5818
 * The general strategy here is that we don't want to wait
5819
 * for the parsing to be completed, before we start importing
5820
 * the file. That's because in the context of a browser,
5821
 * most of the time will be spent waiting for the server to respond.
5822
 *
5823
 * On creation, we push the import path to our import queue, though
5824
 * `import,push`, we also pass it a callback, which it'll call once
5825
 * the file has been fetched, and parsed.
5826
 *
5827
 * @package Less
5828
 * @subpackage tree
5829
 */
5830
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...
5831
5832
	public $options;
5833
	public $index;
5834
	public $path;
5835
	public $features;
5836
	public $currentFileInfo;
5837
	public $css;
5838
	public $skip;
5839
	public $root;
5840
	public $type = 'Import';
5841
5842
    public function __construct($path, $features, $options, $index, $currentFileInfo = null ){
5843
		$this->options = $options;
5844
		$this->index = $index;
5845
		$this->path = $path;
5846
		$this->features = $features;
5847
		$this->currentFileInfo = $currentFileInfo;
5848
5849
		if( is_array($options) ){
5850
			$this->options += array('inline'=>false);
5851
5852
			if( isset($this->options['less']) || $this->options['inline'] ){
5853
				$this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline'];
5854
			} else {
5855
				$pathValue = $this->getPath();
5856
				if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){
5857
					$this->css = true;
5858
				}
5859
			}
5860
		}
5861
	}
5862
5863
//
5864
// The actual import node doesn't return anything, when converted to CSS.
5865
// The reason is that it's used at the evaluation stage, so that the rules
5866
// it imports can be treated like any other rules.
5867
//
5868
// In `eval`, we make sure all Import nodes get evaluated, recursively, so
5869
// we end up with a flat structure, which can easily be imported in the parent
5870
// ruleset.
5871
//
5872
5873
    public function accept($visitor){
5874
5875
		if( $this->features ){
5876
			$this->features = $visitor->visitObj($this->features);
5877
		}
5878
		$this->path = $visitor->visitObj($this->path);
5879
5880
		if( !$this->options['inline'] && $this->root ){
5881
			$this->root = $visitor->visit($this->root);
5882
		}
5883
	}
5884
5885
    /**
5886
     * @see Less_Tree::genCSS
5887
     */
5888
    public function genCSS( $output ){
5889
		if( $this->css ){
5890
5891
			$output->add( '@import ', $this->currentFileInfo, $this->index );
5892
5893
			$this->path->genCSS( $output );
5894
			if( $this->features ){
5895
				$output->add( ' ' );
5896
				$this->features->genCSS( $output );
5897
			}
5898
			$output->add( ';' );
5899
		}
5900
	}
5901
5902
    public function toCSS(){
5903
		$features = $this->features ? ' ' . $this->features->toCSS() : '';
5904
5905
		if ($this->css) {
5906
			return "@import " . $this->path->toCSS() . $features . ";\n";
5907
		} else {
5908
			return "";
5909
		}
5910
	}
5911
5912
	/**
5913
	 * @return string
5914
	 */
5915
    public function getPath(){
5916
		if ($this->path instanceof Less_Tree_Quoted) {
5917
			$path = $this->path->value;
5918
			$path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less';
5919
		} else if ($this->path instanceof Less_Tree_URL) {
5920
			$path = $this->path->value->value;
5921
		}else{
5922
			return null;
5923
		}
5924
5925
		//remove query string and fragment
5926
		return preg_replace('/[\?#][^\?]*$/','',$path);
5927
	}
5928
5929
    public function compileForImport( $env ){
5930
		return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo);
5931
	}
5932
5933
    public function compilePath($env) {
5934
		$path = $this->path->compile($env);
5935
		$rootpath = '';
5936
		if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){
5937
			$rootpath = $this->currentFileInfo['rootpath'];
5938
		}
5939
5940
5941
		if( !($path instanceof Less_Tree_URL) ){
5942
			if( $rootpath ){
5943
				$pathValue = $path->value;
5944
				// Add the base path if the import is relative
5945
				if( $pathValue && Less_Environment::isPathRelative($pathValue) ){
5946
					$path->value = $this->currentFileInfo['uri_root'].$pathValue;
5947
				}
5948
			}
5949
			$path->value = Less_Environment::normalizePath($path->value);
5950
		}
5951
5952
5953
5954
		return $path;
5955
	}
5956
5957
    public function compile( $env ){
5958
5959
		$evald = $this->compileForImport($env);
5960
5961
		//get path & uri
5962
		$path_and_uri = null;
5963
		if( is_callable(Less_Parser::$options['import_callback']) ){
5964
			$path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald);
5965
		}
5966
5967
		if( !$path_and_uri ){
5968
			$path_and_uri = $evald->PathAndUri();
5969
		}
5970
5971
		if( $path_and_uri ){
5972
			list($full_path, $uri) = $path_and_uri;
5973
		}else{
5974
			$full_path = $uri = $evald->getPath();
5975
		}
5976
5977
5978
		//import once
5979
		if( $evald->skip( $full_path, $env) ){
5980
			return array();
5981
		}
5982
5983
		if( $this->options['inline'] ){
5984
			//todo needs to reference css file not import
5985
			//$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...
5986
5987
			Less_Parser::AddParsedFile($full_path);
5988
			$contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
5989
5990
			if( $this->features ){
5991
				return new Less_Tree_Media( array($contents), $this->features->value );
5992
			}
5993
5994
			return array( $contents );
5995
		}
5996
5997
5998
		// css ?
5999
		if( $evald->css ){
6000
			$features = ( $evald->features ? $evald->features->compile($env) : null );
6001
			return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index);
6002
		}
6003
6004
6005
		return $this->ParseImport( $full_path, $uri, $env );
6006
	}
6007
6008
6009
	/**
6010
	 * Using the import directories, get the full absolute path and uri of the import
6011
	 *
6012
	 * @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...
6013
	 */
6014
    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...
6015
6016
		$evald_path = $this->getPath();
6017
6018
		if( $evald_path ){
6019
6020
			$import_dirs = array();
6021
6022
			if( Less_Environment::isPathRelative($evald_path) ){
6023
				//if the path is relative, the file should be in the current directory
6024
				$import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root'];
6025
6026
			}else{
6027
				//otherwise, the file should be relative to the server root
6028
				$import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri'];
6029
6030
				//if the user supplied entryPath isn't the actual root
6031
				$import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = '';
6032
6033
			}
6034
6035
			// always look in user supplied import directories
6036
			$import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] );
6037
6038
6039
			foreach( $import_dirs as $rootpath => $rooturi){
6040
				if( is_callable($rooturi) ){
6041
					list($path, $uri) = call_user_func($rooturi, $evald_path);
6042
					if( is_string($path) ){
6043
						$full_path = $path;
6044
						return array( $full_path, $uri );
6045
					}
6046
				}elseif( !empty($rootpath) ){
6047
6048
6049
					if( $rooturi ){
6050
						if( strpos($evald_path,$rooturi) === 0 ){
6051
							$evald_path = substr( $evald_path, strlen($rooturi) );
6052
						}
6053
					}
6054
6055
					$path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\');
6056
6057
					if( file_exists($path) ){
6058
						$full_path = Less_Environment::normalizePath($path);
6059
						$uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path));
6060
						return array( $full_path, $uri );
6061
					} elseif( file_exists($path.'.less') ){
6062
						$full_path = Less_Environment::normalizePath($path.'.less');
6063
						$uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less'));
6064
						return array( $full_path, $uri );
6065
					}
6066
				}
6067
			}
6068
		}
6069
	}
6070
6071
6072
	/**
6073
	 * Parse the import url and return the rules
6074
	 *
6075
	 * @return Less_Tree_Media|array
6076
	 */
6077
    public function ParseImport( $full_path, $uri, $env ){
6078
6079
		$import_env = clone $env;
6080
		if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){
6081
			$import_env->currentFileInfo['reference'] = true;
6082
		}
6083
6084
		if( (isset($this->options['multiple']) && $this->options['multiple']) ){
6085
			$import_env->importMultiple = true;
6086
		}
6087
6088
		$parser = new Less_Parser($import_env);
6089
		$root = $parser->parseFile($full_path, $uri, true);
6090
6091
6092
		$ruleset = new Less_Tree_Ruleset(array(), $root->rules );
6093
		$ruleset->evalImports($import_env);
6094
6095
		return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules;
6096
	}
6097
6098
6099
	/**
6100
	 * Should the import be skipped?
6101
	 *
6102
	 * @return boolean|null
6103
	 */
6104
	private function Skip($path, $env){
6105
6106
		$path = Less_Parser::winPath(realpath($path));
6107
6108
		if( $path && Less_Parser::FileParsed($path) ){
6109
6110
			if( isset($this->currentFileInfo['reference']) ){
6111
				return true;
6112
			}
6113
6114
			return !isset($this->options['multiple']) && !$env->importMultiple;
6115
		}
6116
6117
	}
6118
}
6119
6120
 
6121
6122
/**
6123
 * Javascript
6124
 *
6125
 * @package Less
6126
 * @subpackage tree
6127
 */
6128
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...
6129
6130
	public $type = 'Javascript';
6131
	public $escaped;
6132
	public $expression;
6133
	public $index;
6134
6135
	/**
6136
	 * @param boolean $index
6137
	 * @param boolean $escaped
6138
	 */
6139
	public function __construct($string, $index, $escaped){
6140
		$this->escaped = $escaped;
6141
		$this->expression = $string;
6142
		$this->index = $index;
6143
	}
6144
6145
	public function compile(){
6146
		return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */');
6147
	}
6148
6149
}
6150
 
6151
6152
/**
6153
 * Keyword
6154
 *
6155
 * @package Less
6156
 * @subpackage tree
6157
 */
6158
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...
6159
6160
	public $value;
6161
	public $type = 'Keyword';
6162
6163
	/**
6164
	 * @param string $value
6165
	 */
6166
	public function __construct($value){
6167
		$this->value = $value;
6168
	}
6169
6170
	public function compile(){
6171
		return $this;
6172
	}
6173
6174
    /**
6175
     * @see Less_Tree::genCSS
6176
     */
6177
	public function genCSS( $output ){
6178
6179
		if( $this->value === '%') {
6180
			throw new Less_Exception_Compiler("Invalid % without number");
6181
		}
6182
6183
		$output->add( $this->value );
6184
	}
6185
6186
	public function compare($other) {
6187
		if ($other instanceof Less_Tree_Keyword) {
6188
			return $other->value === $this->value ? 0 : 1;
6189
		} else {
6190
			return -1;
6191
		}
6192
	}
6193
}
6194
 
6195
6196
/**
6197
 * Media
6198
 *
6199
 * @package Less
6200
 * @subpackage tree
6201
 */
6202
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...
6203
6204
	public $features;
6205
	public $rules;
6206
	public $index;
6207
	public $currentFileInfo;
6208
	public $isReferenced;
6209
	public $type = 'Media';
6210
6211
	public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null ){
6212
6213
		$this->index = $index;
6214
		$this->currentFileInfo = $currentFileInfo;
6215
6216
		$selectors = $this->emptySelectors();
6217
6218
		$this->features = new Less_Tree_Value($features);
6219
6220
		$this->rules = array(new Less_Tree_Ruleset($selectors, $value));
6221
		$this->rules[0]->allowImports = true;
6222
	}
6223
6224
    public function accept( $visitor ){
6225
		$this->features = $visitor->visitObj($this->features);
6226
		$this->rules = $visitor->visitArray($this->rules);
6227
	}
6228
6229
    /**
6230
     * @see Less_Tree::genCSS
6231
     */
6232
    public function genCSS( $output ){
6233
6234
		$output->add( '@media ', $this->currentFileInfo, $this->index );
6235
		$this->features->genCSS( $output );
6236
		Less_Tree::outputRuleset( $output, $this->rules);
6237
6238
	}
6239
6240
	public function compile($env) {
6241
6242
		$media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo );
6243
6244
		$strictMathBypass = false;
6245
		if( Less_Parser::$options['strictMath'] === false) {
6246
			$strictMathBypass = true;
6247
			Less_Parser::$options['strictMath'] = true;
6248
		}
6249
6250
		$media->features = $this->features->compile($env);
6251
6252
		if( $strictMathBypass ){
6253
			Less_Parser::$options['strictMath'] = false;
6254
		}
6255
6256
		$env->mediaPath[] = $media;
6257
		$env->mediaBlocks[] = $media;
6258
6259
		array_unshift($env->frames, $this->rules[0]);
6260
		$media->rules = array($this->rules[0]->compile($env));
6261
		array_shift($env->frames);
6262
6263
		array_pop($env->mediaPath);
6264
6265
		return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env);
6266
	}
6267
6268
	public function variable($name) {
6269
		return $this->rules[0]->variable($name);
6270
	}
6271
6272
	public function find($selector) {
6273
		return $this->rules[0]->find($selector, $this);
6274
	}
6275
6276
	public function emptySelectors(){
6277
		$el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo );
6278
		$sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) );
6279
		$sels[0]->mediaEmpty = true;
6280
        return $sels;
6281
	}
6282
6283
	public function markReferenced(){
6284
		$this->rules[0]->markReferenced();
6285
		$this->isReferenced = true;
6286
		Less_Tree::ReferencedArray($this->rules[0]->rules);
6287
	}
6288
6289
	// evaltop
6290
	public function compileTop($env) {
6291
		$result = $this;
6292
6293
		if (count($env->mediaBlocks) > 1) {
6294
			$selectors = $this->emptySelectors();
6295
			$result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks);
6296
			$result->multiMedia = true;
6297
		}
6298
6299
		$env->mediaBlocks = array();
6300
		$env->mediaPath = array();
6301
6302
		return $result;
6303
	}
6304
6305
	public function compileNested($env) {
6306
		$path = array_merge($env->mediaPath, array($this));
6307
6308
		// Extract the media-query conditions separated with `,` (OR).
6309
		foreach ($path as $key => $p) {
6310
			$value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
6311
			$path[$key] = is_array($value) ? $value : array($value);
6312
		}
6313
6314
		// Trace all permutations to generate the resulting media-query.
6315
		//
6316
		// (a, b and c) with nested (d, e) ->
6317
		//	a and d
6318
		//	a and e
6319
		//	b and c and d
6320
		//	b and c and e
6321
6322
		$permuted = $this->permute($path);
6323
		$expressions = array();
6324
		foreach($permuted as $path){
6325
6326
			for( $i=0, $len=count($path); $i < $len; $i++){
6327
				$path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]);
6328
			}
6329
6330
			for( $i = count($path) - 1; $i > 0; $i-- ){
6331
				array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and')));
6332
			}
6333
6334
			$expressions[] = new Less_Tree_Expression($path);
6335
		}
6336
		$this->features = new Less_Tree_Value($expressions);
6337
6338
6339
6340
		// Fake a tree-node that doesn't output anything.
6341
		return new Less_Tree_Ruleset(array(), array());
6342
	}
6343
6344
	public function permute($arr) {
6345
		if (!$arr)
6346
			return array();
6347
6348
		if (count($arr) == 1)
6349
			return $arr[0];
6350
6351
		$result = array();
6352
		$rest = $this->permute(array_slice($arr, 1));
6353
		foreach ($rest as $r) {
6354
			foreach ($arr[0] as $a) {
6355
				$result[] = array_merge(
6356
					is_array($a) ? $a : array($a),
6357
					is_array($r) ? $r : array($r)
6358
				);
6359
			}
6360
		}
6361
6362
		return $result;
6363
	}
6364
6365
    public function bubbleSelectors($selectors) {
6366
6367
		if( !$selectors) return;
6368
6369
		$this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0])));
6370
	}
6371
6372
}
6373
 
6374
6375
/**
6376
 * A simple css name-value pair
6377
 * ex: width:100px;
6378
 *
6379
 * 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)
6380
 * Using the name-value object can speed up bootstrap compilation slightly, but it breaks color keyword interpretation: color:red -> color:#FF0000;
6381
 *
6382
 * @package Less
6383
 * @subpackage tree
6384
 */
6385
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...
6386
6387
	public $name;
6388
	public $value;
6389
	public $index;
6390
	public $currentFileInfo;
6391
	public $type = 'NameValue';
6392
6393 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...
6394
		$this->name = $name;
6395
		$this->value = $value;
6396
		$this->index = $index;
6397
		$this->currentFileInfo = $currentFileInfo;
6398
	}
6399
6400
    public function genCSS( $output ){
6401
6402
		$output->add(
6403
			$this->name
6404
			. Less_Environment::$_outputMap[': ']
6405
			. $this->value
6406
			. (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";")
6407
			, $this->currentFileInfo, $this->index);
6408
	}
6409
6410
	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...
6411
		return $this;
6412
	}
6413
}
6414
 
6415
6416
/**
6417
 * Negative
6418
 *
6419
 * @package Less
6420
 * @subpackage tree
6421
 */
6422
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...
6423
6424
	public $value;
6425
	public $type = 'Negative';
6426
6427
    public function __construct($node){
6428
		$this->value = $node;
6429
	}
6430
6431
	//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...
6432
	//	$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...
6433
	//}
6434
6435
    /**
6436
     * @see Less_Tree::genCSS
6437
     */
6438
    public function genCSS( $output ){
6439
		$output->add( '-' );
6440
		$this->value->genCSS( $output );
6441
	}
6442
6443
    public function compile($env) {
6444
		if( Less_Environment::isMathOn() ){
6445
			$ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) );
6446
			return $ret->compile($env);
6447
		}
6448
		return new Less_Tree_Negative( $this->value->compile($env) );
6449
	}
6450
} 
6451
6452
/**
6453
 * Operation
6454
 *
6455
 * @package Less
6456
 * @subpackage tree
6457
 */
6458
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...
6459
6460
	public $op;
6461
	public $operands;
6462
	public $isSpaced;
6463
	public $type = 'Operation';
6464
6465
	/**
6466
	 * @param string $op
6467
	 */
6468
	public function __construct($op, $operands, $isSpaced = false){
6469
		$this->op = trim($op);
6470
		$this->operands = $operands;
6471
		$this->isSpaced = $isSpaced;
6472
	}
6473
6474
    public function accept($visitor) {
6475
		$this->operands = $visitor->visitArray($this->operands);
6476
	}
6477
6478
	public function compile($env){
6479
		$a = $this->operands[0]->compile($env);
6480
		$b = $this->operands[1]->compile($env);
6481
6482
6483
		if( Less_Environment::isMathOn() ){
6484
6485
			if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){
6486
				$a = $a->toColor();
6487
6488
			}elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){
6489
				$b = $b->toColor();
6490
6491
			}
6492
6493
			if( !method_exists($a,'operate') ){
6494
				throw new Less_Exception_Compiler("Operation on an invalid type");
6495
			}
6496
6497
			return $a->operate( $this->op, $b);
6498
		}
6499
6500
		return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced );
6501
	}
6502
6503
6504
    /**
6505
     * @see Less_Tree::genCSS
6506
     */
6507
    public function genCSS( $output ){
6508
		$this->operands[0]->genCSS( $output );
6509
		if( $this->isSpaced ){
6510
			$output->add( " " );
6511
		}
6512
		$output->add( $this->op );
6513
		if( $this->isSpaced ){
6514
			$output->add( ' ' );
6515
		}
6516
		$this->operands[1]->genCSS( $output );
6517
	}
6518
6519
}
6520
 
6521
6522
/**
6523
 * Paren
6524
 *
6525
 * @package Less
6526
 * @subpackage tree
6527
 */
6528
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...
6529
6530
	public $value;
6531
	public $type = 'Paren';
6532
6533
	public function __construct($value) {
6534
		$this->value = $value;
6535
	}
6536
6537
    public function accept($visitor){
6538
		$this->value = $visitor->visitObj($this->value);
6539
	}
6540
6541
    /**
6542
     * @see Less_Tree::genCSS
6543
     */
6544
    public function genCSS( $output ){
6545
		$output->add( '(' );
6546
		$this->value->genCSS( $output );
6547
		$output->add( ')' );
6548
	}
6549
6550
	public function compile($env) {
6551
		return new Less_Tree_Paren($this->value->compile($env));
6552
	}
6553
6554
}
6555
 
6556
6557
/**
6558
 * Quoted
6559
 *
6560
 * @package Less
6561
 * @subpackage tree
6562
 */
6563
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...
6564
	public $escaped;
6565
	public $value;
6566
	public $quote;
6567
	public $index;
6568
	public $currentFileInfo;
6569
	public $type = 'Quoted';
6570
6571
	/**
6572
	 * @param string $str
6573
	 */
6574
	public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ){
6575
		$this->escaped = $escaped;
6576
		$this->value = $content;
6577
		if( $str ){
6578
			$this->quote = $str[0];
6579
		}
6580
		$this->index = $index;
6581
		$this->currentFileInfo = $currentFileInfo;
6582
	}
6583
6584
    /**
6585
     * @see Less_Tree::genCSS
6586
     */
6587
    public function genCSS( $output ){
6588
		if( !$this->escaped ){
6589
			$output->add( $this->quote, $this->currentFileInfo, $this->index );
6590
        }
6591
        $output->add( $this->value );
6592
        if( !$this->escaped ){
6593
			$output->add( $this->quote );
6594
        }
6595
    }
6596
6597
	public function compile($env){
6598
6599
		$value = $this->value;
6600
		if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){
6601
			foreach($matches as $i => $match){
6602
				$js = new Less_Tree_JavaScript($matches[1], $this->index, true);
6603
				$js = $js->compile()->value;
6604
				$value = str_replace($matches[0][$i], $js, $value);
6605
			}
6606
		}
6607
6608
		if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){
6609
			foreach($matches[1] as $i => $match){
6610
				$v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo );
6611
				$v = $v->compile($env);
6612
				$v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS();
6613
				$value = str_replace($matches[0][$i], $v, $value);
6614
			}
6615
		}
6616
6617
		return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo);
6618
	}
6619
6620 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...
6621
6622
		if( !Less_Parser::is_method($x, 'toCSS') ){
6623
			return -1;
6624
		}
6625
6626
		$left = $this->toCSS();
6627
		$right = $x->toCSS();
6628
6629
		if ($left === $right) {
6630
			return 0;
6631
		}
6632
6633
		return $left < $right ? -1 : 1;
6634
	}
6635
}
6636
 
6637
6638
/**
6639
 * Rule
6640
 *
6641
 * @package Less
6642
 * @subpackage tree
6643
 */
6644
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...
6645
6646
	public $name;
6647
	public $value;
6648
	public $important;
6649
	public $merge;
6650
	public $index;
6651
	public $inline;
6652
	public $variable;
6653
	public $currentFileInfo;
6654
	public $type = 'Rule';
6655
6656
	/**
6657
	 * @param string $important
6658
	 */
6659
	public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null,  $inline = false){
6660
		$this->name = $name;
6661
		$this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value));
6662
		$this->important = $important ? ' ' . trim($important) : '';
6663
		$this->merge = $merge;
6664
		$this->index = $index;
6665
		$this->currentFileInfo = $currentFileInfo;
6666
		$this->inline = $inline;
6667
		$this->variable = ( is_string($name) && $name[0] === '@');
6668
	}
6669
6670
    public function accept($visitor) {
6671
		$this->value = $visitor->visitObj( $this->value );
6672
	}
6673
6674
    /**
6675
     * @see Less_Tree::genCSS
6676
     */
6677
    public function genCSS( $output ){
6678
6679
		$output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index);
6680
		try{
6681
			$this->value->genCSS( $output);
6682
6683
		}catch( Less_Exception_Parser $e ){
6684
			$e->index = $this->index;
6685
			$e->currentFile = $this->currentFileInfo;
6686
			throw $e;
6687
		}
6688
		$output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index);
6689
	}
6690
6691
	public function compile ($env){
6692
6693
		$name = $this->name;
6694
		if( is_array($name) ){
6695
			// expand 'primitive' name directly to get
6696
			// things faster (~10% for benchmark.less):
6697
			if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){
6698
				$name = $name[0]->value;
6699
			}else{
6700
				$name = $this->CompileName($env,$name);
6701
			}
6702
		}
6703
6704
		$strictMathBypass = Less_Parser::$options['strictMath'];
6705
		if( $name === "font" && !Less_Parser::$options['strictMath'] ){
6706
			Less_Parser::$options['strictMath'] = true;
6707
		}
6708
6709
		try {
6710
			$evaldValue = $this->value->compile($env);
6711
6712
			if( !$this->variable && $evaldValue->type === "DetachedRuleset") {
6713
				throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo);
6714
			}
6715
6716
			if( Less_Environment::$mixin_stack ){
6717
				$return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline);
6718
			}else{
6719
				$this->name = $name;
6720
				$this->value = $evaldValue;
6721
				$return = $this;
6722
			}
6723
6724
		}catch( Less_Exception_Parser $e ){
6725
			if( !is_numeric($e->index) ){
6726
				$e->index = $this->index;
6727
				$e->currentFile = $this->currentFileInfo;
6728
			}
6729
			throw $e;
6730
		}
6731
6732
		Less_Parser::$options['strictMath'] = $strictMathBypass;
6733
6734
		return $return;
6735
	}
6736
6737
6738
    public function CompileName( $env, $name ){
6739
		$output = new Less_Output();
6740
		foreach($name as $n){
6741
			$n->compile($env)->genCSS($output);
6742
		}
6743
		return $output->toString();
6744
	}
6745
6746
    public function makeImportant(){
6747
		return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline);
6748
	}
6749
6750
}
6751
 
6752
6753
/**
6754
 * Ruleset
6755
 *
6756
 * @package Less
6757
 * @subpackage tree
6758
 */
6759
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...
6760
6761
	protected $lookups;
6762
	public $_variables;
6763
	public $_rulesets;
6764
6765
	public $strictImports;
6766
6767
	public $selectors;
6768
	public $rules;
6769
	public $root;
6770
	public $allowImports;
6771
	public $paths;
6772
	public $firstRoot;
6773
	public $type = 'Ruleset';
6774
	public $multiMedia;
6775
	public $allExtends;
6776
6777
	public $ruleset_id;
6778
	public $originalRuleset;
6779
6780
	public $first_oelements;
6781
6782
	public function SetRulesetIndex(){
6783
		$this->ruleset_id = Less_Parser::$next_id++;
6784
		$this->originalRuleset = $this->ruleset_id;
6785
6786
		if( $this->selectors ){
6787
			foreach($this->selectors as $sel){
6788
				if( $sel->_oelements ){
6789
					$this->first_oelements[$sel->_oelements[0]] = true;
6790
				}
6791
			}
6792
		}
6793
	}
6794
6795
	public function __construct($selectors, $rules, $strictImports = null){
6796
		$this->selectors = $selectors;
6797
		$this->rules = $rules;
6798
		$this->lookups = array();
6799
		$this->strictImports = $strictImports;
6800
		$this->SetRulesetIndex();
6801
	}
6802
6803
	public function accept( $visitor ){
6804
		if( $this->paths ){
6805
			$paths_len = count($this->paths);
6806
			for($i = 0,$paths_len; $i < $paths_len; $i++ ){
6807
				$this->paths[$i] = $visitor->visitArray($this->paths[$i]);
6808
			}
6809
		}elseif( $this->selectors ){
6810
			$this->selectors = $visitor->visitArray($this->selectors);
6811
		}
6812
6813
		if( $this->rules ){
6814
			$this->rules = $visitor->visitArray($this->rules);
6815
		}
6816
	}
6817
6818
	public function compile($env){
6819
6820
		$ruleset = $this->PrepareRuleset($env);
6821
6822
6823
		// Store the frames around mixin definitions,
6824
		// so they can be evaluated like closures when the time comes.
6825
		$rsRuleCnt = count($ruleset->rules);
6826 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...
6827
			if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){
6828
				$ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
6829
			}
6830
		}
6831
6832
		$mediaBlockCount = 0;
6833
		if( $env instanceof Less_Environment ){
6834
			$mediaBlockCount = count($env->mediaBlocks);
6835
		}
6836
6837
		// Evaluate mixin calls.
6838
		$this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
6839
6840
6841
		// Evaluate everything else
6842 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...
6843
			if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){
6844
				$ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
6845
			}
6846
		}
6847
6848
        // Evaluate everything else
6849
		for( $i=0; $i<$rsRuleCnt; $i++ ){
6850
			$rule = $ruleset->rules[$i];
6851
6852
            // for rulesets, check if it is a css guard and can be removed
6853
			if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){
6854
6855
                // check if it can be folded in (e.g. & where)
6856
				if( $rule->selectors[0]->isJustParentSelector() ){
6857
					array_splice($ruleset->rules,$i--,1);
6858
					$rsRuleCnt--;
6859
6860
					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...
6861
						$subRule = $rule->rules[$j];
6862
						if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){
6863
							array_splice($ruleset->rules, ++$i, 0, array($subRule));
6864
							$rsRuleCnt++;
6865
						}
6866
					}
6867
6868
                }
6869
            }
6870
        }
6871
6872
6873
		// Pop the stack
6874
		$env->shiftFrame();
6875
6876
		if ($mediaBlockCount) {
6877
			$len = count($env->mediaBlocks);
6878
			for($i = $mediaBlockCount; $i < $len; $i++ ){
6879
				$env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors);
6880
			}
6881
		}
6882
6883
		return $ruleset;
6884
	}
6885
6886
	/**
6887
	 * Compile Less_Tree_Mixin_Call objects
6888
	 *
6889
	 * @param Less_Tree_Ruleset $ruleset
6890
	 * @param integer $rsRuleCnt
6891
	 */
6892
	private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){
6893
		for($i=0; $i < $rsRuleCnt; $i++){
6894
			$rule = $ruleset->rules[$i];
6895
6896
			if( $rule instanceof Less_Tree_Mixin_Call ){
6897
				$rule = $rule->compile($env);
6898
6899
				$temp = array();
6900
				foreach($rule as $r){
6901
					if( ($r instanceof Less_Tree_Rule) && $r->variable ){
6902
						// do not pollute the scope if the variable is
6903
						// already there. consider returning false here
6904
						// but we need a way to "return" variable from mixins
6905
						if( !$ruleset->variable($r->name) ){
6906
							$temp[] = $r;
6907
						}
6908
					}else{
6909
						$temp[] = $r;
6910
					}
6911
				}
6912
				$temp_count = count($temp)-1;
6913
				array_splice($ruleset->rules, $i, 1, $temp);
6914
				$rsRuleCnt += $temp_count;
6915
				$i += $temp_count;
6916
				$ruleset->resetCache();
6917
6918
			}elseif( $rule instanceof Less_Tree_RulesetCall ){
6919
6920
				$rule = $rule->compile($env);
6921
				$rules = array();
6922
				foreach($rule->rules as $r){
6923
					if( ($r instanceof Less_Tree_Rule) && $r->variable ){
6924
						continue;
6925
					}
6926
					$rules[] = $r;
6927
				}
6928
6929
				array_splice($ruleset->rules, $i, 1, $rules);
6930
				$temp_count = count($rules);
6931
				$rsRuleCnt += $temp_count - 1;
6932
				$i += $temp_count-1;
6933
				$ruleset->resetCache();
6934
			}
6935
6936
		}
6937
	}
6938
6939
6940
	/**
6941
	 * Compile the selectors and create a new ruleset object for the compile() method
6942
	 *
6943
	 */
6944
	private function PrepareRuleset($env){
6945
6946
		$hasOnePassingSelector = false;
6947
		$selectors = array();
6948
		if( $this->selectors ){
6949
			Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
6950
6951
			foreach($this->selectors as $s){
6952
				$selector = $s->compile($env);
6953
				$selectors[] = $selector;
6954
				if( $selector->evaldCondition ){
6955
					$hasOnePassingSelector = true;
6956
				}
6957
			}
6958
6959
			Less_Tree_DefaultFunc::reset();
6960
		} else {
6961
			$hasOnePassingSelector = true;
6962
		}
6963
6964
		if( $this->rules && $hasOnePassingSelector ){
6965
			$rules = $this->rules;
6966
		}else{
6967
			$rules = array();
6968
		}
6969
6970
		$ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
6971
6972
		$ruleset->originalRuleset = $this->ruleset_id;
6973
6974
		$ruleset->root = $this->root;
6975
		$ruleset->firstRoot = $this->firstRoot;
6976
		$ruleset->allowImports = $this->allowImports;
6977
6978
6979
		// push the current ruleset to the frames stack
6980
		$env->unshiftFrame($ruleset);
6981
6982
6983
		// Evaluate imports
6984
		if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){
6985
			$ruleset->evalImports($env);
6986
		}
6987
6988
		return $ruleset;
6989
	}
6990
6991
	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...
6992
6993
		$rules_len = count($this->rules);
6994
		for($i=0; $i < $rules_len; $i++){
6995
			$rule = $this->rules[$i];
6996
6997
			if( $rule instanceof Less_Tree_Import ){
6998
				$rules = $rule->compile($env);
6999
				if( is_array($rules) ){
7000
					array_splice($this->rules, $i, 1, $rules);
7001
					$temp_count = count($rules)-1;
7002
					$i += $temp_count;
7003
					$rules_len += $temp_count;
7004
				}else{
7005
					array_splice($this->rules, $i, 1, array($rules));
7006
				}
7007
7008
				$this->resetCache();
7009
			}
7010
		}
7011
	}
7012
7013
	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...
7014
7015
		$important_rules = array();
7016
		foreach($this->rules as $rule){
7017
			if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset ){
7018
				$important_rules[] = $rule->makeImportant();
7019
			}else{
7020
				$important_rules[] = $rule;
7021
			}
7022
		}
7023
7024
		return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports );
7025
	}
7026
7027
	public function matchArgs($args){
7028
		return !$args;
7029
	}
7030
7031
	// lets you call a css selector with a guard
7032
	public function matchCondition( $args, $env ){
7033
		$lastSelector = end($this->selectors);
7034
7035
		if( !$lastSelector->evaldCondition ){
7036
			return false;
7037
		}
7038
		if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){
7039
			return false;
7040
		}
7041
		return true;
7042
	}
7043
7044
	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...
7045
		$this->_rulesets = null;
7046
		$this->_variables = null;
7047
		$this->lookups = array();
7048
	}
7049
7050
	public function variables(){
7051
		$this->_variables = array();
7052
		foreach( $this->rules as $r){
7053
			if ($r instanceof Less_Tree_Rule && $r->variable === true) {
7054
				$this->_variables[$r->name] = $r;
7055
			}
7056
		}
7057
	}
7058
7059
	public function variable($name){
7060
7061
		if( is_null($this->_variables) ){
7062
			$this->variables();
7063
		}
7064
		return isset($this->_variables[$name]) ? $this->_variables[$name] : null;
7065
	}
7066
7067
	public function find( $selector, $self = null ){
7068
7069
		$key = implode(' ',$selector->_oelements);
7070
7071
		if( !isset($this->lookups[$key]) ){
7072
7073
			if( !$self ){
7074
				$self = $this->ruleset_id;
7075
			}
7076
7077
			$this->lookups[$key] = array();
7078
7079
			$first_oelement = $selector->_oelements[0];
7080
7081
			foreach($this->rules as $rule){
7082
				if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){
7083
7084
					if( isset($rule->first_oelements[$first_oelement]) ){
7085
7086
						foreach( $rule->selectors as $ruleSelector ){
7087
							$match = $selector->match($ruleSelector);
7088
							if( $match ){
7089
								if( $selector->elements_len > $match ){
7090
									$this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self));
7091
								} else {
7092
									$this->lookups[$key][] = $rule;
7093
								}
7094
								break;
7095
							}
7096
						}
7097
					}
7098
				}
7099
			}
7100
		}
7101
7102
		return $this->lookups[$key];
7103
	}
7104
7105
7106
	/**
7107
	 * @see Less_Tree::genCSS
7108
	 */
7109
	public function genCSS( $output ){
7110
7111
		if( !$this->root ){
7112
			Less_Environment::$tabLevel++;
7113
		}
7114
7115
		$tabRuleStr = $tabSetStr = '';
7116
		if( !Less_Parser::$options['compress'] ){
7117
			if( Less_Environment::$tabLevel ){
7118
				$tabRuleStr = "\n".str_repeat( '  ' , Less_Environment::$tabLevel );
7119
				$tabSetStr = "\n".str_repeat( '  ' , Less_Environment::$tabLevel-1 );
7120
			}else{
7121
				$tabSetStr = $tabRuleStr = "\n";
7122
			}
7123
		}
7124
7125
7126
		$ruleNodes = array();
7127
		$rulesetNodes = array();
7128
		foreach($this->rules as $rule){
7129
7130
			$class = get_class($rule);
7131
			if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){
7132
				$rulesetNodes[] = $rule;
7133
			}else{
7134
				$ruleNodes[] = $rule;
7135
			}
7136
		}
7137
7138
		// If this is the root node, we don't render
7139
		// 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...
7140
		if( !$this->root ){
7141
7142
			/*
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...
7143
			debugInfo = tree.debugInfo(env, this, tabSetStr);
7144
7145
			if (debugInfo) {
7146
				output.add(debugInfo);
7147
				output.add(tabSetStr);
7148
			}
7149
			*/
7150
7151
			$paths_len = count($this->paths);
7152
			for( $i = 0; $i < $paths_len; $i++ ){
7153
				$path = $this->paths[$i];
7154
				$firstSelector = true;
7155
7156
				foreach($path as $p){
7157
					$p->genCSS( $output, $firstSelector );
7158
					$firstSelector = false;
7159
				}
7160
7161
				if( $i + 1 < $paths_len ){
7162
					$output->add( ',' . $tabSetStr );
7163
				}
7164
			}
7165
7166
			$output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr );
7167
		}
7168
7169
		// Compile rules and rulesets
7170
		$ruleNodes_len = count($ruleNodes);
7171
		$rulesetNodes_len = count($rulesetNodes);
7172
		for( $i = 0; $i < $ruleNodes_len; $i++ ){
7173
			$rule = $ruleNodes[$i];
7174
7175
			// @page{ directive ends up with root elements inside it, a mix of rules and rulesets
7176
			// In this instance we do not know whether it is the last property
7177
			if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){
7178
				Less_Environment::$lastRule = true;
7179
			}
7180
7181
			$rule->genCSS( $output );
7182
7183
			if( !Less_Environment::$lastRule ){
7184
				$output->add( $tabRuleStr );
7185
			}else{
7186
				Less_Environment::$lastRule = false;
7187
			}
7188
		}
7189
7190
		if( !$this->root ){
7191
			$output->add( $tabSetStr . '}' );
7192
			Less_Environment::$tabLevel--;
7193
		}
7194
7195
		$firstRuleset = true;
7196
		$space = ($this->root ? $tabRuleStr : $tabSetStr);
7197
		for( $i = 0; $i < $rulesetNodes_len; $i++ ){
7198
7199
			if( $ruleNodes_len && $firstRuleset ){
7200
				$output->add( $space );
7201
			}elseif( !$firstRuleset ){
7202
				$output->add( $space );
7203
			}
7204
			$firstRuleset = false;
7205
			$rulesetNodes[$i]->genCSS( $output);
7206
		}
7207
7208
		if( !Less_Parser::$options['compress'] && $this->firstRoot ){
7209
			$output->add( "\n" );
7210
		}
7211
7212
	}
7213
7214
7215
	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...
7216
		if( !$this->selectors ){
7217
			return;
7218
		}
7219
		foreach($this->selectors as $selector){
7220
			$selector->markReferenced();
7221
		}
7222
	}
7223
7224
	public function joinSelectors( $context, $selectors ){
7225
		$paths = array();
7226
		if( is_array($selectors) ){
7227
			foreach($selectors as $selector) {
7228
				$this->joinSelector( $paths, $context, $selector);
7229
			}
7230
		}
7231
		return $paths;
7232
	}
7233
7234
	public function joinSelector( &$paths, $context, $selector){
7235
7236
		$hasParentSelector = false;
7237
7238
		foreach($selector->elements as $el) {
7239
			if( $el->value === '&') {
7240
				$hasParentSelector = true;
7241
			}
7242
		}
7243
7244
		if( !$hasParentSelector ){
7245
			if( $context ){
7246
				foreach($context as $context_el){
7247
					$paths[] = array_merge($context_el, array($selector) );
7248
				}
7249
			}else {
7250
				$paths[] = array($selector);
7251
			}
7252
			return;
7253
		}
7254
7255
7256
		// The paths are [[Selector]]
7257
		// The first list is a list of comma separated selectors
7258
		// The inner list is a list of inheritance separated selectors
7259
		// e.g.
7260
		// .a, .b {
7261
		//   .c {
7262
		//   }
7263
		// }
7264
		// == [[.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...
7265
		//
7266
7267
		// the elements from the current selector so far
7268
		$currentElements = array();
7269
		// the current list of new selectors to add to the path.
7270
		// We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
7271
		// by the parents
7272
		$newSelectors = array(array());
7273
7274
7275
		foreach( $selector->elements as $el){
7276
7277
			// non parent reference elements just get added
7278
			if( $el->value !== '&' ){
7279
				$currentElements[] = $el;
7280
			} else {
7281
				// the new list of selectors to add
7282
				$selectorsMultiplied = array();
7283
7284
				// merge the current list of non parent selector elements
7285
				// on to the current list of selectors to add
7286
				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...
7287
					$this->mergeElementsOnToSelectors( $currentElements, $newSelectors);
7288
				}
7289
7290
				// loop through our current selectors
7291
				foreach($newSelectors as $sel){
7292
7293
					// if we don't have any parent paths, the & might be in a mixin so that it can be used
7294
					// whether there are parents or not
7295
					if( !$context ){
7296
						// the combinator used on el should now be applied to the next element instead so that
7297
						// it is not lost
7298
						if( $sel ){
7299
							$sel[0]->elements = array_slice($sel[0]->elements,0);
7300
							$sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo );
7301
						}
7302
						$selectorsMultiplied[] = $sel;
7303
					}else {
7304
7305
						// and the parent selectors
7306
						foreach($context as $parentSel){
7307
							// We need to put the current selectors
7308
							// then join the last selector's elements on to the parents selectors
7309
7310
							// our new selector path
7311
							$newSelectorPath = array();
7312
							// selectors from the parent after the join
7313
							$afterParentJoin = array();
7314
							$newJoinedSelectorEmpty = true;
7315
7316
							//construct the joined selector - if & is the first thing this will be empty,
7317
							// if not newJoinedSelector will be the last set of elements in the selector
7318
							if( $sel ){
7319
								$newSelectorPath = $sel;
7320
								$lastSelector = array_pop($newSelectorPath);
7321
								$newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) );
7322
								$newJoinedSelectorEmpty = false;
7323
							}
7324
							else {
7325
								$newJoinedSelector = $selector->createDerived(array());
7326
							}
7327
7328
							//put together the parent selectors after the join
7329
							if ( count($parentSel) > 1) {
7330
								$afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) );
7331
							}
7332
7333
							if ( $parentSel ){
7334
								$newJoinedSelectorEmpty = false;
7335
7336
								// join the elements so far with the first part of the parent
7337
								$newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
7338
7339
								$newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) );
7340
							}
7341
7342
							if (!$newJoinedSelectorEmpty) {
7343
								// now add the joined selector
7344
								$newSelectorPath[] = $newJoinedSelector;
7345
							}
7346
7347
							// and the rest of the parent
7348
							$newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
7349
7350
							// add that to our new set of selectors
7351
							$selectorsMultiplied[] = $newSelectorPath;
7352
						}
7353
					}
7354
				}
7355
7356
				// our new selectors has been multiplied, so reset the state
7357
				$newSelectors = $selectorsMultiplied;
7358
				$currentElements = array();
7359
			}
7360
		}
7361
7362
		// if we have any elements left over (e.g. .a& .b == .b)
7363
		// add them on to all the current selectors
7364
		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...
7365
			$this->mergeElementsOnToSelectors($currentElements, $newSelectors);
7366
		}
7367
		foreach( $newSelectors as $new_sel){
7368
			if( $new_sel ){
7369
				$paths[] = $new_sel;
7370
			}
7371
		}
7372
	}
7373
7374
	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...
7375
7376
		if( !$selectors ){
7377
			$selectors[] = array( new Less_Tree_Selector($elements) );
7378
			return;
7379
		}
7380
7381
7382
		foreach( $selectors as &$sel){
7383
7384
			// if the previous thing in sel is a parent this needs to join on to it
7385
			if( $sel ){
7386
				$last = count($sel)-1;
7387
				$sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) );
7388
			}else{
7389
				$sel[] = new Less_Tree_Selector( $elements );
7390
			}
7391
		}
7392
	}
7393
}
7394
 
7395
7396
/**
7397
 * RulesetCall
7398
 *
7399
 * @package Less
7400
 * @subpackage tree
7401
 */
7402
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...
7403
7404
	public $variable;
7405
	public $type = "RulesetCall";
7406
7407
    public function __construct($variable){
7408
		$this->variable = $variable;
7409
	}
7410
7411
    public function accept($visitor) {}
7412
7413
    public function compile( $env ){
7414
		$variable = new Less_Tree_Variable($this->variable);
7415
		$detachedRuleset = $variable->compile($env);
7416
		return $detachedRuleset->callEval($env);
7417
	}
7418
}
7419
7420
 
7421
7422
/**
7423
 * Selector
7424
 *
7425
 * @package Less
7426
 * @subpackage tree
7427
 */
7428
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...
7429
7430
	public $elements;
7431
	public $condition;
7432
	public $extendList = array();
7433
	public $_css;
7434
	public $index;
7435
	public $evaldCondition = false;
7436
	public $type = 'Selector';
7437
	public $currentFileInfo = array();
7438
	public $isReferenced;
7439
	public $mediaEmpty;
7440
7441
	public $elements_len = 0;
7442
7443
	public $_oelements;
7444
	public $_oelements_len;
7445
	public $cacheable = true;
7446
7447
	/**
7448
	 * @param boolean $isReferenced
7449
	 */
7450
	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...
7451
7452
		$this->elements = $elements;
7453
		$this->elements_len = count($elements);
7454
		$this->extendList = $extendList;
7455
		$this->condition = $condition;
7456
		if( $currentFileInfo ){
7457
			$this->currentFileInfo = $currentFileInfo;
7458
		}
7459
		$this->isReferenced = $isReferenced;
7460
		if( !$condition ){
7461
			$this->evaldCondition = true;
7462
		}
7463
7464
		$this->CacheElements();
7465
	}
7466
7467
    public function accept($visitor) {
7468
		$this->elements = $visitor->visitArray($this->elements);
7469
		$this->extendList = $visitor->visitArray($this->extendList);
7470
		if( $this->condition ){
7471
			$this->condition = $visitor->visitObj($this->condition);
7472
		}
7473
7474
		if( $visitor instanceof Less_Visitor_extendFinder ){
7475
			$this->CacheElements();
7476
		}
7477
	}
7478
7479
    public function createDerived( $elements, $extendList = null, $evaldCondition = null ){
7480
		$newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced);
7481
		$newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
7482
		return $newSelector;
7483
	}
7484
7485
7486
	public function match( $other ){
7487
7488
		if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){
7489
			return 0;
7490
		}
7491
7492
		for( $i = 0; $i < $other->_oelements_len; $i++ ){
7493
			if( $this->elements[$i]->value !== $other->_oelements[$i]) {
7494
				return 0;
7495
			}
7496
		}
7497
7498
		return $other->_oelements_len; // return number of matched elements
7499
	}
7500
7501
7502
	public function CacheElements(){
7503
7504
		$this->_oelements = array();
7505
		$css = '';
7506
7507
		foreach($this->elements as $v){
7508
7509
			$css .= $v->combinator;
7510
			if( !$v->value_is_object ){
7511
				$css .= $v->value;
7512
				continue;
7513
			}
7514
7515
			if( !property_exists($v->value,'value') || !is_string($v->value->value) ){
7516
				$this->cacheable = false;
7517
				return;
7518
			}
7519
			$css .= $v->value->value;
7520
		}
7521
7522
		$this->_oelements_len = preg_match_all('/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches);
7523
		if( $this->_oelements_len ){
7524
			$this->_oelements = $matches[0];
7525
7526
			if( $this->_oelements[0] === '&' ){
7527
				array_shift($this->_oelements);
7528
				$this->_oelements_len--;
7529
			}
7530
		}
7531
	}
7532
7533
	public function isJustParentSelector(){
7534
		return !$this->mediaEmpty &&
7535
			count($this->elements) === 1 &&
7536
			$this->elements[0]->value === '&' &&
7537
			($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '');
7538
	}
7539
7540
	public function compile($env) {
7541
7542
		$elements = array();
7543
		foreach($this->elements as $el){
7544
			$elements[] = $el->compile($env);
7545
		}
7546
7547
		$extendList = array();
7548
		foreach($this->extendList as $el){
7549
			$extendList[] = $el->compile($el);
7550
		}
7551
7552
		$evaldCondition = false;
7553
		if( $this->condition ){
7554
			$evaldCondition = $this->condition->compile($env);
7555
		}
7556
7557
		return $this->createDerived( $elements, $extendList, $evaldCondition );
7558
	}
7559
7560
7561
	/**
7562
	 * @see Less_Tree::genCSS
7563
	 */
7564
    public function genCSS( $output, $firstSelector = true ){
7565
7566
		if( !$firstSelector && $this->elements[0]->combinator === "" ){
7567
			$output->add(' ', $this->currentFileInfo, $this->index);
7568
		}
7569
7570
		foreach($this->elements as $element){
7571
			$element->genCSS( $output );
7572
		}
7573
	}
7574
7575
    public function markReferenced(){
7576
		$this->isReferenced = true;
7577
	}
7578
7579
    public function getIsReferenced(){
7580
		return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced;
7581
	}
7582
7583
    public function getIsOutput(){
7584
		return $this->evaldCondition;
7585
	}
7586
7587
}
7588
 
7589
7590
/**
7591
 * UnicodeDescriptor
7592
 *
7593
 * @package Less
7594
 * @subpackage tree
7595
 */
7596
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...
7597
7598
	public $value;
7599
	public $type = 'UnicodeDescriptor';
7600
7601
	public function __construct($value){
7602
		$this->value = $value;
7603
	}
7604
7605
    /**
7606
     * @see Less_Tree::genCSS
7607
     */
7608
	public function genCSS( $output ){
7609
		$output->add( $this->value );
7610
	}
7611
7612
	public function compile(){
7613
		return $this;
7614
	}
7615
}
7616
7617
 
7618
7619
/**
7620
 * Unit
7621
 *
7622
 * @package Less
7623
 * @subpackage tree
7624
 */
7625
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...
7626
7627
	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...
7628
	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...
7629
	public $backupUnit;
7630
	public $type = 'Unit';
7631
7632
    public function __construct($numerator = array(), $denominator = array(), $backupUnit = null ){
7633
		$this->numerator = $numerator;
7634
		$this->denominator = $denominator;
7635
		$this->backupUnit = $backupUnit;
7636
	}
7637
7638
    public function __clone(){
7639
	}
7640
7641
    /**
7642
     * @see Less_Tree::genCSS
7643
     */
7644
    public function genCSS( $output ){
7645
7646
		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...
7647
			$output->add( $this->numerator[0] );
7648
		}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...
7649
			$output->add( $this->denominator[0] );
7650
		}elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){
7651
			$output->add( $this->backupUnit );
7652
			return ;
7653
		}
7654
	}
7655
7656
    public function toString(){
7657
		$returnStr = implode('*',$this->numerator);
7658
		foreach($this->denominator as $d){
7659
			$returnStr .= '/'.$d;
7660
		}
7661
		return $returnStr;
7662
	}
7663
7664
    public function __toString(){
7665
		return $this->toString();
7666
	}
7667
7668
7669
	/**
7670
	 * @param Less_Tree_Unit $other
7671
	 */
7672
    public function compare($other) {
7673
		return $this->is( $other->toString() ) ? 0 : -1;
7674
	}
7675
7676
    public function is($unitString){
7677
		return $this->toString() === $unitString;
7678
	}
7679
7680
    public function isLength(){
7681
		$css = $this->toCSS();
7682
		return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css);
7683
	}
7684
7685
    public function isAngle() {
7686
		return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] );
7687
	}
7688
7689
    public function isEmpty(){
7690
		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...
7691
	}
7692
7693
    public function isSingular() {
7694
		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...
7695
	}
7696
7697
7698
    public function usedUnits(){
7699
		$result = array();
7700
7701
		foreach(Less_Tree_UnitConversions::$groups as $groupName){
7702
			$group = Less_Tree_UnitConversions::${$groupName};
7703
7704 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...
7705
				if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
7706
					$result[$groupName] = $atomicUnit;
7707
				}
7708
			}
7709
7710 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...
7711
				if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
7712
					$result[$groupName] = $atomicUnit;
7713
				}
7714
			}
7715
		}
7716
7717
		return $result;
7718
	}
7719
7720
    public function cancel(){
7721
		$counter = array();
7722
		$backup = null;
7723
7724 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...
7725
			if( !$backup ){
7726
				$backup = $atomicUnit;
7727
			}
7728
			$counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1;
7729
		}
7730
7731 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...
7732
			if( !$backup ){
7733
				$backup = $atomicUnit;
7734
			}
7735
			$counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1;
7736
		}
7737
7738
		$this->numerator = array();
7739
		$this->denominator = array();
7740
7741
		foreach($counter as $atomicUnit => $count){
7742
			if( $count > 0 ){
7743
				for( $i = 0; $i < $count; $i++ ){
7744
					$this->numerator[] = $atomicUnit;
7745
				}
7746
			}elseif( $count < 0 ){
7747
				for( $i = 0; $i < -$count; $i++ ){
7748
					$this->denominator[] = $atomicUnit;
7749
				}
7750
			}
7751
		}
7752
7753
		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...
7754
			$this->backupUnit = $backup;
7755
		}
7756
7757
		sort($this->numerator);
7758
		sort($this->denominator);
7759
	}
7760
7761
7762
}
7763
7764
 
7765
7766
/**
7767
 * UnitConversions
7768
 *
7769
 * @package Less
7770
 * @subpackage tree
7771
 */
7772
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...
7773
7774
	public static $groups = array('length','duration','angle');
7775
7776
	public static $length = array(
7777
		'm'=> 1,
7778
		'cm'=> 0.01,
7779
		'mm'=> 0.001,
7780
		'in'=> 0.0254,
7781
		'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...
7782
		'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...
7783
		'pc'=> 0.004233333, // 0.0254 / 72 * 12
7784
		);
7785
7786
	public static $duration = array(
7787
		's'=> 1,
7788
		'ms'=> 0.001
7789
		);
7790
7791
	public static $angle = array(
7792
		'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...
7793
		'deg' => 0.002777778, 		// 1/360,
7794
		'grad'=> 0.0025,			// 1/400,
7795
		'turn'=> 1
7796
		);
7797
7798
} 
7799
7800
/**
7801
 * Url
7802
 *
7803
 * @package Less
7804
 * @subpackage tree
7805
 */
7806
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...
7807
7808
	public $attrs;
7809
	public $value;
7810
	public $currentFileInfo;
7811
	public $isEvald;
7812
	public $type = 'Url';
7813
7814
	public function __construct($value, $currentFileInfo = null, $isEvald = null){
7815
		$this->value = $value;
7816
		$this->currentFileInfo = $currentFileInfo;
7817
		$this->isEvald = $isEvald;
7818
	}
7819
7820
    public function accept( $visitor ){
7821
		$this->value = $visitor->visitObj($this->value);
7822
	}
7823
7824
    /**
7825
     * @see Less_Tree::genCSS
7826
     */
7827
    public function genCSS( $output ){
7828
		$output->add( 'url(' );
7829
		$this->value->genCSS( $output );
7830
		$output->add( ')' );
7831
	}
7832
7833
	/**
7834
	 * @param Less_Functions $ctx
7835
	 */
7836
	public function compile($ctx){
7837
		$val = $this->value->compile($ctx);
7838
7839
		if( !$this->isEvald ){
7840
			// Add the base path if the URL is relative
7841
			if( Less_Parser::$options['relativeUrls']
7842
				&& $this->currentFileInfo
7843
				&& is_string($val->value)
7844
				&& Less_Environment::isPathRelative($val->value)
7845
			){
7846
				$rootpath = $this->currentFileInfo['uri_root'];
7847
				if ( !$val->quote ){
7848
					$rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath );
7849
				}
7850
				$val->value = $rootpath . $val->value;
7851
			}
7852
7853
			$val->value = Less_Environment::normalizePath( $val->value);
7854
		}
7855
7856
		// Add cache buster if enabled
7857
		if( Less_Parser::$options['urlArgs'] ){
7858
			if( !preg_match('/^\s*data:/',$val->value) ){
7859
				$delimiter = strpos($val->value,'?') === false ? '?' : '&';
7860
				$urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
7861
				$hash_pos = strpos($val->value,'#');
7862
				if( $hash_pos !== false ){
7863
					$val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0);
7864
				} else {
7865
					$val->value .= $urlArgs;
7866
				}
7867
			}
7868
		}
7869
7870
		return new Less_Tree_URL($val, $this->currentFileInfo, true);
7871
	}
7872
7873
}
7874
 
7875
7876
/**
7877
 * Value
7878
 *
7879
 * @package Less
7880
 * @subpackage tree
7881
 */
7882
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...
7883
7884
	public $type = 'Value';
7885
	public $value;
7886
7887
	public function __construct($value){
7888
		$this->value = $value;
7889
	}
7890
7891
    public function accept($visitor) {
7892
		$this->value = $visitor->visitArray($this->value);
7893
	}
7894
7895
	public function compile($env){
7896
7897
		$ret = array();
7898
		$i = 0;
7899
		foreach($this->value as $i => $v){
7900
			$ret[] = $v->compile($env);
7901
		}
7902
		if( $i > 0 ){
7903
			return new Less_Tree_Value($ret);
7904
		}
7905
		return $ret[0];
7906
	}
7907
7908
    /**
7909
     * @see Less_Tree::genCSS
7910
     */
7911 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...
7912
		$len = count($this->value);
7913
		for($i = 0; $i < $len; $i++ ){
7914
			$this->value[$i]->genCSS( $output );
7915
			if( $i+1 < $len ){
7916
				$output->add( Less_Environment::$_outputMap[','] );
7917
			}
7918
		}
7919
	}
7920
7921
}
7922
 
7923
7924
/**
7925
 * Variable
7926
 *
7927
 * @package Less
7928
 * @subpackage tree
7929
 */
7930
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...
7931
7932
	public $name;
7933
	public $index;
7934
	public $currentFileInfo;
7935
	public $evaluating = false;
7936
	public $type = 'Variable';
7937
7938
    /**
7939
     * @param string $name
7940
     */
7941
    public function __construct($name, $index = null, $currentFileInfo = null) {
7942
        $this->name = $name;
7943
        $this->index = $index;
7944
		$this->currentFileInfo = $currentFileInfo;
7945
    }
7946
7947
	public function compile($env) {
7948
7949
		if( $this->name[1] === '@' ){
7950
			$v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo);
7951
			$name = '@' . $v->compile($env)->value;
7952
		}else{
7953
			$name = $this->name;
7954
		}
7955
7956
		if ($this->evaluating) {
7957
			throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo);
7958
		}
7959
7960
		$this->evaluating = true;
7961
7962
		foreach($env->frames as $frame){
7963
			if( $v = $frame->variable($name) ){
7964
				$r = $v->value->compile($env);
7965
				$this->evaluating = false;
7966
				return $r;
7967
			}
7968
		}
7969
7970
		throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo);
7971
	}
7972
7973
}
7974
 
7975
7976
7977
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...
7978
7979
	public $selector;
7980
	public $arguments;
7981
	public $index;
7982
	public $currentFileInfo;
7983
7984
	public $important;
7985
	public $type = 'MixinCall';
7986
7987
	/**
7988
	 * less.js: tree.mixin.Call
7989
	 *
7990
	 */
7991
	public function __construct($elements, $args, $index, $currentFileInfo, $important = false){
7992
		$this->selector = new Less_Tree_Selector($elements);
7993
		$this->arguments = $args;
7994
		$this->index = $index;
7995
		$this->currentFileInfo = $currentFileInfo;
7996
		$this->important = $important;
7997
	}
7998
7999
	//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...
8000
	//	$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...
8001
	//	$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...
8002
	//}
8003
8004
8005
	public function compile($env){
8006
8007
		$rules = array();
8008
		$match = false;
8009
		$isOneFound = false;
8010
		$candidates = array();
8011
		$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...
8012
		$conditionResult = array();
8013
8014
		$args = array();
8015
		foreach($this->arguments as $a){
8016
			$args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) );
8017
		}
8018
8019
		foreach($env->frames as $frame){
8020
8021
			$mixins = $frame->find($this->selector);
8022
8023
			if( !$mixins ){
8024
				continue;
8025
			}
8026
8027
			$isOneFound = true;
8028
			$defNone = 0;
8029
			$defTrue = 1;
8030
			$defFalse = 2;
8031
8032
			// To make `default()` function independent of definition order we have two "subpasses" here.
8033
			// At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
8034
			// and build candidate list with corresponding flags. Then, when we know all possible matches,
8035
			// we make a final decision.
8036
8037
			$mixins_len = count($mixins);
8038
			for( $m = 0; $m < $mixins_len; $m++ ){
8039
				$mixin = $mixins[$m];
8040
8041
				if( $this->IsRecursive( $env, $mixin ) ){
8042
					continue;
8043
				}
8044
8045
				if( $mixin->matchArgs($args, $env) ){
8046
8047
					$candidate = array('mixin' => $mixin, 'group' => $defNone);
8048
8049
					if( $mixin instanceof Less_Tree_Ruleset ){
8050
8051
						for( $f = 0; $f < 2; $f++ ){
8052
							Less_Tree_DefaultFunc::value($f);
8053
							$conditionResult[$f] = $mixin->matchCondition( $args, $env);
8054
						}
8055
						if( $conditionResult[0] || $conditionResult[1] ){
8056
							if( $conditionResult[0] != $conditionResult[1] ){
8057
								$candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
8058
							}
8059
8060
							$candidates[] = $candidate;
8061
						}
8062
					}else{
8063
						$candidates[] = $candidate;
8064
					}
8065
8066
					$match = true;
8067
				}
8068
			}
8069
8070
			Less_Tree_DefaultFunc::reset();
8071
8072
8073
			$count = array(0, 0, 0);
8074
			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...
8075
				$count[ $candidates[$m]['group'] ]++;
8076
			}
8077
8078
			if( $count[$defNone] > 0 ){
8079
				$defaultResult = $defFalse;
8080
			} else {
8081
				$defaultResult = $defTrue;
8082
				if( ($count[$defTrue] + $count[$defFalse]) > 1 ){
8083
					throw new Exception( 'Ambiguous use of `default()` found when matching for `'. $this->format($args) + '`' );
8084
				}
8085
			}
8086
8087
8088
			$candidates_length = count($candidates);
8089
			$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...
8090
8091
			for( $m = 0; $m < $candidates_length; $m++){
8092
				$candidate = $candidates[$m]['group'];
8093
				if( ($candidate === $defNone) || ($candidate === $defaultResult) ){
8094
					try{
8095
						$mixin = $candidates[$m]['mixin'];
8096
						if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
8097
							$mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false);
8098
							$mixin->originalRuleset = $mixins[$m]->originalRuleset;
8099
						}
8100
						$rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules);
8101
					} catch (Exception $e) {
8102
						//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...
8103
						throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo);
8104
					}
8105
				}
8106
			}
8107
8108
			if( $match ){
8109
				if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){
8110
					Less_Tree::ReferencedArray($rules);
8111
				}
8112
8113
				return $rules;
8114
			}
8115
		}
8116
8117
		if( $isOneFound ){
8118
			throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo);
8119
8120
		}else{
8121
			throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index);
8122
		}
8123
8124
	}
8125
8126
	/**
8127
	 * Format the args for use in exception messages
8128
	 *
8129
	 */
8130
	private function Format($args){
8131
		$message = array();
8132
		if( $args ){
8133
			foreach($args as $a){
8134
				$argValue = '';
8135
				if( $a['name'] ){
8136
					$argValue += $a['name']+':';
8137
				}
8138
				if( is_object($a['value']) ){
8139
					$argValue += $a['value']->toCSS();
8140
				}else{
8141
					$argValue += '???';
8142
				}
8143
				$message[] = $argValue;
8144
			}
8145
		}
8146
		return implode(', ',$message);
8147
	}
8148
8149
8150
	/**
8151
	 * Are we in a recursive mixin call?
8152
	 *
8153
	 * @return bool
8154
	 */
8155
	private function IsRecursive( $env, $mixin ){
8156
8157
		foreach($env->frames as $recur_frame){
8158
			if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
8159
8160
				if( $mixin === $recur_frame ){
8161
					return true;
8162
				}
8163
8164
				if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){
8165
					return true;
8166
				}
8167
			}
8168
		}
8169
8170
		return false;
8171
	}
8172
8173
}
8174
8175
8176
 
8177
8178
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...
8179
	public $name;
8180
	public $selectors;
8181
	public $params;
8182
	public $arity		= 0;
8183
	public $rules;
8184
	public $lookups		= array();
8185
	public $required	= 0;
8186
	public $frames		= array();
8187
	public $condition;
8188
	public $variadic;
8189
	public $type		= 'MixinDefinition';
8190
8191
8192
	// less.js : /lib/less/tree/mixin.js : tree.mixin.Definition
8193
	public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = array() ){
8194
		$this->name = $name;
8195
		$this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name))));
8196
8197
		$this->params = $params;
8198
		$this->condition = $condition;
8199
		$this->variadic = $variadic;
8200
		$this->rules = $rules;
8201
8202
		if( $params ){
8203
			$this->arity = count($params);
8204
			foreach( $params as $p ){
8205
				if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) {
8206
					$this->required++;
8207
				}
8208
			}
8209
		}
8210
8211
		$this->frames = $frames;
8212
		$this->SetRulesetIndex();
8213
	}
8214
8215
8216
8217
	//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...
8218
	//	$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...
8219
	//	$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...
8220
	//	$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...
8221
	//}
8222
8223
8224
	public function toCSS(){
8225
		return '';
8226
	}
8227
8228
	// less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
8229
	public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){
8230
		$frame = new Less_Tree_Ruleset(null, array());
8231
		$params = $this->params;
8232
		$mixinEnv = null;
8233
		$argsLength = 0;
8234
8235
		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...
8236
			$argsLength = count($args);
8237
			for($i = 0; $i < $argsLength; $i++ ){
8238
				$arg = $args[$i];
8239
8240
				if( $arg && $arg['name'] ){
8241
					$isNamedFound = false;
8242
8243
					foreach($params as $j => $param){
8244
						if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) {
8245
							$evaldArguments[$j] = $arg['value']->compile($env);
8246
							array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) );
8247
							$isNamedFound = true;
8248
							break;
8249
						}
8250
					}
8251
					if ($isNamedFound) {
8252
						array_splice($args, $i, 1);
8253
						$i--;
8254
						$argsLength--;
8255
						continue;
8256
					} else {
8257
						throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found');
8258
					}
8259
				}
8260
			}
8261
		}
8262
8263
		$argIndex = 0;
8264
		foreach($params as $i => $param){
8265
8266
			if ( isset($evaldArguments[$i]) ){ continue; }
8267
8268
			$arg = null;
8269
			if( isset($args[$argIndex]) ){
8270
				$arg = $args[$argIndex];
8271
			}
8272
8273
			if (isset($param['name']) && $param['name']) {
8274
8275
				if( isset($param['variadic']) ){
8276
					$varargs = array();
8277 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...
8278
						$varargs[] = $args[$j]['value']->compile($env);
8279
					}
8280
					$expression = new Less_Tree_Expression($varargs);
8281
					array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env)));
8282
				}else{
8283
					$val = ($arg && $arg['value']) ? $arg['value'] : false;
8284
8285
					if ($val) {
8286
						$val = $val->compile($env);
8287
					} else if ( isset($param['value']) ) {
8288
8289
						if( !$mixinEnv ){
8290
							$mixinEnv = new Less_Environment();
8291
							$mixinEnv->frames = array_merge( array($frame), $mixinFrames);
8292
						}
8293
8294
						$val = $param['value']->compile($mixinEnv);
8295
						$frame->resetCache();
8296
					} else {
8297
						throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")");
8298
					}
8299
8300
					array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val));
8301
					$evaldArguments[$i] = $val;
8302
				}
8303
			}
8304
8305
			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...
8306 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...
8307
					$evaldArguments[$j] = $args[$j]['value']->compile($env);
8308
				}
8309
			}
8310
			$argIndex++;
8311
		}
8312
8313
		ksort($evaldArguments);
8314
		$evaldArguments = array_values($evaldArguments);
8315
8316
		return $frame;
8317
	}
8318
8319
	public function compile($env) {
8320
		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...
8321
			return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames );
8322
		}
8323
		return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames );
8324
	}
8325
8326
	public function evalCall($env, $args = NULL, $important = NULL) {
8327
8328
		Less_Environment::$mixin_stack++;
8329
8330
		$_arguments = array();
8331
8332
		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...
8333
			$mixinFrames = array_merge($this->frames, $env->frames);
8334
		}else{
8335
			$mixinFrames = $env->frames;
8336
		}
8337
8338
		$frame = $this->compileParams($env, $mixinFrames, $args, $_arguments);
8339
8340
		$ex = new Less_Tree_Expression($_arguments);
8341
		array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env)));
8342
8343
8344
		$ruleset = new Less_Tree_Ruleset(null, $this->rules);
8345
		$ruleset->originalRuleset = $this->ruleset_id;
8346
8347
8348
		$ruleSetEnv = new Less_Environment();
8349
		$ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames );
8350
		$ruleset = $ruleset->compile( $ruleSetEnv );
8351
8352
		if( $important ){
8353
			$ruleset = $ruleset->makeImportant();
8354
		}
8355
8356
		Less_Environment::$mixin_stack--;
8357
8358
		return $ruleset;
8359
	}
8360
8361
8362
	public function matchCondition($args, $env) {
8363
8364
		if( !$this->condition ){
8365
			return true;
8366
		}
8367
8368
		// set array to prevent error on array_merge
8369
		if(!is_array($this->frames)) {
8370
             $this->frames = array();
8371
        }
8372
8373
		$frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args );
8374
8375
		$compile_env = new Less_Environment();
8376
		$compile_env->frames = array_merge(
8377
				array($frame)		// the parameter variables
8378
				, $this->frames		// the parent namespace/mixin frames
8379
				, $env->frames		// the current environment frames
8380
			);
8381
8382
		$compile_env->functions = $env->functions;
8383
8384
		return (bool)$this->condition->compile($compile_env);
8385
	}
8386
8387
	public function matchArgs($args, $env = NULL){
8388
		$argsLength = count($args);
8389
8390
		if( !$this->variadic ){
8391
			if( $argsLength < $this->required ){
8392
				return false;
8393
			}
8394
			if( $argsLength > count($this->params) ){
8395
				return false;
8396
			}
8397
		}else{
8398
			if( $argsLength < ($this->required - 1)){
8399
				return false;
8400
			}
8401
		}
8402
8403
		$len = min($argsLength, $this->arity);
8404
8405
		for( $i = 0; $i < $len; $i++ ){
8406
			if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){
8407
				if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){
8408
					return false;
8409
				}
8410
			}
8411
		}
8412
8413
		return true;
8414
	}
8415
8416
}
8417
 
8418
8419
/**
8420
 * Extend Finder Visitor
8421
 *
8422
 * @package Less
8423
 * @subpackage visitor
8424
 */
8425
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...
8426
8427
	public $contexts = array();
8428
	public $allExtendsStack;
8429
	public $foundExtends;
8430
8431
	public function __construct(){
8432
		$this->contexts = array();
8433
		$this->allExtendsStack = array(array());
8434
		parent::__construct();
8435
	}
8436
8437
	/**
8438
	 * @param Less_Tree_Ruleset $root
8439
	 */
8440
    public function run($root){
8441
		$root = $this->visitObj($root);
8442
		$root->allExtends =& $this->allExtendsStack[0];
8443
		return $root;
8444
	}
8445
8446
    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...
8447
		$visitDeeper = false;
8448
	}
8449
8450
    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...
8451
		$visitDeeper = false;
8452
	}
8453
8454
    public function visitRuleset($rulesetNode){
8455
8456
		if( $rulesetNode->root ){
8457
			return;
8458
		}
8459
8460
		$allSelectorsExtendList = array();
8461
8462
		// get &:extend(.a); rules which apply to all selectors in this ruleset
8463
		if( $rulesetNode->rules ){
8464
			foreach($rulesetNode->rules as $rule){
8465
				if( $rule instanceof Less_Tree_Extend ){
8466
					$allSelectorsExtendList[] = $rule;
8467
					$rulesetNode->extendOnEveryPath = true;
8468
				}
8469
			}
8470
		}
8471
8472
8473
		// now find every selector and apply the extends that apply to all extends
8474
		// and the ones which apply to an individual extend
8475
		foreach($rulesetNode->paths as $selectorPath){
8476
			$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...
8477
8478
			$j = 0;
8479
			foreach($selector->extendList as $extend){
8480
				$this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
8481
			}
8482
			foreach($allSelectorsExtendList as $extend){
8483
				$this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
8484
			}
8485
		}
8486
8487
		$this->contexts[] = $rulesetNode->selectors;
8488
	}
8489
8490
    public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){
8491
		$this->foundExtends = true;
8492
		$extend = clone $extend;
8493
		$extend->findSelfSelectors( $selectorPath );
8494
		$extend->ruleset = $rulesetNode;
8495
		if( $j === 0 ){
8496
			$extend->firstExtendOnThisSelectorPath = true;
8497
		}
8498
8499
		$end_key = count($this->allExtendsStack)-1;
8500
		$this->allExtendsStack[$end_key][] = $extend;
8501
		$j++;
8502
	}
8503
8504
8505
    public function visitRulesetOut( $rulesetNode ){
8506
		if( !is_object($rulesetNode) || !$rulesetNode->root ){
8507
			array_pop($this->contexts);
8508
		}
8509
	}
8510
8511
    public function visitMedia( $mediaNode ){
8512
		$mediaNode->allExtends = array();
8513
		$this->allExtendsStack[] =& $mediaNode->allExtends;
8514
	}
8515
8516
    public function visitMediaOut(){
8517
		array_pop($this->allExtendsStack);
8518
	}
8519
8520
    public function visitDirective( $directiveNode ){
8521
		$directiveNode->allExtends = array();
8522
		$this->allExtendsStack[] =& $directiveNode->allExtends;
8523
	}
8524
8525
    public function visitDirectiveOut(){
8526
		array_pop($this->allExtendsStack);
8527
	}
8528
}
8529
8530
8531
 
8532
8533
/*
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...
8534
class Less_Visitor_import extends Less_VisitorReplacing{
8535
8536
	public $_visitor;
8537
	public $_importer;
8538
	public $importCount;
8539
8540
	function __construct( $evalEnv ){
8541
		$this->env = $evalEnv;
8542
		$this->importCount = 0;
8543
		parent::__construct();
8544
	}
8545
8546
8547
	function run( $root ){
8548
		$root = $this->visitObj($root);
8549
		$this->isFinished = true;
8550
8551
		//if( $this->importCount === 0) {
8552
		//	$this->_finish();
8553
		//}
8554
	}
8555
8556
	function visitImport($importNode, &$visitDeeper ){
8557
		$importVisitor = $this;
8558
		$inlineCSS = $importNode->options['inline'];
8559
8560
		if( !$importNode->css || $inlineCSS ){
8561
			$evaldImportNode = $importNode->compileForImport($this->env);
8562
8563
			if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){
8564
				$importNode = $evaldImportNode;
8565
				$this->importCount++;
8566
				$env = clone $this->env;
8567
8568
				if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){
8569
					$env->importMultiple = true;
8570
				}
8571
8572
				//get path & uri
8573
				$path_and_uri = null;
8574
				if( is_callable(Less_Parser::$options['import_callback']) ){
8575
					$path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode);
8576
				}
8577
8578
				if( !$path_and_uri ){
8579
					$path_and_uri = $importNode->PathAndUri();
8580
				}
8581
8582
				if( $path_and_uri ){
8583
					list($full_path, $uri) = $path_and_uri;
8584
				}else{
8585
					$full_path = $uri = $importNode->getPath();
8586
				}
8587
8588
8589
				//import once
8590
				if( $importNode->skip( $full_path, $env) ){
8591
					return array();
8592
				}
8593
8594
				if( $importNode->options['inline'] ){
8595
					//todo needs to reference css file not import
8596
					//$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true );
8597
8598
					Less_Parser::AddParsedFile($full_path);
8599
					$contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
8600
8601
					if( $importNode->features ){
8602
						return new Less_Tree_Media( array($contents), $importNode->features->value );
8603
					}
8604
8605
					return array( $contents );
8606
				}
8607
8608
8609
				// css ?
8610
				if( $importNode->css ){
8611
					$features = ( $importNode->features ? $importNode->features->compile($env) : null );
8612
					return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index);
8613
				}
8614
8615
				return $importNode->ParseImport( $full_path, $uri, $env );
8616
			}
8617
8618
		}
8619
8620
		$visitDeeper = false;
8621
		return $importNode;
8622
	}
8623
8624
8625
	function visitRule( $ruleNode, &$visitDeeper ){
8626
		$visitDeeper = false;
8627
		return $ruleNode;
8628
	}
8629
8630
	function visitDirective($directiveNode, $visitArgs){
8631
		array_unshift($this->env->frames,$directiveNode);
8632
		return $directiveNode;
8633
	}
8634
8635
	function visitDirectiveOut($directiveNode) {
8636
		array_shift($this->env->frames);
8637
	}
8638
8639
	function visitMixinDefinition($mixinDefinitionNode, $visitArgs) {
8640
		array_unshift($this->env->frames,$mixinDefinitionNode);
8641
		return $mixinDefinitionNode;
8642
	}
8643
8644
	function visitMixinDefinitionOut($mixinDefinitionNode) {
8645
		array_shift($this->env->frames);
8646
	}
8647
8648
	function visitRuleset($rulesetNode, $visitArgs) {
8649
		array_unshift($this->env->frames,$rulesetNode);
8650
		return $rulesetNode;
8651
	}
8652
8653
	function visitRulesetOut($rulesetNode) {
8654
		array_shift($this->env->frames);
8655
	}
8656
8657
	function visitMedia($mediaNode, $visitArgs) {
8658
		array_unshift($this->env->frames, $mediaNode->ruleset);
8659
		return $mediaNode;
8660
	}
8661
8662
	function visitMediaOut($mediaNode) {
8663
		array_shift($this->env->frames);
8664
	}
8665
8666
}
8667
*/
8668
8669
8670
 
8671
8672
/**
8673
 * Join Selector Visitor
8674
 *
8675
 * @package Less
8676
 * @subpackage visitor
8677
 */
8678
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...
8679
8680
	public $contexts = array( array() );
8681
8682
	/**
8683
	 * @param Less_Tree_Ruleset $root
8684
	 */
8685
	public function run( $root ){
8686
		return $this->visitObj($root);
8687
	}
8688
8689
    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...
8690
		$visitDeeper = false;
8691
	}
8692
8693
    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...
8694
		$visitDeeper = false;
8695
	}
8696
8697
    public function visitRuleset( $rulesetNode ){
8698
8699
		$paths = array();
8700
8701
		if( !$rulesetNode->root ){
8702
			$selectors = array();
8703
8704
			if( $rulesetNode->selectors && $rulesetNode->selectors ){
8705
				foreach($rulesetNode->selectors as $selector){
8706
					if( $selector->getIsOutput() ){
8707
						$selectors[] = $selector;
8708
					}
8709
				}
8710
			}
8711
8712
			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...
8713
				$rulesetNode->selectors = null;
8714
				$rulesetNode->rules = null;
8715
			}else{
8716
				$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...
8717
				$paths = $rulesetNode->joinSelectors( $context, $selectors);
8718
			}
8719
8720
			$rulesetNode->paths = $paths;
8721
		}
8722
8723
		$this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
8724
	}
8725
8726
    public function visitRulesetOut(){
8727
		array_pop($this->contexts);
8728
	}
8729
8730
    public function visitMedia($mediaNode) {
8731
		$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...
8732
8733
		if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){
8734
			$mediaNode->rules[0]->root = true;
8735
		}
8736
	}
8737
8738
}
8739
8740
 
8741
8742
/**
8743
 * Process Extends Visitor
8744
 *
8745
 * @package Less
8746
 * @subpackage visitor
8747
 */
8748
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...
8749
8750
	public $allExtendsStack;
8751
8752
	/**
8753
	 * @param Less_Tree_Ruleset $root
8754
	 */
8755
	public function run( $root ){
8756
		$extendFinder = new Less_Visitor_extendFinder();
8757
		$extendFinder->run( $root );
8758
		if( !$extendFinder->foundExtends){
8759
			return $root;
8760
		}
8761
8762
		$root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends);
8763
8764
		$this->allExtendsStack = array();
8765
		$this->allExtendsStack[] = &$root->allExtends;
8766
8767
		return $this->visitObj( $root );
8768
	}
8769
8770
	private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){
8771
		//
8772
		// chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
8773
		// the selector we would do normally, but we are also adding an extend with the same target selector
8774
		// this means this new extend can then go and alter other extends
8775
		//
8776
		// this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
8777
		// this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
8778
		// we look at each selector at a time, as is done in visitRuleset
8779
8780
		$extendsToAdd = array();
8781
8782
8783
		//loop through comparing every extend with every target extend.
8784
		// a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
8785
		// e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
8786
		// and the second is the target.
8787
		// the separation into two lists allows us to process a subset of chains with a bigger set, as is the
8788
		// case when processing media queries
8789
		for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){
8790
			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...
8791
8792
				$extend = $extendsList[$extendIndex];
8793
				$targetExtend = $extendsListTarget[$targetExtendIndex];
8794
8795
				// look for circular references
8796
				if( in_array($targetExtend->object_id, $extend->parent_ids,true) ){
8797
					continue;
8798
				}
8799
8800
				// find a match in the target extends self selector (the bit before :extend)
8801
				$selectorPath = array( $targetExtend->selfSelectors[0] );
8802
				$matches = $this->findMatch( $extend, $selectorPath);
8803
8804
8805
				if( $matches ){
8806
8807
					// we found a match, so for each self selector..
8808
					foreach($extend->selfSelectors as $selfSelector ){
8809
8810
8811
						// process the extend as usual
8812
						$newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector);
8813
8814
						// but now we create a new extend from it
8815
						$newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0);
8816
						$newExtend->selfSelectors = $newSelector;
8817
8818
						// add the extend onto the list of extends for that selector
8819
						end($newSelector)->extendList = array($newExtend);
8820
						//$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...
8821
8822
						// record that we need to add it.
8823
						$extendsToAdd[] = $newExtend;
8824
						$newExtend->ruleset = $targetExtend->ruleset;
8825
8826
						//remember its parents for circular references
8827
						$newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids);
8828
8829
						// only process the selector once.. if we have :extend(.a,.b) then multiple
8830
						// extends will look at the same selector path, so when extending
8831
						// we know that any others will be duplicates in terms of what is added to the css
8832
						if( $targetExtend->firstExtendOnThisSelectorPath ){
8833
							$newExtend->firstExtendOnThisSelectorPath = true;
8834
							$targetExtend->ruleset->paths[] = $newSelector;
8835
						}
8836
					}
8837
				}
8838
			}
8839
		}
8840
8841
		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...
8842
			// try to detect circular references to stop a stack overflow.
8843
			// may no longer be needed.			$this->extendChainCount++;
8844
			if( $iterationCount > 100) {
8845
8846
				try{
8847
					$selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
8848
					$selectorTwo = $extendsToAdd[0]->selector->toCSS();
8849
				}catch(Exception $e){
8850
					$selectorOne = "{unable to calculate}";
8851
					$selectorTwo = "{unable to calculate}";
8852
				}
8853
8854
				throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:"+$selectorOne+":extend(" + $selectorTwo+")");
8855
			}
8856
8857
			// now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
8858
			$extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1);
8859
		}
8860
8861
		return array_merge($extendsList, $extendsToAdd);
8862
	}
8863
8864
8865
	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...
8866
		$visitDeeper = false;
8867
	}
8868
8869
	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...
8870
		$visitDeeper = false;
8871
	}
8872
8873
	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...
8874
		$visitDeeper = false;
8875
	}
8876
8877
	protected function visitRuleset($rulesetNode){
8878
8879
8880
		if( $rulesetNode->root ){
8881
			return;
8882
		}
8883
8884
		$allExtends	= end($this->allExtendsStack);
8885
		$paths_len = count($rulesetNode->paths);
8886
8887
		// look at each selector path in the ruleset, find any extend matches and then copy, find and replace
8888
		foreach($allExtends as $allExtend){
8889
			for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){
8890
8891
				// extending extends happens initially, before the main pass
8892
				if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){
8893
					continue;
8894
				}
8895
8896
				$selectorPath = $rulesetNode->paths[$pathIndex];
8897
8898
				if( end($selectorPath)->extendList ){
8899
					continue;
8900
				}
8901
8902
				$this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath);
8903
8904
			}
8905
		}
8906
	}
8907
8908
8909
	private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){
8910
		$matches = $this->findMatch($extend, $selectorPath);
8911
8912
		if( $matches ){
8913
			foreach($extend->selfSelectors as $selfSelector ){
8914
				$rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector);
8915
			}
8916
		}
8917
	}
8918
8919
8920
8921
	private function findMatch($extend, $haystackSelectorPath ){
8922
8923
8924
		if( !$this->HasMatches($extend, $haystackSelectorPath) ){
8925
			return false;
8926
		}
8927
8928
8929
		//
8930
		// look through the haystack selector path to try and find the needle - extend.selector
8931
		// returns an array of selector matches that can then be replaced
8932
		//
8933
		$needleElements = $extend->selector->elements;
8934
		$potentialMatches = array();
8935
		$potentialMatches_len = 0;
8936
		$potentialMatch = null;
8937
		$matches = array();
8938
8939
8940
8941
		// loop through the haystack elements
8942
		$haystack_path_len = count($haystackSelectorPath);
8943
		for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){
8944
			$hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
8945
8946
			$haystack_elements_len = count($hackstackSelector->elements);
8947
			for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){
8948
8949
				$haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
8950
8951
				// if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
8952
				if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){
8953
					$potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator);
8954
					$potentialMatches_len++;
8955
				}
8956
8957
				for($i = 0; $i < $potentialMatches_len; $i++ ){
8958
8959
					$potentialMatch = &$potentialMatches[$i];
8960
					$potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex );
8961
8962
8963
					// if we are still valid and have finished, test whether we have elements after and whether these are allowed
8964
					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...
8965
						$potentialMatch['finished'] = true;
8966
8967
						if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){
8968
							$potentialMatch = null;
8969
						}
8970
					}
8971
8972
					// if null we remove, if not, we are still valid, so either push as a valid match or continue
8973
					if( $potentialMatch ){
8974
						if( $potentialMatch['finished'] ){
8975
							$potentialMatch['length'] = $extend->selector->elements_len;
8976
							$potentialMatch['endPathIndex'] = $haystackSelectorIndex;
8977
							$potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
8978
							$potentialMatches = array(); // we don't allow matches to overlap, so start matching again
8979
							$potentialMatches_len = 0;
8980
							$matches[] = $potentialMatch;
8981
						}
8982
						continue;
8983
					}
8984
8985
					array_splice($potentialMatches, $i, 1);
8986
					$potentialMatches_len--;
8987
					$i--;
8988
				}
8989
			}
8990
		}
8991
8992
		return $matches;
8993
	}
8994
8995
8996
	// Before going through all the nested loops, lets check to see if a match is possible
8997
	// Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
8998
	private function HasMatches($extend, $haystackSelectorPath){
8999
9000
		if( !$extend->selector->cacheable ){
9001
			return true;
9002
		}
9003
9004
		$first_el = $extend->selector->_oelements[0];
9005
9006
		foreach($haystackSelectorPath as $hackstackSelector){
9007
			if( !$hackstackSelector->cacheable ){
9008
				return true;
9009
			}
9010
9011
			if( in_array($first_el, $hackstackSelector->_oelements) ){
9012
				return true;
9013
			}
9014
		}
9015
9016
		return false;
9017
	}
9018
9019
9020
	/**
9021
	 * @param integer $hackstackElementIndex
9022
	 */
9023
	private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){
9024
9025
9026
		if( $potentialMatch['matched'] > 0 ){
9027
9028
			// selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
9029
			// then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
9030
			// what the resulting combinator will be
9031
			$targetCombinator = $haystackElement->combinator;
9032
			if( $targetCombinator === '' && $hackstackElementIndex === 0 ){
9033
				$targetCombinator = ' ';
9034
			}
9035
9036
			if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){
9037
				return null;
9038
			}
9039
		}
9040
9041
		// if we don't match, null our match to indicate failure
9042
		if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){
9043
			return null;
9044
		}
9045
9046
		$potentialMatch['finished'] = false;
9047
		$potentialMatch['matched']++;
9048
9049
		return $potentialMatch;
9050
	}
9051
9052
9053
	private function isElementValuesEqual( $elementValue1, $elementValue2 ){
9054
9055
		if( $elementValue1 === $elementValue2 ){
9056
			return true;
9057
		}
9058
9059
		if( is_string($elementValue1) || is_string($elementValue2) ) {
9060
			return false;
9061
		}
9062
9063
		if( $elementValue1 instanceof Less_Tree_Attribute ){
9064
			return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 );
9065
		}
9066
9067
		$elementValue1 = $elementValue1->value;
9068
		if( $elementValue1 instanceof Less_Tree_Selector ){
9069
			return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 );
9070
		}
9071
9072
		return false;
9073
	}
9074
9075
9076
	/**
9077
	 * @param Less_Tree_Selector $elementValue1
9078
	 */
9079
	private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){
9080
9081
		$elementValue2 = $elementValue2->value;
9082
		if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){
9083
			return false;
9084
		}
9085
9086
		for( $i = 0; $i < $elementValue1->elements_len; $i++ ){
9087
9088
			if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){
9089
				if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){
9090
					return false;
9091
				}
9092
			}
9093
9094
			if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){
9095
				return false;
9096
			}
9097
		}
9098
9099
		return true;
9100
	}
9101
9102
9103
	/**
9104
	 * @param Less_Tree_Attribute $elementValue1
9105
	 */
9106
	private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){
9107
9108
		if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){
9109
			return false;
9110
		}
9111
9112
		if( !$elementValue1->value || !$elementValue2->value ){
9113
			if( $elementValue1->value || $elementValue2->value ) {
9114
				return false;
9115
			}
9116
			return true;
9117
		}
9118
9119
		$elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value );
9120
		$elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value );
9121
9122
		return $elementValue1 === $elementValue2;
9123
	}
9124
9125
9126
	private function extendSelector($matches, $selectorPath, $replacementSelector){
9127
9128
		//for a set of matches, replace each match with the replacement selector
9129
9130
		$currentSelectorPathIndex = 0;
9131
		$currentSelectorPathElementIndex = 0;
9132
		$path = array();
9133
		$selectorPath_len = count($selectorPath);
9134
9135
		for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){
9136
9137
9138
			$match = $matches[$matchIndex];
9139
			$selector = $selectorPath[ $match['pathIndex'] ];
9140
9141
			$firstElement = new Less_Tree_Element(
9142
				$match['initialCombinator'],
9143
				$replacementSelector->elements[0]->value,
9144
				$replacementSelector->elements[0]->index,
9145
				$replacementSelector->elements[0]->currentFileInfo
9146
			);
9147
9148 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...
9149
				$last_path = end($path);
9150
				$last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
9151
				$currentSelectorPathElementIndex = 0;
9152
				$currentSelectorPathIndex++;
9153
			}
9154
9155
			$newElements = array_merge(
9156
				array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice
9157
				, array($firstElement)
9158
				, array_slice($replacementSelector->elements,1)
9159
				);
9160
9161
			if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){
9162
				$last_key = count($path)-1;
9163
				$path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements);
9164
			}else{
9165
				$path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] ));
9166
				$path[] = new Less_Tree_Selector( $newElements );
9167
			}
9168
9169
			$currentSelectorPathIndex = $match['endPathIndex'];
9170
			$currentSelectorPathElementIndex = $match['endPathElementIndex'];
9171
			if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){
9172
				$currentSelectorPathElementIndex = 0;
9173
				$currentSelectorPathIndex++;
9174
			}
9175
		}
9176
9177 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...
9178
			$last_path = end($path);
9179
			$last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
9180
			$currentSelectorPathIndex++;
9181
		}
9182
9183
		$slice_len = $selectorPath_len - $currentSelectorPathIndex;
9184
		$path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len));
9185
9186
		return $path;
9187
	}
9188
9189
9190
	protected function visitMedia( $mediaNode ){
9191
		$newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) );
9192
		$this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends);
9193
	}
9194
9195
	protected function visitMediaOut(){
9196
		array_pop( $this->allExtendsStack );
9197
	}
9198
9199
	protected function visitDirective( $directiveNode ){
9200
		$newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) );
9201
		$this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends);
9202
	}
9203
9204
	protected function visitDirectiveOut(){
9205
		array_pop($this->allExtendsStack);
9206
	}
9207
9208
} 
9209
9210
/**
9211
 * toCSS Visitor
9212
 *
9213
 * @package Less
9214
 * @subpackage visitor
9215
 */
9216
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...
9217
9218
	private $charset;
9219
9220
	public function __construct(){
9221
		parent::__construct();
9222
	}
9223
9224
	/**
9225
	 * @param Less_Tree_Ruleset $root
9226
	 */
9227
	public function run( $root ){
9228
		return $this->visitObj($root);
9229
	}
9230
9231
	public function visitRule( $ruleNode ){
9232
		if( $ruleNode->variable ){
9233
			return array();
9234
		}
9235
		return $ruleNode;
9236
	}
9237
9238
	public function visitMixinDefinition($mixinNode){
9239
		// mixin definitions do not get eval'd - this means they keep state
9240
		// so we have to clear that state here so it isn't used if toCSS is called twice
9241
		$mixinNode->frames = array();
9242
		return array();
9243
	}
9244
9245
	public function visitExtend(){
9246
		return array();
9247
	}
9248
9249
	public function visitComment( $commentNode ){
9250
		if( $commentNode->isSilent() ){
9251
			return array();
9252
		}
9253
		return $commentNode;
9254
	}
9255
9256
	public function visitMedia( $mediaNode, &$visitDeeper ){
9257
		$mediaNode->accept($this);
9258
		$visitDeeper = false;
9259
9260
		if( !$mediaNode->rules ){
9261
			return array();
9262
		}
9263
		return $mediaNode;
9264
	}
9265
9266
	public function visitDirective( $directiveNode ){
9267
		if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){
9268
			return array();
9269
		}
9270
		if( $directiveNode->name === '@charset' ){
9271
			// Only output the debug info together with subsequent @charset definitions
9272
			// a comment (or @media statement) before the actual @charset directive would
9273
			// be considered illegal css as it has to be on the first line
9274
			if( isset($this->charset) && $this->charset ){
9275
9276
				//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...
9277
				//	$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...
9278
				//	$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...
9279
				//	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...
9280
				//}
9281
9282
9283
				return array();
9284
			}
9285
			$this->charset = true;
9286
		}
9287
		return $directiveNode;
9288
	}
9289
9290
	public function checkPropertiesInRoot( $rulesetNode ){
9291
9292
		if( !$rulesetNode->firstRoot ){
9293
			return;
9294
		}
9295
9296
		foreach($rulesetNode->rules as $ruleNode){
9297
			if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){
9298
				$msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null);
9299
				throw new Less_Exception_Compiler($msg);
9300
			}
9301
		}
9302
	}
9303
9304
9305
	public function visitRuleset( $rulesetNode, &$visitDeeper ){
9306
9307
		$visitDeeper = false;
9308
9309
		$this->checkPropertiesInRoot( $rulesetNode );
9310
9311
		if( $rulesetNode->root ){
9312
			return $this->visitRulesetRoot( $rulesetNode );
9313
		}
9314
9315
		$rulesets = array();
9316
		$rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
9317
9318
9319
		// Compile rules and rulesets
9320
		$nodeRuleCnt = count($rulesetNode->rules);
9321
		for( $i = 0; $i < $nodeRuleCnt; ){
9322
			$rule = $rulesetNode->rules[$i];
9323
9324
			if( property_exists($rule,'rules') ){
9325
				// visit because we are moving them out from being a child
9326
				$rulesets[] = $this->visitObj($rule);
9327
				array_splice($rulesetNode->rules,$i,1);
9328
				$nodeRuleCnt--;
9329
				continue;
9330
			}
9331
			$i++;
9332
		}
9333
9334
9335
		// accept the visitor to remove rules and refactor itself
9336
		// then we can decide now whether we want it or not
9337
		if( $nodeRuleCnt > 0 ){
9338
			$rulesetNode->accept($this);
9339
9340
			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...
9341
9342
				if( count($rulesetNode->rules) >  1 ){
9343
					$this->_mergeRules( $rulesetNode->rules );
9344
					$this->_removeDuplicateRules( $rulesetNode->rules );
9345
				}
9346
9347
				// now decide whether we keep the ruleset
9348
				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...
9349
					//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...
9350
					array_splice($rulesets,0,0,array($rulesetNode));
9351
				}
9352
			}
9353
9354
		}
9355
9356
9357
		if( count($rulesets) === 1 ){
9358
			return $rulesets[0];
9359
		}
9360
		return $rulesets;
9361
	}
9362
9363
9364
	/**
9365
	 * Helper function for visitiRuleset
9366
	 *
9367
	 * return array|Less_Tree_Ruleset
9368
	 */
9369
	private function visitRulesetRoot( $rulesetNode ){
9370
		$rulesetNode->accept( $this );
9371
		if( $rulesetNode->firstRoot || $rulesetNode->rules ){
9372
			return $rulesetNode;
9373
		}
9374
		return array();
9375
	}
9376
9377
9378
	/**
9379
	 * Helper function for visitRuleset()
9380
	 *
9381
	 * @return array
9382
	 */
9383
	private function visitRulesetPaths($rulesetNode){
9384
9385
		$paths = array();
9386
		foreach($rulesetNode->paths as $p){
9387
			if( $p[0]->elements[0]->combinator === ' ' ){
9388
				$p[0]->elements[0]->combinator = '';
9389
			}
9390
9391
			foreach($p as $pi){
9392
				if( $pi->getIsReferenced() && $pi->getIsOutput() ){
9393
					$paths[] = $p;
9394
					break;
9395
				}
9396
			}
9397
		}
9398
9399
		return $paths;
9400
	}
9401
9402
	protected function _removeDuplicateRules( &$rules ){
9403
		// remove duplicates
9404
		$ruleCache = array();
9405
		for( $i = count($rules)-1; $i >= 0 ; $i-- ){
9406
			$rule = $rules[$i];
9407
			if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){
9408
9409
				if( !isset($ruleCache[$rule->name]) ){
9410
					$ruleCache[$rule->name] = $rule;
9411
				}else{
9412
					$ruleList =& $ruleCache[$rule->name];
9413
9414
					if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){
9415
						$ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
9416
					}
9417
9418
					$ruleCSS = $rule->toCSS();
9419
					if( array_search($ruleCSS,$ruleList) !== false ){
9420
						array_splice($rules,$i,1);
9421
					}else{
9422
						$ruleList[] = $ruleCSS;
9423
					}
9424
				}
9425
			}
9426
		}
9427
	}
9428
9429
	protected function _mergeRules( &$rules ){
9430
		$groups = array();
9431
9432
		//obj($rules);
9433
9434
		$rules_len = count($rules);
9435
		for( $i = 0; $i < $rules_len; $i++ ){
9436
			$rule = $rules[$i];
9437
9438
			if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){
9439
9440
				$key = $rule->name;
9441
				if( $rule->important ){
9442
					$key .= ',!';
9443
				}
9444
9445
				if( !isset($groups[$key]) ){
9446
					$groups[$key] = array();
9447
				}else{
9448
					array_splice($rules, $i--, 1);
9449
					$rules_len--;
9450
				}
9451
9452
				$groups[$key][] = $rule;
9453
			}
9454
		}
9455
9456
9457
		foreach($groups as $parts){
9458
9459
			if( count($parts) > 1 ){
9460
				$rule = $parts[0];
9461
				$spacedGroups = array();
9462
				$lastSpacedGroup = array();
9463
				$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...
9464
				foreach($parts as $p){
9465
					if( $p->merge === '+' ){
9466
						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...
9467
							$spacedGroups[] = self::toExpression($lastSpacedGroup);
9468
						}
9469
						$lastSpacedGroup = array();
9470
					}
9471
					$lastSpacedGroup[] = $p;
9472
				}
9473
9474
				$spacedGroups[] = self::toExpression($lastSpacedGroup);
9475
				$rule->value = self::toValue($spacedGroups);
9476
			}
9477
		}
9478
9479
	}
9480
9481
	public static function toExpression($values){
9482
		$mapped = array();
9483
		foreach($values as $p){
9484
			$mapped[] = $p->value;
9485
		}
9486
		return new Less_Tree_Expression( $mapped );
9487
	}
9488
9489
	public static function toValue($values){
9490
		//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...
9491
9492
		$mapped = array();
9493
		foreach($values as $p){
9494
			$mapped[] = $p;
9495
		}
9496
		return new Less_Tree_Value($mapped);
9497
	}
9498
}
9499
9500
 
9501
9502
/**
9503
 * Parser Exception
9504
 *
9505
 * @package Less
9506
 * @subpackage exception
9507
 */
9508
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...
9509
9510
	/**
9511
	 * The current file
9512
	 *
9513
	 * @var Less_ImportedFile
9514
	 */
9515
	public $currentFile;
9516
9517
	/**
9518
	 * The current parser index
9519
	 *
9520
	 * @var integer
9521
	 */
9522
	public $index;
9523
9524
	protected $input;
9525
9526
	protected $details = array();
9527
9528
9529
	/**
9530
	 * Constructor
9531
	 *
9532
	 * @param string $message
9533
	 * @param Exception $previous Previous exception
9534
	 * @param integer $index The current parser index
9535
	 * @param Less_FileInfo|string $currentFile The file
9536
	 * @param integer $code The exception code
9537
	 */
9538
	public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
9539
9540
		if (PHP_VERSION_ID < 50300) {
9541
			$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...
9542
			parent::__construct($message, $code);
9543
		} else {
9544
			parent::__construct($message, $code, $previous);
9545
		}
9546
9547
		$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...
9548
		$this->index = $index;
9549
9550
		$this->genMessage();
9551
	}
9552
9553
9554
	protected function getInput(){
9555
9556
		if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){
9557
			$this->input = file_get_contents( $this->currentFile['filename'] );
9558
		}
9559
	}
9560
9561
9562
9563
	/**
9564
	 * Converts the exception to string
9565
	 *
9566
	 * @return string
9567
	 */
9568
	public function genMessage(){
9569
9570
		if( $this->currentFile && $this->currentFile['filename'] ){
9571
			$this->message .= ' in '.basename($this->currentFile['filename']);
9572
		}
9573
9574
		if( $this->index !== null ){
9575
			$this->getInput();
9576
			if( $this->input ){
9577
				$line = self::getLineNumber();
9578
				$this->message .= ' on line '.$line.', column '.self::getColumn();
9579
9580
				$lines = explode("\n",$this->input);
9581
9582
				$count = count($lines);
9583
				$start_line = max(0, $line-3);
9584
				$last_line = min($count, $start_line+6);
9585
				$num_len = strlen($last_line);
9586
				for( $i = $start_line; $i < $last_line; $i++ ){
9587
					$this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i];
9588
				}
9589
			}
9590
		}
9591
9592
	}
9593
9594
	/**
9595
	 * Returns the line number the error was encountered
9596
	 *
9597
	 * @return integer
9598
	 */
9599
	public function getLineNumber(){
9600
		if( $this->index ){
9601
			// https://bugs.php.net/bug.php?id=49790
9602
			if (ini_get("mbstring.func_overload")) {
9603
				return substr_count(substr($this->input, 0, $this->index), "\n") + 1;
9604
			} else {
9605
				return substr_count($this->input, "\n", 0, $this->index) + 1;
9606
			}
9607
		}
9608
		return 1;
9609
	}
9610
9611
9612
	/**
9613
	 * Returns the column the error was encountered
9614
	 *
9615
	 * @return integer
9616
	 */
9617
	public function getColumn(){
9618
9619
		$part = substr($this->input, 0, $this->index);
9620
		$pos = strrpos($part,"\n");
9621
		return $this->index - $pos;
9622
	}
9623
9624
}
9625
 
9626
9627
/**
9628
 * Chunk Exception
9629
 *
9630
 * @package Less
9631
 * @subpackage exception
9632
 */
9633
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...
9634
9635
9636
	protected $parserCurrentIndex = 0;
9637
9638
	protected $emitFrom = 0;
9639
9640
	protected $input_len;
9641
9642
9643
	/**
9644
	 * Constructor
9645
	 *
9646
	 * @param string $input
9647
	 * @param Exception $previous Previous exception
9648
	 * @param integer $index The current parser index
9649
	 * @param Less_FileInfo|string $currentFile The file
9650
	 * @param integer $code The exception code
9651
	 */
9652
	public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
9653
9654
		$this->message = 'ParseError: Unexpected input'; //default message
9655
9656
		$this->index = $index;
9657
9658
		$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...
9659
9660
		$this->input = $input;
9661
		$this->input_len = strlen($input);
9662
9663
		$this->Chunks();
9664
		$this->genMessage();
9665
	}
9666
9667
9668
	/**
9669
	 * See less.js chunks()
9670
	 * We don't actually need the chunks
9671
	 *
9672
	 */
9673
	protected function Chunks(){
9674
		$level = 0;
9675
		$parenLevel = 0;
9676
		$lastMultiCommentEndBrace = null;
9677
		$lastOpening = null;
9678
		$lastMultiComment = null;
9679
		$lastParen = null;
9680
9681
		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...
9682
			$cc = $this->CharCode($this->parserCurrentIndex);
9683
			if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
9684
				// a-z or whitespace
9685
				continue;
9686
			}
9687
9688
			switch ($cc) {
9689
9690
				// (
9691
				case 40:
9692
					$parenLevel++;
9693
					$lastParen = $this->parserCurrentIndex;
9694
					continue;
9695
9696
				// )
9697
				case 41:
9698
					$parenLevel--;
9699
					if( $parenLevel < 0 ){
9700
						return $this->fail("missing opening `(`");
9701
					}
9702
					continue;
9703
9704
				// ;
9705
				case 59:
9706
					//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...
9707
					continue;
9708
9709
				// {
9710
				case 123:
9711
					$level++;
9712
					$lastOpening = $this->parserCurrentIndex;
9713
					continue;
9714
9715
				// }
9716
				case 125:
9717
					$level--;
9718
					if( $level < 0 ){
9719
						return $this->fail("missing opening `{`");
9720
9721
					}
9722
					//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...
9723
					continue;
9724
				// \
9725
				case 92:
9726
					if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; continue; }
9727
					return $this->fail("unescaped `\\`");
9728
9729
				// ", ' and `
9730
				case 34:
9731
				case 39:
9732
				case 96:
9733
					$matched = 0;
9734
					$currentChunkStartIndex = $this->parserCurrentIndex;
9735
					for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
9736
						$cc2 = $this->CharCode($this->parserCurrentIndex);
9737
						if ($cc2 > 96) { continue; }
9738
						if ($cc2 == $cc) { $matched = 1; break; }
9739
						if ($cc2 == 92) {        // \
9740
							if ($this->parserCurrentIndex == $this->input_len - 1) {
9741
								return $this->fail("unescaped `\\`");
9742
							}
9743
							$this->parserCurrentIndex++;
9744
						}
9745
					}
9746
					if ($matched) { continue; }
9747
					return $this->fail("unmatched `" + chr($cc) + "`", $currentChunkStartIndex);
9748
9749
				// /, check for comment
9750
				case 47:
9751
					if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { continue; }
9752
					$cc2 = $this->CharCode($this->parserCurrentIndex+1);
9753
					if ($cc2 == 47) {
9754
						// //, find lnfeed
9755
						for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
9756
							$cc2 = $this->CharCode($this->parserCurrentIndex);
9757
							if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }
9758
						}
9759
					} else if ($cc2 == 42) {
9760
						// /*, find */
9761
						$lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
9762
						for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
9763
							$cc2 = $this->CharCode($this->parserCurrentIndex);
9764
							if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }
9765
							if ($cc2 != 42) { continue; }
9766
							if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }
9767
						}
9768
						if ($this->parserCurrentIndex == $this->input_len - 1) {
9769
							return $this->fail("missing closing `*/`", $currentChunkStartIndex);
9770
						}
9771
					}
9772
					continue;
9773
9774
				// *, check for unmatched */
9775
				case 42:
9776
					if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {
9777
						return $this->fail("unmatched `/*`");
9778
					}
9779
					continue;
9780
			}
9781
		}
9782
9783
		if( $level !== 0 ){
9784
			if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){
9785
				return $this->fail("missing closing `}` or `*/`", $lastOpening);
9786
			} else {
9787
				return $this->fail("missing closing `}`", $lastOpening);
9788
			}
9789
		} else if ( $parenLevel !== 0 ){
9790
			return $this->fail("missing closing `)`", $lastParen);
9791
		}
9792
9793
9794
		//chunk didn't fail
9795
9796
9797
		//$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...
9798
	}
9799
9800
	public function CharCode($pos){
9801
		return ord($this->input[$pos]);
9802
	}
9803
9804
9805
	public function fail( $msg, $index = null ){
9806
9807
		if( !$index ){
9808
			$this->index = $this->parserCurrentIndex;
9809
		}else{
9810
			$this->index = $index;
9811
		}
9812
		$this->message = 'ParseError: '.$msg;
9813
	}
9814
9815
9816
	/*
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...
9817
	function emitChunk( $force = false ){
9818
		$len = $this->parserCurrentIndex - $this->emitFrom;
9819
		if ((($len < 512) && !$force) || !$len) {
9820
			return;
9821
		}
9822
		$chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
9823
		$this->emitFrom = $this->parserCurrentIndex + 1;
9824
	}
9825
	*/
9826
9827
}
9828
 
9829
9830
/**
9831
 * Compiler Exception
9832
 *
9833
 * @package Less
9834
 * @subpackage exception
9835
 */
9836
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...
9837
9838
} 
9839
9840
/**
9841
 * Parser output with source map
9842
 *
9843
 * @package Less
9844
 * @subpackage Output
9845
 */
9846
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...
9847
9848
	/**
9849
	 * The source map generator
9850
	 *
9851
	 * @var Less_SourceMap_Generator
9852
	 */
9853
	protected $generator;
9854
9855
	/**
9856
	 * Current line
9857
	 *
9858
	 * @var integer
9859
	 */
9860
	protected $lineNumber = 0;
9861
9862
	/**
9863
	 * Current column
9864
	 *
9865
	 * @var integer
9866
	 */
9867
	protected $column = 0;
9868
9869
	/**
9870
	 * Array of contents map (file and its content)
9871
	 *
9872
	 * @var array
9873
	 */
9874
	protected $contentsMap = array();
9875
9876
	/**
9877
	 * Constructor
9878
	 *
9879
	 * @param array $contentsMap Array of filename to contents map
9880
	 * @param Less_SourceMap_Generator $generator
9881
	 */
9882
	public function __construct(array $contentsMap, $generator){
9883
		$this->contentsMap = $contentsMap;
9884
		$this->generator = $generator;
9885
	}
9886
9887
	/**
9888
	 * Adds a chunk to the stack
9889
	 * The $index for less.php may be different from less.js since less.php does not chunkify inputs
9890
	 *
9891
	 * @param string $chunk
9892
	 * @param string $fileInfo
9893
	 * @param integer $index
9894
	 * @param mixed $mapLines
9895
	 */
9896
	public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
9897
9898
		//ignore adding empty strings
9899
		if( $chunk === '' ){
9900
			return;
9901
		}
9902
9903
9904
		$sourceLines = array();
9905
		$sourceColumns = ' ';
9906
9907
9908
		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...
9909
9910
			$url = $fileInfo['currentUri'];
9911
9912
			if( isset($this->contentsMap[$url]) ){
9913
				$inputSource = substr($this->contentsMap[$url], 0, $index);
9914
				$sourceLines = explode("\n", $inputSource);
9915
				$sourceColumns = end($sourceLines);
9916
			}else{
9917
				throw new Exception('Filename '.$url.' not in contentsMap');
9918
			}
9919
9920
		}
9921
9922
		$lines = explode("\n", $chunk);
9923
		$columns = end($lines);
9924
9925
		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...
9926
9927
			if(!$mapLines){
9928
				$this->generator->addMapping(
9929
						$this->lineNumber + 1,					// generated_line
9930
						$this->column,							// generated_column
9931
						count($sourceLines),					// original_line
9932
						strlen($sourceColumns),					// original_column
9933
						$fileInfo
9934
				);
9935
			}else{
9936
				for($i = 0, $count = count($lines); $i < $count; $i++){
9937
					$this->generator->addMapping(
9938
						$this->lineNumber + $i + 1,				// generated_line
9939
						$i === 0 ? $this->column : 0,			// generated_column
9940
						count($sourceLines) + $i,				// original_line
9941
						$i === 0 ? strlen($sourceColumns) : 0, 	// original_column
9942
						$fileInfo
9943
					);
9944
				}
9945
			}
9946
		}
9947
9948
		if(count($lines) === 1){
9949
			$this->column += strlen($columns);
9950
		}else{
9951
			$this->lineNumber += count($lines) - 1;
9952
			$this->column = strlen($columns);
9953
		}
9954
9955
		// add only chunk
9956
		parent::add($chunk);
9957
	}
9958
9959
} 
9960
9961
/**
9962
 * Encode / Decode Base64 VLQ.
9963
 *
9964
 * @package Less
9965
 * @subpackage SourceMap
9966
 */
9967
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...
9968
9969
	/**
9970
	 * Shift
9971
	 *
9972
	 * @var integer
9973
	 */
9974
	private $shift = 5;
9975
9976
	/**
9977
	 * Mask
9978
	 *
9979
	 * @var integer
9980
	 */
9981
	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...
9982
9983
	/**
9984
	 * Continuation bit
9985
	 *
9986
	 * @var integer
9987
	 */
9988
	private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
9989
9990
	/**
9991
	 * Char to integer map
9992
	 *
9993
	 * @var array
9994
	 */
9995
	private $charToIntMap = array(
9996
		'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6,
9997
		'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13,
9998
		'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20,
9999
		'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27,
10000
		'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34,
10001
		'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41,
10002
		'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48,
10003
		'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56,
10004
		5 => 57,	6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63,
10005
	);
10006
10007
	/**
10008
	 * Integer to char map
10009
	 *
10010
	 * @var array
10011
	 */
10012
	private $intToCharMap = array(
10013
		0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G',
10014
		7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N',
10015
		14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
10016
		21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b',
10017
		28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i',
10018
		35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p',
10019
		42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w',
10020
		49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
10021
		56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+',
10022
		63 => '/',
10023
	);
10024
10025
	/**
10026
	 * Constructor
10027
	 */
10028
	public function __construct(){
10029
		// I leave it here for future reference
10030
		// 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...
10031
		// {
10032
		//	 $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...
10033
		//	 $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...
10034
		// }
10035
	}
10036
10037
	/**
10038
	 * Convert from a two-complement value to a value where the sign bit is
10039
	 * is placed in the least significant bit.	For example, as decimals:
10040
	 *	 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
10041
	 *	 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
10042
	 * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
10043
	 * even on a 64 bit machine.
10044
	 * @param string $aValue
10045
	 */
10046
	public function toVLQSigned($aValue){
10047
		return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
10048
	}
10049
10050
	/**
10051
	 * Convert to a two-complement value from a value where the sign bit is
10052
	 * is placed in the least significant bit. For example, as decimals:
10053
	 *	 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
10054
	 *	 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
10055
	 * We assume that the value was generated with a 32 bit machine in mind.
10056
	 * Hence
10057
	 *	 1 becomes -2147483648
10058
	 * even on a 64 bit machine.
10059
	 * @param integer $aValue
10060
	 */
10061
	public function fromVLQSigned($aValue){
10062
		return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
10063
	}
10064
10065
	/**
10066
	 * Return the base 64 VLQ encoded value.
10067
	 *
10068
	 * @param string $aValue The value to encode
10069
	 * @return string The encoded value
10070
	 */
10071
	public function encode($aValue){
10072
		$encoded = '';
10073
		$vlq = $this->toVLQSigned($aValue);
10074
		do
10075
		{
10076
			$digit = $vlq & $this->mask;
10077
			$vlq = $this->zeroFill($vlq, $this->shift);
10078
			if($vlq > 0){
10079
				$digit |= $this->continuationBit;
10080
			}
10081
			$encoded .= $this->base64Encode($digit);
10082
		} while($vlq > 0);
10083
10084
		return $encoded;
10085
	}
10086
10087
	/**
10088
	 * Return the value decoded from base 64 VLQ.
10089
	 *
10090
	 * @param string $encoded The encoded value to decode
10091
	 * @return integer The decoded value
10092
	 */
10093
	public function decode($encoded){
10094
		$vlq = 0;
10095
		$i = 0;
10096
		do
10097
		{
10098
			$digit = $this->base64Decode($encoded[$i]);
10099
			$vlq |= ($digit & $this->mask) << ($i * $this->shift);
10100
			$i++;
10101
		} while($digit & $this->continuationBit);
10102
10103
		return $this->fromVLQSigned($vlq);
10104
	}
10105
10106
	/**
10107
	 * Right shift with zero fill.
10108
	 *
10109
	 * @param integer $a number to shift
10110
	 * @param integer $b number of bits to shift
10111
	 * @return integer
10112
	 */
10113
	public function zeroFill($a, $b){
10114
		return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
10115
	}
10116
10117
	/**
10118
	 * Encode single 6-bit digit as base64.
10119
	 *
10120
	 * @param integer $number
10121
	 * @return string
10122
	 * @throws Exception If the number is invalid
10123
	 */
10124
	public function base64Encode($number){
10125
		if($number < 0 || $number > 63){
10126
			throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
10127
		}
10128
		return $this->intToCharMap[$number];
10129
	}
10130
10131
	/**
10132
	 * Decode single 6-bit digit from base64
10133
	 *
10134
	 * @param string $char
10135
	 * @return number
10136
	 * @throws Exception If the number is invalid
10137
	 */
10138
	public function base64Decode($char){
10139
		if(!array_key_exists($char, $this->charToIntMap)){
10140
			throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
10141
		}
10142
		return $this->charToIntMap[$char];
10143
	}
10144
10145
}
10146
 
10147
10148
/**
10149
 * Source map generator
10150
 *
10151
 * @package Less
10152
 * @subpackage Output
10153
 */
10154
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...
10155
10156
	/**
10157
	 * What version of source map does the generator generate?
10158
	 */
10159
	const VERSION = 3;
10160
10161
	/**
10162
	 * Array of default options
10163
	 *
10164
	 * @var array
10165
	 */
10166
	protected $defaultOptions = array(
10167
			// an optional source root, useful for relocating source files
10168
			// on a server or removing repeated values in the 'sources' entry.
10169
			// This value is prepended to the individual entries in the 'source' field.
10170
			'sourceRoot'			=> '',
10171
10172
			// an optional name of the generated code that this source map is associated with.
10173
			'sourceMapFilename'		=> null,
10174
10175
			// url of the map
10176
			'sourceMapURL'			=> null,
10177
10178
			// absolute path to a file to write the map to
10179
			'sourceMapWriteTo'		=> null,
10180
10181
			// output source contents?
10182
			'outputSourceFiles'		=> false,
10183
10184
			// base path for filename normalization
10185
			'sourceMapRootpath'		=> '',
10186
10187
			// base path for filename normalization
10188
			'sourceMapBasepath'   => ''
10189
	);
10190
10191
	/**
10192
	 * The base64 VLQ encoder
10193
	 *
10194
	 * @var Less_SourceMap_Base64VLQ
10195
	 */
10196
	protected $encoder;
10197
10198
	/**
10199
	 * Array of mappings
10200
	 *
10201
	 * @var array
10202
	 */
10203
	protected $mappings = array();
10204
10205
	/**
10206
	 * The root node
10207
	 *
10208
	 * @var Less_Tree_Ruleset
10209
	 */
10210
	protected $root;
10211
10212
	/**
10213
	 * Array of contents map
10214
	 *
10215
	 * @var array
10216
	 */
10217
	protected $contentsMap = array();
10218
10219
	/**
10220
	 * File to content map
10221
	 *
10222
	 * @var array
10223
	 */
10224
	protected $sources = array();
10225
	protected $source_keys = array();
10226
10227
	/**
10228
	 * Constructor
10229
	 *
10230
	 * @param Less_Tree_Ruleset $root The root node
10231
	 * @param array $options Array of options
10232
	 */
10233
	public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){
10234
		$this->root = $root;
10235
		$this->contentsMap = $contentsMap;
10236
		$this->encoder = new Less_SourceMap_Base64VLQ();
10237
10238
		$this->SetOptions($options);
10239
		
10240
		$this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true);
10241
		$this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true);
10242
	}
10243
10244
	/**
10245
	 * Generates the CSS
10246
	 *
10247
	 * @return string
10248
	 */
10249
	public function generateCSS(){
10250
		$output = new Less_Output_Mapped($this->contentsMap, $this);
10251
10252
		// catch the output
10253
		$this->root->genCSS($output);
10254
10255
10256
		$sourceMapUrl				= $this->getOption('sourceMapURL');
10257
		$sourceMapFilename			= $this->getOption('sourceMapFilename');
10258
		$sourceMapContent			= $this->generateJson();
10259
		$sourceMapWriteTo			= $this->getOption('sourceMapWriteTo');
10260
10261
		if( !$sourceMapUrl && $sourceMapFilename ){
10262
			$sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
10263
		}
10264
10265
		// write map to a file
10266
		if( $sourceMapWriteTo ){
10267
			$this->saveMap($sourceMapWriteTo, $sourceMapContent);
10268
		}
10269
10270
		// inline the map
10271
		if( !$sourceMapUrl ){
10272
			$sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
10273
		}
10274
10275
		if( $sourceMapUrl ){
10276
			$output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) );
10277
		}
10278
10279
		return $output->toString();
10280
	}
10281
10282
	/**
10283
	 * Saves the source map to a file
10284
	 *
10285
	 * @param string $file The absolute path to a file
10286
	 * @param string $content The content to write
10287
	 * @throws Exception If the file could not be saved
10288
	 */
10289
	protected function saveMap($file, $content){
10290
		$dir = dirname($file);
10291
		// directory does not exist
10292
		if( !is_dir($dir) ){
10293
			// FIXME: create the dir automatically?
10294
			throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
10295
		}
10296
		// FIXME: proper saving, with dir write check!
10297
		if(file_put_contents($file, $content) === false){
10298
			throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
10299
		}
10300
		return true;
10301
	}
10302
10303
	/**
10304
	 * Normalizes the filename
10305
	 *
10306
	 * @param string $filename
10307
	 * @return string
10308
	 */
10309
	protected function normalizeFilename($filename){
10310
10311
		$filename = $this->fixWindowsPath($filename);
10312
10313
		$rootpath = $this->getOption('sourceMapRootpath');
10314
		$basePath = $this->getOption('sourceMapBasepath');
10315
10316
		// "Trim" the 'sourceMapBasepath' from the output filename.
10317
		if (strpos($filename, $basePath) === 0) {
10318
			$filename = substr($filename, strlen($basePath));
10319
		}
10320
10321
		// Remove extra leading path separators.
10322
		if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){
10323
			$filename = substr($filename, 1);
10324
		}
10325
10326
		return $rootpath . $filename;
10327
	}
10328
10329
	/**
10330
	 * Adds a mapping
10331
	 *
10332
	 * @param integer $generatedLine The line number in generated file
10333
	 * @param integer $generatedColumn The column number in generated file
10334
	 * @param integer $originalLine The line number in original file
10335
	 * @param integer $originalColumn The column number in original file
10336
	 * @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...
10337
	 */
10338
	public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){
10339
10340
		$this->mappings[] = array(
10341
			'generated_line' => $generatedLine,
10342
			'generated_column' => $generatedColumn,
10343
			'original_line' => $originalLine,
10344
			'original_column' => $originalColumn,
10345
			'source_file' => $fileInfo['currentUri']
10346
		);
10347
10348
		$this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
10349
	}
10350
10351
10352
	/**
10353
	 * Generates the JSON source map
10354
	 *
10355
	 * @return string
10356
	 * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
10357
	 */
10358
	protected function generateJson(){
10359
10360
		$sourceMap = array();
10361
		$mappings = $this->generateMappings();
10362
10363
		// File version (always the first entry in the object) and must be a positive integer.
10364
		$sourceMap['version'] = self::VERSION;
10365
10366
10367
		// An optional name of the generated code that this source map is associated with.
10368
		$file = $this->getOption('sourceMapFilename');
10369
		if( $file ){
10370
			$sourceMap['file'] = $file;
10371
		}
10372
10373
10374
		// 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.
10375
		$root = $this->getOption('sourceRoot');
10376
		if( $root ){
10377
			$sourceMap['sourceRoot'] = $root;
10378
		}
10379
10380
10381
		// A list of original sources used by the 'mappings' entry.
10382
		$sourceMap['sources'] = array();
10383
		foreach($this->sources as $source_uri => $source_filename){
10384
			$sourceMap['sources'][] = $this->normalizeFilename($source_filename);
10385
		}
10386
10387
10388
		// A list of symbol names used by the 'mappings' entry.
10389
		$sourceMap['names'] = array();
10390
10391
		// A string with the encoded mapping data.
10392
		$sourceMap['mappings'] = $mappings;
10393
10394
		if( $this->getOption('outputSourceFiles') ){
10395
			// An optional list of source content, useful when the 'source' can't be hosted.
10396
			// The contents are listed in the same order as the sources above.
10397
			// 'null' may be used if some original sources should be retrieved by name.
10398
			$sourceMap['sourcesContent'] = $this->getSourcesContent();
10399
		}
10400
10401
		// less.js compat fixes
10402
		if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){
10403
			unset($sourceMap['sourceRoot']);
10404
		}
10405
10406
		return json_encode($sourceMap);
10407
	}
10408
10409
	/**
10410
	 * Returns the sources contents
10411
	 *
10412
	 * @return array|null
10413
	 */
10414
	protected function getSourcesContent(){
10415
		if(empty($this->sources)){
10416
			return;
10417
		}
10418
		$content = array();
10419
		foreach($this->sources as $sourceFile){
10420
			$content[] = file_get_contents($sourceFile);
10421
		}
10422
		return $content;
10423
	}
10424
10425
	/**
10426
	 * Generates the mappings string
10427
	 *
10428
	 * @return string
10429
	 */
10430
	public function generateMappings(){
10431
10432
		if( !count($this->mappings) ){
10433
			return '';
10434
		}
10435
10436
		$this->source_keys = array_flip(array_keys($this->sources));
10437
10438
10439
		// group mappings by generated line number.
10440
		$groupedMap = $groupedMapEncoded = array();
10441
		foreach($this->mappings as $m){
10442
			$groupedMap[$m['generated_line']][] = $m;
10443
		}
10444
		ksort($groupedMap);
10445
10446
		$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
10447
10448
		foreach($groupedMap as $lineNumber => $line_map){
10449
			while(++$lastGeneratedLine < $lineNumber){
10450
				$groupedMapEncoded[] = ';';
10451
			}
10452
10453
			$lineMapEncoded = array();
10454
			$lastGeneratedColumn = 0;
10455
10456
			foreach($line_map as $m){
10457
				$mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
10458
				$lastGeneratedColumn = $m['generated_column'];
10459
10460
				// find the index
10461
				if( $m['source_file'] ){
10462
					$index = $this->findFileIndex($m['source_file']);
10463
					if( $index !== false ){
10464
						$mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
10465
						$lastOriginalIndex = $index;
10466
10467
						// lines are stored 0-based in SourceMap spec version 3
10468
						$mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
10469
						$lastOriginalLine = $m['original_line'] - 1;
10470
10471
						$mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
10472
						$lastOriginalColumn = $m['original_column'];
10473
					}
10474
				}
10475
10476
				$lineMapEncoded[] = $mapEncoded;
10477
			}
10478
10479
			$groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
10480
		}
10481
10482
		return rtrim(implode($groupedMapEncoded), ';');
10483
	}
10484
10485
	/**
10486
	 * Finds the index for the filename
10487
	 *
10488
	 * @param string $filename
10489
	 * @return integer|false
10490
	 */
10491
	protected function findFileIndex($filename){
10492
		return $this->source_keys[$filename];
10493
	}
10494
10495
	/**
10496
	 * fix windows paths
10497
	 * @param  string $path
10498
	 * @return string      
10499
	 */
10500
	public function fixWindowsPath($path, $addEndSlash = false){
10501
		$slash = ($addEndSlash) ? '/' : '';
10502
		if( !empty($path) ){
10503
			$path = str_replace('\\', '/', $path);
10504
			$path = rtrim($path,'/') . $slash;
10505
		}
10506
10507
		return $path;
10508
	}
10509
10510
}