Skip to content

Commit ccbcf0b

Browse files
committed
TASK: Introduce value Objects for rgba and hsla colors
The conversion between those colors is verified via unittests
1 parent 3cf45aa commit ccbcf0b

5 files changed

Lines changed: 460 additions & 0 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
namespace PackageFactory\ColorHelper\Domain\ValueObject;
3+
4+
interface ColorInterface {
5+
6+
/**
7+
* @return RgbaColor
8+
*/
9+
public function asRgba(): RgbaColor;
10+
11+
/**
12+
* @return HslaColor
13+
*/
14+
public function asHsla(): HslaColor;
15+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace PackageFactory\ColorHelper\Domain\ValueObject;
5+
6+
class HslaColor implements ColorInterface
7+
{
8+
9+
/**
10+
* @var int
11+
*/
12+
private $hue;
13+
14+
/**
15+
* @var int
16+
*/
17+
private $saturation;
18+
19+
/**
20+
* @var int
21+
*/
22+
private $lightness;
23+
/**
24+
* @var int
25+
*/
26+
private $alpha;
27+
28+
/**
29+
* HslaColor constructor.
30+
* @param int $hue
31+
* @param int $saturation
32+
* @param int $lightness
33+
* @param int $alpha
34+
*/
35+
public function __construct(int $hue, int $saturation, int $lightness, int $alpha = 255)
36+
{
37+
if ($hue < 0 || $hue > 359 ) {
38+
throw new \InvalidArgumentException('argument hue has to be an integer between 0 and 359, ' . $hue . ' was given.' );
39+
}
40+
if ($saturation < 0 || $saturation > 100 ) {
41+
throw new \InvalidArgumentException('argument saturation has to be an integer between 0 and 100, ' . $saturation . ' was given.');
42+
}
43+
if ($lightness < 0 || $lightness > 100 ) {
44+
throw new \InvalidArgumentException('argument luminosity has to be an integer between 0 and 100, ' . $lightness . ' was given.');
45+
}
46+
if ($alpha < 0 || $alpha > 255 ) {
47+
throw new \InvalidArgumentException('argument alpha has to be an integer between 0 and 255, ' . $alpha . ' was given');
48+
}
49+
50+
$this->hue = $hue;
51+
$this->saturation = $saturation;
52+
$this->lightness = $lightness;
53+
$this->alpha = $alpha;
54+
}
55+
56+
/**
57+
* @return int
58+
*/
59+
public function getHue(): int
60+
{
61+
return $this->hue;
62+
}
63+
64+
/**
65+
* @return int
66+
*/
67+
public function getSaturation(): int
68+
{
69+
return $this->saturation;
70+
}
71+
72+
/**
73+
* @return int
74+
*/
75+
public function getLightness(): int
76+
{
77+
return $this->lightness;
78+
}
79+
80+
/**
81+
* @return int
82+
*/
83+
public function getAlpha(): int
84+
{
85+
return $this->alpha;
86+
}
87+
88+
/**
89+
* @return RgbaColor
90+
*/
91+
public function asRgba(): RgbaColor
92+
{
93+
$S = $this->saturation / 100;
94+
$L = $this->lightness / 100;
95+
$C = (1 - abs((2 * $L) - 1)) * $S;
96+
$X = $C * ( 1 - abs( (($this->hue / 60) % 2) - 1));
97+
$m = $L - ($C / 2);
98+
99+
if ($this->hue < 0) {
100+
throw new \UnexpectedValueException('this should never be thrown');
101+
} elseif ($this->hue < 60) {
102+
$r = $C;
103+
$g = $X;
104+
$b = 0;
105+
} elseif ($this->hue < 120) {
106+
$r = $X;
107+
$g = $C;
108+
$b = 0;
109+
} elseif ($this->hue < 180) {
110+
$r = 0;
111+
$g = $C;
112+
$b = $X;
113+
} elseif ($this->hue < 240) {
114+
$r = 0;
115+
$g = $X;
116+
$b = $C;
117+
} elseif ($this->hue < 300) {
118+
$r = $X;
119+
$g = 0;
120+
$b = $C;
121+
} elseif ($this->hue < 360) {
122+
$r = $C;
123+
$g = 0;
124+
$b = $X;
125+
} else {
126+
throw new \UnexpectedValueException('this should never be thrown');
127+
}
128+
129+
$R = ($r + $m) * 255;
130+
$G = ($g + $m) * 255;
131+
$B = ($b + $m) * 255;
132+
133+
return new RgbaColor(
134+
(int)round($R),
135+
(int)round($G),
136+
(int)round($B),
137+
$this->alpha
138+
);
139+
}
140+
141+
/**
142+
* @return HslaColor
143+
*/
144+
public function asHsla(): HslaColor
145+
{
146+
return $this;
147+
}
148+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace PackageFactory\ColorHelper\Domain\ValueObject;
5+
6+
class RgbaColor implements ColorInterface
7+
{
8+
9+
/**
10+
* @var int
11+
*/
12+
private $red;
13+
14+
/**
15+
* @var int
16+
*/
17+
private $green;
18+
19+
/**
20+
* @var int
21+
*/
22+
private $blue;
23+
24+
/**
25+
* @var int
26+
*/
27+
private $alpha;
28+
29+
/**
30+
* RgbColor constructor.
31+
* @param int $red
32+
* @param int $green
33+
* @param int $blue
34+
* @param int $alpha
35+
*/
36+
public function __construct(int $red, int $green, int $blue, int $alpha = 255)
37+
{
38+
if ($red < 0 || $red > 255 ) {
39+
throw new \InvalidArgumentException('argument red has to be an integer between 0 and 255');
40+
}
41+
if ($green < 0 || $green > 255 ) {
42+
throw new \InvalidArgumentException('argument green has to be an integer between 0 and 255');
43+
}
44+
if ($blue < 0 || $blue > 255 ) {
45+
throw new \InvalidArgumentException('argument blue has to be an integer between 0 and 255');
46+
}
47+
if ($alpha < 0 || $alpha > 255 ) {
48+
throw new \InvalidArgumentException('argument alpha has to be an integer between 0 and 255');
49+
}
50+
51+
$this->red = $red;
52+
$this->green = $green;
53+
$this->blue = $blue;
54+
$this->alpha = $alpha;
55+
}
56+
57+
/**
58+
* @return int
59+
*/
60+
public function getRed(): int
61+
{
62+
return $this->red;
63+
}
64+
65+
/**
66+
* @return int
67+
*/
68+
public function getGreen(): int
69+
{
70+
return $this->green;
71+
}
72+
73+
/**
74+
* @return int
75+
*/
76+
public function getBlue(): int
77+
{
78+
return $this->blue;
79+
}
80+
81+
/**
82+
* @return int
83+
*/
84+
public function getAlpha():int
85+
{
86+
return $this->alpha;
87+
}
88+
89+
/**
90+
* @return string
91+
*/
92+
public function getHex():string
93+
{
94+
if ($this->alpha == 255) {
95+
return '#'
96+
. str_pad(dechex($this->red), 2, '0')
97+
. str_pad(dechex($this->green), 2, '0')
98+
. str_pad(dechex($this->blue), 2, '0');
99+
} else {
100+
return '#'
101+
. str_pad(dechex($this->red), 2, '0')
102+
. str_pad(dechex($this->green), 2, '0')
103+
. str_pad(dechex($this->blue), 2, '0')
104+
. str_pad(dechex($this->alpha), 2, '0');
105+
}
106+
}
107+
108+
/**
109+
* @return RgbaColor
110+
*/
111+
public function asRgba(): RgbaColor
112+
{
113+
return $this;
114+
}
115+
116+
/**
117+
* @return HslaColor
118+
*/
119+
public function asHsla(): HslaColor
120+
{
121+
$r = $this->red / 255;
122+
$g = $this->green / 255;
123+
$b = $this->blue / 255;
124+
$Cmax = max($r, $g, $b);
125+
$Cmin = min($r, $g, $b);
126+
$lambda = $Cmax - $Cmin;
127+
$l = ($Cmax + $Cmin) / 2 ;
128+
129+
if ($lambda == 0) {
130+
$hue = 0;
131+
} elseif ($Cmax == $r) {
132+
$hue = 60 * ((($g-$b) / $lambda) % 6);
133+
} elseif ($Cmax == $g) {
134+
$hue = 60 * ((($b-$r) / $lambda) + 2);
135+
} elseif ($Cmax == $b) {
136+
$hue = 60 * ((($r-$g) / $lambda) + 4);
137+
} else {
138+
throw new \UnexpectedValueException('this should never be thrown');
139+
}
140+
141+
if ($hue < 0) $hue += 360;
142+
if ($hue > 359) $hue -= 360;
143+
144+
if ($lambda == 0) {
145+
$s = 0;
146+
} else {
147+
$s = $lambda / ( 1 - abs((2 * $l) - 1));
148+
}
149+
150+
$lightness = $l * 100;
151+
$saturation = $s * 100;
152+
153+
return new HslaColor(
154+
(int)round($hue),
155+
(int)round($saturation),
156+
(int)round($lightness),
157+
$this->alpha
158+
);
159+
}
160+
}

Tests/Unit/ColorTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace PackageFactory\ColorHelper\Tests\Unit;
4+
5+
use PackageFactory\ColorHelper\Domain\ValueObject\HslaColor;
6+
use PackageFactory\ColorHelper\Domain\ValueObject\RgbaColor;
7+
use PHPUnit\Framework\TestCase;
8+
use Symfony\Component\Yaml\Yaml;
9+
10+
class ColorTest extends TestCase
11+
{
12+
/**
13+
* @return array
14+
*/
15+
public function getColorFixtures ():array
16+
{
17+
$yaml = Yaml::parseFile(__DIR__ . '/Colors.yaml');
18+
array_walk(
19+
$yaml,
20+
function($item, $name)
21+
{
22+
$item['name'] = $name;
23+
}
24+
);
25+
$yaml = array_values($yaml);
26+
$yaml = array_map(
27+
function($item) {
28+
return [$item];
29+
},
30+
$yaml
31+
);
32+
return $yaml;
33+
}
34+
35+
/**
36+
* @test
37+
* @dataProvider getColorFixtures
38+
*/
39+
public function conversionOfRgbToHslWorks($colorFixture) {
40+
$rgb = $colorFixture['rgb'];
41+
$hsl = $colorFixture['hsl'];
42+
$rgbColor = new RgbaColor($rgb[0], $rgb[1], $rgb[2]);
43+
$hslColor = $rgbColor->asHsla();
44+
self::assertEquals($hsl[0], $hslColor->getHue());
45+
self::assertEquals($hsl[1], $hslColor->getSaturation());
46+
self::assertEquals($hsl[2], $hslColor->getLightness());
47+
}
48+
49+
/**
50+
* @test
51+
* @dataProvider getColorFixtures
52+
*/
53+
public function conversionOfHslToRgbWorks($colorFixture) {
54+
$rgb = $colorFixture['rgb'];
55+
$hsl = $colorFixture['hsl'];
56+
$hslColor = new HslaColor($hsl[0], $hsl[1], $hsl[2]);
57+
$rgbColor = $hslColor->asRgba();
58+
self::assertEquals($rgb[0], $rgbColor->getRed());
59+
self::assertEquals($rgb[1], $rgbColor->getGreen());
60+
self::assertEquals($rgb[2], $rgbColor->getBlue());
61+
}
62+
63+
/**
64+
* @test
65+
* @dataProvider getColorFixtures
66+
*/
67+
public function conversionOfRgbToHexbWorks($colorFixture) {
68+
$rgb = $colorFixture['rgb'];
69+
$hex = $colorFixture['hex'];
70+
$rgbColor = new RgbaColor($rgb[0], $rgb[1], $rgb[2]);
71+
self::assertEquals($hex, strtoupper($rgbColor->getHex()));
72+
}
73+
}

0 commit comments

Comments
 (0)