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

Contrib/less.php/Functions.php (19 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Builtin functions
5
 *
6
 * @package Less
7
 * @subpackage function
8
 * @see http://lesscss.org/functions/
9
 */
10
class Less_Functions{
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...
11
12
	public $env;
13
	public $currentFileInfo;
14
15
	function __construct($env, $currentFileInfo = null ){
0 ignored issues
show
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...
16
		$this->env = $env;
17
		$this->currentFileInfo = $currentFileInfo;
18
	}
19
20
21
	/**
22
	 * @param string $op
23
	 */
24
    public static function operate( $op, $a, $b ){
25
		switch ($op) {
26
			case '+': return $a + $b;
27
			case '-': return $a - $b;
28
			case '*': return $a * $b;
29
			case '/': return $a / $b;
30
		}
31
	}
32
33
	public static function clamp($val, $max = 1){
34
		return min( max($val, 0), $max);
35
	}
36
37
	public static function fround( $value ){
38
39
		if( $value === 0 ){
40
			return $value;
41
		}
42
43
		if( Less_Parser::$options['numPrecision'] ){
44
			$p = pow(10, Less_Parser::$options['numPrecision']);
45
			return round( $value * $p) / $p;
46
		}
47
		return $value;
48
	}
49
50
    public static function number($n){
51
52
		if ($n instanceof Less_Tree_Dimension) {
53
			return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value);
54
		} else if (is_numeric($n)) {
55
			return $n;
56
		} else {
57
			throw new Less_Exception_Compiler("color functions take numbers as parameters");
58
		}
59
	}
60
61
    public static function scaled($n, $size = 255 ){
62
		if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){
63
			return (float)$n->value * $size / 100;
64
		} else {
65
			return Less_Functions::number($n);
66
		}
67
	}
68
69
	public function rgb ($r, $g, $b){
70
		return $this->rgba($r, $g, $b, 1.0);
71
	}
72
73
	public function rgba($r, $g, $b, $a){
74
		$rgb = array($r, $g, $b);
75
		$rgb = array_map(array('Less_Functions','scaled'),$rgb);
76
77
		$a = self::number($a);
78
		return new Less_Tree_Color($rgb, $a);
79
	}
80
81
	public function hsl($h, $s, $l){
82
		return $this->hsla($h, $s, $l, 1.0);
83
	}
84
85
	public function hsla($h, $s, $l, $a){
86
87
		$h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int
88
		$s = self::clamp(self::number($s));
89
		$l = self::clamp(self::number($l));
90
		$a = self::clamp(self::number($a));
91
92
		$m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
93
94
		$m1 = $l * 2 - $m2;
95
96
		return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255,
97
							self::hsla_hue($h, $m1, $m2) * 255,
98
							self::hsla_hue($h - 1/3, $m1, $m2) * 255,
99
							$a);
100
	}
101
102
	/**
103
	 * @param double $h
104
	 */
105
	public function hsla_hue($h, $m1, $m2){
106
		$h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h);
107
		if	  ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
108
		else if ($h * 2 < 1) return $m2;
109
		else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
110
		else				 return $m1;
111
	}
112
113
	public function hsv($h, $s, $v) {
114
		return $this->hsva($h, $s, $v, 1.0);
115
	}
116
117
	/**
118
	 * @param double $a
119
	 */
120
	public function hsva($h, $s, $v, $a) {
121
		$h = ((Less_Functions::number($h) % 360) / 360 ) * 360;
122
		$s = Less_Functions::number($s);
123
		$v = Less_Functions::number($v);
124
		$a = Less_Functions::number($a);
125
126
		$i = floor(($h / 60) % 6);
127
		$f = ($h / 60) - $i;
128
129
		$vs = array( $v,
130
				  $v * (1 - $s),
131
				  $v * (1 - $f * $s),
132
				  $v * (1 - (1 - $f) * $s));
133
134
		$perm = array(array(0, 3, 1),
135
					array(2, 0, 1),
136
					array(1, 0, 3),
137
					array(1, 2, 0),
138
					array(3, 1, 0),
139
					array(0, 1, 2));
140
141
		return $this->rgba($vs[$perm[$i][0]] * 255,
142
						 $vs[$perm[$i][1]] * 255,
143
						 $vs[$perm[$i][2]] * 255,
144
						 $a);
145
	}
146
147
	public function hue($color){
148
		$c = $color->toHSL();
149
		return new Less_Tree_Dimension(Less_Parser::round($c['h']));
150
	}
151
152
	public function saturation($color){
153
		$c = $color->toHSL();
154
		return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
155
	}
156
157
	public function lightness($color){
158
		$c = $color->toHSL();
159
		return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
160
	}
161
162
	public function hsvhue( $color ){
163
		$hsv = $color->toHSV();
164
		return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) );
165
	}
166
167
168
	public function hsvsaturation( $color ){
169
		$hsv = $color->toHSV();
170
		return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' );
171
	}
172
173
	public function hsvvalue( $color ){
174
		$hsv = $color->toHSV();
175
		return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' );
176
	}
177
178
	public function red($color) {
179
		return new Less_Tree_Dimension( $color->rgb[0] );
180
	}
181
182
	public function green($color) {
183
		return new Less_Tree_Dimension( $color->rgb[1] );
184
	}
185
186
	public function blue($color) {
187
		return new Less_Tree_Dimension( $color->rgb[2] );
188
	}
189
190
	public function alpha($color){
191
		$c = $color->toHSL();
192
		return new Less_Tree_Dimension($c['a']);
193
	}
194
195
	public function luma ($color) {
196
		return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%');
197
	}
198
199
	public function luminance( $color ){
200
		$luminance =
201
			(0.2126 * $color->rgb[0] / 255)
202
		  + (0.7152 * $color->rgb[1] / 255)
203
		  + (0.0722 * $color->rgb[2] / 255);
204
205
		return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%');
206
	}
207
208
	public function saturate($color, $amount = null){
209
		// filter: saturate(3.2);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
210
		// should be kept as is, so check for color
211
		if( !property_exists($color,'rgb') ){
212
			return null;
213
		}
214
		$hsl = $color->toHSL();
215
216
		$hsl['s'] += $amount->value / 100;
217
		$hsl['s'] = self::clamp($hsl['s']);
218
219
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
220
	}
221
222
	/**
223
	 * @param Less_Tree_Dimension $amount
224
	 */
225 View Code Duplication
	public function desaturate($color, $amount){
0 ignored issues
show
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...
226
		$hsl = $color->toHSL();
227
228
		$hsl['s'] -= $amount->value / 100;
229
		$hsl['s'] = self::clamp($hsl['s']);
230
231
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
232
	}
233
234
235
236 View Code Duplication
	public function lighten($color, $amount){
0 ignored issues
show
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...
237
		$hsl = $color->toHSL();
238
239
		$hsl['l'] += $amount->value / 100;
240
		$hsl['l'] = self::clamp($hsl['l']);
241
242
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
243
	}
244
245
	public function darken($color, $amount){
246
247
		if( $color instanceof Less_Tree_Color ){
248
			$hsl = $color->toHSL();
249
			$hsl['l'] -= $amount->value / 100;
250
			$hsl['l'] = self::clamp($hsl['l']);
251
252
			return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
253
		}
254
255
		Less_Functions::Expected('color',$color);
256
	}
257
258 View Code Duplication
	public function fadein($color, $amount){
0 ignored issues
show
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...
259
		$hsl = $color->toHSL();
260
		$hsl['a'] += $amount->value / 100;
261
		$hsl['a'] = self::clamp($hsl['a']);
262
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
263
	}
264
265 View Code Duplication
	public function fadeout($color, $amount){
0 ignored issues
show
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...
266
		$hsl = $color->toHSL();
267
		$hsl['a'] -= $amount->value / 100;
268
		$hsl['a'] = self::clamp($hsl['a']);
269
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
270
	}
271
272 View Code Duplication
	public function fade($color, $amount){
0 ignored issues
show
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...
273
		$hsl = $color->toHSL();
274
275
		$hsl['a'] = $amount->value / 100;
276
		$hsl['a'] = self::clamp($hsl['a']);
277
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
278
	}
279
280
281
282
	public function spin($color, $amount){
283
		$hsl = $color->toHSL();
284
		$hue = fmod($hsl['h'] + $amount->value, 360);
285
286
		$hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
287
288
		return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
289
	}
290
291
	//
292
	// Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
293
	// http://sass-lang.com
294
	//
295
296
	/**
297
	 * @param Less_Tree_Color $color1
298
	 */
299
	public function mix($color1, $color2, $weight = null){
300
		if (!$weight) {
301
			$weight = new Less_Tree_Dimension('50', '%');
302
		}
303
304
		$p = $weight->value / 100.0;
305
		$w = $p * 2 - 1;
306
		$hsl1 = $color1->toHSL();
307
		$hsl2 = $color2->toHSL();
308
		$a = $hsl1['a'] - $hsl2['a'];
309
310
		$w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
311
		$w2 = 1 - $w1;
312
313
		$rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
314
					 $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
315
					 $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2);
316
317
		$alpha = $color1->alpha * $p + $color2->alpha * (1 - $p);
318
319
		return new Less_Tree_Color($rgb, $alpha);
320
	}
321
322
	public function greyscale($color){
323
		return $this->desaturate($color, new Less_Tree_Dimension(100));
324
	}
325
326
327
	public function contrast( $color, $dark = null, $light = null, $threshold = null){
328
		// filter: contrast(3.2);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
329
		// should be kept as is, so check for color
330
		if( !property_exists($color,'rgb') ){
331
			return null;
332
		}
333
		if( !$light ){
334
			$light = $this->rgba(255, 255, 255, 1.0);
335
		}
336
		if( !$dark ){
337
			$dark = $this->rgba(0, 0, 0, 1.0);
338
		}
339
		//Figure out which is actually light and dark!
340
		if( $dark->luma() > $light->luma() ){
341
			$t = $light;
342
			$light = $dark;
343
			$dark = $t;
344
		}
345
		if( !$threshold ){
346
			$threshold = 0.43;
347
		} else {
348
			$threshold = Less_Functions::number($threshold);
349
		}
350
351
		if( $color->luma() < $threshold ){
352
			return $light;
353
		} else {
354
			return $dark;
355
		}
356
	}
357
358
	public function e ($str){
359
		if( is_string($str) ){
360
			return new Less_Tree_Anonymous($str);
361
		}
362
		return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
363
	}
364
365
	public function escape ($str){
366
367
		$revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$');
368
369
		return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
370
	}
371
372
373
	/**
374
	 * todo: This function will need some additional work to make it work the same as less.js
375
	 *
376
	 */
377
	public function replace( $string, $pattern, $replacement, $flags = null ){
378
		$result = $string->value;
379
380
		$expr = '/'.str_replace('/','\\/',$pattern->value).'/';
381
		if( $flags && $flags->value){
382
			$expr .= self::replace_flags($flags->value);
383
		}
384
385
		$result = preg_replace($expr,$replacement->value,$result);
386
387
388
		if( property_exists($string,'quote') ){
389
			return new Less_Tree_Quoted( $string->quote, $result, $string->escaped);
390
		}
391
		return new Less_Tree_Quoted( '', $result );
392
	}
393
394
	public static function replace_flags($flags){
395
		$flags = str_split($flags,1);
396
		$new_flags = '';
397
398
		foreach($flags as $flag){
399
			switch($flag){
400
				case 'e':
401
				case 'g':
402
				break;
403
404
				default:
405
				$new_flags .= $flag;
406
				break;
407
			}
408
		}
409
410
		return $new_flags;
411
	}
412
413
	public function _percent(){
414
		$string = func_get_arg(0);
415
416
		$args = func_get_args();
417
		array_shift($args);
418
		$result = $string->value;
419
420
		foreach($args as $arg){
421
			if( preg_match('/%[sda]/i',$result, $token) ){
422
				$token = $token[0];
423
				$value = stristr($token, 's') ? $arg->value : $arg->toCSS();
424
				$value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
425
				$result = preg_replace('/%[sda]/i',$value, $result, 1);
426
			}
427
		}
428
		$result = str_replace('%%', '%', $result);
429
430
		return new Less_Tree_Quoted( $string->quote , $result, $string->escaped);
431
	}
432
433
    public function unit( $val, $unit = null) {
434
		if( !($val instanceof Less_Tree_Dimension) ){
435
			throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') );
436
		}
437
438
		if( $unit ){
439
			if( $unit instanceof Less_Tree_Keyword ){
440
				$unit = $unit->value;
441
			} else {
442
				$unit = $unit->toCSS();
443
			}
444
		} else {
445
			$unit = "";
446
		}
447
		return new Less_Tree_Dimension($val->value, $unit );
448
    }
449
450
	public function convert($val, $unit){
451
		return $val->convertTo($unit->value);
452
	}
453
454
	public function round($n, $f = false) {
455
456
		$fraction = 0;
457
		if( $f !== false ){
458
			$fraction = $f->value;
459
		}
460
461
		return $this->_math('Less_Parser::round',null, $n, $fraction);
462
	}
463
464
	public function pi(){
465
		return new Less_Tree_Dimension(M_PI);
466
	}
467
468
	public function mod($a, $b) {
469
		return new Less_Tree_Dimension( $a->value % $b->value, $a->unit);
470
	}
471
472
473
474
	public function pow($x, $y) {
475
		if( is_numeric($x) && is_numeric($y) ){
476
			$x = new Less_Tree_Dimension($x);
477
			$y = new Less_Tree_Dimension($y);
478
		}elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){
479
			throw new Less_Exception_Compiler('Arguments must be numbers');
480
		}
481
482
		return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit );
483
	}
484
485
	// var mathFunctions = [{name:"ce ...
486
	public function ceil( $n ){		return $this->_math('ceil', null, $n); }
487
	public function floor( $n ){	return $this->_math('floor', null, $n); }
488
	public function sqrt( $n ){		return $this->_math('sqrt', null, $n); }
489
	public function abs( $n ){		return $this->_math('abs', null, $n); }
490
491
	public function tan( $n ){		return $this->_math('tan', '', $n);	}
492
	public function sin( $n ){		return $this->_math('sin', '', $n);	}
493
	public function cos( $n ){		return $this->_math('cos', '', $n);	}
494
495
	public function atan( $n ){		return $this->_math('atan', 'rad', $n);	}
496
	public function asin( $n ){		return $this->_math('asin', 'rad', $n);	}
497
	public function acos( $n ){		return $this->_math('acos', 'rad', $n);	}
498
499
	private function _math() {
500
		$args = func_get_args();
501
		$fn = array_shift($args);
502
		$unit = array_shift($args);
503
504
		if ($args[0] instanceof Less_Tree_Dimension) {
505
506
			if( $unit === null ){
507
				$unit = $args[0]->unit;
508
			}else{
509
				$args[0] = $args[0]->unify();
510
			}
511
			$args[0] = (float)$args[0]->value;
512
			return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit);
513
		} else if (is_numeric($args[0])) {
514
			return call_user_func_array($fn,$args);
515
		} else {
516
			throw new Less_Exception_Compiler("math functions take numbers as parameters");
517
		}
518
	}
519
520
	/**
521
	 * @param boolean $isMin
522
	 */
523
	private function _minmax( $isMin, $args ){
524
525
		$arg_count = count($args);
526
527
		if( $arg_count < 1 ){
528
			throw new Less_Exception_Compiler( 'one or more arguments required');
529
		}
530
531
		$j = null;
0 ignored issues
show
$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...
532
		$unitClone = null;
533
		$unitStatic = null;
534
535
536
		$order = array();	// elems only contains original argument values.
537
		$values = array();	// key is the unit.toString() for unified tree.Dimension values,
538
							// value is the index into the order array.
539
540
541
		for( $i = 0; $i < $arg_count; $i++ ){
542
			$current = $args[$i];
543
			if( !($current instanceof Less_Tree_Dimension) ){
544
				if( is_array($args[$i]->value) ){
545
					$args[] = $args[$i]->value;
546
				}
547
				continue;
548
			}
549
550
			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...
551
				$temp = new Less_Tree_Dimension($current->value, $unitClone);
552
				$currentUnified = $temp->unify();
553
			}else{
554
				$currentUnified = $current->unify();
555
			}
556
557
			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...
558
				$unit = $unitStatic;
559
			}else{
560
				$unit = $currentUnified->unit->toString();
561
			}
562
563
			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...
564
				$unitStatic = $unit;
565
			}
566
567
			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...
568
				$unitClone = $current->unit->toString();
569
			}
570
571
			if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){
572
				$j = $values[''];
573
			}elseif( isset($values[$unit]) ){
574
				$j = $values[$unit];
575
			}else{
576
577
				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...
578
					throw new Less_Exception_Compiler( 'incompatible types');
579
				}
580
				$values[$unit] = count($order);
581
				$order[] = $current;
582
				continue;
583
			}
584
585
586
			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...
587
				$temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone);
588
				$referenceUnified = $temp->unifiy();
0 ignored issues
show
The method unifiy() does not exist on Less_Tree_Dimension. Did you maybe mean unify()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
589
			}else{
590
				$referenceUnified = $order[$j]->unify();
591
			}
592
			if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){
593
				$order[$j] = $current;
594
			}
595
		}
596
597
		if( count($order) == 1 ){
598
			return $order[0];
599
		}
600
		$args = array();
601
		foreach($order as $a){
602
			$args[] = $a->toCSS($this->env);
603
		}
604
		return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')');
605
	}
606
607
	public function min(){
608
		$args = func_get_args();
609
		return $this->_minmax( true, $args );
610
	}
611
612
	public function max(){
613
		$args = func_get_args();
614
		return $this->_minmax( false, $args );
615
	}
616
617
	public function getunit($n){
618
		return new Less_Tree_Anonymous($n->unit);
619
	}
620
621
	public function argb($color) {
622
		return new Less_Tree_Anonymous($color->toARGB());
623
	}
624
625
	public function percentage($n) {
626
		return new Less_Tree_Dimension($n->value * 100, '%');
627
	}
628
629
	public function color($n) {
630
631
		if( $n instanceof Less_Tree_Quoted ){
632
			$colorCandidate = $n->value;
633
			$returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
634
			if( $returnColor ){
635
				return $returnColor;
636
			}
637
			if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){
638
				return new Less_Tree_Color(substr($colorCandidate, 1));
639
			}
640
			throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
641
		} else {
642
			throw new Less_Exception_Compiler("argument must be a string");
643
		}
644
	}
645
646
647
	public function iscolor($n) {
648
		return $this->_isa($n, 'Less_Tree_Color');
649
	}
650
651
	public function isnumber($n) {
652
		return $this->_isa($n, 'Less_Tree_Dimension');
653
	}
654
655
	public function isstring($n) {
656
		return $this->_isa($n, 'Less_Tree_Quoted');
657
	}
658
659
	public function iskeyword($n) {
660
		return $this->_isa($n, 'Less_Tree_Keyword');
661
	}
662
663
	public function isurl($n) {
664
		return $this->_isa($n, 'Less_Tree_Url');
665
	}
666
667
	public function ispixel($n) {
668
		return $this->isunit($n, 'px');
669
	}
670
671
	public function ispercentage($n) {
672
		return $this->isunit($n, '%');
673
	}
674
675
	public function isem($n) {
676
		return $this->isunit($n, 'em');
677
	}
678
679
	/**
680
	 * @param string $unit
681
	 */
682
	public function isunit( $n, $unit ){
683
		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');
684
	}
685
686
	/**
687
	 * @param string $type
688
	 */
689
	private function _isa($n, $type) {
690
		return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
691
	}
692
693
	public function tint($color, $amount) {
694
		return $this->mix( $this->rgb(255,255,255), $color, $amount);
695
	}
696
697
	public function shade($color, $amount) {
698
		return $this->mix($this->rgb(0, 0, 0), $color, $amount);
699
	}
700
701
	public function extract($values, $index ){
702
		$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...
703
		// handle non-array values as an array of length 1
704
		// return 'undefined' if index is invalid
705
		if( property_exists($values,'value') && is_array($values->value) ){
706
			if( isset($values->value[$index]) ){
707
				return $values->value[$index];
708
			}
709
			return null;
710
711
		}elseif( (int)$index === 0 ){
712
			return $values;
713
		}
714
715
		return null;
716
	}
717
718
	public function length($values){
719
		$n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1;
720
		return new Less_Tree_Dimension($n);
721
	}
722
723
	public function datauri($mimetypeNode, $filePathNode = null ) {
724
725
		$filePath = ( $filePathNode ? $filePathNode->value : null );
726
		$mimetype = $mimetypeNode->value;
727
728
		$args = 2;
729
		if( !$filePath ){
730
			$filePath = $mimetype;
731
			$args = 1;
732
		}
733
734
		$filePath = str_replace('\\','/',$filePath);
735
		if( Less_Environment::isPathRelative($filePath) ){
736
737
			if( Less_Parser::$options['relativeUrls'] ){
738
				$temp = $this->currentFileInfo['currentDirectory'];
739
			} else {
740
				$temp = $this->currentFileInfo['entryPath'];
741
			}
742
743
			if( !empty($temp) ){
744
				$filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath);
745
			}
746
747
		}
748
749
750
		// detect the mimetype if not given
751
		if( $args < 2 ){
752
753
			/* incomplete
754
			$mime = require('mime');
755
			mimetype = mime.lookup(path);
756
757
			// use base 64 unless it's an ASCII or UTF-8 format
758
			var charset = mime.charsets.lookup(mimetype);
759
			useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
760
			if (useBase64) mimetype += ';base64';
761
			*/
762
763
			$mimetype = Less_Mime::lookup($filePath);
764
765
			$charset = Less_Mime::charsets_lookup($mimetype);
766
			$useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8'));
767
			if( $useBase64 ){ $mimetype .= ';base64'; }
768
769
		}else{
770
			$useBase64 = preg_match('/;base64$/',$mimetype);
771
		}
772
773
774
		if( file_exists($filePath) ){
775
			$buf = @file_get_contents($filePath);
776
		}else{
777
			$buf = false;
778
		}
779
780
781
		// IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
782
		// and the --ieCompat flag is enabled, return a normal url() instead.
783
		$DATA_URI_MAX_KB = 32;
784
		$fileSizeInKB = round( strlen($buf) / 1024 );
785
		if( $fileSizeInKB >= $DATA_URI_MAX_KB ){
786
			$url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
787
			return $url->compile($this);
788
		}
789
790
		if( $buf ){
791
			$buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
792
			$filePath = '"data:' . $mimetype . ',' . $buf . '"';
793
		}
794
795
		return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) );
796
	}
797
798
	//svg-gradient
799
	public function svggradient( $direction ){
800
801
		$throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
802
		$arguments = func_get_args();
803
804
		if( count($arguments) < 3 ){
805
			throw new Less_Exception_Compiler( $throw_message );
806
		}
807
808
		$stops = array_slice($arguments,1);
809
		$gradientType = 'linear';
810
		$rectangleDimension = 'x="0" y="0" width="1" height="1"';
811
		$useBase64 = true;
812
		$directionValue = $direction->toCSS();
813
814
815
		switch( $directionValue ){
816
			case "to bottom":
817
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
818
				break;
819
			case "to right":
820
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
821
				break;
822
			case "to bottom right":
823
				$gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
824
				break;
825
			case "to top right":
826
				$gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
827
				break;
828
			case "ellipse":
829
			case "ellipse at center":
830
				$gradientType = "radial";
831
				$gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
832
				$rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
833
				break;
834
			default:
835
				throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" );
836
		}
837
838
		$returner = '<?xml version="1.0" ?>' .
839
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' .
840
			'<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
841
842
		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...
843
			if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){
844
				$color = $stops[$i]->value[0];
845
				$position = $stops[$i]->value[1];
846
			}else{
847
				$color = $stops[$i];
848
				$position = null;
849
			}
850
851
			if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){
852
				throw new Less_Exception_Compiler( $throw_message );
853
			}
854
			if( $position ){
855
				$positionValue = $position->toCSS();
856
			}elseif( $i === 0 ){
857
				$positionValue = '0%';
858
			}else{
859
				$positionValue = '100%';
860
			}
861
			$alpha = $color->alpha;
862
			$returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>';
863
		}
864
865
		$returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>';
866
867
868
		if( $useBase64 ){
869
			$returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'";
870
		}else{
871
			$returner = "'data:image/svg+xml,".$returner."'";
872
		}
873
874
		return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
875
	}
876
877
878
	/**
879
	 * @param string $type
880
	 */
881
	private static function Expected( $type, $arg ){
882
883
		$debug = debug_backtrace();
884
		array_shift($debug);
885
		$last = array_shift($debug);
886
		$last = array_intersect_key($last,array('function'=>'','class'=>'','line'=>''));
887
888
		$message = 'Object of type '.get_class($arg).' passed to darken function. Expecting `'.$type.'`. '.$arg->toCSS().'. '.print_r($last,true);
889
		throw new Less_Exception_Compiler($message);
890
891
	}
892
893
	/**
894
	 * Php version of javascript's `encodeURIComponent` function
895
	 *
896
	 * @param string $string The string to encode
897
	 * @return string The encoded string
898
	 */
899
	public static function encodeURIComponent($string){
900
		$revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
901
		return strtr(rawurlencode($string), $revert);
902
	}
903
904
905
	// Color Blending
906
	// ref: http://www.w3.org/TR/compositing-1
907
908
	public function colorBlend( $mode, $color1, $color2 ){
909
		$ab = $color1->alpha;	// backdrop
910
		$as = $color2->alpha;	// source
911
		$r = array();			// result
912
913
		$ar = $as + $ab * (1 - $as);
914
		for( $i = 0; $i < 3; $i++ ){
915
			$cb = $color1->rgb[$i] / 255;
916
			$cs = $color2->rgb[$i] / 255;
917
			$cr = call_user_func( $mode, $cb, $cs );
918
			if( $ar ){
919
				$cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
920
			}
921
			$r[$i] = $cr * 255;
922
		}
923
924
		return new Less_Tree_Color($r, $ar);
925
	}
926
927
	public function multiply($color1, $color2 ){
928
		return $this->colorBlend( array($this,'colorBlendMultiply'),  $color1, $color2 );
929
	}
930
931
	private function colorBlendMultiply($cb, $cs){
932
		return $cb * $cs;
933
	}
934
935
	public function screen($color1, $color2 ){
936
		return $this->colorBlend( array($this,'colorBlendScreen'),  $color1, $color2 );
937
	}
938
939
	private function colorBlendScreen( $cb, $cs){
940
		return $cb + $cs - $cb * $cs;
941
	}
942
943
	public function overlay($color1, $color2){
944
		return $this->colorBlend( array($this,'colorBlendOverlay'),  $color1, $color2 );
945
	}
946
947
	private function colorBlendOverlay($cb, $cs ){
948
		$cb *= 2;
949
		return ($cb <= 1)
950
			? $this->colorBlendMultiply($cb, $cs)
951
			: $this->colorBlendScreen($cb - 1, $cs);
952
	}
953
954
	public function softlight($color1, $color2){
955
		return $this->colorBlend( array($this,'colorBlendSoftlight'),  $color1, $color2 );
956
	}
957
958
	private function colorBlendSoftlight($cb, $cs ){
959
		$d = 1;
960
		$e = $cb;
961
		if( $cs > 0.5 ){
962
			$e = 1;
963
			$d = ($cb > 0.25) ? sqrt($cb)
964
				: ((16 * $cb - 12) * $cb + 4) * $cb;
965
		}
966
		return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
967
	}
968
969
	public function hardlight($color1, $color2){
970
		return $this->colorBlend( array($this,'colorBlendHardlight'),  $color1, $color2 );
971
	}
972
973
	private function colorBlendHardlight( $cb, $cs ){
974
		return $this->colorBlendOverlay($cs, $cb);
975
	}
976
977
	public function difference($color1, $color2) {
978
		return $this->colorBlend( array($this,'colorBlendDifference'),  $color1, $color2 );
979
	}
980
981
	private function colorBlendDifference( $cb, $cs ){
982
		return abs($cb - $cs);
983
	}
984
985
	public function exclusion( $color1, $color2 ){
986
		return $this->colorBlend( array($this,'colorBlendExclusion'),  $color1, $color2 );
987
	}
988
989
	private function colorBlendExclusion( $cb, $cs ){
990
		return $cb + $cs - 2 * $cb * $cs;
991
	}
992
993
	public function average($color1, $color2){
994
		return $this->colorBlend( array($this,'colorBlendAverage'),  $color1, $color2 );
995
	}
996
997
	// non-w3c functions:
998
	public function colorBlendAverage($cb, $cs ){
999
		return ($cb + $cs) / 2;
1000
	}
1001
1002
	public function negation($color1, $color2 ){
1003
		return $this->colorBlend( array($this,'colorBlendNegation'),  $color1, $color2 );
1004
	}
1005
1006
	public function colorBlendNegation($cb, $cs){
1007
		return 1 - abs($cb + $cs - 1);
1008
	}
1009
1010
	// ~ End of Color Blending
1011
1012
}
1013