Issues (46)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/LesserPhp/Library/Functions.php (5 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
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
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...
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
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
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