Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
128 / 128
100.00% covered (success)
100.00%
24 / 24
CRAP
100.00% covered (success)
100.00%
1 / 1
Hsl
100.00% covered (success)
100.00%
128 / 128
100.00% covered (success)
100.00%
24 / 24
63
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 setH
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setS
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setL
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setA
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getH
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getS
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getL
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getA
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasA
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasAlpha
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toRgb
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
1 / 1
9
 toHex
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toArray
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 render
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
7
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __set
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __isset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __unset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
4
 offsetGet
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
6
 offsetSet
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
6
 offsetUnset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Color\Color;
15
16use OutOfRangeException;
17
18/**
19 * Pop Color HSL color class
20 *
21 * @category   Pop
22 * @package    Pop\Color
23 * @author     Nick Sagona, III <dev@nolainteractive.com>
24 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
25 * @license    http://www.popphp.org/license     New BSD License
26 * @version    1.0.0
27 */
28class Hsl extends AbstractColor implements \ArrayAccess
29{
30
31    /**
32     * H value
33     * @var int
34     */
35    protected int $h = 0;
36
37    /**
38     * S value
39     * @var int
40     */
41    protected int $s = 0;
42
43    /**
44     * L value
45     * @var int
46     */
47    protected int $l = 0;
48
49    /**
50     * Alpha value
51     * @var ?float
52     */
53    protected ?float $a = null;
54
55    /**
56     * Constructor
57     *
58     * Instantiate the CSS HSL color object
59     *
60     * @param int|string        $h
61     * @param int|string        $s
62     * @param int|string        $l
63     * @param float|string|null $a
64     */
65    public function __construct(int|string $h, int|string $s, int|string $l, float|string|null $a = null)
66    {
67        $this->setH($h);
68        $this->setS($s);
69        $this->setL($l);
70        if ($a !== null) {
71            $this->setA($a);
72        }
73    }
74
75    /**
76     * Set H value
77     *
78     * @param  int|string $h
79     * @throws OutOfRangeException
80     * @return self
81     */
82    public function setH(int|string $h): self
83    {
84        $h = (int)$h;
85        if (($h > 360) || ($h < 0)) {
86            throw new OutOfRangeException('Error: The value of $h must be between 0 and 360.');
87        }
88        $this->h = $h;
89        return $this;
90    }
91
92    /**
93     * Set S value
94     *
95     * @param  int|string $s
96     * @throws OutOfRangeException
97     * @return self
98     */
99    public function setS(int|string $s): self
100    {
101        $s = (int)$s;
102        if (($s > 100) || ($s < 0)) {
103            throw new OutOfRangeException('Error: The value of $s must be between 0 and 100.');
104        }
105        $this->s = $s;
106        return $this;
107    }
108
109    /**
110     * Set L value
111     *
112     * @param  int|string $l
113     * @throws OutOfRangeException
114     * @return self
115     */
116    public function setL(int|string $l): self
117    {
118        $l = (int)$l;
119        if (($l > 100) || ($l < 0)) {
120            throw new OutOfRangeException('Error: The value of $l must be between 0 and 100.');
121        }
122        $this->l = $l;
123        return $this;
124    }
125
126    /**
127     * Set A value
128     *
129     * @param  float|string $a
130     * @throws OutOfRangeException
131     * @return self
132     */
133    public function setA(float|string $a): self
134    {
135        $a = (float)$a;
136        if (($a > 1) || ($a < 0)) {
137            throw new OutOfRangeException('Error: The value of $l must be between 0 and 1.');
138        }
139        $this->a = $a;
140        return $this;
141    }
142
143    /**
144     * Get H value
145     *
146     * @return int
147     */
148    public function getH(): int
149    {
150        return $this->h;
151    }
152
153    /**
154     * Get S value
155     *
156     * @return int
157     */
158    public function getS(): int
159    {
160        return $this->s;
161    }
162
163    /**
164     * Get L value
165     *
166     * @return int
167     */
168    public function getL(): int
169    {
170        return $this->l;
171    }
172
173    /**
174     * Get A value
175     *
176     * @return float|null
177     */
178    public function getA(): float|null
179    {
180        return $this->a;
181    }
182
183    /**
184     * Determine if the color object has an alpha value
185     *
186     * @return bool
187     */
188    public function hasA(): bool
189    {
190        return ($this->a !== null);
191    }
192
193    /**
194     * Determine if the color object has an alpha value (alias)
195     *
196     * @return bool
197     */
198    public function hasAlpha(): bool
199    {
200        return ($this->a !== null);
201    }
202
203    /**
204     * Convert to RGB
205     *
206     * @return Rgb
207     */
208    public function toRgb(): Rgb
209    {
210        $s = $this->s / 100;
211        $v = $this->l / 100;
212
213        if ($this->s == 0) {
214            $r = round($v * 255);
215            $g = round($v * 255);
216            $b = round($v * 255);
217        } else {
218            $h = $this->h / 360;
219            $h = $h * 6;
220            if ($h == 6) {
221                $h = 0;
222            }
223
224            $i  = floor($h);
225            $v1 = $v * (1 - $s);
226            $v2 = $v * (1 - ($s * ($h - $i)));
227            $v3 = $v * (1 - ($s * (1 - ($h - $i))));
228
229            switch ($i) {
230                case 0:
231                    $r = $v;
232                    $g = $v3;
233                    $b = $v1;
234                    break;
235                case 1:
236                    $r = $v2;
237                    $g = $v;
238                    $b = $v1;
239                    break;
240                case 2:
241                    $r = $v1;
242                    $g = $v;
243                    $b = $v3;
244                    break;
245                case 3:
246                    $r = $v1;
247                    $g = $v;
248                    $b = $v3;
249                    break;
250                case 4:
251                    $r = $v3;
252                    $g = $v1;
253                    $b = $v;
254                    break;
255                default:
256                    $r = $v;
257                    $g = $v1;
258                    $b = $v2;
259            }
260
261            $r = round($r * 255);
262            $g = round($g * 255);
263            $b = round($b * 255);
264        }
265
266        return new Rgb($r, $g, $b, $this->a);
267    }
268
269    /**
270     * Convert to hex
271     *
272     * @return Hex
273     */
274    public function toHex(): Hex
275    {
276        return $this->toRgb()->toHex();
277    }
278
279    /**
280     * Convert to array
281     *
282     * @param  bool $assoc
283     * @return array
284     */
285    public function toArray(bool $assoc = true): array
286    {
287        $hsl = [];
288
289        if ($assoc) {
290            $hsl['h'] = $this->h;
291            $hsl['s'] = $this->s . '%';
292            $hsl['l'] = $this->l . '%';
293            if ($this->a !== null) {
294                $hsl['a'] = $this->a;
295            }
296        } else {
297            $hsl[] = $this->h;
298            $hsl[] = $this->s . '%';
299            $hsl[] = $this->l . '%';
300            if ($this->a !== null) {
301                $hsl[] = $this->a;
302            }
303        }
304
305        return $hsl;
306    }
307
308    /**
309     * Convert to readable string
310     *
311     * @param  ?string $format
312     * @return string
313     */
314    public function render(?string $format = null): string
315    {
316        if ($format == self::COMMA) {
317            return $this->h . ', ' . $this->s . ', ' . $this->l . (!empty($this->a) ? ', ' . $this->a : '');
318        } else if ($format == self::CSS) {
319            return (($this->a !== null) ? 'hsla(' : 'hsl(') . implode(', ', $this->toArray()) . ')';
320        } else if ($format == self::PERCENT) {
321            $rgb = $this->toRgb();
322            return $rgb->render($format);
323        } else {
324            return $this->h . ' ' . $this->s . ' ' . $this->l . (!empty($this->a) ? ' ' . $this->a : '');
325        }
326    }
327
328    /**
329     * Return CSS-formatted string
330     *
331     * @return string
332     */
333    public function __toString(): string
334    {
335        return $this->render(self::CSS);
336    }
337
338    /**
339     * Magic method to set the color value
340     *
341     * @param  string $name
342     * @param  mixed  $value
343     * @throws Exception
344     * @return void
345     */
346    public function __set(string $name, mixed $value): void
347    {
348        $this->offsetSet($name, $value);
349    }
350
351    /**
352     * Magic method to return the color value
353     *
354     * @param  string $name
355     * @throws Exception
356     * @return mixed
357     */
358    public function __get(string $name): mixed
359    {
360        return $this->offsetGet($name);
361    }
362
363    /**
364     * Magic method to return whether the color value exists
365     *
366     * @param  string $name
367     * @return bool
368     */
369    public function __isset(string $name): bool
370    {
371        return $this->offsetExists($name);
372    }
373
374    /**
375     * Magic method to unset color value
376     *
377     * @param  string $name
378     * @throws Exception
379     * @return void
380     */
381    public function __unset(string $name): void
382    {
383        throw new Exception('You cannot unset the properties of this color object.');
384    }
385
386    /**
387     * ArrayAccess offsetExists
388     *
389     * @param  mixed $offset
390     * @return bool
391     */
392    public function offsetExists(mixed $offset): bool
393    {
394        return (($offset == 'h') || ($offset == 's') || ($offset == 'l') || ($offset == 'a'));
395    }
396
397    /**
398     * ArrayAccess offsetGet
399     *
400     * @param  mixed $offset
401     * @throws Exception
402     * @return mixed
403     */
404    public function offsetGet(mixed $offset): mixed
405    {
406        switch ($offset) {
407            case 'h':
408                return $this->getH();
409                break;
410            case 's':
411                return $this->getS();
412                break;
413            case 'l':
414                return $this->getL();
415                break;
416            case 'a':
417                return $this->getA();
418                break;
419            default:
420                throw new Exception("Error: You can only use 'h', 's', 'l' or 'a'.");
421        }
422    }
423
424    /**
425     * ArrayAccess offsetSet
426     *
427     * @param  mixed $offset
428     * @param  mixed $value
429     * @throws Exception
430     * @return void
431     */
432    public function offsetSet(mixed $offset, mixed $value): void
433    {
434        switch ($offset) {
435            case 'h':
436                $this->setH($value);
437                break;
438            case 's':
439                $this->setS($value);
440                break;
441            case 'l':
442                $this->setL($value);
443                break;
444            case 'a':
445                $this->setA($value);
446                break;
447            default:
448                throw new Exception("Error: You can only use 'h', 's', 'l' or 'a'.");
449        }
450    }
451
452    /**
453     * ArrayAccess offsetUnset
454     *
455     * @param  mixed $offset
456     * @throws Exception
457     * @return void
458     */
459    public function offsetUnset(mixed $offset): void
460    {
461        throw new Exception('You cannot unset the properties of this color object.');
462    }
463
464}