Functions::asin()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace LesserPhp\Library;
4
5
use LesserPhp\Color\Converter;
6
use LesserPhp\Compiler;
7
use LesserPhp\Exception\GeneralException;
8
9
/**
10
 * lesserphp
11
 * https://www.maswaba.de/lesserphp
12
 *
13
 * LESS CSS compiler, adapted from http://lesscss.org
14
 *
15
 * Copyright 2013, Leaf Corcoran <[email protected]>
16
 * Copyright 2016, Marcus Schwarz <[email protected]>
17
 * Licensed under MIT or GPLv3, see LICENSE
18
 * @package LesserPhp
19
 */
20
class Functions
21
{
22
23
    /**
24
     * @var \LesserPhp\Library\Assertions
25
     */
26
    private $assertions;
27
28
    /**
29
     * @var \LesserPhp\Library\Coerce
30
     */
31
    private $coerce;
32
33
    /**
34
     * @var \LesserPhp\Compiler
35
     */
36
    private $compiler;
37
38
    /**
39
     * @var \LesserPhp\Color\Converter
40
     */
41
    private $converter;
42
43
    public static $TRUE = ["keyword", "true"];
44
    public static $FALSE = ["keyword", "false"];
45
    public static $lengths = ["px", "m", "cm", "mm", "in", "pt", "pc"];
46
    public static $times = ["s", "ms"];
47
    public static $angles = ["rad", "deg", "grad", "turn"];
48
    public static $lengths_to_base = [1, 3779.52755906, 37.79527559, 3.77952756, 96, 1.33333333, 16];
49
50 64
51
    /**
52 64
     * Functions constructor.
53 64
     *
54 64
     * @param \LesserPhp\Library\Assertions $assertions
55 64
     * @param \LesserPhp\Library\Coerce     $coerce
56 64
     * @param \LesserPhp\Compiler           $compiler
57
     * @param \LesserPhp\Color\Converter    $converter
58 1
     */
59
    public function __construct(Assertions $assertions, Coerce $coerce, Compiler $compiler, Converter $converter)
60 1
    {
61
        $this->assertions = $assertions;
62
        $this->coerce = $coerce;
63 1
        $this->compiler = $compiler; // temporary solution to get it working
64 1
        $this->converter = $converter;
65 1
    }
66
67
    /**
68
     * @param array $args
69 1
     *
70
     * @return array
71 1
     * @throws \LesserPhp\Exception\GeneralException
72
     */
73
    public function pow(array $args)
74 1
    {
75
        list($base, $exp) = $this->assertions->assertArgs($args, 2, 'pow');
76 1
77
        return [
78 1
            'number',
79
            pow($this->assertions->assertNumber($base), $this->assertions->assertNumber($exp)),
80
            $args[2][0][2],
81 1
        ];
82
    }
83 1
84 1
    /**
85
     * @return float
86
     */
87
    public function pi()
88 1
    {
89
        return M_PI;
90
    }
91 1
92
    /**
93 1
     * @param array $args
94 1
     *
95
     * @return array
96
     * @throws \LesserPhp\Exception\GeneralException
97
     */
98 1
    public function mod(array $args)
99
    {
100
        list($a, $b) = $this->assertions->assertArgs($args, 2, 'mod');
101 1
102
        return ['number', $this->assertions->assertNumber($a) % $this->assertions->assertNumber($b), $args[2][0][2]];
103 1
    }
104 1
105
    /**
106
     * @param array $color
107
     *
108 1
     * @return int
109
     * @throws \LesserPhp\Exception\GeneralException
110
     */
111 2
    public function red(array $color)
112
    {
113 2
        $color = $this->coerce->coerceColor($color);
114
        if ($color === null) {
115
            throw new GeneralException('color expected for red()');
116 2
        }
117 2
118
        return $color[1];
119
    }
120 2
121
    /**
122
     * @param array $color
123 1
     *
124
     * @return int
125 1
     * @throws \LesserPhp\Exception\GeneralException
126
     */
127
    public function green(array $color)
128 2
    {
129
        $color = $this->coerce->coerceColor($color);
130 2
        if ($color === null) {
131
            throw new GeneralException('color expected for green()');
132 1
        }
133
134 1
        return $color[2];
135 1
    }
136
137 1
    /**
138 1
     * @param array $color
139
     *
140 1
     * @return int
141 1
     * @throws \LesserPhp\Exception\GeneralException
142 1
     */
143
    public function blue(array $color)
144
    {
145
        $color = $this->coerce->coerceColor($color);
146 1
        if ($color === null) {
147
            throw new GeneralException('color expected for blue()');
148
        }
149 3
150
        return $color[3];
151 3
    }
152
153 2
    /**
154
     * @param array $args
155 2
     *
156 2
     * @return array
157
     * @throws \LesserPhp\Exception\GeneralException
158 2
     */
159 2
    public function convert(array $args)
160
    {
161 2
        list($value, $to) = $this->assertions->assertArgs($args, 2, 'convert');
162 1
163 1
        // If it's a keyword, grab the string version instead
164
        if (is_array($to) && $to[0] === 'keyword') {
165
            $to = $to[1];
166
        }
167 1
168
        return $this->convertMe($value, $to);
169
    }
170 1
171
    /**
172 1
     * @param array $num
173
     *
174
     * @return array
175 1
     * @throws \LesserPhp\Exception\GeneralException
176
     */
177 1
    public function abs(array $num)
178
    {
179
        return ['number', abs($this->assertions->assertNumber($num)), $num[2]];
180 1
    }
181
182 1
    /**
183
     * @param array $args
184
     *
185 1
     * @return int
186
     * @throws \LesserPhp\Exception\GeneralException
187 1
     */
188
    public function min(array $args)
189 1
    {
190
        $values = $this->assertions->assertMinArgs($args, 1, 'min');
191
192 1
        $firstFormat = $values[0][2];
193
194 1
        $minIndex = 0;
195
        $minValue = $values[0][1];
196 1
197
        foreach ($values as $a => $value) {
198
            $converted = $this->convertMe($value, $firstFormat);
199 1
200
            if ($converted[1] < $minValue) {
201 1
                $minIndex = $a;
202
                $minValue = $value[1];
203 1
            }
204
        }
205
206 1
        return $values[$minIndex];
207
    }
208 1
209
    /**
210
     * @param array $args
211 1
     *
212
     * @return int
213 1
     * @throws \LesserPhp\Exception\GeneralException
214 1
     */
215
    public function max(array $args)
216 1
    {
217 1
        $values = $this->assertions->assertMinArgs($args, 1, 'max');
218
219
        $firstFormat = $values[0][2];
220 1
221
        $maxIndex = 0;
222
        $maxValue = $values[0][1];
223 2
224
        foreach ($values as $a => $value) {
225 2
            $converted = $this->convertMe($value, $firstFormat);
226
227
            if ($converted[1] > $maxValue) {
228 1
                $maxIndex = $a;
229
                $maxValue = $value[1];
230 1
            }
231
        }
232
233 3
        return $values[$maxIndex];
234
    }
235 3
236
    /**
237
     * @param $num
238 1
     *
239
     * @return float
240 1
     * @throws \LesserPhp\Exception\GeneralException
241
     */
242
    public function tan($num)
243 2
    {
244
        return tan($this->assertions->assertNumber($num));
245 2
    }
246
247
    /**
248 1
     * @param $num
249
     *
250 1
     * @return float
251
     * @throws \LesserPhp\Exception\GeneralException
252
     */
253 1
    public function sin($num)
254
    {
255 1
        return sin($this->assertions->assertNumber($num));
256
    }
257
258
    /**
259
     * @param $num
260
     *
261
     * @return float
262
     * @throws \LesserPhp\Exception\GeneralException
263 1
     */
264
    public function cos($num)
265 1
    {
266 1
        return cos($this->assertions->assertNumber($num));
267
    }
268
269
    /**
270 1
     * @param $num
271 1
     *
272 1
     * @return array
273 1
     * @throws \LesserPhp\Exception\GeneralException
274 1
     */
275 1
    public function atan($num)
276
    {
277
        $num = atan($this->assertions->assertNumber($num));
278
279 1
        return ['number', $num, 'rad'];
280
    }
281 1
282
    /**
283
     * @param $num
284
     *
285
     * @return array
286
     * @throws \LesserPhp\Exception\GeneralException
287
     */
288
    public function asin($num)
289
    {
290
        $num = asin($this->assertions->assertNumber($num));
291 1
292
        return ['number', $num, 'rad'];
293 1
    }
294 1
295
    /**
296 1
     * @param $num
297
     *
298 1
     * @return array
299
     * @throws \LesserPhp\Exception\GeneralException
300 1
     */
301 1
    public function acos($num)
302 1
    {
303 1
        $num = acos($this->assertions->assertNumber($num));
304 1
305
        return ['number', $num, 'rad'];
306
    }
307
308 1
    /**
309
     * @param $num
310 1
     *
311
     * @return float
312
     * @throws \LesserPhp\Exception\GeneralException
313
     */
314
    public function sqrt($num)
315 1
    {
316
        return sqrt($this->assertions->assertNumber($num));
317
    }
318
319 13
    /**
320
     * @param $value
321 13
     *
322 13
     * @return mixed
323 2
     * @throws \LesserPhp\Exception\GeneralException
324 2
     */
325 1
    public function extract($value)
326
    {
327 1
        list($list, $idx) = $this->assertions->assertArgs($value, 2, 'extract');
328 12
        $idx = $this->assertions->assertNumber($idx);
329 12
        // 1 indexed
330
        if ($list[0] === 'list' && isset($list[2][$idx - 1])) {
331 12
            return $list[2][$idx - 1];
332 6
        }
333 3
334
        return null;
335 4
    }
336
337
    /**
338
     * @param $value
339 1
     *
340
     * @return array
341 1
     */
342
    public function isnumber($value)
343
    {
344 1
        return $this->toBool($value[0] === 'number');
345 1
    }
346 1
347
    /**
348 1
     * @param $value
349 1
     *
350 1
     * @return array
351 1
     */
352 1
    public function isstring($value)
353
    {
354
        return $this->toBool($value[0] === 'string');
355 1
    }
356 1
357
    /**
358
     * @param $value
359
     *
360 1
     * @return array
361 1
     */
362 1
    public function iscolor($value)
363 1
    {
364
        $yesno = $this->coerce->coerceColor($value) !== null;
365
        return $this->toBool($yesno);
366 1
    }
367
368
    /**
369
     * @param $value
370
     *
371 1
     * @return array
372
     */
373 1
    public function iskeyword($value)
374
    {
375
        return $this->toBool($value[0] === 'keyword');
376 1
    }
377
378 1
    /**
379
     * @param $value
380 1
     *
381
     * @return array
382
     */
383 1
    public function ispixel($value)
384
    {
385 1
        return $this->toBool($value[0] === 'number' && $value[2] === 'px');
386
    }
387 1
388
    /**
389
     * @param $value
390 1
     *
391
     * @return array
392 1
     */
393 1
    public function ispercentage($value)
394
    {
395 1
        return $this->toBool($value[0] === 'number' && $value[2] === '%');
396
    }
397
398
    /**
399
     * @param $value
400
     *
401
     * @return array
402
     */
403
    public function isem($value)
404 1
    {
405
        return $this->toBool($value[0] === 'number' && $value[2] === 'em');
406 1
    }
407 1
408
    /**
409
     * @param $value
410 1
     *
411 1
     * @return array
412 1
     */
413
    public function isrem($value)
414
    {
415 1
        return $this->toBool($value[0] === 'number' && $value[2] === 'rem');
416
    }
417
418
    /**
419
     * @param $color
420 1
     *
421
     * @return string
422 1
     * @throws \LesserPhp\Exception\GeneralException
423
     */
424 1
    public function rgbahex($color)
425 1
    {
426
        $color = $this->coerce->coerceColor($color);
427 1
        if ($color === null) {
428
            throw new GeneralException('color expected for rgbahex');
429
        }
430 2
431
        return sprintf(
432 2
            '#%02x%02x%02x%02x',
433
            isset($color[4]) ? $color[4] * 255 : 255,
434 2
            $color[1],
435 2
            $color[2],
436
            $color[3]
437 2
        );
438
    }
439
440 1
    /**
441
     * @param $color
442 1
     *
443
     * @return string
444 1
     * @throws \LesserPhp\Exception\GeneralException
445 1
     */
446
    public function argb($color)
447 1
    {
448
        return $this->rgbahex($color);
449
    }
450 1
451
    /**
452 1
     * Given an url, decide whether to output a regular link or the base64-encoded contents of the file
453
     *
454 1
     * @param  array $value either an argument list (two strings) or a single string
455 1
     *
456
     * @return string        formatted url(), either as a link or base64-encoded
457 1
     */
458
    public function data_uri($value)
459
    {
460 1
        $mime = ($value[0] === 'list') ? $value[2][0][2] : null;
461
        $url = ($value[0] === 'list') ? $value[2][1][2][0] : $value[2][0];
462 1
463
        $fullpath = $this->findImport($url);
464 1
465
        if ($fullpath && ($fsize = filesize($fullpath)) !== false) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fullpath 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...
466 1
            // IE8 can't handle data uris larger than 32KB
467 1
            if ($fsize / 1024 < 32) {
468 1
                if ($mime === null) {
469
                    $finfo = new \finfo(FILEINFO_MIME);
470
                    $mime = explode('; ', $finfo->file($fullpath));
471 1
                    $mime = $mime[0];
472
                }
473
474 1
                //todo find out why this suddenly breakes data-uri-test
475
                if ($mime !== null && $mime !== 'text/x-php') {
476 1
                    // fallback if the mime type is still unknown
477 1
                    $url = sprintf('data:%s;base64,%s', $mime, base64_encode(file_get_contents($fullpath)));
478
                }
479 1
            }
480
        }
481
482 1
        return 'url("' . $url . '")';
483
    }
484 1
485 1
    // utility func to unquote a string
486
487 1
    /**
488
     * @param array $arg
489
     *
490 1
     * @return array
491
     * @throws \LesserPhp\Exception\GeneralException
492 1
     */
493
    public function e(array $arg)
494 1
    {
495
        switch ($arg[0]) {
496
            case 'list':
497 1
                $items = $arg[2];
498
                if (isset($items[0])) {
499 1
                    return $this->e($items[0]);
500
                }
501 1
                throw new GeneralException('unrecognised input');
502
            case 'string':
503
                $arg[1] = '';
504
505
                return $arg;
506
            case 'keyword':
507
                return $arg;
508
            default:
509 1
                return ['keyword', $this->compiler->compileValue($arg)];
510
        }
511 1
    }
512
513 1
    /**
514
     * @param array $args
515
     *
516
     * @return array
517
     * @throws \LesserPhp\Exception\GeneralException
518
     */
519
    public function _sprintf(array $args)
520
    {
521
        if ($args[0] !== 'list') {
522
            return $args;
523
        }
524 2
        $values = $args[2];
525
        $string = array_shift($values);
526 2
        $template = $this->compiler->compileValue($this->e($string));
527 2
528 1
        $i = 0;
529
        if (preg_match_all('/%[dsa]/', $template, $m)) {
530
            foreach ($m[0] as $match) {
531 1
                $val = isset($values[$i]) ?
532
                    $this->compiler->reduce($values[$i]) : ['keyword', ''];
533
534
                // lessjs compat, renders fully expanded color, not raw color
535 1
                $color = $this->coerce->coerceColor($val);
536
                if ($color !== null) {
537 1
                    $val = $color;
538 1
                }
539
540 1
                $i++;
541
                $rep = $this->compiler->compileValue($this->e($val));
542
                $template = preg_replace(
543 1
                    '/' . Compiler::pregQuote($match) . '/',
544
                    $rep,
545 1
                    $template,
546
                    1
547 1
                );
548
            }
549
        }
550
551
        $d = $string[0] === 'string' ? $string[1] : '"';
552
553 1
        return ['string', $d, [$template]];
554
    }
555 1
556
    /**
557
     * @param array $arg
558
     *
559 1
     * @return array
560 1
     * @throws \LesserPhp\Exception\GeneralException
561 1
     */
562
    public function floor(array $arg)
563 1
    {
564 1
        $value = $this->assertions->assertNumber($arg);
565
566 1
        return ['number', floor($value), $arg[2]];
567 1
    }
568
569 1
    /**
570
     * @param array $arg
571
     *
572 1
     * @return array
573 1
     * @throws \LesserPhp\Exception\GeneralException
574
     */
575 1
    public function ceil(array $arg)
576 1
    {
577
        $value = $this->assertions->assertNumber($arg);
578
579 1
        return ['number', ceil($value), $arg[2]];
580 1
    }
581 1
582 1
    /**
583
     * @param array $arg
584
     *
585 1
     * @return array
586 1
     * @throws \LesserPhp\Exception\GeneralException
587
     */
588
    public function round(array $arg)
589 1
    {
590
        if ($arg[0] !== 'list') {
591
            $value = $this->assertions->assertNumber($arg);
592 1
593
            return ['number', round($value), $arg[2]];
594 1
        } else {
595 1
            $value = $this->assertions->assertNumber($arg[2][0]);
596 1
            $precision = $this->assertions->assertNumber($arg[2][1]);
597
598 1
            return ['number', round($value, $precision), $arg[2][0][2]];
599 1
        }
600 1
    }
601 1
602 1
    /**
603 1
     * @param array $arg
604 1
     *
605 1
     * @return array
606
     * @throws \LesserPhp\Exception\GeneralException
607 1
     */
608
    public function unit(array $arg)
609
    {
610
        if ($arg[0] === 'list') {
611
            list($number, $newUnit) = $arg[2];
612
613 1
            return [
614 1
                'number',
615 1
                $this->assertions->assertNumber($number),
616
                $this->compiler->compileValue($this->e($newUnit)),
617
            ];
618 1
        } else {
619 1
            return ['number', $this->assertions->assertNumber($arg), ''];
620 1
        }
621 1
    }
622
623
624 1
    /**
625 1
     * @param array $args
626 1
     *
627
     * @return array
628
     */
629 1
    public function darken(array $args)
630
    {
631
        list($color, $delta) = $this->compiler->colorArgs($args);
632 1
633
        $hsl = $this->converter->toHSL($color);
634 1
        $hsl[3] = $this->converter->clamp($hsl[3] - $delta, 100);
635 1
636
        return $this->converter->toRGB($hsl);
637
    }
638
639 3
    /**
640
     * @param array $args
641 3
     *
642 3
     * @return array
643
     */
644
    public function lighten(array $args)
645 3
    {
646 2
        list($color, $delta) = $this->compiler->colorArgs($args);
647
648
        $hsl = $this->converter->toHSL($color);
649
        $hsl[3] = $this->converter->clamp($hsl[3] + $delta, 100);
650 3
651
        return $this->converter->toRGB($hsl);
652 2
    }
653
654 1
    /**
655 1
     * @param array $args
656 1
     *
657
     * @return array
658 1
     */
659
    public function saturate(array $args)
660 1
    {
661
        list($color, $delta) = $this->compiler->colorArgs($args);
662
663
        $hsl = $this->converter->toHSL($color);
664
        $hsl[2] = $this->converter->clamp($hsl[2] + $delta, 100);
665 3
666
        return $this->converter->toRGB($hsl);
667 1
    }
668 1
669
    /**
670 1
     * @param array $args
671
     *
672
     * @return array
673 1
     */
674
    public function desaturate(array $args)
675 1
    {
676
        list($color, $delta) = $this->compiler->colorArgs($args);
677
678
        $hsl = $this->converter->toHSL($color);
679 3
        $hsl[2] = $this->converter->clamp($hsl[2] - $delta, 100);
680
681 1
        return $this->converter->toRGB($hsl);
682 1
    }
683
684 1
    /**
685 1
     * @param array $args
686
     *
687 1
     * @return array
688 1
     */
689
    public function spin(array $args)
690 1
    {
691
        list($color, $delta) = $this->compiler->colorArgs($args);
692
693
        $hsl = $this->converter->toHSL($color);
694
695
        $hsl[1] += $delta % 360;
696 1
        if ($hsl[1] < 0) {
697 1
            $hsl[1] += 360;
698
        }
699
700 1
        return $this->converter->toRGB($hsl);
701 1
    }
702
703
    /**
704 1
     * @param array $args
705 1
     *
706
     * @return int[]
707
     */
708 1
    public function fadeout(array $args)
709 1
    {
710
        list($color, $delta) = $this->compiler->colorArgs($args);
711
        $color[4] = $this->converter->clamp((isset($color[4]) ? $color[4] : 1) - $delta / 100);
712 1
713
        return $color;
714 1
    }
715
716
    /**
717
     * @param array $args
718 2
     *
719
     * @return int[]
720
     */
721
    public function fadein(array $args)
722
    {
723
        list($color, $delta) = $this->compiler->colorArgs($args);
724
        $color[4] = $this->converter->clamp((isset($color[4]) ? $color[4] : 1) + $delta / 100);
725
726 7
        return $color;
727
    }
728 7
729 5
    /**
730
     * @param array $color
731 7
     *
732
     * @return float
733
     * @throws \LesserPhp\Exception\GeneralException
734
     */
735
    public function hue(array $color)
736
    {
737
        $hsl = $this->converter->toHSL($this->assertions->assertColor($color));
738
739
        return round($hsl[1]);
740
    }
741
742 4
    /**
743
     * @param array $color
744 4
     *
745 4
     * @return float
746 4
     * @throws \LesserPhp\Exception\GeneralException
747 4
     */
748
    public function saturation(array $color)
749
    {
750
        $hsl = $this->converter->toHSL($this->assertions->assertColor($color));
751 2
752
        return round($hsl[2]);
753
    }
754
755
    /**
756
     * @param array $color
757
     *
758
     * @return float
759 4
     * @throws \LesserPhp\Exception\GeneralException
760
     */
761 4
    public function lightness(array $color)
762
    {
763
        $hsl = $this->converter->toHSL($this->assertions->assertColor($color));
764
765
        return round($hsl[3]);
766
    }
767
768
    /**
769
     * get the alpha of a color
770
     * defaults to 1 for non-colors or colors without an alpha
771
     *
772
     * @param array $value
773
     *
774
     * @return int|null
775
     */
776
    public function alpha(array $value)
777
    {
778
        $color = $this->coerce->coerceColor($value);
779
        if ($color !== null) {
780
            return isset($color[4]) ? $color[4] : 1;
781
        }
782
783
        return null;
784
    }
785
786
    /**
787
     * set the alpha of the color
788
     *
789
     * @param array $args
790
     *
791
     * @return int[]
792
     */
793
    public function fade(array $args)
794
    {
795
        list($color, $alpha) = $this->compiler->colorArgs($args);
796
        $color[4] = $this->converter->clamp($alpha / 100.0);
797
798
        return $color;
799
    }
800
801
    /**
802
     * @param array $arg
803
     *
804
     * @return array
805
     * @throws \LesserPhp\Exception\GeneralException
806
     */
807
    public function percentage(array $arg)
808
    {
809
        $num = $this->assertions->assertNumber($arg);
810
811
        return ['number', $num * 100, '%'];
812
    }
813
814
    /**
815
     * mixes two colors by weight
816
     * mix(@color1, @color2, [@weight: 50%]);
817
     * http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
818
     *
819
     * @param array $args
820
     *
821
     * @return mixed
822
     * @throws \LesserPhp\Exception\GeneralException
823
     */
824
    public function mix(array $args)
825
    {
826
        if ($args[0] !== 'list' || count($args[2]) < 2) {
827
            throw new GeneralException('mix expects (color1, color2, weight)');
828
        }
829
830
        list($first, $second) = $args[2];
831
        $first = $this->assertions->assertColor($first);
832
        $second = $this->assertions->assertColor($second);
833
834
        $firstAlpha = $this->alpha($first);
835
        $secondAlpha = $this->alpha($second);
836
837
        if (isset($args[2][2])) {
838
            $weight = $args[2][2][1] / 100.0;
839
        } else {
840
            $weight = 0.5;
841
        }
842
843
        $w = $weight * 2 - 1;
844
        $a = $firstAlpha - $secondAlpha;
845
846
        $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0;
847
        $w2 = 1.0 - $w1;
848
849
        $new = [
850
            'color',
851
            $w1 * $first[1] + $w2 * $second[1],
852
            $w1 * $first[2] + $w2 * $second[2],
853
            $w1 * $first[3] + $w2 * $second[3],
854
        ];
855
856
        // do not change the following to type safe comparison...
857
        if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
858
            $new[] = $firstAlpha * $weight + $secondAlpha * ($weight - 1);
859
        }
860
861
        return $this->compiler->fixColor($new);
862
    }
863
864
    /**
865
     * @param array $args
866
     *
867
     * @return array|null
868
     * @throws \LesserPhp\Exception\GeneralException
869
     */
870
    public function contrast(array $args)
871
    {
872
        $darkColor = ['color', 0, 0, 0];
873
        $lightColor = ['color', 255, 255, 255];
874
        $threshold = 0.43;
875
876
        if ($args[0] === 'list') {
877
            $inputColor = isset($args[2][0]) ? $this->assertions->assertColor($args[2][0]) : $lightColor;
878
            $darkColor = isset($args[2][1]) ? $this->assertions->assertColor($args[2][1]) : $darkColor;
879
            $lightColor = isset($args[2][2]) ? $this->assertions->assertColor($args[2][2]) : $lightColor;
880
            if (isset($args[2][3])) {
881
                if (isset($args[2][3][2]) && $args[2][3][2] === '%') {
882
                    $args[2][3][1] /= 100;
883
                    unset($args[2][3][2]);
884
                }
885
                $threshold = $this->assertions->assertNumber($args[2][3]);
886
            }
887
        } else {
888
            $inputColor = $this->assertions->assertColor($args);
889
        }
890
891
        $inputColor = $this->coerce->coerceColor($inputColor);
892
        $darkColor = $this->coerce->coerceColor($darkColor);
893
        $lightColor = $this->coerce->coerceColor($lightColor);
894
895
        //Figure out which is actually light and dark!
896
        if ($this->luma($darkColor) > $this->luma($lightColor)) {
0 ignored issues
show
Bug introduced by
It seems like $darkColor defined by $this->coerce->coerceColor($darkColor) on line 892 can also be of type null; however, LesserPhp\Library\Functions::luma() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $lightColor defined by $this->coerce->coerceColor($lightColor) on line 893 can also be of type null; however, LesserPhp\Library\Functions::luma() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
897
            $t = $lightColor;
898
            $lightColor = $darkColor;
899
            $darkColor = $t;
900
        }
901
902
        $inputColorAlpha = $this->alpha($inputColor);
0 ignored issues
show
Bug introduced by
It seems like $inputColor defined by $this->coerce->coerceColor($inputColor) on line 891 can also be of type null; however, LesserPhp\Library\Functions::alpha() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
903
        if (($this->luma($inputColor) * $inputColorAlpha) < $threshold) {
0 ignored issues
show
Bug introduced by
It seems like $inputColor defined by $this->coerce->coerceColor($inputColor) on line 891 can also be of type null; however, LesserPhp\Library\Functions::luma() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
904
            return $lightColor;
905
        }
906
907
        return $darkColor;
908
    }
909
910
    /**
911
     * @param array $color
912
     *
913
     * @return float
914
     */
915
    public function luma(array $color)
916
    {
917
        $color = $this->coerce->coerceColor($color);
918
        return (0.2126 * $color[1] / 255) + (0.7152 * $color[2] / 255) + (0.0722 * $color[3] / 255);
919
    }
920
921
922
    /**
923
     * @param array $number
924
     * @param       $to
925
     *
926
     * @return array
927
     * @throws \LesserPhp\Exception\GeneralException
928
     */
929
    public function convertMe(array $number, $to)
930
    {
931
        $value = $this->assertions->assertNumber($number);
932
        $from = $number[2];
933
934
        // easy out
935
        if ($from == $to) {
936
            return $number;
937
        }
938
939
        // check if the from value is a length
940
        if (($fromIndex = array_search($from, static::$lengths)) !== false) {
941
            // make sure to value is too
942
            if (in_array($to, static::$lengths)) {
943
                // do the actual conversion
944
                $toIndex = array_search($to, static::$lengths);
945
                $px = $value * static::$lengths_to_base[$fromIndex];
946
                $result = $px * (1 / static::$lengths_to_base[$toIndex]);
947
948
                $result = round($result, 8);
949
950
                return ['number', $result, $to];
951
            }
952
        }
953
954
        // do the same check for times
955
        if (in_array($from, static::$times) && in_array($to, static::$times)) {
956
            // currently only ms and s are valid
957
            if ($to === 'ms') {
958
                $result = $value * 1000;
959
            } else {
960
                $result = $value / 1000;
961
            }
962
963
            $result = round($result, 8);
964
965
            return ['number', $result, $to];
966
        }
967
968
        // lastly check for an angle
969
        if (in_array($from, static::$angles)) {
970
            // convert whatever angle it is into degrees
971
            $deg = $value;
972
            if ($from === 'rad') {
973
                $deg = rad2deg($value);
974
            } else {
975
                if ($from === 'turn') {
976
                    $deg = $value * 360;
977
                } else {
978
                    if ($from === 'grad') {
979
                        $deg = $value / (400 / 360);
980
                    }
981
                }
982
            }
983
984
            // Then convert it from degrees into desired unit
985
            $result = 0;
986
            if ($to === 'deg') {
987
                $result = $deg;
988
            }
989
990
            if ($to === 'rad') {
991
                $result = deg2rad($deg);
992
            }
993
994
            if ($to === 'turn') {
995
                $result = $value / 360;
996
            }
997
998
            if ($to === 'grad') {
999
                $result = $value * (400 / 360);
1000
            }
1001
1002
            $result = round($result, 8);
1003
1004
            return ['number', $result, $to];
1005
        }
1006
1007
        // we don't know how to convert these
1008
        throw new GeneralException("Cannot convert {$from} to {$to}");
1009
    }
1010
1011
    /**
1012
     * @param bool $a
1013
     *
1014
     * @return array
1015
     */
1016
    public function toBool($a)
1017
    {
1018
        if ($a) {
1019
            return static::$TRUE;
1020
        } else {
1021
            return static::$FALSE;
1022
        }
1023
    }
1024
1025
    /**
1026
     * attempts to find the path of an import url, returns null for css files
1027
     *
1028
     * @param string $url
1029
     *
1030
     * @return null|string
1031
     */
1032
    public function findImport($url)
1033
    {
1034
        foreach ($this->compiler->getImportDirs() as $dir) {
1035
            $full = $dir . (mb_substr($dir, -1) !== '/' ? '/' : '') . $url;
1036
            if ($this->fileExists($file = $full . '.less') || $this->fileExists($file = $full)) {
1037
                return $file;
1038
            }
1039
        }
1040
1041
        return null;
1042
    }
1043
1044
    /**
1045
     * @param string $name
1046
     *
1047
     * @return bool
1048
     */
1049
    public function fileExists($name)
1050
    {
1051
        return is_file($name);
1052
    }
1053
}
1054