From 1d91e86ffb66d39ac552dece1a34f9a40dbc9d7e Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Wed, 24 Apr 2024 20:08:45 +0200 Subject: [PATCH 01/93] Add Identicon class to generate SVG identicons The new class, Identicon, has been added to the src directory. It's designed to generate SVG identicons based on a name string, size, pixels and symmetry option. It includes methods for generating hashes, matrices and converting hexadecimal characters to boolean values. --- src/Identicon.php | 158 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/Identicon.php diff --git a/src/Identicon.php b/src/Identicon.php new file mode 100644 index 0000000..6d5fcc5 --- /dev/null +++ b/src/Identicon.php @@ -0,0 +1,158 @@ +name = $name; + $this->size = $size; + $this->pixels = $pixels; + $this->symmetry = $symmetry; + $this->hash = $this->generateHash(); + } + + public function generateHash(): string + { + return md5($this->name); + } + + public function getSVG(): string + { + $larvatar = new SVG($this->size, $this->size); + $doc = $larvatar->getDocument(); + + $color = $this->getColor(); + list($darkColor, $lightColor) = $color->getColorSet($this->textLightness, $this->backgroundLightness); + + if ($this->symmetry) { + $matrix = $this->generateSymmetricMatrix(); + } else { + $matrix = $this->generateMatrix(); + } + + foreach ($matrix as $y => $array) { + foreach ($array as $x => $value) { + if ($value == true) { + $square = new SVGRect( + (int) $x * ($this->size / $this->pixels), + (int) $y * ($this->size / $this->pixels), + (int) $this->size / $this->pixels, + (int) $this->size / $this->pixels); + $square->setStyle('fill', $darkColor->getHex()); + $doc->addChild($square); + } + } + + } + + return $larvatar; + } + + private function getColor(): Color + { + return new Color(ColorType::Hex, $this->generateHexColor()); + } + + public function generateHexColor(array $names = null, int $offset = 0): string + { + if ($names == null) { + $names = $this->name; + } + //$name = implode(' ', $names); + return '#'.substr($this->hash, $offset, 6); + } + + /** + * Generate a symmetric identicon matrix based on the provided hash + * + * @return array The generated symmetric matrix + */ + public function generateSymmetricMatrix(): array + { + preg_match_all('/(\w)(\w)/', $this->hash, $chars); + $symmetryMatrix = $this->getSymmetryMatrix(); + $divider = count($symmetryMatrix); + + for ($i = 0; $i < pow($this->pixels, 2); $i++) { + $index = (int) ($i / 3); + $data = $this->convertStrToBool(substr($this->hash, $i, 1)); + + foreach ($symmetryMatrix[$i % $divider] as $item) { + $matrix[$index][$item] = $data; + } + } + + return $matrix; + } + + /** + * Returns the symmetry matrix. + * + * @return array The symmetry matrix. + */ + private function getSymmetryMatrix(): array + { + $items = []; + $i = $this->pixels - 1; + for ($x = 0; $x <= $i / 2; $x++) { + $items[$x] = [$x]; + if ($x !== $i - $x) { + $items[$x][] = $i - $x; + } + } + return $items; + } + + /** + * Converts a hexadecimal character to a boolean value. + * + * @param string $char The hexadecimal character to convert. + * @return bool The boolean value converted from the hexadecimal character. + */ + private function convertStrToBool(string $char): bool + { + return (bool) round(hexdec($char) / 10); + } + + /** + * Generates a matrix based on the given offset value. + * + * @param int $offset The offset value for generating the matrix. Defaults to 0. + * @return array The generated matrix. + */ + public function generateMatrix(int $offset = 0): array + { + $column = 0; + $row = 0; + for ($i = 0; $i < pow($this->pixels, 2); $i++) { + $matrix[$i % $this->pixels][floor($i / $this->pixels)] = + $this->convertStrToBool(substr($this->hash, $i, 1)); + if ($column == $this->pixels && $row < $this->pixels) { + $row++; + $column = -1; + } + if ($column < $this->pixels) { + $column++; + } + } + + return $matrix; + } + +} + From 8f91b75f182bb283c07ff1af2e197c3e8c1385ab Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Wed, 24 Apr 2024 22:06:36 +0200 Subject: [PATCH 02/93] Add make method to Color class Added a new static method, `make`, to the Color class to simplify the creation of new Color objects. This improvement requires either an array or a string as parameters, along with a color type. --- src/Color.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Color.php b/src/Color.php index 0055e71..7f783f5 100644 --- a/src/Color.php +++ b/src/Color.php @@ -27,6 +27,11 @@ public function __construct(ColorType $type, array|string $color) } } + public static function make(ColorType $type, array|string $color): Color + { + return new Color($type, $color); + } + /** * Converts a hexadecimal color code to RGB color * @param string $hexStr Hexadecimal color code, with or without '#' From 206d9a433cb27f4554fa686c25eab16b4c1f4cb5 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Wed, 24 Apr 2024 22:06:54 +0200 Subject: [PATCH 03/93] Refactor Identicon class for cleaner code The Identicon class has been refactored to improve code readability and eliminate redundancy. The class now makes use of the Name object instead of a string 'name' variable. Unused methods are removed, and setters for 'size', 'pixels', and 'symmetry' properties are added. Overall, these changes provide a cleaner, leaner representation. --- src/Identicon.php | 55 ++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/src/Identicon.php b/src/Identicon.php index 6d5fcc5..13173e0 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -8,27 +8,17 @@ class Identicon { - public string $name; + public Name $name; public int $size = 125; public int $pixels = 5; - public $hash; private float $backgroundLightness = 0.8; private float $textLightness = 0.35; - private bool $symmetry; + private bool $symmetry = true; - public function __construct(string $name, bool $symmetry = true, int $size = 125, int $pixels = 5) + public function __construct(Name $name) { $this->name = $name; - $this->size = $size; - $this->pixels = $pixels; - $this->symmetry = $symmetry; - $this->hash = $this->generateHash(); - } - - public function generateHash(): string - { - return md5($this->name); } public function getSVG(): string @@ -36,7 +26,7 @@ public function getSVG(): string $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); - $color = $this->getColor(); + $color = Color::make(ColorType::Hex, $this->name->getHexColor()); list($darkColor, $lightColor) = $color->getColorSet($this->textLightness, $this->backgroundLightness); if ($this->symmetry) { @@ -47,7 +37,7 @@ public function getSVG(): string foreach ($matrix as $y => $array) { foreach ($array as $x => $value) { - if ($value == true) { + if ($value) { $square = new SVGRect( (int) $x * ($this->size / $this->pixels), (int) $y * ($this->size / $this->pixels), @@ -63,20 +53,6 @@ public function getSVG(): string return $larvatar; } - private function getColor(): Color - { - return new Color(ColorType::Hex, $this->generateHexColor()); - } - - public function generateHexColor(array $names = null, int $offset = 0): string - { - if ($names == null) { - $names = $this->name; - } - //$name = implode(' ', $names); - return '#'.substr($this->hash, $offset, 6); - } - /** * Generate a symmetric identicon matrix based on the provided hash * @@ -84,13 +60,13 @@ public function generateHexColor(array $names = null, int $offset = 0): string */ public function generateSymmetricMatrix(): array { - preg_match_all('/(\w)(\w)/', $this->hash, $chars); + preg_match_all('/(\w)(\w)/', $this->name->getHash(), $chars); $symmetryMatrix = $this->getSymmetryMatrix(); $divider = count($symmetryMatrix); for ($i = 0; $i < pow($this->pixels, 2); $i++) { $index = (int) ($i / 3); - $data = $this->convertStrToBool(substr($this->hash, $i, 1)); + $data = $this->convertStrToBool(substr($this->name->getHash(), $i, 1)); foreach ($symmetryMatrix[$i % $divider] as $item) { $matrix[$index][$item] = $data; @@ -141,7 +117,7 @@ public function generateMatrix(int $offset = 0): array $row = 0; for ($i = 0; $i < pow($this->pixels, 2); $i++) { $matrix[$i % $this->pixels][floor($i / $this->pixels)] = - $this->convertStrToBool(substr($this->hash, $i, 1)); + $this->convertStrToBool(substr($this->name->getHash(), $i, 1)); if ($column == $this->pixels && $row < $this->pixels) { $row++; $column = -1; @@ -154,5 +130,20 @@ public function generateMatrix(int $offset = 0): array return $matrix; } + public function setSize(int $size): void + { + $this->size = $size; + } + + public function setPixels(int $pixels): void + { + $this->pixels = $pixels; + } + + public function setSymmetry(bool $symmetry): void + { + $this->symmetry = $symmetry; + } + } From 28b41a9cd9980346a83ee5d0557654a065a94878 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sat, 18 May 2024 22:17:30 +0200 Subject: [PATCH 04/93] Remove Color class and associated tests --- src/Color.php | 402 -------------------------------------------- tests/ColorTest.php | 369 ---------------------------------------- 2 files changed, 771 deletions(-) delete mode 100644 src/Color.php delete mode 100644 tests/ColorTest.php diff --git a/src/Color.php b/src/Color.php deleted file mode 100644 index 7f783f5..0000000 --- a/src/Color.php +++ /dev/null @@ -1,402 +0,0 @@ -setHex($color); - break; - case ColorType::RGB: - $this->setRGB($color); - break; - case ColorType::HSL: - $this->setHSL($color); - } - } - - public static function make(ColorType $type, array|string $color): Color - { - return new Color($type, $color); - } - - /** - * Converts a hexadecimal color code to RGB color - * @param string $hexStr Hexadecimal color code, with or without '#' - * @return array|int[] Array of red, green, and blue values [0..255] - * @throws InvalidArgumentException if the provided hex code is invalid - */ - public static function HexToRGB(string $hexStr): array - { - $hexStr = str_replace('#', '', $hexStr); - $length = strlen($hexStr); - - if (!in_array($length, [3, 6]) || preg_match("/^[0-9a-fA-F]+$/", $hexStr) !== 1) { - throw new InvalidArgumentException('Invalid Hex format.'); - } - - $hexStr = preg_replace("/[^0-9A-Fa-f]/", '', $hexStr); - if ($length === 6) { - $colorVal = hexdec($hexStr); - - return array( - 0xFF & ($colorVal >> 0x10), - 0xFF & ($colorVal >> 0x8), - 0xFF & $colorVal - ); - } - - return array( - hexdec(str_repeat(substr($hexStr, 0, 1), 2)), - hexdec(str_repeat(substr($hexStr, 1, 1), 2)), - hexdec(str_repeat(substr($hexStr, 2, 1), 2)) - ); - } - - /** - * Convert RGB color to HSL color space. - * - * @param int $red The red component of the RGB color (0-255). - * @param int $green The green component of the RGB color (0-255). - * @param int $blue The blue component of the RGB color (0-255). - * @return array An array containing the HSL color values (hue, saturation, lightness). - */ - public static function RGBToHSL(int $red, int $green, int $blue): array - { - list($maxRGB, $minRGB, $chroma, $value, $hue) = self::calculateCVH($red, $green, $blue); - - if ($chroma == 0) { - return array(0, 0, $value); - } - - $lightness = ($maxRGB + $minRGB) / 2; - $saturation = $chroma / (1 - abs(2 * $value - $chroma - 1)); - - - return array(round($hue), round($saturation, 2), round($lightness, 2)); - } - - /** - * Calculate the components Chroma, Value, and Hue based on RGB color. - * - * @param int $red The red component of the RGB color (0-255). - * @param int $green The green component of the RGB color (0-255). - * @param int $blue The blue component of the RGB color (0-255). - * @return array An array containing the calculated values (maxRGB, minRGB, chroma, value, hue). - */ - public static function calculateCVH(int $red, int $green, int $blue): array - { - $normalizedRed = $red / 255; - $normalizedGreen = $green / 255; - $normalizedBlue = $blue / 255; - - $maxRGB = max($normalizedRed, $normalizedGreen, $normalizedBlue); - $minRGB = min($normalizedRed, $normalizedGreen, $normalizedBlue); - $chroma = $maxRGB - $minRGB; - $value = $maxRGB; // also called brightness - if ($chroma == 0) { - $hue = 0; - } elseif ($maxRGB == $normalizedRed) { - $hue = 60 * (($normalizedGreen - $normalizedBlue) / $chroma); - } elseif ($maxRGB == $normalizedGreen) { - $hue = 60 * (2 + ($normalizedBlue - $normalizedRed) / $chroma); - } else { - $hue = 60 * (4 + ($normalizedRed - $normalizedGreen) / $chroma); - } - - if ($hue < 0) { - $hue += 360; - } - return array($maxRGB, $minRGB, $chroma, $value, $hue); - } - - /** - * Convert RGB color to HSV color space. - * - * @param int $red The red component of the RGB color (0-255). - * @param int $green The green component of the RGB color (0-255). - * @param int $blue The blue component of the RGB color (0-255). - * @return array An array containing the HSV color values (hue, saturation, value). - */ - public static function RGBToHSV(int $red, int $green, int $blue): array - { - list($maxRGB, $minRGB, $chroma, $value, $hue) = self::calculateCVH($red, $green, $blue); - - if ($chroma == 0) { - return array(0, 0, $value); - } - - $saturation = $chroma / $maxRGB * 100; - - - return array($hue, $saturation, $value); - } - - /** - * Convert HSV color to RGB color space. - * - * @param int $hue The hue component of the HSV color (0-360). - * @param float $saturation The saturation component of the HSV color (0-1). - * @param float $value The value component of the HSV color (0-1). - * @return array An array containing the RGB color values (red, green, blue). - * @throws InvalidArgumentException if any of the parameters exceed their intended ranges. - * @throws Exception if RGB calculation is not possible. - */ - public static function HSVToRGB(int $hue, float $saturation, float $value): array - { - self::validateParameters($hue, $saturation, $value); - - $chroma = $value * $saturation; - $hueNormalized = $hue / 60; - $hMod2 = $hueNormalized - 2 * floor($hueNormalized / 2); - $secondMax = $chroma * (1 - abs($hMod2 - 1)); - - list($red, $green, $blue) = self::calculateRGBRange($hueNormalized, $chroma, $secondMax); - - return self::finalizeRGBCalculation($red, $green, $blue, $value, $chroma); - } - - private static function validateParameters(int $hue, float $saturation, float $value): void - { - if ($hue < 0 || $hue > 360 || - $saturation < 0 || $saturation > 1 || - $value < 0 || $value > 1) { - throw new InvalidArgumentException('Parameters exceed their intended ranges.'); - } - } - - /** - * Calculate the RGB range based on the normalized hue value. - * - * @param float $hueNormalized The normalized hue value (0-6). - * @param float $chroma The chroma value (0 - 360). - * @param float $secondMax The second maximum value. - * @return array An array containing the RGB color values. - */ - private static function calculateRGBRange(float $hueNormalized, float $chroma, float $secondMax): array - { - if (0 <= $hueNormalized && $hueNormalized < 1) { - return [$chroma, $secondMax, 0]; - } elseif (1 <= $hueNormalized && $hueNormalized < 2) { - return [$secondMax, $chroma, 0]; - } elseif (2 <= $hueNormalized && $hueNormalized < 3) { - return [0, $chroma, $secondMax]; - } elseif (3 <= $hueNormalized && $hueNormalized < 4) { - return [0, $secondMax, $chroma]; - } elseif (4 <= $hueNormalized && $hueNormalized < 5) { - return [$secondMax, 0, $chroma]; - } elseif (5 <= $hueNormalized && $hueNormalized < 6) { - return [$chroma, 0, $secondMax]; - } else { - return []; - } - } - - /** - * Finalize the RGB color calculation based on the given parameters. - * - * @param float $red The red component of the RGB color (0-255). - * @param float $green The green component of the RGB color (0-255). - * @param float $blue The blue component of the RGB color (0-255). - * @param float $value The value component of the RGB color (0-1). - * @param float $chroma The chroma component of the RGB color (0-1). - * @param bool $isLightness Flag indicating if the calculation is for lightness. - * @return array An array containing the finalized RGB color values (red, green, blue). - */ - private static function finalizeRGBCalculation( - float $red, - float $green, - float $blue, - float $value, - float $chroma, - bool $isLightness = false - ): array { - $m = $isLightness ? $value - $chroma / 2 : $value - $chroma; - - return array_map(fn($color) => intval(round(($color + $m) * 255)), [$red, $green, $blue]); - } - - /** - * Convert RGB color to hexadecimal color representation. - * - * @param int $red The red component of the RGB color (0-255). - * @param int $green The green component of the RGB color (0-255). - * @param int $blue The blue component of the RGB color (0-255). - * @return string The hexadecimal representation of the RGB color. - */ - public static function RGBToHex(int $red, int $green, int $blue): string - { - $hexr = str_pad(dechex($red), 2, "0", STR_PAD_LEFT); - $hexg = str_pad(dechex($green), 2, "0", STR_PAD_LEFT); - $hexb = str_pad(dechex($blue), 2, "0", STR_PAD_LEFT); - return $hexr.$hexg.$hexb; - } - - /** - * Convert HSL color to RGB color space. - * - * @param int $hue The hue component of the HSL color (0-359). - * @param float $saturation The saturation component of the HSL color (0-1). - * @param float $lightness The lightness component of the HSL color (0-1). - * @return array An array containing the RGB color values (red, green, blue). - * @throws Exception If RGB calculation is not possible. - */ - private static function HSLToRGB(int $hue, float $saturation, float $lightness): array - { - $chroma = (1 - abs(2 * $lightness - 1)) * $saturation; - $hueNormalized = $hue / 60; - $hMod2 = $hueNormalized - 2 * floor($hueNormalized / 2); - $intermediateValue = $chroma * (1 - abs($hMod2 - 1)); - - list($red, $green, $blue) = self::calculateRGBRange($hueNormalized, $chroma, $intermediateValue); - - if (!isset($red) || !isset($green) || !isset($blue)) { - throw new Exception('RGB calculation not possible. Check inputs!'); - } - - return self::finalizeRGBCalculation($red, $green, $blue, $lightness, $chroma, true); - } - - /** - * Get the HSL color values of the current object. - * - * @return array An array containing the HSL color values (hue, saturation, lightness). - */ - public function getHSL(): array - { - return $this->hsl; - } - - /** - * Set the HSL color values and update the RGB and hex color values. - * - * @param array $color An array containing the HSL color values (hue, saturation, lightness). - * @return void - */ - public function setHSL(array $color): void - { - $this->hsl = $color; - $this->rgb = $this->HSLToRGB($color[0], $color[1], $color[2]); - $this->hex = $this->RGBToHex($this->rgb[0], $this->rgb[1], $this->rgb[2]); - } - - /** - * Get the RGB color values. - * - * @return array An array containing the RGB color values (red, green, blue). - */ - public function getRGB(): array - { - return $this->rgb; - } - - /** - * Set the RGB color. - * - * @param array $color An array containing the RGB color values (red, green, blue). - * @return void - */ - public function setRGB(array $color): void - { - $this->rgb = $color; - $this->hex = Color::RGBToHex($color[0], $color[1], $color[2]); - $this->hsl = Color::RGBToHSL($color[0], $color[1], $color[2]); - } - - /** - * Get the hexadecimal representation of the color. - * - * @return string The hexadecimal string representation of the color. - */ - public function getHex(): string - { - return '#'.$this->hex; - } - - /** - * Set the hexadecimal color value. - * - * @param string $color The hexadecimal color value. - * @return void - */ - public function setHex(string $color): void - { - if (!preg_match("/#[0-9a-fA-F]{3}/", $color) && - !preg_match("/#[0-9a-fA-F]{6}/", $color)) { - return; - } - $color = substr($color, 1); - $this->hex = $color; - - $this->rgb = Color::HexToRGB($this->hex); - $this->hsl = Color::RGBToHSL($this->rgb[0], $this->rgb[1], $this->rgb[2]); - } - - /** - * Get a color set based on the HSL color. - * - * @param float $darkLightness The lightness value for the dark color (0.0-1.0). - * @param float $lightLightness The lightness value for the light color (0.0-1.0). - * @return array An array containing the dark and light color. - */ - public function getColorSet(float $darkLightness = 0.35, float $lightLightness = 0.8): array - { - list($hue, $saturation, $lightness) = $this->hsl; - - $dark = new Color(ColorType::HSL, [$hue, $saturation, $darkLightness]); - $light = new Color(ColorType::HSL, [$hue, $saturation, $lightLightness]); - return array($dark, $light); - } - - /** - * Brighten the color by a specified amount. - * - * @param int $amount The amount to brighten the color by as a percentage (default: 10). - * @return void - */ - public function brighten(int $amount = 10): void - { - list($hue, $saturation, $lightness) = $this->hsl; - $lightness = clamp($lightness + $amount / 100, 0, 1); - $this->setHSL(array($hue, $saturation, $lightness)); - } - - /** - * Clamp a number between a minimum and maximum value. - * - * @param int|float $num The number to clamp. - * @param int|float $min The minimum value. - * @param int|float $max The maximum value. - * @return int|float The clamped number. - * @deprecated v1.4.0 - */ - private static function clamp(int|float $num, int|float $min, int|float $max): int|float - { - return clamp($num, $min, $max); - } - - /** - * Darken the color by reducing its lightness value. - * - * @param int $amount The amount by which to darken the color (0-100). - * @return void - */ - public function darken(int $amount = 10): void - { - list($hue, $saturation, $lightness) = $this->hsl; - $lightness = clamp($lightness - $amount / 100, 0, 1); - $this->setHSL(array($hue, $saturation, $lightness)); - } -} diff --git a/tests/ColorTest.php b/tests/ColorTest.php deleted file mode 100644 index 0cd65e2..0000000 --- a/tests/ColorTest.php +++ /dev/null @@ -1,369 +0,0 @@ -assertEquals($expectedResult, $actualResult, 'Case 1 failed: White'); - - // Test case 2: black - $hexColor = '#000000'; - $expectedResult = [0, 0, 0]; - $actualResult = Color::HexToRGB($hexColor); - $this->assertEquals($expectedResult, $actualResult, 'Case 2 failed: Black'); - - // Test case 3: Mountain Meadow - $hexColor = '#11c380'; - $expectedResult = [17, 195, 128]; - $actualResult = Color::HexToRGB($hexColor); - $this->assertEquals($expectedResult, $actualResult, 'Case 3 failed: Mountain Meadow'); - - // Test case 4: #8c5a45 - $hexColor = '#8c5a45'; - $expectedResult = [140, 90, 69]; - $actualResult = Color::HexToRGB($hexColor); - $this->assertEquals($expectedResult, $actualResult, 'Case 4 failed: #8c5a45'); - - // Test case 4: #8c5a45 - $hexColor = '#345'; - $expectedResult = [51, 68, 85]; - $actualResult = Color::HexToRGB($hexColor); - $this->assertEquals($expectedResult, $actualResult, 'Case failed: #345'); - - - } - - /** - * Tests the RGBToHSL method with a variety of colors. - * - * @throws \Exception - */ - public function testRGBToHSL(): void - { - // Test case 1: White - $rgbColor = [255, 255, 255]; - $expectedResult = [0, 0, 1]; - $actualResult = Color::RGBToHSL($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult); - - // Test case 2: Black - $rgbColor = [0, 0, 0]; - $expectedResult = [0, 0, 0]; - $actualResult = Color::RGBToHSL($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult); - - // Test case 3: Red - $rgbColor = [255, 0, 0]; - $expectedResult = [0, 1, 0.5]; - $actualResult = Color::RGBToHSL($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult); - - // Test case 4: Green - $rgbColor = [0, 255, 0]; - $expectedResult = [120, 1, 0.5]; - $actualResult = Color::RGBToHSL($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult); - - // Test case 5: Blue - $rgbColor = [0, 0, 255]; - $expectedResult = [240, 1, 0.5]; - $actualResult = Color::RGBToHSL($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult); - - // Test case 6: Mountain Meadow - $rgbColor = [17, 195, 128]; - $expectedResult = [157, 0.84, 0.42]; - $actualResult = Color::RGBToHSL($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 6 failed: Mountain Meadow'); - - // Test case 6: #8c5a45 - $rgbColor = [140, 90, 69]; - $expectedResult = [18, 0.34, 0.41]; - $actualResult = Color::RGBToHSL($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 7 failed: #8c5a45'); - } - - /** - * Tests the RGBToHSV method with a variety of colors. - * - * @throws \Exception - */ - public function testRGBToHSV(): void - { - // Test case 1: White - $rgbColor = [255, 255, 255]; - $expectedResult = [0, 0, 1]; - $actualResult = Color::RGBToHSV($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 1 failed: White'); - - // Test case 2: Black - $rgbColor = [0, 0, 0]; - $expectedResult = [0, 0, 0]; - $actualResult = Color::RGBToHSV($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 2 failed: Black'); - - // Test case 3: Red - $rgbColor = [255, 0, 0]; - $expectedResult = [0, 100, 1]; - $actualResult = Color::RGBToHSV($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 3 failed: Red'); - - // Test case 4: Green - $rgbColor = [0, 255, 0]; - $expectedResult = [120, 100, 1]; - $actualResult = Color::RGBToHSV($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 4 failed: Green'); - - // Test case 5: Blue - $rgbColor = [0, 0, 255]; - $expectedResult = [240, 100, 1]; - $actualResult = Color::RGBToHSV($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 5 failed: Blue'); - } - - /** - * Tests the RGBToHex method. - * - * @throws \Exception - */ - public function testRGBToHex(): void - { - // Test case 1: White - $rgbColor = [255, 255, 255]; - $expectedResult = 'ffffff'; - $actualResult = Color::RGBToHex($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 1 failed: White'); - - // Test case 2: Black - $rgbColor = [0, 0, 0]; - $expectedResult = '000000'; - $actualResult = Color::RGBToHex($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 2 failed: Black'); - - // Test case 3: Red - $rgbColor = [255, 0, 0]; - $expectedResult = 'ff0000'; - $actualResult = Color::RGBToHex($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 3 failed: Red'); - - // Test case 4: Green - $rgbColor = [0, 255, 0]; - $expectedResult = '00ff00'; - $actualResult = Color::RGBToHex($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 4 failed: Green'); - - // Test case 5: Blue - $rgbColor = [0, 0, 255]; - $expectedResult = '0000ff'; - $actualResult = Color::RGBToHex($rgbColor[0], $rgbColor[1], $rgbColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 5 failed: Blue'); - - } - - /** - * Tests the HSVToRGB method. - * - * @throws \Exception - */ - public function testHSVToRGB(): void - { - // Test case 1: White - $HSVColor = [0, 0, 1]; - $expectedResult = [255, 255, 255]; - $actualResult = Color::HSVToRGB($HSVColor[0], $HSVColor[1], $HSVColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 1 failed: White'); - - // Test case 2: Black - $HSVColor = [0, 0, 0]; - $expectedResult = [0, 0, 0]; - $actualResult = Color::HSVToRGB($HSVColor[0], $HSVColor[1], $HSVColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 2 failed: Black'); - - // Test case 3: Red - $HSVColor = [0, 1, 1]; - $expectedResult = [255, 0, 0]; - $actualResult = Color::HSVToRGB($HSVColor[0], $HSVColor[1], $HSVColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 3 failed: Red'); - - // Test case 4: Green - $HSVColor = [120, 1, 1]; - $expectedResult = [0, 255, 0]; - $actualResult = Color::HSVToRGB($HSVColor[0], $HSVColor[1], $HSVColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 4 failed: Green'); - - // Test case 5: Blue - $HSVColor = [240, 1, 1]; - $expectedResult = [0, 0, 255]; - $actualResult = Color::HSVToRGB($HSVColor[0], $HSVColor[1], $HSVColor[2]); - $this->assertEquals($expectedResult, $actualResult, 'Case 5 failed: Blue'); - } - - /** - * Tests the calculateCVH method. - * - * @throws \Exception - */ - public function testCalculateCVH(): void - { - // Test case 1: RGB (255, 255, 255) - $expectedResult = [1.0, 1.0, 0, 1.0, 0]; - $actualResult = Color::calculateCVH(255, 255, 255); - $this->assertEquals($expectedResult, $actualResult, 'Case 1 failed: White'); - - // Test case 2: RGB (0, 0, 0) - $expectedResult = [0, 0, 0, 0, 0]; - $actualResult = Color::calculateCVH(0, 0, 0); - $this->assertEquals($expectedResult, $actualResult, 'Case 2 failed: Black'); - - // Test case 3: RGB (255, 0, 0) - $expectedResult = [1.0, 0, 1.0, 1.0, 0]; - $actualResult = Color::calculateCVH(255, 0, 0); - $this->assertEquals($expectedResult, $actualResult, 'Case 3 failed: Red'); - - // Test case 4: RGB (0, 255, 0) - $expectedResult = [1.0, 0, 1.0, 1.0, 120]; - $actualResult = Color::calculateCVH(0, 255, 0); - $this->assertEquals($expectedResult, $actualResult, 'Case 4 failed: Green'); - - // Test case 5: RGB (0, 0, 255) - $expectedResult = [1, 0, 1.0, 1.0, 240]; - $actualResult = Color::calculateCVH(0, 0, 255); - $this->assertEquals($expectedResult, $actualResult, 'Case 5 failed: Blue'); - } - - /** - * Tests the setHex method in different scenarios. - * - * @return void - */ - public function testSetHex(): void - { - $color = new Color(ColorType::RGB, [255, 255, 255]); - - $color->setHex('#000000'); - $this->assertEquals([0, 0, 0], $color->getRGB()); - $this->assertEquals('#000000', $color->getHex()); - $this->assertEquals([0, 0, 0], $color->getHSL()); - - $color->setHex('#FF0000'); - $this->assertEquals([255, 0, 0], $color->getRGB()); - $this->assertEquals('#FF0000', $color->getHex()); - $this->assertEquals([0, 1, 0.5], $color->getHSL()); - - $color->setHex('#00FF00'); - $this->assertEquals([0, 255, 0], $color->getRGB()); - $this->assertEquals('#00FF00', $color->getHex()); - $this->assertEquals([120, 1, 0.5], $color->getHSL()); - - $color->setHex('#0000FF'); - $this->assertEquals([0, 0, 255], $color->getRGB()); - $this->assertEquals('#0000FF', $color->getHex()); - $this->assertEquals([240, 1, 0.5], $color->getHSL()); - } - - /** - * Tests the setRGB method in different scenarios. - * - * @return void - */ - public function testSetRGB(): void - { - $color = new Color(ColorType::Hex, '#ffffff'); - - $color->setRGB([0, 0, 0]); - $this->assertEquals([0, 0, 0], $color->getRGB()); - $this->assertEquals([0, 0, 0], $color->getHSL()); - $this->assertEquals('#000000', $color->getHex()); - - $color->setRGB([255, 0, 0]); - $this->assertEquals([255, 0, 0], $color->getRGB()); - $this->assertEquals([0, 1, 0.5], $color->getHSL()); - $this->assertEquals('#ff0000', $color->getHex()); - - $color->setRGB([0, 255, 0]); - $this->assertEquals([0, 255, 0], $color->getRGB()); - $this->assertEquals([120, 1, 0.5], $color->getHSL()); - $this->assertEquals('#00ff00', $color->getHex()); - - $color->setRGB([0, 0, 255]); - $this->assertEquals([0, 0, 255], $color->getRGB()); - $this->assertEquals([240, 1, 0.5], $color->getHSL()); - $this->assertEquals('#0000ff', $color->getHex()); - } - - public function testGetColorSet(): void - { - $color = new Color(ColorType::HSL, [240, 0.5, 0.5]); - list($dark, $light) = $color->getColorSet(); - - $this->assertEquals([240, 0.5, 0.35], $dark->getHSL()); - $this->assertEquals([240, 0.5, 0.8], $light->getHSL()); - } - - public function testBrightenWithoutParameter(): void - { - $color = new Color(ColorType::HSL, [240, 0.5, 0.5]); - $color->brighten(); - $this->assertEquals([240, 0.5, 0.6], $color->getHSL()); - } - - public function testBrightenWithParameter(): void - { - $color = new Color(ColorType::HSL, [240, 0.5, 0.5]); - $color->brighten(20); - $this->assertEquals([240, 0.5, 0.7], $color->getHSL()); - } - - public function testBrightenWithOutOfRangeParameter(): void - { - $color = new Color(ColorType::HSL, [240, 0.5, 0.5]); - $color->brighten(120); - $this->assertEquals([240, 0.5, 1.0], $color->getHSL()); - - $color->brighten(-120); - $this->assertEquals([240, 0.5, 0.0], $color->getHSL()); - } - - public function testDarkenWithoutParameter(): void - { - $color = new Color(ColorType::HSL, [240, 0.5, 0.5]); - $color->darken(); - $this->assertEquals([240, 0.5, 0.4], $color->getHSL()); - } - - public function testDarkenWithParameter(): void - { - $color = new Color(ColorType::HSL, [240, 0.5, 0.5]); - $color->darken(20); - $this->assertEquals([240, 0.5, 0.3], $color->getHSL()); - } - - public function testDarkenWithOutOfRangeParameter(): void - { - $color = new Color(ColorType::HSL, [240, 0.5, 0.5]); - $color->darken(120); - $this->assertEquals([240, 0.5, 0.0], $color->getHSL()); - - $color->darken(-120); - $this->assertEquals([240, 0.5, 1.0], $color->getHSL()); - } - -} From 8ac391193c5502cefd9cb2283f815607692b0df3 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sat, 18 May 2024 22:17:42 +0200 Subject: [PATCH 05/93] Add ColorTrait for color set generation A new trait, ColorTrait, has been added. This trait provides a function for generating a color set given a name, with configurable lightness values for the text and background colors. The colors are returned in HSL format. --- src/Traits/ColorTrait.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/Traits/ColorTrait.php diff --git a/src/Traits/ColorTrait.php b/src/Traits/ColorTrait.php new file mode 100644 index 0000000..8321769 --- /dev/null +++ b/src/Traits/ColorTrait.php @@ -0,0 +1,28 @@ +getHexColor(); + + $dark = $color->toHSL(); + $dark->setLightness($textLightness); + $light = $color->toHSL(); + $light->setLightness($backgroundLightness); + return [$dark, $light]; + } + +} \ No newline at end of file From 86a8a4feb358149b1286239ab8e9ebb25c369aae Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sat, 18 May 2024 22:17:51 +0200 Subject: [PATCH 06/93] Add "renfordt/colors" to composer.json This commit adds the "renfordt/colors" dependency to the composer.json file. Now the project can utilize this library in its development process. The version specified is "dev-main". --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5d5491d..feb2ee0 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,8 @@ "php": "^8.1", "ext-gd": "*", "meyfa/php-svg": "^0.14.0", - "renfordt/clamp": "^1.0" + "renfordt/clamp": "^1.0", + "renfordt/colors": "dev-main" }, "require-dev": { "phpunit/phpunit": "^10.5", From 4707c1100d45b357f3c86aec9c4119616df112b7 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sat, 18 May 2024 22:18:14 +0200 Subject: [PATCH 07/93] Add color manipulation and encoding functionality to Identicon The Identicon class has been updated to include the use of ColorTrait for better handling of colors. The getSVG method has been modified to include an optional encoding parameter for output conversion. Update also includes minor refactor of color definitions and SVG generation code for better compliance with the color handling changes. --- src/Identicon.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Identicon.php b/src/Identicon.php index 13173e0..9575916 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -2,12 +2,17 @@ namespace Renfordt\Larvatar; +use Renfordt\Colors\HexColor; +use Renfordt\Colors\HSLColor; use Renfordt\Larvatar\Enum\ColorType; +use Renfordt\Larvatar\Traits\ColorTrait; use SVG\Nodes\Shapes\SVGRect; use SVG\SVG; class Identicon { + use ColorTrait; + public Name $name; public int $size = 125; public int $pixels = 5; @@ -21,13 +26,16 @@ public function __construct(Name $name) $this->name = $name; } - public function getSVG(): string + public function getSVG(string|null $encoding = null): string { $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); - $color = Color::make(ColorType::Hex, $this->name->getHexColor()); - list($darkColor, $lightColor) = $color->getColorSet($this->textLightness, $this->backgroundLightness); + /** + * @var HSLColor $darkColor + * @var HSLColor $lightColor + */ + list($darkColor, $lightColor) = $this->getColorSet($this->name, $this->textLightness, $this->backgroundLightness); if ($this->symmetry) { $matrix = $this->generateSymmetricMatrix(); @@ -43,13 +51,17 @@ public function getSVG(): string (int) $y * ($this->size / $this->pixels), (int) $this->size / $this->pixels, (int) $this->size / $this->pixels); - $square->setStyle('fill', $darkColor->getHex()); + $square->setStyle('fill', $darkColor->toHex()); $doc->addChild($square); } } } + if ($encoding == 'base64') { + return 'data:image/svg+xml;base64,'.base64_encode($larvatar); + } + return $larvatar; } From f4014fcee5a869d0db2b6a033740af1c02a5039f Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sat, 18 May 2024 22:18:30 +0200 Subject: [PATCH 08/93] Refactor InitialsAvatar class to use Name object The `InitialsAvatar` class has been refactored to use a `Name` object instead of an array of strings. The `setColor`, `setName`, and `make` methods now accept a `Name` object, and the `generate` method no longer accepts an array of names. Additionally, the `getNames` method and all related methods to generate color from array of names have been removed, as they are no longer relevant. The code now uses `HSLColor` instead of the previous `Color` object for color related operations. --- src/InitialsAvatar.php | 106 ++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 65 deletions(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index df53c3b..4484dc1 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -2,8 +2,10 @@ namespace Renfordt\Larvatar; +use Renfordt\Colors\HSLColor; use Renfordt\Larvatar\Enum\ColorType; use Renfordt\Larvatar\Enum\FormTypes; +use Renfordt\Larvatar\Traits\ColorTrait; use SVG\Nodes\Shapes\SVGCircle; use SVG\Nodes\Shapes\SVGPolygon; use SVG\Nodes\Shapes\SVGRect; @@ -12,9 +14,11 @@ class InitialsAvatar { + use ColorTrait; + private string $fontPath = ''; private string $fontFamily = ''; - private array $names = []; + private Name $name; private int $size = 128; private int $fontSize = 0; private FormTypes $form = FormTypes::Circle; @@ -25,22 +29,30 @@ class InitialsAvatar private int $offset = 0; /** - * Create an instance of InitialsAvatar - * @param string $name First and last name or username, seperated by a space + * Constructs a new instance of the class. + * + * @param Name $name The name object to set + * + * @return void */ - public function __construct(string $name = '') + public function __construct(Name $name) { $this->setName($name); } /** - * Sets the names for the avatar - * @param string $name Names seperated by a space for generating the avatar + * Sets the name for the user + * @param Name $name The user's name * @return void */ - public function setName(string $name): void + public function setName(Name $name): void + { + $this->name = $name; + } + + public static function make(Name $name): InitialsAvatar { - $this->names = explode(' ', $name); + return new self($name); } /** @@ -51,16 +63,19 @@ public function setName(string $name): void * * @return string The generated avatar in SVG format or the base64-encoded avatar image */ - public function generate(array $names = [], string|null $encoding = null): string + public function generate(string|null $encoding = null): string { - $names = $this->getNames($names); $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); $this->addFontIfNotEmpty(); - $color = $this->getColor($names); - list($darkColor, $lightColor) = $color->getColorSet($this->textLightness, $this->backgroundLightness); + /** + * @var HSLColor $darkColor + * @var HSLColor $lightColor + */ + list($darkColor, $lightColor) = $this->getColorSet($this->name, $this->textLightness, + $this->backgroundLightness); if ($this->form == FormTypes::Circle) { $halfSize = $this->size / 2; @@ -72,7 +87,7 @@ public function generate(array $names = [], string|null $encoding = null): strin } - $initials = $this->getInitials($names, $darkColor); + $initials = $this->getInitials($this->name->getSplitNames(), $darkColor); $doc->addChild($outlineForm); $doc->addChild($initials); @@ -83,19 +98,6 @@ public function generate(array $names = [], string|null $encoding = null): strin return $larvatar; } - /** - * Retrieves the names array - * If the provided $names array is empty, - * it returns the default names array - * - * @param array $names The names array - * @return array The names array to be returned - */ - private function getNames(array $names): array - { - return empty($names) ? $this->names : $names; - } - /** * Adds a font if the font path and font family are not empty * @return void @@ -107,44 +109,17 @@ private function addFontIfNotEmpty(): void } } - /** - * Retrieves the color based on the given array of names - * - * @param array $names An array of names - * @return Color The color object with the generated hex color - */ - private function getColor(array $names): Color - { - return new Color(ColorType::Hex, $this->generateHexColor($names, $this->offset)); - } - - /** - * Generates a hex color code based on the names - * @param array|null $names Array of names used to generate the hex color code. - * @param int $offset Offset of the hash, similar to a seed - * @return string Returns a color hash code, e.g. '#123456' - */ - public function generateHexColor(array $names = null, int $offset = 0): string - { - if ($names == null) { - $names = $this->names; - } - $name = implode(' ', $names); - $hash = md5($name); - return '#'.substr($hash, $offset, 6); - } - /** * Get a circle SVG element * * @param float $halfSize Half of the size of the circle - * @param Color $lightColor The light color to fill the circle with + * @param HSLColor $lightColor The light color to fill the circle with * @return SVGCircle The circle SVG element */ - private function getCircle(float $halfSize, Color $lightColor): SVGCircle + private function getCircle(float $halfSize, HSLColor $lightColor): SVGCircle { $circle = new SVGCircle($halfSize, $halfSize, $halfSize); - $circle->setStyle('fill', $lightColor->getHex()); + $circle->setStyle('fill', $lightColor->toHex()); return $circle; } @@ -153,14 +128,14 @@ private function getCircle(float $halfSize, Color $lightColor): SVGCircle * Get a square SVGRect * * @param float $size Half of the square size - * @param Color $lightColor The color of the square + * @param HSLColor $lightColor The color of the square * * @return SVGRect The generated square SVGRect object */ - private function getSquare(float $size, Color $lightColor): SVGRect + private function getSquare(float $size, HSLColor $lightColor): SVGRect { $square = new SVGRect(0, 0, $size, $size); - $square->setStyle('fill', $lightColor->getHex()); + $square->setStyle('fill', $lightColor->toHex()); return $square; } @@ -168,10 +143,11 @@ private function getSquare(float $size, Color $lightColor): SVGRect * Get a polygon shape * * @param float $size The size of the polygon - * @param Color $lightColor The light color to fill the polygon + * @param HSLColor $lightColor The light color to fill the polygon + * @param int $rotation * @return SVGPolygon The polygon shape with the specified size and color */ - private function getHexagon(float $size, Color $lightColor, int $rotation = 0): SVGPolygon + private function getHexagon(float $size, HSLColor $lightColor, int $rotation = 0): SVGPolygon { $rotation = pi() / 180 * $rotation; @@ -182,17 +158,17 @@ private function getHexagon(float $size, Color $lightColor, int $rotation = 0): } $polygon = new SVGPolygon($edgePoints); - $polygon->setStyle('fill', $lightColor->getHex()); + $polygon->setStyle('fill', $lightColor->toHex()); return $polygon; } /** * Generates initials for the given names and returns SVGText object * @param array $names List of names - * @param Color $darkColor Dark color object + * @param HSLColor $darkColor Dark color object * @return SVGText SVGText object containing the initials */ - private function getInitials(array $names, Color $darkColor): SVGText + private function getInitials(array $names, HSLColor $darkColor): SVGText { $initialsText = ''; foreach ($names as $name) { @@ -200,7 +176,7 @@ private function getInitials(array $names, Color $darkColor): SVGText } $initials = new SVGText($initialsText, '50%', '55%'); - $initials->setStyle('fill', $darkColor->getHex()); + $initials->setStyle('fill', $darkColor->toHex()); $initials->setStyle('text-anchor', 'middle'); $initials->setStyle('dominant-baseline', 'middle'); $initials->setStyle('font-weight', $this->fontWeight); From 4757f65f63654137deb6e73f559810f92c7b30b6 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sat, 18 May 2024 22:18:47 +0200 Subject: [PATCH 09/93] Add Name class with hashing capabilities A new class, Name, has been added which allows for the handling of a name as a string, including hashing the name. The class also includes methods for retrieving split names and converting the hash into a hexadecimal color. --- src/Name.php | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/Name.php diff --git a/src/Name.php b/src/Name.php new file mode 100644 index 0000000..26df5e8 --- /dev/null +++ b/src/Name.php @@ -0,0 +1,90 @@ +name = $name; + $this->splitNames = explode(' ', $name); + $this->hash = $this->hash(); + } + + /** + * Calculates the MD5 hash of the name. + * + * @return string + */ + private function hash(): string + { + return md5($this->name); + } + + /** + * Create an instance of the Name class. + * + * @param string $name The name to be used for creating the Name object. + * @return Name The newly created Name object. + */ + public static function make(string $name): Name + { + return new Name($name); + } + + /** + * Get the hexadecimal color value + * + * @param int $offset The starting offset for the substring + * @return HexColor The hexadecimal color string + */ + public function getHexColor(int $offset = 0): HexColor + { + return HexColor::make('#'.substr($this->hash, $offset, 6)); + } + + /** + * Get the name of the object. + * + * @return string The name of the object. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Retrieves the split names. + * + * @return array The split names. + */ + public function getSplitNames(): array + { + return $this->splitNames; + } + + /** + * Get the hash value of the name + * + * @return string The hashed name. + */ + public function getHash(): string + { + return $this->hash; + } +} \ No newline at end of file From 7e3d93700ea015961df2dea7713fcee3fadee471 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 07:12:47 +0200 Subject: [PATCH 10/93] Update font properties and refactor initials retrieval The fontWeight property of InitialsAvatar class was changed from 'normal' to 'regular'. Additionally, a new method 'getName' was added to get the name of the avatar. Lastly, the method 'getInitials' was refactored to directly get initials from the name property instead of an array of names, and the fontFamily hard-coded to "Segoe UI, Helvetica, sans-serif". --- src/InitialsAvatar.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index 4484dc1..82aa4b7 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -23,7 +23,7 @@ class InitialsAvatar private int $fontSize = 0; private FormTypes $form = FormTypes::Circle; private int $rotation; - private string $fontWeight = 'normal'; + private string $fontWeight = 'regular'; private float $backgroundLightness = 0.8; private float $textLightness = 0.35; private int $offset = 0; @@ -50,6 +50,11 @@ public function setName(Name $name): void $this->name = $name; } + public function getName(): Name + { + return $this->name; + } + public static function make(Name $name): InitialsAvatar { return new self($name); @@ -86,8 +91,7 @@ public function generate(string|null $encoding = null): string $outlineForm = $this->getHexagon($this->size, $lightColor, $this->rotation); } - - $initials = $this->getInitials($this->name->getSplitNames(), $darkColor); + $initials = $this->getInitials($darkColor); $doc->addChild($outlineForm); $doc->addChild($initials); @@ -168,19 +172,17 @@ private function getHexagon(float $size, HSLColor $lightColor, int $rotation = 0 * @param HSLColor $darkColor Dark color object * @return SVGText SVGText object containing the initials */ - private function getInitials(array $names, HSLColor $darkColor): SVGText + private function getInitials( HSLColor $darkColor): SVGText { - $initialsText = ''; - foreach ($names as $name) { - $initialsText .= substr($name, 0, 1); - } + $initialsText = $this->name->getInitials(); $initials = new SVGText($initialsText, '50%', '55%'); $initials->setStyle('fill', $darkColor->toHex()); $initials->setStyle('text-anchor', 'middle'); $initials->setStyle('dominant-baseline', 'middle'); $initials->setStyle('font-weight', $this->fontWeight); - $initials->setFontFamily($this->fontFamily); + $initials->setFontFamily("Segoe UI, Helvetica, sans-serif"); + //$initials->setFontFamily($this->fontFamily); if ($this->fontSize == 0) { $this->fontSize = $this->calculateFontSize($initialsText); } From 25ce8390293f2ad7e9d2f25e6265ac9ffbb45c1a Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 07:13:23 +0200 Subject: [PATCH 11/93] Reorganize methods and add getInitials in Name class The `make` method in the Name class has been moved for better organization, while the `getHash` method has been relocated. Furthermore, a new method named `getInitials` that extracts the initials from a name has been introduced to the class. --- src/Name.php | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/Name.php b/src/Name.php index 26df5e8..fd526af 100644 --- a/src/Name.php +++ b/src/Name.php @@ -36,6 +36,17 @@ private function hash(): string return md5($this->name); } + /** + * Get the hexadecimal color value + * + * @param int $offset The starting offset for the substring + * @return HexColor The hexadecimal color string + */ + public function getHexColor(int $offset = 0): HexColor + { + return HexColor::make('#'.substr($this->hash, $offset, 6)); + } + /** * Create an instance of the Name class. * @@ -48,43 +59,47 @@ public static function make(string $name): Name } /** - * Get the hexadecimal color value + * Get the name of the object. * - * @param int $offset The starting offset for the substring - * @return HexColor The hexadecimal color string + * @return string The name of the object. */ - public function getHexColor(int $offset = 0): HexColor + public function getName(): string { - return HexColor::make('#'.substr($this->hash, $offset, 6)); + return $this->name; } /** - * Get the name of the object. + * Get the hash value of the name * - * @return string The name of the object. + * @return string The hashed name. */ - public function getName(): string + public function getHash(): string { - return $this->name; + return $this->hash; } /** - * Retrieves the split names. + * Get the initials of the name * - * @return array The split names. + * @return string The initials of the name. */ - public function getSplitNames(): array + public function getInitials(): string { - return $this->splitNames; + $initials = ''; + foreach ($this->getSplitNames() as $name) { + $initials .= mb_substr($name, 0, 1, 'UTF-8'); + } + + return $initials; } /** - * Get the hash value of the name + * Retrieves the split names. * - * @return string The hashed name. + * @return array The split names. */ - public function getHash(): string + public function getSplitNames(): array { - return $this->hash; + return $this->splitNames; } } \ No newline at end of file From 8053f200823dee79e56c195c2a5ab9e9e6f51bde Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:11:24 +0200 Subject: [PATCH 12/93] Add abstract Avatar class The newly added Avatar abstract class forms the basis for avatar-specific implementations. It includes properties and getters/setters for avatar related attributes like font size, font family, name, lightness values, and more, plus it has abstract methods for HTML and base64 conversion. --- src/Avatar.php | 182 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/Avatar.php diff --git a/src/Avatar.php b/src/Avatar.php new file mode 100644 index 0000000..553955b --- /dev/null +++ b/src/Avatar.php @@ -0,0 +1,182 @@ +fontSize; + } + + /** + * Set the font size. + * + * @param int $fontSize The font size. + * @return void + */ + public function setFontSize(int $fontSize): void + { + $this->fontSize = $fontSize; + } + + /** + * Get the font family + * + * @return string The font family + */ + public function getFontFamily(): string + { + return $this->fontFamily; + } + + /** + * Set the font family for the application + * + * @param string $fontFamily The font family to set + * @return void + */ + public function setFontFamily(string $fontFamily): void + { + $this->fontFamily = $fontFamily; + } + + /** + * Get the font path + * + * @return string The font path + */ + public function getFontPath(): string + { + return $this->fontPath; + } + + /** + * Set the font path + * + * @param string $fontPath The path to the font + * @return void + */ + public function setFontPath(string $fontPath): void + { + $this->fontPath = $fontPath; + } + + /** + * Get the name of the object + * + * @return Name The name of the object + */ + public function getName(): Name + { + return $this->name; + } + + /** + * Set the Name object for the given instance + * + * @param Name $name The Name object + * + * @return void + */ + public function setName(Name $name): void + { + $this->name = $name; + } + + /** + * Get the background lightness value + * + * @return float The background lightness value + */ + public function getBackgroundLightness(): float + { + return $this->backgroundLightness; + } + + /** + * Set the background lightness + * + * @param float $backgroundLightness The background lightness value to set (between 0 and 1) + * @return void + */ + public function setBackgroundLightness(float $backgroundLightness): void + { + $this->backgroundLightness = clamp($backgroundLightness, 0, 1); + } + + /** + * Get the lightness value of the text + * + * @return float The lightness value of the text + */ + public function getTextLightness(): float + { + return $this->textLightness; + } + + /** + * Set the text lightness value + * + * @param float $textLightness The text lightness value to be set + * @return void + */ + public function setTextLightness(float $textLightness): void + { + $this->textLightness = clamp($textLightness, 0, 1); + } + + /** + * Get the font weight + * + * @return string The font weight + */ + public function getFontWeight(): string + { + return $this->fontWeight; + } + + /** + * Set the font weight for the application + * + * @param string $fontWeight The font weight to set + * @return void + */ + public function setFontWeight(string $fontWeight): void + { + $this->fontWeight = $fontWeight; + } + + +} \ No newline at end of file From 38d91d3b2bd17143948c46d93d4b0e460e7e0be2 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:11:40 +0200 Subject: [PATCH 13/93] Reorder methods and add getHTML method in Gravatar.php Reordered setEmail and setHash methods to improve code readability. In addition, a new getHTML method was added to provide an HTML string for the Gravatar image. The generateGravatarLink method was also modified to improve the Gravatar link generation logic. --- src/Gravatar.php | 57 ++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/Gravatar.php b/src/Gravatar.php index 4e5dc44..7c222a6 100644 --- a/src/Gravatar.php +++ b/src/Gravatar.php @@ -22,6 +22,27 @@ public function __construct(string $email) $this->setEmail($email); } + /** + * Sets the email for Gravatar. It is used to gernerate a hash which is passed to the Gravatar API + * @param string $email + * @return void + */ + public function setEmail(string $email): void + { + $this->email = $email; + $this->setHash($email); + } + + /** + * Generates the hash value for the email address + * @param $email + * @return void + */ + protected function setHash($email): void + { + $this->hash = md5(strtolower(trim($email))); + } + /** * Sets the type for the avatar. * @param LarvatarTypes $type All enums except LarvatarTypes::InitialsAvatar are allowed @@ -35,17 +56,6 @@ public function setType(LarvatarTypes $type): void $this->type = $type; } - /** - * Sets the email for Gravatar. It is used to gernerate a hash which is passed to the Gravatar API - * @param string $email - * @return void - */ - public function setEmail(string $email): void - { - $this->email = $email; - $this->setHash($email); - } - /** * Sets the size of the Gravatar * @param int $size Size in px for the Gravatar @@ -56,14 +66,18 @@ public function setSize(int $size): void $this->size = $size; } + public function getHTML(): string + { + return ''; + } + /** - * Generates the hash value for the email address - * @param $email - * @return void + * Generate the link to the Gravatar + * @return string */ - protected function setHash($email): void + public function generateGravatarLink(): string { - $this->hash = md5(strtolower(trim($email))); + return 'https://www.gravatar.com/avatar/'.$this->hash.$this->getAdditionalParameters(); } /** @@ -73,7 +87,7 @@ protected function setHash($email): void */ protected function getAdditionalParameters(): string { - $link = match($this->type) { + $link = match ($this->type) { LarvatarTypes::Gravatar => '?d=', LarvatarTypes::mp => '?d=mp&f=y', LarvatarTypes::identicon => '?d=identicon&f=y', @@ -85,13 +99,4 @@ protected function getAdditionalParameters(): string }; return $link.'&s='.$this->size; } - - /** - * Generate the link to the Gravatar - * @return string - */ - public function generateGravatarLink(): string - { - return 'https://www.gravatar.com/avatar/'.$this->hash.$this->getAdditionalParameters(); - } } From 50d6aad76c8666c2208f418a9ff593397efae5c0 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:11:52 +0200 Subject: [PATCH 14/93] Refactor Identicon class and enhance functionality The Identicon class has been refactored to extend from the Avatar class, and several unused properties and methods have been removed. New methods for setting pixel count and symmetry have been added. Handling for HTML and base64 representations of the Identicon object has also been incorporated, centralizing format conversion logic within the class. --- src/Identicon.php | 80 +++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/src/Identicon.php b/src/Identicon.php index 9575916..56f70af 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -2,23 +2,16 @@ namespace Renfordt\Larvatar; -use Renfordt\Colors\HexColor; use Renfordt\Colors\HSLColor; -use Renfordt\Larvatar\Enum\ColorType; use Renfordt\Larvatar\Traits\ColorTrait; use SVG\Nodes\Shapes\SVGRect; use SVG\SVG; -class Identicon +class Identicon extends Avatar { use ColorTrait; - public Name $name; - public int $size = 125; public int $pixels = 5; - - private float $backgroundLightness = 0.8; - private float $textLightness = 0.35; private bool $symmetry = true; public function __construct(Name $name) @@ -26,7 +19,49 @@ public function __construct(Name $name) $this->name = $name; } - public function getSVG(string|null $encoding = null): string + /** + * Sets the number of pixels. + * + * @param int $pixels The number of pixels to set. + * @return void + */ + public function setPixels(int $pixels): void + { + $this->pixels = $pixels; + } + + /** + * Sets the symmetry property of the object. + * + * @param bool $symmetry The symmetry value to set. + * @return void + */ + public function setSymmetry(bool $symmetry): void + { + $this->symmetry = $symmetry; + } + + /** + * Returns the HTML representation of the image. + * + * @param bool $base64 Determines whether the HTML representation should be in base64 format or not. + * @return string The HTML representation of the image. + */ + public function getHTML(bool $base64 = false): string + { + if (!$base64) { + return $this->getSVG(); + } + + return ''; + } + + /** + * Returns the SVG representation of the Identicon . + * + * @return string The SVG representation of the Identicon. + */ + public function getSVG(): string { $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); @@ -35,7 +70,8 @@ public function getSVG(string|null $encoding = null): string * @var HSLColor $darkColor * @var HSLColor $lightColor */ - list($darkColor, $lightColor) = $this->getColorSet($this->name, $this->textLightness, $this->backgroundLightness); + list($darkColor, $lightColor) = $this->getColorSet($this->name, $this->textLightness, + $this->backgroundLightness); if ($this->symmetry) { $matrix = $this->generateSymmetricMatrix(); @@ -58,10 +94,6 @@ public function getSVG(string|null $encoding = null): string } - if ($encoding == 'base64') { - return 'data:image/svg+xml;base64,'.base64_encode($larvatar); - } - return $larvatar; } @@ -142,20 +174,14 @@ public function generateMatrix(int $offset = 0): array return $matrix; } - public function setSize(int $size): void - { - $this->size = $size; - } - - public function setPixels(int $pixels): void - { - $this->pixels = $pixels; - } - - public function setSymmetry(bool $symmetry): void + /** + * Returns the base64 representation of the SVG image. + * + * @return string The base64 encoded string representing the SVG image. + */ + public function getBase64(): string { - $this->symmetry = $symmetry; + return 'data:image/svg+xml;base64,'.base64_encode($this->getSVG()); } - } From 7c23aa9602b84641f35f918bb13fa8e2fd0b5589 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:12:03 +0200 Subject: [PATCH 15/93] Refactor InitialsAvatar class and add base64 output method The InitialsAvatar class underwent significant refactoring to improve its structure. Several properties have been removed, transitioning the class to inherit from the Avatar class. Additionally, new methods for getting and setting certain properties such as form and rotation have been introduced. Notably, the method for generating SVG output has been modified to include a new base64 output option, improving the flexibility of this class. --- src/InitialsAvatar.php | 214 ++++++++++++----------------------------- 1 file changed, 64 insertions(+), 150 deletions(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index 82aa4b7..9fc8066 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -12,20 +12,12 @@ use SVG\Nodes\Texts\SVGText; use SVG\SVG; -class InitialsAvatar +class InitialsAvatar extends Avatar { use ColorTrait; - private string $fontPath = ''; - private string $fontFamily = ''; - private Name $name; - private int $size = 128; - private int $fontSize = 0; private FormTypes $form = FormTypes::Circle; private int $rotation; - private string $fontWeight = 'regular'; - private float $backgroundLightness = 0.8; - private float $textLightness = 0.35; private int $offset = 0; /** @@ -40,24 +32,73 @@ public function __construct(Name $name) $this->setName($name); } + + public static function make(Name $name): InitialsAvatar + { + return new self($name); + } + /** - * Sets the name for the user - * @param Name $name The user's name + * Sets the form of the application + * + * @param string|FormTypes $form The form type * @return void */ - public function setName(Name $name): void + public function setForm(string|FormTypes $form): void { - $this->name = $name; + if (is_string($form)) { + $form = FormTypes::from($form); + } + + $this->form = $form; } - public function getName(): Name + /** + * Sets the rotation angle of the element + * + * @param int $angle The rotation angle value + * + * @return void + */ + public function setRotation(int $angle): void { - return $this->name; + $this->rotation = $angle; } - public static function make(Name $name): InitialsAvatar + /** + * Retrieves the offset of the object + * + * @return int The offset value + */ + public function getOffset(): int { - return new self($name); + return $this->offset; + } + + /** + * Sets the offset for the avatar + * + * @param int $offset The offset in pixel + * @return void + */ + public function setOffset(int $offset): void + { + $this->offset = $offset; + } + + /** + * Returns the HTML representation of the code. + * + * @param bool $base64 Determines if the image source should be in base64 format. Default is false. + * @return string The HTML representation of the code. + */ + function getHTML(bool $base64 = false): string + { + if (!$base64) { + return $this->generate(); + } + + return ''; } /** @@ -68,7 +109,7 @@ public static function make(Name $name): InitialsAvatar * * @return string The generated avatar in SVG format or the base64-encoded avatar image */ - public function generate(string|null $encoding = null): string + public function generate(bool $base64 = false): string { $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); @@ -96,9 +137,6 @@ public function generate(string|null $encoding = null): string $doc->addChild($outlineForm); $doc->addChild($initials); - if ($encoding == 'base64') { - return 'data:image/svg+xml;base64,'.base64_encode($larvatar); - } return $larvatar; } @@ -172,7 +210,7 @@ private function getHexagon(float $size, HSLColor $lightColor, int $rotation = 0 * @param HSLColor $darkColor Dark color object * @return SVGText SVGText object containing the initials */ - private function getInitials( HSLColor $darkColor): SVGText + private function getInitials(HSLColor $darkColor): SVGText { $initialsText = $this->name->getInitials(); @@ -203,136 +241,12 @@ protected function calculateFontSize(string $initials): int } /** - * Sets the font size for the text + * Returns the base64 encoded string representing the SVG image. * - * @param int $size The font size in pixel - * @return void + * @return string The base64 encoded string representing the SVG image. */ - public function setFontSize(int $size): void + function getBase64(): string { - $this->fontSize = $size; + return 'data:image/svg+xml;base64,'.base64_encode($this->generate()); } - - /** - * Sets the size of the avatar - * @param int $size Size in pixel - * @return void - */ - public function setSize(int $size): void - { - $this->size = $size; - } - - /** - * Sets the font which shall be used for the avatar - * @param string $fontFamily Font Family, e.g. 'Roboto' - * @param string $path Relative path to the true type font with a leading slash, e.g. '/font/Roboto-Bold.ttf' - * @return void - */ - public function setFont(string $fontFamily, string $path): void - { - $this->fontFamily = $fontFamily; - $this->fontPath = $path; - } - - /** - * Sets the form of the application - * - * @param string|FormTypes $form The form type - * @return void - */ - public function setForm(string|FormTypes $form): void - { - if (is_string($form)) { - $form = FormTypes::from($form); - } - - $this->form = $form; - } - - /** - * Sets the rotation angle of the element - * - * @param int $angle The rotation angle value - * - * @return void - */ - public function setRotation(int $angle): void - { - $this->rotation = $angle; - } - - /** - * Sets the font weight - * @param string $fontWeight The font weight to set - * @return void - */ - public function setFontWeight(string $fontWeight): void - { - $this->fontWeight = $fontWeight; - } - - /** - * Get the lightness of the background color. - * - * @return float The lightness value of the background color. - */ - public function getBackgroundLightness(): float - { - return $this->backgroundLightness; - } - - /** - * Sets the lightness of the background - * - * @param float $lightness Lightness value (between 0 and 1) - * @return void - */ - public function setBackgroundLightness(float $lightness): void - { - $this->backgroundLightness = clamp($lightness, 0, 1); - } - - /** - * Get the lightness of the text - * - * @return float The lightness of the text - */ - public function getTextLightness(): float - { - return $this->textLightness; - } - - /** - * Sets the lightness of the text - * - * @param float $lightness Lightness value ranging from 0 to 1 - * @return void - */ - public function setTextLightness(float $lightness): void - { - $this->textLightness = clamp($lightness, 0, 1); - } - - /** - * Retrieves the offset of the object - * - * @return int The offset value - */ - public function getOffset(): int - { - return $this->offset; - } - - /** - * Sets the offset for the avatar - * - * @param int $offset The offset in pixel - * @return void - */ - public function setOffset(int $offset): void - { - $this->offset = $offset; - } - } From cc322d4719a728799c95472ad38e89ad2189348e Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:12:14 +0200 Subject: [PATCH 16/93] Add IdenticonLarvatar to LarvatarTypes enum A new enum value, IdenticonLarvatar, has been added to the LarvatarTypes enum. This update incorporates the new Larvatar option within the available list of types. --- src/Enum/LarvatarTypes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Enum/LarvatarTypes.php b/src/Enum/LarvatarTypes.php index f7d3581..bf8637d 100644 --- a/src/Enum/LarvatarTypes.php +++ b/src/Enum/LarvatarTypes.php @@ -12,4 +12,5 @@ enum LarvatarTypes: int case wavatar = 5; case retro = 6; case robohash = 7; + case IdenticonLarvatar = 8; } \ No newline at end of file From 6c80ebab383da195ad71ba5acbcdc06e277260e2 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:12:26 +0200 Subject: [PATCH 17/93] Refactor Larvatar class and modify constructor The Larvatar class was refactored for better organization and readability. The constructor's signature was changed to take a mandatory LarvatarTypes object instead of a mix-type parameter. A static `make` method was added for more intuitive object creation. The `$name` property is now an instance of the `Name` class, and the method `getBase64()` was moved towards the end of the class for logical grouping. --- src/Larvatar.php | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/Larvatar.php b/src/Larvatar.php index 7ca9902..c1e98f9 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -6,13 +6,13 @@ class Larvatar { + public InitialsAvatar $initialsAvatar; protected LarvatarTypes $type = LarvatarTypes::mp; - protected string $name; + protected Name $name; protected string $email; protected string $font; protected string $fontPath; protected int $size = 100; - public InitialsAvatar $initialsAvatar; /** * Constructs a new instance of the class. @@ -21,28 +21,32 @@ class Larvatar * @param string $email The email. Default is an empty string. * @param int|LarvatarTypes $type The type. Default is LarvatarTypes::mp. */ - public function __construct(string $name = '', string $email = '', int|LarvatarTypes $type = LarvatarTypes::mp) + public function __construct(LarvatarTypes $type, string $name = '', string $email = '') { - $this->name = $name; + $this->name = Name::make($name); $this->email = $email; - if (is_int($type)) { - $this->type = LarvatarTypes::from($type); - } elseif ($type instanceof LarvatarTypes) { - $this->type = $type; - } + $this->type = $type; if ($this->type == LarvatarTypes::InitialsAvatar) { $this->initialsAvatar = new InitialsAvatar($this->name); } } + public static function make( + LarvatarTypes $type, + string $name = '', + string $email = '' + ): Larvatar { + return new self($type, $name, $email); + } + /** * Generates the HTML or SVG code directly for usage * @return string HTML or SVG code */ public function getImageHTML(string $encoding = ''): string { - if ($this->type == LarvatarTypes::InitialsAvatar) { + if ($this->type == LarvatarTypes::InitialsAvatar ) { if (isset($this->font) && $this->font != '' && $this->fontPath != '') { $this->initialsAvatar->setFont($this->font, $this->fontPath); } @@ -61,16 +65,6 @@ public function getImageHTML(string $encoding = ''): string return ''; } - /** - * Get the base64 string representation of the initials' avatar. - * - * @return string The base64 encoded string of the initials' avatar. - */ - public function getBase64(): string - { - return $this->initialsAvatar->generate(encoding: 'base64'); - } - /** * Set the font for Initial Avatar * @param string $fontFamily Font family of the used font, e.g. 'Roboto' @@ -93,4 +87,14 @@ public function setSize(int $size): void { $this->size = $size; } + + /** + * Get the base64 string representation of the initials' avatar. + * + * @return string The base64 encoded string of the initials' avatar. + */ + public function getBase64(): string + { + return $this->initialsAvatar->generate(encoding: 'base64'); + } } \ No newline at end of file From ec647483f241ac936034bedb15e4c6d00795b5e7 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:14:00 +0200 Subject: [PATCH 18/93] Add getSize and setSize methods in Avatar class The commit adds getSize and setSize methods in the Avatar class. These methods allow getting and setting the size of an Avatar object respectively. It provides more control and functionality in managing Avatar objects. --- src/Avatar.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Avatar.php b/src/Avatar.php index 553955b..d49962c 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -178,5 +178,26 @@ public function setFontWeight(string $fontWeight): void $this->fontWeight = $fontWeight; } + /** + * Get the size of the object + * + * @return int The size of the object + */ + public function getSize(): int + { + return $this->size; + } + + /** + * Set the size of the object + * + * @param int $size The size to set for the object + * @return void + */ + public function setSize(int $size): void + { + $this->size = $size; + } + } \ No newline at end of file From 5b9610e2b872d3037e6c5821c2d805bf2ada8d93 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:16:13 +0200 Subject: [PATCH 19/93] Add 'setFont' method in Avatar class The commit introduces a new method 'setFont' in the Avatar class. This method allows setting the font family and path for the avatar, streamlining the process of customizing the appearance of the avatar. --- src/Avatar.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Avatar.php b/src/Avatar.php index d49962c..4f817ce 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -199,5 +199,19 @@ public function setSize(int $size): void $this->size = $size; } + /** + * Sets the font family and path + * + * @param string $font The font family + * @param string $path The font path + * + * @return void + */ + public function setFont(string $font, string $path) + { + $this->setFontFamily($font); + $this->setFontPath($path); + } + } \ No newline at end of file From 743dca573e58605def37274a4754c70cc4852a01 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:16:47 +0200 Subject: [PATCH 20/93] Remove explicit 'base64' encoding specification The hardcoded 'base64' encoding specification was removed to make the code cleaner and easier to maintain. This change doesn't affect the functionality as the 'generate' method is capable of handling encoding implicitly. --- src/Larvatar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Larvatar.php b/src/Larvatar.php index c1e98f9..cf66b10 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -52,7 +52,7 @@ public function getImageHTML(string $encoding = ''): string } $this->initialsAvatar->setSize($this->size); if ($encoding == 'base64') { - return ''; + return ''; } else { return $this->initialsAvatar->generate(); } @@ -95,6 +95,6 @@ public function setSize(int $size): void */ public function getBase64(): string { - return $this->initialsAvatar->generate(encoding: 'base64'); + return $this->initialsAvatar->generate(); } } \ No newline at end of file From 297d5b05419a26ec00083750578f33b01486c3c6 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 12:35:26 +0200 Subject: [PATCH 21/93] Refactor Larvatar class to use Avatar interface This commit updates the Larvatar class to use the more generic Avatar interface instead of a specific InitialsAvatar class. This change allows us to add more types of Avatars in the future. This update also affects the associated LarvatarTrait, which now references the newly added "avatar" property. --- src/Larvatar.php | 18 ++++++++++-------- src/Traits/LarvatarTrait.php | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Larvatar.php b/src/Larvatar.php index cf66b10..f1bbc87 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -6,7 +6,7 @@ class Larvatar { - public InitialsAvatar $initialsAvatar; + public Avatar $avatar; protected LarvatarTypes $type = LarvatarTypes::mp; protected Name $name; protected string $email; @@ -28,7 +28,9 @@ public function __construct(LarvatarTypes $type, string $name = '', string $emai $this->type = $type; if ($this->type == LarvatarTypes::InitialsAvatar) { - $this->initialsAvatar = new InitialsAvatar($this->name); + $this->avatar = InitialsAvatar::make($this->name); + } elseif ($this == LarvatarTypes::IdenticonLarvatar) { + $this->avatar = Identicon::make($this->name); } } @@ -46,15 +48,15 @@ public static function make( */ public function getImageHTML(string $encoding = ''): string { - if ($this->type == LarvatarTypes::InitialsAvatar ) { + if ($this->type == LarvatarTypes::InitialsAvatar || $this->type == LarvatarTypes::IdenticonLarvatar) { if (isset($this->font) && $this->font != '' && $this->fontPath != '') { - $this->initialsAvatar->setFont($this->font, $this->fontPath); + $this->avatar->setFont($this->font, $this->fontPath); } - $this->initialsAvatar->setSize($this->size); + $this->avatar->setSize($this->size); if ($encoding == 'base64') { - return ''; + return $this->avatar->getHTML(true); } else { - return $this->initialsAvatar->generate(); + return $this->avatar->getHTML(); } } @@ -95,6 +97,6 @@ public function setSize(int $size): void */ public function getBase64(): string { - return $this->initialsAvatar->generate(); + return $this->avatar->generate(); } } \ No newline at end of file diff --git a/src/Traits/LarvatarTrait.php b/src/Traits/LarvatarTrait.php index 8650ef5..f9b23c1 100644 --- a/src/Traits/LarvatarTrait.php +++ b/src/Traits/LarvatarTrait.php @@ -21,7 +21,7 @@ public function getAvatar(string $name, string $email = '', int $size = 100, Lar $larvatar = new Larvatar($name, $email, $type); $larvatar->setSize($size); $larvatar->setFont('Roboto,sans-serif', '/font/Roboto-Bold.ttf'); - $larvatar->initialsAvatar->setFontWeight('bold'); + $larvatar->avatar->setFontWeight('bold'); return $larvatar->getImageHTML($encoding); } } \ No newline at end of file From 1201832ebe10dc0ed55eae744891e6f084b4c007 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 13:32:41 +0200 Subject: [PATCH 22/93] Add static factory method in Identicon.php A new static factory method, 'make(Name $name): Identicon', was added to the "Identicon.php" file. This method simplifies object creation by allowing instantiation via a static method call. --- src/Identicon.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Identicon.php b/src/Identicon.php index 56f70af..ee9e0c5 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -19,6 +19,11 @@ public function __construct(Name $name) $this->name = $name; } + public static function make(Name $name): Identicon + { + return new static($name); + } + /** * Sets the number of pixels. * From e81895ab5aefa02c88f603ce30a419eb979a614c Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 13:32:52 +0200 Subject: [PATCH 23/93] Refactor Larvatar.php for code optimization This commit includes changes in Larvatar.php including fixing a wrong condition check and refactoring the getImageHTML function. The function's argument is changed to boolean 'base64' for readability and simplicity instead of a string 'encoding', and unnecessary if/else conditions are removed. --- src/Larvatar.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Larvatar.php b/src/Larvatar.php index f1bbc87..f549fb3 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -28,8 +28,8 @@ public function __construct(LarvatarTypes $type, string $name = '', string $emai $this->type = $type; if ($this->type == LarvatarTypes::InitialsAvatar) { - $this->avatar = InitialsAvatar::make($this->name); - } elseif ($this == LarvatarTypes::IdenticonLarvatar) { + $this->avatar = InitialsAvatar::make($this->name); + } elseif ($this->type == LarvatarTypes::IdenticonLarvatar) { $this->avatar = Identicon::make($this->name); } } @@ -46,18 +46,14 @@ public static function make( * Generates the HTML or SVG code directly for usage * @return string HTML or SVG code */ - public function getImageHTML(string $encoding = ''): string + public function getImageHTML(bool $base64 = false): string { if ($this->type == LarvatarTypes::InitialsAvatar || $this->type == LarvatarTypes::IdenticonLarvatar) { if (isset($this->font) && $this->font != '' && $this->fontPath != '') { $this->avatar->setFont($this->font, $this->fontPath); } $this->avatar->setSize($this->size); - if ($encoding == 'base64') { - return $this->avatar->getHTML(true); - } else { - return $this->avatar->getHTML(); - } + return $this->avatar->getHTML($base64); } $gravatar = new Gravatar($this->email); From 39c93bb27a21d7188d5821006e5a53cb67b15cac Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 13:57:42 +0200 Subject: [PATCH 24/93] Update getAvatar method in LarvatarTrait Updated the `getAvatar` method in the `LarvatarTrait` class. Changes include modifying the encoding parameter type to boolean and adjusting the order of parameters in the Larvatar constructor call. --- src/Traits/LarvatarTrait.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Traits/LarvatarTrait.php b/src/Traits/LarvatarTrait.php index f9b23c1..eccb53b 100644 --- a/src/Traits/LarvatarTrait.php +++ b/src/Traits/LarvatarTrait.php @@ -16,9 +16,9 @@ trait LarvatarTrait * @param string $encoding The encoding type for the avatar image. Default is empty string. * @return string The HTML representation of the avatar image. */ - public function getAvatar(string $name, string $email = '', int $size = 100, LarvatarTypes $type = LarvatarTypes::InitialsAvatar, string $encoding = ''): string + public function getAvatar(string $name, string $email = '', int $size = 100, LarvatarTypes $type = LarvatarTypes::InitialsAvatar, bool $encoding = false): string { - $larvatar = new Larvatar($name, $email, $type); + $larvatar = new Larvatar($type, $name, $email); $larvatar->setSize($size); $larvatar->setFont('Roboto,sans-serif', '/font/Roboto-Bold.ttf'); $larvatar->avatar->setFontWeight('bold'); From 7fef20226100fb6275d06825a5fdac4c375a62f5 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Sun, 19 May 2024 14:06:02 +0200 Subject: [PATCH 25/93] Remove setting font weight in LarvatarTrait The line for setting the font weight for the avatar in the LarvatarTrait has been removed. This simplifies the code by not unnecessarily emphasizing the font weight. --- src/Traits/LarvatarTrait.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Traits/LarvatarTrait.php b/src/Traits/LarvatarTrait.php index eccb53b..d5ae88d 100644 --- a/src/Traits/LarvatarTrait.php +++ b/src/Traits/LarvatarTrait.php @@ -21,7 +21,6 @@ public function getAvatar(string $name, string $email = '', int $size = 100, Lar $larvatar = new Larvatar($type, $name, $email); $larvatar->setSize($size); $larvatar->setFont('Roboto,sans-serif', '/font/Roboto-Bold.ttf'); - $larvatar->avatar->setFontWeight('bold'); return $larvatar->getImageHTML($encoding); } } \ No newline at end of file From 315a4a1c81ee0027400fb34d26af53ac6c51d08f Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Tue, 6 Aug 2024 22:47:53 +0200 Subject: [PATCH 26/93] Update renfordt/colors version requirement Switch from 'dev-main' to '^1.0' for renfordt/colors dependency to ensure compatibility with stable releases and improve dependency management. This change minimizes potential issues associated with using a development branch. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index feb2ee0..f4adb19 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "ext-gd": "*", "meyfa/php-svg": "^0.14.0", "renfordt/clamp": "^1.0", - "renfordt/colors": "dev-main" + "renfordt/colors": "^1.0" }, "require-dev": { "phpunit/phpunit": "^10.5", From 9c369826dbe0758ba9671e3561884719261c7f0e Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 20:07:00 +0100 Subject: [PATCH 27/93] Add PHP 8.4 to GitHub Actions workflow Updated the PHP GitHub Actions workflow to include PHP 8.4 in the testing matrix. This ensures compatibility and testing for the latest PHP version. --- .github/workflows/php.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 875db76..79fe0c0 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -82,6 +82,7 @@ jobs: - "8.1" - "8.2" - "8.3" + - "8.4" steps: - name: Configure Git to avoid issues with line endings From fd37e2a9ad308bc6be7efed0efe83746da886164 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 20:16:53 +0100 Subject: [PATCH 28/93] Add PHP CS Fixer to development dependencies PHP CS Fixer has been added to the require-dev section in composer.json. This tool helps in automatically fixing PHP coding standards issues, ensuring a consistent codebase. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f4adb19..f481564 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ }, "require-dev": { "phpunit/phpunit": "^10.5", - "orchestra/testbench": "^v8.22" + "orchestra/testbench": "^v8.22", + "friendsofphp/php-cs-fixer": "^3.64" }, "extra": { "laravel": { From 0d9729c230ecded9c0bcbc7f4b75f81d883d2ec4 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 20:17:11 +0100 Subject: [PATCH 29/93] Make methods public and reformat code Updated visibility of several methods to public and reformatted code for better readability. This includes aligning the list arguments and ensuring proper indentations across files. --- src/Avatar.php | 6 +- src/Enum/LarvatarTypes.php | 2 +- src/Gravatar.php | 203 ++++++++++++++++---------------- src/Identicon.php | 11 +- src/InitialsAvatar.php | 11 +- src/Larvatar.php | 196 +++++++++++++++--------------- src/LarvatarServiceProvider.php | 2 +- src/Name.php | 2 +- src/Traits/ColorTrait.php | 2 +- src/Traits/LarvatarTrait.php | 2 +- 10 files changed, 221 insertions(+), 216 deletions(-) diff --git a/src/Avatar.php b/src/Avatar.php index 4f817ce..ad6273a 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -21,14 +21,14 @@ abstract class Avatar * @return string The HTML representation of the data. * @throws \Exception If the HTML generation fails. */ - abstract function getHTML(bool $base64 = false): string; + abstract public function getHTML(bool $base64 = false): string; /** * Abstract function to get the base64 representation of a string * * @return string The base64 representation of the string */ - abstract function getBase64(): string; + abstract public function getBase64(): string; /** * Get the font size @@ -214,4 +214,4 @@ public function setFont(string $font, string $path) } -} \ No newline at end of file +} diff --git a/src/Enum/LarvatarTypes.php b/src/Enum/LarvatarTypes.php index bf8637d..a8a1765 100644 --- a/src/Enum/LarvatarTypes.php +++ b/src/Enum/LarvatarTypes.php @@ -13,4 +13,4 @@ enum LarvatarTypes: int case retro = 6; case robohash = 7; case IdenticonLarvatar = 8; -} \ No newline at end of file +} diff --git a/src/Gravatar.php b/src/Gravatar.php index 7c222a6..1066aa6 100644 --- a/src/Gravatar.php +++ b/src/Gravatar.php @@ -1,102 +1,101 @@ -setEmail($email); - } - - /** - * Sets the email for Gravatar. It is used to gernerate a hash which is passed to the Gravatar API - * @param string $email - * @return void - */ - public function setEmail(string $email): void - { - $this->email = $email; - $this->setHash($email); - } - - /** - * Generates the hash value for the email address - * @param $email - * @return void - */ - protected function setHash($email): void - { - $this->hash = md5(strtolower(trim($email))); - } - - /** - * Sets the type for the avatar. - * @param LarvatarTypes $type All enums except LarvatarTypes::InitialsAvatar are allowed - * @return void - */ - public function setType(LarvatarTypes $type): void - { - if ($type == LarvatarTypes::InitialsAvatar) { - return; - } - $this->type = $type; - } - - /** - * Sets the size of the Gravatar - * @param int $size Size in px for the Gravatar - * @return void - */ - public function setSize(int $size): void - { - $this->size = $size; - } - - public function getHTML(): string - { - return ''; - } - - /** - * Generate the link to the Gravatar - * @return string - */ - public function generateGravatarLink(): string - { - return 'https://www.gravatar.com/avatar/'.$this->hash.$this->getAdditionalParameters(); - } - - /** - * Depending on the selected type the missing parameters for Gravatar API will be selected - * @return string - * @throws Exception - */ - protected function getAdditionalParameters(): string - { - $link = match ($this->type) { - LarvatarTypes::Gravatar => '?d=', - LarvatarTypes::mp => '?d=mp&f=y', - LarvatarTypes::identicon => '?d=identicon&f=y', - LarvatarTypes::monsterid => '?d=monsterid&f=y', - LarvatarTypes::wavatar => '?d=wavatar&f=y', - LarvatarTypes::retro => '?d=retro&f=y', - LarvatarTypes::robohash => '?d=robohash&f=y', - LarvatarTypes::InitialsAvatar => throw new Exception('Initials Avatar is not supported for Gravatars.') - }; - return $link.'&s='.$this->size; - } -} +setEmail($email); + } + + /** + * Sets the email for Gravatar. It is used to gernerate a hash which is passed to the Gravatar API + * @param string $email + * @return void + */ + public function setEmail(string $email): void + { + $this->email = $email; + $this->setHash($email); + } + + /** + * Generates the hash value for the email address + * @param $email + * @return void + */ + protected function setHash($email): void + { + $this->hash = md5(strtolower(trim($email))); + } + + /** + * Sets the type for the avatar. + * @param LarvatarTypes $type All enums except LarvatarTypes::InitialsAvatar are allowed + * @return void + */ + public function setType(LarvatarTypes $type): void + { + if ($type == LarvatarTypes::InitialsAvatar) { + return; + } + $this->type = $type; + } + + /** + * Sets the size of the Gravatar + * @param int $size Size in px for the Gravatar + * @return void + */ + public function setSize(int $size): void + { + $this->size = $size; + } + + public function getHTML(): string + { + return ''; + } + + /** + * Generate the link to the Gravatar + * @return string + */ + public function generateGravatarLink(): string + { + return 'https://www.gravatar.com/avatar/'.$this->hash.$this->getAdditionalParameters(); + } + + /** + * Depending on the selected type the missing parameters for Gravatar API will be selected + * @return string + * @throws Exception + */ + protected function getAdditionalParameters(): string + { + $link = match ($this->type) { + LarvatarTypes::Gravatar => '?d=', + LarvatarTypes::mp => '?d=mp&f=y', + LarvatarTypes::identicon => '?d=identicon&f=y', + LarvatarTypes::monsterid => '?d=monsterid&f=y', + LarvatarTypes::wavatar => '?d=wavatar&f=y', + LarvatarTypes::retro => '?d=retro&f=y', + LarvatarTypes::robohash => '?d=robohash&f=y', + LarvatarTypes::InitialsAvatar => throw new Exception('Initials Avatar is not supported for Gravatars.') + }; + return $link.'&s='.$this->size; + } +} diff --git a/src/Identicon.php b/src/Identicon.php index ee9e0c5..a720c3f 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -75,8 +75,11 @@ public function getSVG(): string * @var HSLColor $darkColor * @var HSLColor $lightColor */ - list($darkColor, $lightColor) = $this->getColorSet($this->name, $this->textLightness, - $this->backgroundLightness); + list($darkColor, $lightColor) = $this->getColorSet( + $this->name, + $this->textLightness, + $this->backgroundLightness + ); if ($this->symmetry) { $matrix = $this->generateSymmetricMatrix(); @@ -91,7 +94,8 @@ public function getSVG(): string (int) $x * ($this->size / $this->pixels), (int) $y * ($this->size / $this->pixels), (int) $this->size / $this->pixels, - (int) $this->size / $this->pixels); + (int) $this->size / $this->pixels + ); $square->setStyle('fill', $darkColor->toHex()); $doc->addChild($square); } @@ -189,4 +193,3 @@ public function getBase64(): string return 'data:image/svg+xml;base64,'.base64_encode($this->getSVG()); } } - diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index 9fc8066..eb87135 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -92,7 +92,7 @@ public function setOffset(int $offset): void * @param bool $base64 Determines if the image source should be in base64 format. Default is false. * @return string The HTML representation of the code. */ - function getHTML(bool $base64 = false): string + public function getHTML(bool $base64 = false): string { if (!$base64) { return $this->generate(); @@ -120,8 +120,11 @@ public function generate(bool $base64 = false): string * @var HSLColor $darkColor * @var HSLColor $lightColor */ - list($darkColor, $lightColor) = $this->getColorSet($this->name, $this->textLightness, - $this->backgroundLightness); + list($darkColor, $lightColor) = $this->getColorSet( + $this->name, + $this->textLightness, + $this->backgroundLightness + ); if ($this->form == FormTypes::Circle) { $halfSize = $this->size / 2; @@ -245,7 +248,7 @@ protected function calculateFontSize(string $initials): int * * @return string The base64 encoded string representing the SVG image. */ - function getBase64(): string + public function getBase64(): string { return 'data:image/svg+xml;base64,'.base64_encode($this->generate()); } diff --git a/src/Larvatar.php b/src/Larvatar.php index f549fb3..3c2b70a 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -1,98 +1,98 @@ -name = Name::make($name); - $this->email = $email; - $this->type = $type; - - if ($this->type == LarvatarTypes::InitialsAvatar) { - $this->avatar = InitialsAvatar::make($this->name); - } elseif ($this->type == LarvatarTypes::IdenticonLarvatar) { - $this->avatar = Identicon::make($this->name); - } - } - - public static function make( - LarvatarTypes $type, - string $name = '', - string $email = '' - ): Larvatar { - return new self($type, $name, $email); - } - - /** - * Generates the HTML or SVG code directly for usage - * @return string HTML or SVG code - */ - public function getImageHTML(bool $base64 = false): string - { - if ($this->type == LarvatarTypes::InitialsAvatar || $this->type == LarvatarTypes::IdenticonLarvatar) { - if (isset($this->font) && $this->font != '' && $this->fontPath != '') { - $this->avatar->setFont($this->font, $this->fontPath); - } - $this->avatar->setSize($this->size); - return $this->avatar->getHTML($base64); - } - - $gravatar = new Gravatar($this->email); - $gravatar->setType($this->type); - $gravatar->setSize($this->size); - - return ''; - } - - /** - * Set the font for Initial Avatar - * @param string $fontFamily Font family of the used font, e.g. 'Roboto' - * @param string $path Relative path to the true type font file, starting with a /, e.g. '/font/Roboto-Bold.ttf' - * @return void - */ - public function setFont(string $fontFamily, string $path): void - { - $this->font = $fontFamily; - $this->fontPath = $path; - } - - /** - * Sets the size of the object. - * - * @param int $size The size of the object. - * @return void - */ - public function setSize(int $size): void - { - $this->size = $size; - } - - /** - * Get the base64 string representation of the initials' avatar. - * - * @return string The base64 encoded string of the initials' avatar. - */ - public function getBase64(): string - { - return $this->avatar->generate(); - } -} \ No newline at end of file +name = Name::make($name); + $this->email = $email; + $this->type = $type; + + if ($this->type == LarvatarTypes::InitialsAvatar) { + $this->avatar = InitialsAvatar::make($this->name); + } elseif ($this->type == LarvatarTypes::IdenticonLarvatar) { + $this->avatar = Identicon::make($this->name); + } + } + + public static function make( + LarvatarTypes $type, + string $name = '', + string $email = '' + ): Larvatar { + return new self($type, $name, $email); + } + + /** + * Generates the HTML or SVG code directly for usage + * @return string HTML or SVG code + */ + public function getImageHTML(bool $base64 = false): string + { + if ($this->type == LarvatarTypes::InitialsAvatar || $this->type == LarvatarTypes::IdenticonLarvatar) { + if (isset($this->font) && $this->font != '' && $this->fontPath != '') { + $this->avatar->setFont($this->font, $this->fontPath); + } + $this->avatar->setSize($this->size); + return $this->avatar->getHTML($base64); + } + + $gravatar = new Gravatar($this->email); + $gravatar->setType($this->type); + $gravatar->setSize($this->size); + + return ''; + } + + /** + * Set the font for Initial Avatar + * @param string $fontFamily Font family of the used font, e.g. 'Roboto' + * @param string $path Relative path to the true type font file, starting with a /, e.g. '/font/Roboto-Bold.ttf' + * @return void + */ + public function setFont(string $fontFamily, string $path): void + { + $this->font = $fontFamily; + $this->fontPath = $path; + } + + /** + * Sets the size of the object. + * + * @param int $size The size of the object. + * @return void + */ + public function setSize(int $size): void + { + $this->size = $size; + } + + /** + * Get the base64 string representation of the initials' avatar. + * + * @return string The base64 encoded string of the initials' avatar. + */ + public function getBase64(): string + { + return $this->avatar->generate(); + } +} diff --git a/src/LarvatarServiceProvider.php b/src/LarvatarServiceProvider.php index 0172313..01f28df 100644 --- a/src/LarvatarServiceProvider.php +++ b/src/LarvatarServiceProvider.php @@ -14,4 +14,4 @@ public function register() { $this->app->make('Renfordt\Larvatar\Larvatar'); } -} \ No newline at end of file +} diff --git a/src/Name.php b/src/Name.php index fd526af..ada31b6 100644 --- a/src/Name.php +++ b/src/Name.php @@ -102,4 +102,4 @@ public function getSplitNames(): array { return $this->splitNames; } -} \ No newline at end of file +} diff --git a/src/Traits/ColorTrait.php b/src/Traits/ColorTrait.php index 8321769..578db12 100644 --- a/src/Traits/ColorTrait.php +++ b/src/Traits/ColorTrait.php @@ -25,4 +25,4 @@ public function getColorSet(Name $name, float $textLightness = 0.35, float $back return [$dark, $light]; } -} \ No newline at end of file +} diff --git a/src/Traits/LarvatarTrait.php b/src/Traits/LarvatarTrait.php index d5ae88d..fed84b2 100644 --- a/src/Traits/LarvatarTrait.php +++ b/src/Traits/LarvatarTrait.php @@ -23,4 +23,4 @@ public function getAvatar(string $name, string $email = '', int $size = 100, Lar $larvatar->setFont('Roboto,sans-serif', '/font/Roboto-Bold.ttf'); return $larvatar->getImageHTML($encoding); } -} \ No newline at end of file +} From 473a155f795c3d526509dde782c43c4c77b30e9a Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 20:55:32 +0100 Subject: [PATCH 30/93] Set default value for expectedData parameter The expectedData parameter in LarvatarTraitTest was updated to have a default empty string value. This change ensures that the test can be executed without explicitly passing this parameter. --- tests/Traits/LarvatarTraitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Traits/LarvatarTraitTest.php b/tests/Traits/LarvatarTraitTest.php index fe9c185..49faa3b 100644 --- a/tests/Traits/LarvatarTraitTest.php +++ b/tests/Traits/LarvatarTraitTest.php @@ -20,7 +20,7 @@ public function testGetAvatar( int $size, LarvatarTypes $type, string $encoding = '', - string $expectedData + string $expectedData = '' ) { $result = $this->getAvatar($name, $email, $size, $type, $encoding); $this->assertSame($expectedData, $result); From e7b0aa2d3cad76077db46acfdf8e4fc49dfdf6d4 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:03:02 +0100 Subject: [PATCH 31/93] Refactor test methods and add new test cases. Updated method signatures in LarvatarTraitTest to use boolean for encoding and removed default parameters. Added new test cases to handle default parameters, different avatar types, and encoding variations to enhance test coverage. --- tests/Traits/LarvatarTraitTest.php | 52 +++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/tests/Traits/LarvatarTraitTest.php b/tests/Traits/LarvatarTraitTest.php index 49faa3b..f2b356a 100644 --- a/tests/Traits/LarvatarTraitTest.php +++ b/tests/Traits/LarvatarTraitTest.php @@ -19,8 +19,8 @@ public function testGetAvatar( string $email, int $size, LarvatarTypes $type, - string $encoding = '', - string $expectedData = '' + bool $encoding, + string $expectedData ) { $result = $this->getAvatar($name, $email, $size, $type, $encoding); $this->assertSame($expectedData, $result); @@ -34,9 +34,53 @@ public function dataProviderForGetAvatarTest(): array 'test@test.com', 100, LarvatarTypes::InitialsAvatar, - 'base64', + true, '' - ] + ], + // additional cases... + ]; + } + + // New test cases + public function testGetAvatarWithDefaultParameters() + { + $result = $this->getAvatar('Default Name'); + $this->assertNotEmpty($result); + } + + /** + * @dataProvider dataProviderForDifferentAvatarTypes + */ + public function testGetAvatarWithDifferentAvatarTypes(LarvatarTypes $type) + { + $result = $this->getAvatar('Name', 'email@example.com', 100, $type, false); + $this->assertNotEmpty($result); + } + + public function dataProviderForDifferentAvatarTypes(): array + { + return [ + [LarvatarTypes::InitialsAvatar], + [LarvatarTypes::Gravatar], + [LarvatarTypes::IdenticonLarvatar], + // add other types if any... + ]; + } + + /** + * @dataProvider dataProviderForEncodingVariations + */ + public function testGetAvatarWithEncodingVariations(bool $encoding) + { + $result = $this->getAvatar('Name', 'email@example.com', 100, LarvatarTypes::InitialsAvatar, $encoding); + $this->assertNotEmpty($result); + } + + public function dataProviderForEncodingVariations(): array + { + return [ + [true], + [false], ]; } } \ No newline at end of file From 97fe5a3278b8cc2b63defe7a2c083aa24638628e Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:12:26 +0100 Subject: [PATCH 32/93] Refactor tests to use Name factory method Simplified test cases by abstracting name creation to the Name::make factory method. This change ensures consistency and reduces redundancy, improving code readability and maintainability. Additionally, updated method signatures and assertions to align with new patterns. --- tests/InitialsAvatarTest.php | 96 +++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index 00aef5f..2ec78c1 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -1,10 +1,12 @@ -setName('Test Name'); $this->assertEquals( @@ -23,7 +26,8 @@ public function testHexGeneration(): void public function testCreateLarvatarByConstructor(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = new InitialsAvatar($name); $this->assertEquals( 'TN', $initialsAvatar->generate() @@ -32,7 +36,8 @@ public function testCreateLarvatarByConstructor(): void public function testCreateLarvatarByMethod(): void { - $initialsAvatar = new InitialsAvatar(); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setName('Test Name'); $this->assertEquals( 'TN', @@ -40,10 +45,22 @@ public function testCreateLarvatarByMethod(): void ); } + public function testCreateLarvatarBySetNameMethod(): void + { + $name1 = Name::make('Different Name'); + $initialsAvatar = InitialsAvatar::make($name1); + $name2 = Name::make('Test Name'); + $initialsAvatar->setName($name2); + $this->assertEquals( + 'TN', + $initialsAvatar->generate() + ); + } + public function testSetFont(): void { - $initialsAvatar = new InitialsAvatar(); - $initialsAvatar->setName('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setFont('Roboto', '/../src/font/Roboto-Bold.ttf'); $this->assertEquals( @@ -54,7 +71,8 @@ public function testSetFont(): void public function testSetSize(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setSize(500); $this->assertEquals( 'TN', @@ -64,23 +82,25 @@ public function testSetSize(): void public function testGenerateWithBase64(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $svg = $initialsAvatar->generate(); - $base64 = $initialsAvatar->generate([], 'base64'); + $base64 = $initialsAvatar->generate(true); $this->assertEquals( - 'data:image/svg+xml;base64,'.base64_encode($svg), + 'data:image/svg+xml;base64,' . base64_encode($svg), $base64 ); } public function testGetSquare(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $reflect = new \ReflectionClass($initialsAvatar); $method = $reflect->getMethod('getSquare'); - $color = new Color(ColorType::Hex, '#000000'); + $color = new HexColor::create('#000000'); $result = $method->invoke($initialsAvatar, 128, $color); @@ -94,12 +114,13 @@ public function testGetSquare(): void public function testGetHexagon(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $reflect = new \ReflectionClass($initialsAvatar); $method = $reflect->getMethod('getHexagon'); $method->setAccessible(true); - $color = new Color(ColorType::Hex, '#000000'); + $color = new HexColor::create('#000000'); $expectedPoints = [ [119.4256258422, 96], @@ -119,7 +140,8 @@ public function testGetHexagon(): void public function testSetRotation(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setRotation(45); $reflector = new \ReflectionObject($initialsAvatar); $property = $reflector->getProperty('rotation'); @@ -129,7 +151,8 @@ public function testSetRotation(): void public function testSetForm(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setForm('circle'); $reflector = new \ReflectionObject($initialsAvatar); @@ -157,12 +180,15 @@ public function testSetFormWithInvalidValue(): void { $this->expectException(\ValueError::class); - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setForm('invalid_form'); } + public function testSetFontWeight(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setFontWeight('bold'); $reflector = new \ReflectionObject($initialsAvatar); $property = $reflector->getProperty('fontWeight'); @@ -172,7 +198,8 @@ public function testSetFontWeight(): void public function testGetBackgroundLightnessDefaultValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $this->assertEquals( 0.8, $initialsAvatar->getBackgroundLightness() @@ -181,7 +208,8 @@ public function testGetBackgroundLightnessDefaultValue(): void public function testGetBackgroundLightnessAfterSettingValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setBackgroundLightness(0.7); $this->assertEquals( 0.7, @@ -191,7 +219,8 @@ public function testGetBackgroundLightnessAfterSettingValue(): void public function testGetBackgroundLightnessAfterSettingExceedingValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setBackgroundLightness(1.1); $this->assertEquals( 1.0, @@ -201,16 +230,19 @@ public function testGetBackgroundLightnessAfterSettingExceedingValue(): void public function testGetBackgroundLightnessAfterSettingTooLowValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setBackgroundLightness(-1.3); $this->assertEquals( 0.0, $initialsAvatar->getBackgroundLightness() ); } + public function testGetTextLightnessDefaultValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $this->assertEquals( 0.35, $initialsAvatar->getTextLightness() @@ -219,7 +251,8 @@ public function testGetTextLightnessDefaultValue(): void public function testGetTextLightnessAfterSettingValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setTextLightness(0.5); $this->assertEquals( 0.5, @@ -229,7 +262,8 @@ public function testGetTextLightnessAfterSettingValue(): void public function testGetTextLightnessAfterSettingExceedingValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setTextLightness(1.1); $this->assertEquals( 1.0, @@ -239,20 +273,23 @@ public function testGetTextLightnessAfterSettingExceedingValue(): void public function testGetTextLightnessAfterSettingTooLowValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setTextLightness(-1.3); $this->assertEquals( 0.0, $initialsAvatar->getTextLightness() ); } + /** * Tests if the set offset returns correct value * @return void */ public function testGetOffsetIsSetCorrectly(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setOffset(10); $this->assertSame(10, $initialsAvatar->getOffset()); } @@ -263,7 +300,8 @@ public function testGetOffsetIsSetCorrectly(): void */ public function testGetOffsetReturnsDefaultValue(): void { - $initialsAvatar = new InitialsAvatar('Test Name'); + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $this->assertSame(0, $initialsAvatar->getOffset()); } } From 97f8f9825403f7bc701a933157109506affa07f5 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:14:14 +0100 Subject: [PATCH 33/93] Support string input in InitialsAvatar::make Enhanced the make method to accept both Name objects and string inputs. If a string is provided, it is converted to a Name object internally. This provides greater flexibility for users when creating InitialsAvatar instances. --- src/InitialsAvatar.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index eb87135..2ef6569 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -33,8 +33,12 @@ public function __construct(Name $name) } - public static function make(Name $name): InitialsAvatar + public static function make(Name|string $name): InitialsAvatar { + if(is_string($name)) { + $name = Name::make($name); + } + return new self($name); } From db7e60368da6aff5e719f71019bf0c2ecc8b6c93 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:15:23 +0100 Subject: [PATCH 34/93] Add tests for InitialsAvatar with string and fix HexColor instantiation Added a new test to create InitialsAvatar using a string directly and validated SVG output. Corrected HexColor instantiation in existing tests to avoid redundant 'new' keyword. --- tests/InitialsAvatarTest.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index 2ec78c1..d8c6f1d 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -38,7 +38,15 @@ public function testCreateLarvatarByMethod(): void { $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); - $initialsAvatar->setName('Test Name'); + $this->assertEquals( + 'TN', + $initialsAvatar->generate() + ); + } + + public function testCreateLarvatarByMethodWithString(): void + { + $initialsAvatar = InitialsAvatar::make('Test Name'); $this->assertEquals( 'TN', $initialsAvatar->generate() @@ -100,7 +108,7 @@ public function testGetSquare(): void $reflect = new \ReflectionClass($initialsAvatar); $method = $reflect->getMethod('getSquare'); - $color = new HexColor::create('#000000'); + $color = HexColor::create('#000000'); $result = $method->invoke($initialsAvatar, 128, $color); @@ -120,7 +128,7 @@ public function testGetHexagon(): void $method = $reflect->getMethod('getHexagon'); $method->setAccessible(true); - $color = new HexColor::create('#000000'); + $color = HexColor::create('#000000'); $expectedPoints = [ [119.4256258422, 96], From 9d7c639c07189fb6122f90b09b2a388f2791821c Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:19:38 +0100 Subject: [PATCH 35/93] Support string input for setName method Extended the setName method to accept both Name objects and strings. If a string is provided, it is converted to a Name object using Name::make. This enhancement improves flexibility in the method's usage. --- src/Avatar.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avatar.php b/src/Avatar.php index ad6273a..d9bc0b7 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -110,8 +110,11 @@ public function getName(): Name * * @return void */ - public function setName(Name $name): void + public function setName(Name|string $name): void { + if (is_string($name)) { + $name = Name::make($name); + } $this->name = $name; } From cf853a90104fbd34e48f36da8a70f918e0e58ae7 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:21:05 +0100 Subject: [PATCH 36/93] Standardize docblock param formatting Aligned the formatting of docblock parameter descriptions by removing extra spaces between param types and parameter names. This ensures consistency and improves readability across the codebase. --- src/Avatar.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Avatar.php b/src/Avatar.php index d9bc0b7..e51cdc7 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -17,7 +17,7 @@ abstract class Avatar /** * Retrieves the HTML representation of the data. * - * @param bool $base64 Whether to return the HTML as base64 encoded string. + * @param bool $base64 Whether to return the HTML as base64 encoded string. * @return string The HTML representation of the data. * @throws \Exception If the HTML generation fails. */ @@ -43,7 +43,7 @@ public function getFontSize(): int /** * Set the font size. * - * @param int $fontSize The font size. + * @param int $fontSize The font size. * @return void */ public function setFontSize(int $fontSize): void @@ -64,7 +64,7 @@ public function getFontFamily(): string /** * Set the font family for the application * - * @param string $fontFamily The font family to set + * @param string $fontFamily The font family to set * @return void */ public function setFontFamily(string $fontFamily): void @@ -85,7 +85,7 @@ public function getFontPath(): string /** * Set the font path * - * @param string $fontPath The path to the font + * @param string $fontPath The path to the font * @return void */ public function setFontPath(string $fontPath): void @@ -106,7 +106,7 @@ public function getName(): Name /** * Set the Name object for the given instance * - * @param Name $name The Name object + * @param Name $name The Name object * * @return void */ @@ -131,7 +131,7 @@ public function getBackgroundLightness(): float /** * Set the background lightness * - * @param float $backgroundLightness The background lightness value to set (between 0 and 1) + * @param float $backgroundLightness The background lightness value to set (between 0 and 1) * @return void */ public function setBackgroundLightness(float $backgroundLightness): void @@ -152,7 +152,7 @@ public function getTextLightness(): float /** * Set the text lightness value * - * @param float $textLightness The text lightness value to be set + * @param float $textLightness The text lightness value to be set * @return void */ public function setTextLightness(float $textLightness): void @@ -173,7 +173,7 @@ public function getFontWeight(): string /** * Set the font weight for the application * - * @param string $fontWeight The font weight to set + * @param string $fontWeight The font weight to set * @return void */ public function setFontWeight(string $fontWeight): void @@ -194,7 +194,7 @@ public function getSize(): int /** * Set the size of the object * - * @param int $size The size to set for the object + * @param int $size The size to set for the object * @return void */ public function setSize(int $size): void @@ -205,8 +205,8 @@ public function setSize(int $size): void /** * Sets the font family and path * - * @param string $font The font family - * @param string $path The font path + * @param string $font The font family + * @param string $path The font path * * @return void */ From aafcbef314686c11f31dbd3f91924fcb6a6b54b5 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:22:52 +0100 Subject: [PATCH 37/93] Set default values for fontFamily and fontPath Initialized fontFamily and fontPath with empty strings to ensure they have default values. This change prevents potential null reference errors and improves code robustness. --- src/Avatar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avatar.php b/src/Avatar.php index e51cdc7..200ff93 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -6,8 +6,8 @@ abstract class Avatar { protected int $fontSize = 0; - protected string $fontFamily; - protected string $fontPath; + protected string $fontFamily = ''; + protected string $fontPath = ''; protected string $fontWeight = 'regular'; protected Name $name; protected int $size = 100; From 6e9585ec2112e9272806693c41521ca714ea0167 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:28:14 +0100 Subject: [PATCH 38/93] Refactor InitialsAvatar and HexColor usage in tests Updated `InitialsAvatarTest` to use the `make` method for `InitialsAvatar` instantiation. Modified `HexColor::create` calls to convert to HSL format to ensure consistency with the color processing methods. --- tests/InitialsAvatarTest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index d8c6f1d..44cf1cd 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -15,8 +15,7 @@ final class InitialsAvatarTest extends TestCase public function testHexGeneration(): void { $name = Name::make('Test Name'); - $initialsAvatar = new InitialsAvatar($name); - $initialsAvatar->setName('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); $this->assertEquals( '#9c3564', @@ -108,7 +107,7 @@ public function testGetSquare(): void $reflect = new \ReflectionClass($initialsAvatar); $method = $reflect->getMethod('getSquare'); - $color = HexColor::create('#000000'); + $color = HexColor::create('#000000')->toHSL(); $result = $method->invoke($initialsAvatar, 128, $color); @@ -128,7 +127,7 @@ public function testGetHexagon(): void $method = $reflect->getMethod('getHexagon'); $method->setAccessible(true); - $color = HexColor::create('#000000'); + $color = HexColor::create('#000000')->toHSL(); $expectedPoints = [ [119.4256258422, 96], From f986283211b44ec1aeaece8e7088b391d011b7a3 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 21:30:14 +0100 Subject: [PATCH 39/93] Remove obsolete test for hex generation The testHexGeneration method was removed from InitialsAvatarTest. This test is no longer relevant to the current functionality of the system. --- tests/InitialsAvatarTest.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index 44cf1cd..12119db 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -1,4 +1,5 @@ assertEquals( - '#9c3564', - $initialsAvatar->generateHexColor() - ); - } - public function testCreateLarvatarByConstructor(): void { $name = Name::make('Test Name'); From 293028349719f7b8de7f28c4ae64251cf50ff46b Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 22:03:25 +0100 Subject: [PATCH 40/93] Refactor Larvatar instantiation order in tests Updated the constructor argument order for Larvatar instances in all test cases, ensuring consistency with changes in the Larvatar class. This improves maintainability and readability of the test code. --- tests/LarvatarTest.php | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/LarvatarTest.php b/tests/LarvatarTest.php index 2bf7db8..aa1a015 100644 --- a/tests/LarvatarTest.php +++ b/tests/LarvatarTest.php @@ -8,7 +8,7 @@ class LarvatarTest extends TestCase { public function testCreateLarvatar(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::InitialsAvatar); + $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $this->assertEquals( 'TN', $larvatar->getImageHTML() @@ -17,7 +17,7 @@ public function testCreateLarvatar(): void public function testCreateLarvatarWithInt(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', 0); + $larvatar = new Larvatar(0, 'Test Name', 'test@example.com'); $this->assertEquals( 'TN', $larvatar->getImageHTML() @@ -32,12 +32,12 @@ public function testCreateLarvatarException(): void $this->expectExceptionMessage('is not a valid backing value'); - $larvatar = new Larvatar('Test Name', 'test@example.com', 700); + $larvatar = new Larvatar(700, 'Test Name', 'test@example.com'); } public function testSetFont(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::InitialsAvatar); + $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $larvatar->setFont('Roboto', '/../src/font/Roboto-Bold.ttf'); $this->assertEquals( 'TN', @@ -47,7 +47,7 @@ public function testSetFont(): void public function testCreateGravatar(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::Gravatar); + $larvatar = new Larvatar(LarvatarTypes::Gravatar, 'Test Name', 'test@example.com'); $this->assertEquals( '', $larvatar->getImageHTML() @@ -56,7 +56,7 @@ public function testCreateGravatar(): void public function testCreateMp(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::mp); + $larvatar = new Larvatar(LarvatarTypes::mp, 'Test Name', 'test@example.com'); $this->assertEquals( '', $larvatar->getImageHTML() @@ -65,7 +65,7 @@ public function testCreateMp(): void public function testCreateIdenticon(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::identicon); + $larvatar = new Larvatar(LarvatarTypes::identicon, 'Test Name', 'test@example.com'); $this->assertEquals( '', $larvatar->getImageHTML() @@ -74,7 +74,7 @@ public function testCreateIdenticon(): void public function testCreateMonsterid(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::monsterid); + $larvatar = new Larvatar(LarvatarTypes::monsterid, 'Test Name', 'test@example.com'); $this->assertEquals( '', $larvatar->getImageHTML() @@ -83,7 +83,7 @@ public function testCreateMonsterid(): void public function testCreateWavatar(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::wavatar); + $larvatar = new Larvatar(LarvatarTypes::wavatar, 'Test Name', 'test@example.com'); $this->assertEquals( '', $larvatar->getImageHTML() @@ -92,7 +92,7 @@ public function testCreateWavatar(): void public function testCreateRetro(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::retro); + $larvatar = new Larvatar(LarvatarTypes::retro, 'Test Name', 'test@example.com'); $this->assertEquals( '', $larvatar->getImageHTML() @@ -101,7 +101,7 @@ public function testCreateRetro(): void public function testCreateRobohash(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::robohash); + $larvatar = new Larvatar(LarvatarTypes::robohash, 'Test Name', 'test@example.com'); $this->assertEquals( '', $larvatar->getImageHTML() @@ -115,7 +115,7 @@ public function testCreateRobohash(): void */ public function testSetSize(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::mp); + $larvatar = new Larvatar(LarvatarTypes::mp, 'Test Name', 'test@example.com'); $larvatar->setSize(50); $this->assertEquals( '', @@ -130,7 +130,7 @@ public function testSetSize(): void */ public function testSetSizeWithLargeValue(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::mp); + $larvatar = new Larvatar(LarvatarTypes::mp, 'Test Name', 'test@example.com'); $larvatar->setSize(1000); $this->assertEquals( '', @@ -145,13 +145,14 @@ public function testSetSizeWithLargeValue(): void */ public function testSetSizeWithSmallValue(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::mp); + $larvatar = new Larvatar(LarvatarTypes::mp, 'Test Name', 'test@example.com'); $larvatar->setSize(1); $this->assertEquals( '', $larvatar->getImageHTML() ); } + /** * testGetBase64 method * @@ -159,7 +160,7 @@ public function testSetSizeWithSmallValue(): void */ public function testGetBase64(): void { - $larvatar = new Larvatar('Test Name', 'test@example.com', LarvatarTypes::InitialsAvatar); + $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $this->assertMatchesRegularExpression( '/^data:image\/svg\+xml;base64,[A-Za-z0-9+\/]+=*$/', $larvatar->getBase64() @@ -175,7 +176,7 @@ public function testGetBase64WithRandomName(): void { $faker = Faker\Factory::create(); $randomName = $faker->name; - $larvatar = new Larvatar($randomName, 'test@example.com', LarvatarTypes::InitialsAvatar); + $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, $randomName, 'test@example.com'); $this->assertMatchesRegularExpression( '/^data:image\/svg\+xml;base64,[A-Za-z0-9+\/]+=*$/', $larvatar->getBase64() From 27c4906bb4abeed097f9d0ca423695d56fff74d5 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Thu, 21 Nov 2024 22:06:31 +0100 Subject: [PATCH 41/93] Remove testCreateLarvatarWithInt method The testCreateLarvatarWithInt method was removed due to redundancy. The functionality tested by this method is covered by other existing tests, ensuring the same level of code correctness and coverage. --- tests/LarvatarTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/LarvatarTest.php b/tests/LarvatarTest.php index aa1a015..35f5fde 100644 --- a/tests/LarvatarTest.php +++ b/tests/LarvatarTest.php @@ -15,15 +15,6 @@ public function testCreateLarvatar(): void ); } - public function testCreateLarvatarWithInt(): void - { - $larvatar = new Larvatar(0, 'Test Name', 'test@example.com'); - $this->assertEquals( - 'TN', - $larvatar->getImageHTML() - ); - } - public function testCreateLarvatarException(): void { set_error_handler(static function (int $errno, string $errstr): never { From 78f406ba4dba8eeb34373a64b963391eb4735179 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 15:52:23 +0100 Subject: [PATCH 42/93] Set default fontFamily to 'sans serif' in Avatar.php Updated the $fontFamily property to use 'sans serif' as the default value. This ensures that the Avatar component has a fallback font family for consistency across different environments. --- src/Avatar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avatar.php b/src/Avatar.php index 200ff93..c8b8128 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -6,7 +6,7 @@ abstract class Avatar { protected int $fontSize = 0; - protected string $fontFamily = ''; + protected string $fontFamily = 'sans serif'; protected string $fontPath = ''; protected string $fontWeight = 'regular'; protected Name $name; From 1f0654408f88e66ea6855004e1f8b090b3a6eb31 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 15:52:42 +0100 Subject: [PATCH 43/93] Remove unused ColorType import from InitialsAvatar This commit eliminates an unnecessary import statement for ColorType in `InitialsAvatar.php`. Removing unused imports helps maintain cleaner and more efficient code. --- src/InitialsAvatar.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index 2ef6569..622f0a1 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -3,7 +3,6 @@ namespace Renfordt\Larvatar; use Renfordt\Colors\HSLColor; -use Renfordt\Larvatar\Enum\ColorType; use Renfordt\Larvatar\Enum\FormTypes; use Renfordt\Larvatar\Traits\ColorTrait; use SVG\Nodes\Shapes\SVGCircle; From da84213c3314783aa70f8829813b782286490640 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 15:53:11 +0100 Subject: [PATCH 44/93] Update SVG dimensions and circle color in InitialsAvatarTest Reduced the SVG dimensions from 128x128 to 100x100 and adjusted the circle's fill color from #e5b3ca to #e5b3c9 in the InitialsAvatarTest. This change ensures consistency with design specifications and updated requirements. --- tests/InitialsAvatarTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index 12119db..3df70ca 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -18,7 +18,7 @@ public function testCreateLarvatarByConstructor(): void $name = Name::make('Test Name'); $initialsAvatar = new InitialsAvatar($name); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } From 219d1664f07cacb166303cdd7ac5ff311f0a9e24 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 16:00:17 +0100 Subject: [PATCH 45/93] Update .gitignore to exclude additional cache files Added .php-cs-fixer.cache and .phpunit.result.cache to .gitignore to prevent committing these generated cache files. This will help keep the repository clean and reduce unnecessary data in commits. --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 02345e7..d48b197 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /vendor /src/index.php /.idea -composer.lock \ No newline at end of file +composer.lock +.php-cs-fixer.cache +.phpunit.result.cache \ No newline at end of file From 33f59af44060edd7331d61db06b1071eff3d71a6 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 16:04:06 +0100 Subject: [PATCH 46/93] Remove version from docker-compose.yml The version declaration for docker-compose has been removed. This change aims to use the default Compose file version, ensuring compatibility and reducing potential issues with specific Docker Compose versions. --- docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index b036ab9..8d065b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.7" services: php: build: From 6be3088aa1ac1f9bafe4d54ab93fe80a1376de81 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 16:12:06 +0100 Subject: [PATCH 47/93] Adjust fontWeight to 'normal' in Avatar.php Updated the fontWeight property from 'regular' to 'normal' for consistency with standard CSS terminology. This change ensures better compatibility and readability when the property is used in CSS contexts. --- src/Avatar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avatar.php b/src/Avatar.php index c8b8128..094cd17 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -8,7 +8,7 @@ abstract class Avatar protected string $fontFamily = 'sans serif'; protected string $fontPath = ''; - protected string $fontWeight = 'regular'; + protected string $fontWeight = 'normal'; protected Name $name; protected int $size = 100; protected float $backgroundLightness = 0.8; From 7fa83cd2ca81cf9ec03e5ea450ff6c99fe5901b2 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 16:12:16 +0100 Subject: [PATCH 48/93] Refactor SVG generation and documentation updates Refactor the SVG generation method to simplify and remove base64 encoding option. Enhance documentation for more clarity on parameters and return types, ensuring consistency in method descriptions and adding condition checks for optional elements. --- src/InitialsAvatar.php | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index 622f0a1..069fafe 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -34,7 +34,7 @@ public function __construct(Name $name) public static function make(Name|string $name): InitialsAvatar { - if(is_string($name)) { + if (is_string($name)) { $name = Name::make($name); } @@ -105,14 +105,11 @@ public function getHTML(bool $base64 = false): string } /** - * Generates an avatar based on the given names and encoding + * Generates an SVG representation with initials and shape based on the provided configurations. * - * @param array $names An array of names to generate initials from - * @param string|null $encoding The encoding type for the output ('base64' or null) - * - * @return string The generated avatar in SVG format or the base64-encoded avatar image + * @return string The generated SVG content as a string. */ - public function generate(bool $base64 = false): string + public function generate(): string { $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); @@ -140,7 +137,9 @@ public function generate(bool $base64 = false): string $initials = $this->getInitials($darkColor); - $doc->addChild($outlineForm); + if (isset($outlineForm)) { + $doc->addChild($outlineForm); + } $doc->addChild($initials); return $larvatar; @@ -211,10 +210,10 @@ private function getHexagon(float $size, HSLColor $lightColor, int $rotation = 0 } /** - * Generates initials for the given names and returns SVGText object - * @param array $names List of names - * @param HSLColor $darkColor Dark color object - * @return SVGText SVGText object containing the initials + * Generates an SVG text element with the initials and formats it with the given dark color and font settings. + * + * @param HSLColor $darkColor The dark color used to fill the text, provided in HSL format and converted to hex. + * @return SVGText The SVG text element containing the formatted initials. */ private function getInitials(HSLColor $darkColor): SVGText { From d26927279215f2fef3e745cef44b384c24a0f03a Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 16:17:04 +0100 Subject: [PATCH 49/93] Update method parameters and return types Modified the setName method to accept a Name object or a string and updated setFont method to explicitly declare a void return type. These changes improve the flexibility and clarity of the code interface. --- src/Avatar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avatar.php b/src/Avatar.php index 094cd17..e67067b 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -106,7 +106,7 @@ public function getName(): Name /** * Set the Name object for the given instance * - * @param Name $name The Name object + * @param Name|string $name The Name object or a string of the name * * @return void */ @@ -210,7 +210,7 @@ public function setSize(int $size): void * * @return void */ - public function setFont(string $font, string $path) + public function setFont(string $font, string $path): void { $this->setFontFamily($font); $this->setFontPath($path); From 2ffcb2cacb8c86868f1e1c85c4e359f19966f772 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 16:24:40 +0100 Subject: [PATCH 50/93] Add missing `Exception` namespace import Imported the `Exception` class to preserve consistency in exception handling and updated the docblock to use the simpler `Exception` alias. This ensures the proper namespace resolution for exceptions thrown in the `getHTML` method. --- src/Avatar.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avatar.php b/src/Avatar.php index e67067b..b7e78f0 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -2,6 +2,8 @@ namespace Renfordt\Larvatar; +use Exception; + abstract class Avatar { protected int $fontSize = 0; @@ -19,7 +21,7 @@ abstract class Avatar * * @param bool $base64 Whether to return the HTML as base64 encoded string. * @return string The HTML representation of the data. - * @throws \Exception If the HTML generation fails. + * @throws Exception If the HTML generation fails. */ abstract public function getHTML(bool $base64 = false): string; From 4dfeacfb0ba2e4fff76e169ff4e32fdb042faf89 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 16:26:13 +0100 Subject: [PATCH 51/93] Update tests for InitialsAvatar and remove unused imports Adjusted SVG code in tests to include font-family and changed SVG dimensions. Removed unused ColorType import and switched to unqualified class names for Reflection classes. Renamed generate method to getBase64 for clarity. --- tests/InitialsAvatarTest.php | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index 3df70ca..511588f 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -4,7 +4,6 @@ use PHPUnit\Framework\TestCase; use Renfordt\Colors\HexColor; -use Renfordt\Larvatar\Enum\ColorType; use Renfordt\Larvatar\Enum\FormTypes; use Renfordt\Larvatar\InitialsAvatar; use Renfordt\Larvatar\Name; @@ -18,7 +17,7 @@ public function testCreateLarvatarByConstructor(): void $name = Name::make('Test Name'); $initialsAvatar = new InitialsAvatar($name); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -28,7 +27,7 @@ public function testCreateLarvatarByMethod(): void $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -37,7 +36,7 @@ public function testCreateLarvatarByMethodWithString(): void { $initialsAvatar = InitialsAvatar::make('Test Name'); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -49,7 +48,7 @@ public function testCreateLarvatarBySetNameMethod(): void $name2 = Name::make('Test Name'); $initialsAvatar->setName($name2); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -61,7 +60,7 @@ public function testSetFont(): void $initialsAvatar->setFont('Roboto', '/../src/font/Roboto-Bold.ttf'); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -82,7 +81,7 @@ public function testGenerateWithBase64(): void $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); $svg = $initialsAvatar->generate(); - $base64 = $initialsAvatar->generate(true); + $base64 = $initialsAvatar->getBase64(); $this->assertEquals( 'data:image/svg+xml;base64,' . base64_encode($svg), @@ -94,7 +93,7 @@ public function testGetSquare(): void { $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); - $reflect = new \ReflectionClass($initialsAvatar); + $reflect = new ReflectionClass($initialsAvatar); $method = $reflect->getMethod('getSquare'); $color = HexColor::create('#000000')->toHSL(); @@ -113,7 +112,7 @@ public function testGetHexagon(): void { $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); - $reflect = new \ReflectionClass($initialsAvatar); + $reflect = new ReflectionClass($initialsAvatar); $method = $reflect->getMethod('getHexagon'); $method->setAccessible(true); @@ -140,7 +139,7 @@ public function testSetRotation(): void $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setRotation(45); - $reflector = new \ReflectionObject($initialsAvatar); + $reflector = new ReflectionObject($initialsAvatar); $property = $reflector->getProperty('rotation'); $property->setAccessible(true); $this->assertEquals(45, $property->getValue($initialsAvatar)); @@ -152,7 +151,7 @@ public function testSetForm(): void $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setForm('circle'); - $reflector = new \ReflectionObject($initialsAvatar); + $reflector = new ReflectionObject($initialsAvatar); $property = $reflector->getProperty('form'); $property->setAccessible(true); $this->assertEquals(FormTypes::Circle, $property->getValue($initialsAvatar)); @@ -175,7 +174,7 @@ public function testSetForm(): void public function testSetFormWithInvalidValue(): void { - $this->expectException(\ValueError::class); + $this->expectException(ValueError::class); $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); @@ -187,7 +186,7 @@ public function testSetFontWeight(): void $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setFontWeight('bold'); - $reflector = new \ReflectionObject($initialsAvatar); + $reflector = new ReflectionObject($initialsAvatar); $property = $reflector->getProperty('fontWeight'); $property->setAccessible(true); $this->assertEquals('bold', $property->getValue($initialsAvatar)); From f58409ebe3e2738414758b6eada4ab104e3817fc Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 17:37:47 +0100 Subject: [PATCH 52/93] Update SVG text fill color in InitialsAvatarTest asserts Change the hex color code from #852e55 to #852d55 in the SVG string assertions within InitialsAvatarTest.php. This ensures that the test cases correctly validate the expected SVG output. --- tests/InitialsAvatarTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index 511588f..aeab020 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -17,7 +17,7 @@ public function testCreateLarvatarByConstructor(): void $name = Name::make('Test Name'); $initialsAvatar = new InitialsAvatar($name); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -27,7 +27,7 @@ public function testCreateLarvatarByMethod(): void $name = Name::make('Test Name'); $initialsAvatar = InitialsAvatar::make($name); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -36,7 +36,7 @@ public function testCreateLarvatarByMethodWithString(): void { $initialsAvatar = InitialsAvatar::make('Test Name'); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -48,7 +48,7 @@ public function testCreateLarvatarBySetNameMethod(): void $name2 = Name::make('Test Name'); $initialsAvatar->setName($name2); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -60,7 +60,7 @@ public function testSetFont(): void $initialsAvatar->setFont('Roboto', '/../src/font/Roboto-Bold.ttf'); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } @@ -71,7 +71,7 @@ public function testSetSize(): void $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setSize(500); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } From 5d36753978fd5cc147cd719130b4f6b84d7b63de Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 17:53:11 +0100 Subject: [PATCH 53/93] Set default fontFamily to an empty string Changed the default value of the `fontFamily` property from 'sans serif' to an empty string in `Avatar.php`. This allows for more flexibility when specifying font families externally. --- src/Avatar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avatar.php b/src/Avatar.php index b7e78f0..5b23735 100644 --- a/src/Avatar.php +++ b/src/Avatar.php @@ -8,7 +8,7 @@ abstract class Avatar { protected int $fontSize = 0; - protected string $fontFamily = 'sans serif'; + protected string $fontFamily = ''; protected string $fontPath = ''; protected string $fontWeight = 'normal'; protected Name $name; From 891bccbad4dd8de93b4e953385d8a10155587a35 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 17:53:20 +0100 Subject: [PATCH 54/93] Set default font family if none is provided Implemented a check for an empty font family and set the default to 'Segoe UI, Helvetica, sans-serif' if it is empty. This ensures that the avatar initials text will always have a defined font, enhancing consistency and appearance. --- src/InitialsAvatar.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index 069fafe..18ae674 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -11,6 +11,8 @@ use SVG\Nodes\Texts\SVGText; use SVG\SVG; +use function PHPUnit\Framework\isEmpty; + class InitialsAvatar extends Avatar { use ColorTrait; @@ -219,13 +221,14 @@ private function getInitials(HSLColor $darkColor): SVGText { $initialsText = $this->name->getInitials(); + $fontFamily = isEmpty($this->fontFamily) ? 'Segoe UI, Helvetica, sans-serif' : $this->fontFamily; + $initials = new SVGText($initialsText, '50%', '55%'); $initials->setStyle('fill', $darkColor->toHex()); $initials->setStyle('text-anchor', 'middle'); $initials->setStyle('dominant-baseline', 'middle'); $initials->setStyle('font-weight', $this->fontWeight); - $initials->setFontFamily("Segoe UI, Helvetica, sans-serif"); - //$initials->setFontFamily($this->fontFamily); + $initials->setFontFamily($fontFamily); if ($this->fontSize == 0) { $this->fontSize = $this->calculateFontSize($initialsText); } From 548316a7484d419310d6fb3c88409378e66cc6fd Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 17:53:30 +0100 Subject: [PATCH 55/93] Refactor docblocks for clearer parameter descriptions Improve documentation by clarifying parameter purposes and fixing formatting issues in the Larvatar class. These changes enhance the readability and maintainability of the code by making function signatures more consistent and informative. --- src/Larvatar.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Larvatar.php b/src/Larvatar.php index 3c2b70a..e705ceb 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -15,11 +15,13 @@ class Larvatar protected int $size = 100; /** - * Constructs a new instance of the class. + * Constructor for creating an instance of a Larvatar. * - * @param string $name The name. Default is an empty string. - * @param string $email The email. Default is an empty string. - * @param int|LarvatarTypes $type The type. Default is LarvatarTypes::mp. + * @param LarvatarTypes $type The type of Larvatar to create. + * @param string $name Optional. The name to be used for generating the avatar. Defaults to an empty string. + * @param string $email Optional. The email to be associated with the avatar. Defaults to an empty string. + * + * @return void */ public function __construct(LarvatarTypes $type, string $name = '', string $email = '') { @@ -60,13 +62,13 @@ public function getImageHTML(bool $base64 = false): string $gravatar->setType($this->type); $gravatar->setSize($this->size); - return ''; + return ''; } /** * Set the font for Initial Avatar - * @param string $fontFamily Font family of the used font, e.g. 'Roboto' - * @param string $path Relative path to the true type font file, starting with a /, e.g. '/font/Roboto-Bold.ttf' + * @param string $fontFamily Font family of the used font, e.g. 'Roboto' + * @param string $path Relative path to the true type font file, starting with a /, e.g. '/font/Roboto-Bold.ttf' * @return void */ public function setFont(string $fontFamily, string $path): void @@ -78,7 +80,7 @@ public function setFont(string $fontFamily, string $path): void /** * Sets the size of the object. * - * @param int $size The size of the object. + * @param int $size The size of the object. * @return void */ public function setSize(int $size): void From 9edd3685b23bd093986340e80f2258acf54553ee Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 20:13:28 +0100 Subject: [PATCH 56/93] Update SVG tests to include font-family and error messages Added 'font-family' attribute to SVG text elements in various test cases to ensure consistent rendering across different environments. Updated exception message to specify type requirement for Larvatar constructor arguments. --- tests/InitialsAvatarTest.php | 2 +- tests/LarvatarTest.php | 4 ++-- tests/Traits/LarvatarTraitTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index aeab020..6ce5989 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -71,7 +71,7 @@ public function testSetSize(): void $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setSize(500); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } diff --git a/tests/LarvatarTest.php b/tests/LarvatarTest.php index 35f5fde..425ead7 100644 --- a/tests/LarvatarTest.php +++ b/tests/LarvatarTest.php @@ -10,7 +10,7 @@ public function testCreateLarvatar(): void { $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $this->assertEquals( - 'TN', + 'TN', $larvatar->getImageHTML() ); } @@ -21,7 +21,7 @@ public function testCreateLarvatarException(): void throw new Exception($errstr, $errno); }, E_USER_WARNING); - $this->expectExceptionMessage('is not a valid backing value'); + $this->expectExceptionMessage('must be of type Renfordt\Larvatar\Enum\LarvatarTypes'); $larvatar = new Larvatar(700, 'Test Name', 'test@example.com'); } diff --git a/tests/Traits/LarvatarTraitTest.php b/tests/Traits/LarvatarTraitTest.php index f2b356a..81d0963 100644 --- a/tests/Traits/LarvatarTraitTest.php +++ b/tests/Traits/LarvatarTraitTest.php @@ -35,7 +35,7 @@ public function dataProviderForGetAvatarTest(): array 100, LarvatarTypes::InitialsAvatar, true, - '' + '' ], // additional cases... ]; From ec7ad82a06838dd9343b05db777d2b0947852c66 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 20:22:33 +0100 Subject: [PATCH 57/93] Fix empty fontFamily check in InitialsAvatar.php. Previously, the function incorrectly used isEmpty(), which caused issues when determining the default font family. Replaced it with empty() to accurately check for an empty value and set the font family accordingly. --- src/InitialsAvatar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index 18ae674..ea88564 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -221,7 +221,7 @@ private function getInitials(HSLColor $darkColor): SVGText { $initialsText = $this->name->getInitials(); - $fontFamily = isEmpty($this->fontFamily) ? 'Segoe UI, Helvetica, sans-serif' : $this->fontFamily; + $fontFamily = empty($this->fontFamily) ? 'Segoe UI, Helvetica, sans-serif' : $this->fontFamily; $initials = new SVGText($initialsText, '50%', '55%'); $initials->setStyle('fill', $darkColor->toHex()); From 681567424cba1411f024b1ddfd9676585c877df2 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 22:02:34 +0100 Subject: [PATCH 58/93] Refactor code style and improve hash generation Adjusted code style by removing extra spaces within parentheses and concatenation for consistency. Improved hash generation in the generateMatrix method by using SHA-256 instead of the original hash. These changes will enhance code readability and ensure better hash unpredictability. --- src/Identicon.php | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Identicon.php b/src/Identicon.php index a720c3f..3109e11 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -27,7 +27,7 @@ public static function make(Name $name): Identicon /** * Sets the number of pixels. * - * @param int $pixels The number of pixels to set. + * @param int $pixels The number of pixels to set. * @return void */ public function setPixels(int $pixels): void @@ -38,7 +38,7 @@ public function setPixels(int $pixels): void /** * Sets the symmetry property of the object. * - * @param bool $symmetry The symmetry value to set. + * @param bool $symmetry The symmetry value to set. * @return void */ public function setSymmetry(bool $symmetry): void @@ -49,7 +49,7 @@ public function setSymmetry(bool $symmetry): void /** * Returns the HTML representation of the image. * - * @param bool $base64 Determines whether the HTML representation should be in base64 format or not. + * @param bool $base64 Determines whether the HTML representation should be in base64 format or not. * @return string The HTML representation of the image. */ public function getHTML(bool $base64 = false): string @@ -58,7 +58,7 @@ public function getHTML(bool $base64 = false): string return $this->getSVG(); } - return ''; + return ''; } /** @@ -91,16 +91,15 @@ public function getSVG(): string foreach ($array as $x => $value) { if ($value) { $square = new SVGRect( - (int) $x * ($this->size / $this->pixels), - (int) $y * ($this->size / $this->pixels), - (int) $this->size / $this->pixels, - (int) $this->size / $this->pixels + (int)$x * ($this->size / $this->pixels), + (int)$y * ($this->size / $this->pixels), + (int)$this->size / $this->pixels, + (int)$this->size / $this->pixels ); $square->setStyle('fill', $darkColor->toHex()); $doc->addChild($square); } } - } return $larvatar; @@ -118,7 +117,7 @@ public function generateSymmetricMatrix(): array $divider = count($symmetryMatrix); for ($i = 0; $i < pow($this->pixels, 2); $i++) { - $index = (int) ($i / 3); + $index = (int)($i / 3); $data = $this->convertStrToBool(substr($this->name->getHash(), $i, 1)); foreach ($symmetryMatrix[$i % $divider] as $item) { @@ -150,27 +149,29 @@ private function getSymmetryMatrix(): array /** * Converts a hexadecimal character to a boolean value. * - * @param string $char The hexadecimal character to convert. + * @param string $char The hexadecimal character to convert. * @return bool The boolean value converted from the hexadecimal character. */ private function convertStrToBool(string $char): bool { - return (bool) round(hexdec($char) / 10); + return (bool)round(hexdec($char) / 10); } /** * Generates a matrix based on the given offset value. * - * @param int $offset The offset value for generating the matrix. Defaults to 0. + * @param int $offset The offset value for generating the matrix. Defaults to 0. * @return array The generated matrix. */ public function generateMatrix(int $offset = 0): array { $column = 0; $row = 0; + $hash = hash('sha256', $this->name->getHash()); + dump($hash); for ($i = 0; $i < pow($this->pixels, 2); $i++) { $matrix[$i % $this->pixels][floor($i / $this->pixels)] = - $this->convertStrToBool(substr($this->name->getHash(), $i, 1)); + $this->convertStrToBool(substr($hash, $i, 1)); if ($column == $this->pixels && $row < $this->pixels) { $row++; $column = -1; @@ -190,6 +191,6 @@ public function generateMatrix(int $offset = 0): array */ public function getBase64(): string { - return 'data:image/svg+xml;base64,'.base64_encode($this->getSVG()); + return 'data:image/svg+xml;base64,' . base64_encode($this->getSVG()); } } From 651a237a01a2b84e9340406a0dd7ad56066decb6 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 22:05:28 +0100 Subject: [PATCH 59/93] Change visibility of generateMatrix method to private This modification ensures that the generateMatrix method is not exposed outside the Identicon class, reducing potential misuse and improving encapsulation. The function's access level is now restricted to internal class operations, enhancing overall code security. --- src/Identicon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Identicon.php b/src/Identicon.php index 3109e11..f850cc4 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -163,7 +163,7 @@ private function convertStrToBool(string $char): bool * @param int $offset The offset value for generating the matrix. Defaults to 0. * @return array The generated matrix. */ - public function generateMatrix(int $offset = 0): array + private function generateMatrix(int $offset = 0): array { $column = 0; $row = 0; From c3fc06cdcbd25b55fd741bedb85aded47f2262b4 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 22:05:44 +0100 Subject: [PATCH 60/93] Support Name object in Larvatar constructor Updated Larvatar constructor to accept either a string or a Name object for the name parameter. This flexibility allows more versatile handling of name values and simplifies object creation when a Name instance is already available. --- src/Larvatar.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Larvatar.php b/src/Larvatar.php index e705ceb..1f110b8 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -18,14 +18,19 @@ class Larvatar * Constructor for creating an instance of a Larvatar. * * @param LarvatarTypes $type The type of Larvatar to create. - * @param string $name Optional. The name to be used for generating the avatar. Defaults to an empty string. + * @param string|Name $name Optional. The name to be used for generating the avatar. Defaults to an empty string. * @param string $email Optional. The email to be associated with the avatar. Defaults to an empty string. * * @return void */ - public function __construct(LarvatarTypes $type, string $name = '', string $email = '') + public function __construct(LarvatarTypes $type, string|Name $name = '', string $email = '') { - $this->name = Name::make($name); + if (is_string($name)) { + $this->name = Name::make($name); + } else { + $this->name = $name; + } + $this->email = $email; $this->type = $type; @@ -38,7 +43,7 @@ public function __construct(LarvatarTypes $type, string $name = '', string $emai public static function make( LarvatarTypes $type, - string $name = '', + string|Name $name = '', string $email = '' ): Larvatar { return new self($type, $name, $email); From 023e213232dd5288ba99c02512c9fdf49f49cbd2 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 22:12:33 +0100 Subject: [PATCH 61/93] Add instructions for upgrading to version 2.0 Introduced a new section in the README for upgrading to version 2.0, along with updates to the usage examples and additional avatar types. Also, included new methods for configuring avatars, ensuring users are aware of breaking changes and how to migrate smoothly. --- README.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 8a1faed..43ca980 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ install it to you project: composer require renfordt/larvatar ``` +## Upgrading to 2.0 + +Version 2.0 brings many breaking changes. Check the [Upgrade Guide](UPGRADING.md) to avoid any issues. + + ## Usage The general usage is simple. Create a new Larvatar class, insert name and email and the avatar type you wish. @@ -30,8 +35,17 @@ The general usage is simple. Create a new Larvatar class, insert name and email use Renfordt\Larvatar\Enum\LarvatarTypes; use Renfordt\Larvatar\Larvatar; -$larvatar = new Larvatar('Test Name', 'test@test.com', LarvatarTypes::InitialsAvatar); +// Larvatar::make($type, $name = '', $email = '') +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: 'Test Name' + ); + +// Optional settings $larvatar->setFont('Roboto,sans-serif', './font/Roboto-bold.ttf'); +$larvatar->setSize(100); + +// Get the SVG Code for embedding directly in your page echo $larvatar->getImageHTML(); // if you need base64 encryption, currently this works only for InitialsAvatar @@ -40,17 +54,36 @@ echo $larvatar->getImageHTML('base64'); echo $larvatar->getBase64(); ``` -There are currently eight different types of avatars available: +```php +$name = \Renfordt\Larvatar\Name::make('Test Name'); + +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: $name + ); +``` + +There are currently nine different types of avatars available: ```php -\Renfordt\Larvatar\Enum\LarvatarTypes::InitialsAvatar; // Microsoft Teams like avatar with initials -\Renfordt\Larvatar\Enum\LarvatarTypes::Gravatar; // Gravatar -\Renfordt\Larvatar\Enum\LarvatarTypes::mp; // (Gravatar) MysticPerson, simple cartoon-style silhouette (default) -\Renfordt\Larvatar\Enum\LarvatarTypes::identicon; // (Gravatar) A geometric pattern based on a email hash -\Renfordt\Larvatar\Enum\LarvatarTypes::monsterid; // (Gravatar) A generated monster different colors and faces -\Renfordt\Larvatar\Enum\LarvatarTypes::wavatar; // (Gravatar) generated faces with differing features and backgrounds -\Renfordt\Larvatar\Enum\LarvatarTypes::retro; // (Gravatar) 8-bit arcade-style pixelated faces -\Renfordt\Larvatar\Enum\LarvatarTypes::robohash; // (Gravatar) A generated robot with different colors, faces, etc +// Microsoft Teams like avatar with initials +\Renfordt\Larvatar\Enum\LarvatarTypes::InitialsAvatar; +// Identicons with more possibilities to adjust +\Renfordt\Larvatar\Enum\LarvatarTypes::IdenticonLarvatar; +// Gravatar +\Renfordt\Larvatar\Enum\LarvatarTypes::Gravatar; +// (Gravatar) MysticPerson, simple cartoon-style silhouette (default) +\Renfordt\Larvatar\Enum\LarvatarTypes::mp; +// (Gravatar) A geometric pattern based on a email hash +\Renfordt\Larvatar\Enum\LarvatarTypes::identicon; +// (Gravatar) A generated monster different colors and faces +\Renfordt\Larvatar\Enum\LarvatarTypes::monsterid; +// (Gravatar) generated faces with differing features and backgrounds +\Renfordt\Larvatar\Enum\LarvatarTypes::wavatar; +// (Gravatar) 8-bit arcade-style pixelated faces +\Renfordt\Larvatar\Enum\LarvatarTypes::retro; +// (Gravatar) A generated robot with different colors, faces, etc +\Renfordt\Larvatar\Enum\LarvatarTypes::robohash; ``` ## InitialsAvatar @@ -62,7 +95,10 @@ hexagon and a square. Choose it by using the `setForm()` method. The input is ei Enum `FormTypes`. ```PHP -$larvatar = new Larvatar('Your Name', type: LarvatarTypes::InitialsAvatar); +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: 'Test Name' + ); $larvatar->initialsAvatar->setForm('circle'); $larvatar->initialsAvatar->setForm('square'); $larvatar->initialsAvatar->setForm('hexagon'); @@ -75,7 +111,10 @@ $larvatar->initialsAvatar->setForm(FormTypes::Hexagon); If you are using the hexagon form, you have additionally the possibility to rotate the form: ```PHP -$larvatar = new Larvatar('Your Name', type: LarvatarTypes::InitialsAvatar); +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: 'Test Name' + ); $larvatar->initialsAvatar->setForm(FormTypes::Hexagon); $larvatar->initialsAvatar->setRotation(30); ``` @@ -88,7 +127,10 @@ and `setTextLightness()`. The parameter is a float with a value range `0` to `1` is a lighter color. ```PHP -$larvatar = new Larvatar('Your Name', type: LarvatarTypes::InitialsAvatar); +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: 'Test Name' + ); $larvatar->initialsAvatar->setBackgroundLightness(0.1); $larvatar->initialsAvatar->setTextLightness(0.8); ``` @@ -96,7 +138,10 @@ $larvatar->initialsAvatar->setTextLightness(0.8); Additionally, you can change the offset which will generate a different color. ```PHP -$larvatar = new Larvatar('Your Name', type: LarvatarTypes::InitialsAvatar); +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: 'Test Name' + ); $larvatar->initialsAvatar->setOffset(4); ``` @@ -105,6 +150,22 @@ $larvatar->initialsAvatar->setOffset(4); You can also change the font weight with the method `setFontWeight()`. ```PHP -$larvatar = new Larvatar('Your Name', type: LarvatarTypes::InitialsAvatar); +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: 'Test Name' + ); $larvatar->initialsAvatar->setFontWeight('bold'); +``` + +## Identicons (Larvatar Style) + +```PHP +$larvatar = Larvatar::make( + type: LarvatarTypes::IdenticonLarvatar, + name: 'Test Name' + ); + +// optional settings +$larvatar->avatar->setSymmetry(false); +$larvatar->avatar->setPixels(8); ``` \ No newline at end of file From 635201d70032f7410c6a758c717c52c9758ffb98 Mon Sep 17 00:00:00 2001 From: Jannik Renfordt Date: Fri, 22 Nov 2024 22:16:44 +0100 Subject: [PATCH 62/93] Add UPGRADING.md with new Larvatar instantiation Created a new UPGRADING.md file to document changes in Larvatar instantiation. The new format uses a static `make` method while the old format used a constructor. This change improves code readability and simplifies object creation. --- UPGRADING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 UPGRADING.md diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000..3a4e4dc --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,13 @@ +New: +```php +$larvatar = Larvatar::make( + type: LarvatarTypes::InitialsAvatar, + name: 'Test Name' + ); +``` + + +Old: +```php +$larvatar = new Larvatar('Test Name', 'test@test.com', LarvatarTypes::InitialsAvatar); +``` \ No newline at end of file From e757a8921b13c605050a4e9437200805e706bc26 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 19:18:14 +0100 Subject: [PATCH 63/93] Remove debug statement from Identicon.php The debug function 'dump($hash)' was removed from the file. This cleanup improves performance and ensures no residual debug statements impact the production environment. --- src/Identicon.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Identicon.php b/src/Identicon.php index f850cc4..6459385 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -168,7 +168,6 @@ private function generateMatrix(int $offset = 0): array $column = 0; $row = 0; $hash = hash('sha256', $this->name->getHash()); - dump($hash); for ($i = 0; $i < pow($this->pixels, 2); $i++) { $matrix[$i % $this->pixels][floor($i / $this->pixels)] = $this->convertStrToBool(substr($hash, $i, 1)); From bf989e4987c2d6e64cd55ca20daa10846a7bbac4 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 19:24:41 +0100 Subject: [PATCH 64/93] Update composer.json minimum stability to stable Changed the minimum-stability setting from 'dev' to 'stable' in composer.json. This ensures that only stable packages are preferred during dependency resolution, increasing the reliability of production deployments. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f481564..bd4db9b 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "Renfordt\\Larvatar\\": "src/" } }, - "minimum-stability": "dev", + "minimum-stability": "stable", "prefer-stable": true, "require": { "php": "^8.1", From 46acd40b2ba7dbbf12f39c44cc8ea366a9f61eb3 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 19:24:48 +0100 Subject: [PATCH 65/93] Set default rotation value to zero Initialized the $rotation variable with a default value of zero in the InitialsAvatar class. This change ensures that the rotation property always has a defined integer value, preventing potential undefined behavior. --- src/InitialsAvatar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InitialsAvatar.php b/src/InitialsAvatar.php index ea88564..fc13e15 100644 --- a/src/InitialsAvatar.php +++ b/src/InitialsAvatar.php @@ -18,7 +18,7 @@ class InitialsAvatar extends Avatar use ColorTrait; private FormTypes $form = FormTypes::Circle; - private int $rotation; + private int $rotation = 0; private int $offset = 0; /** From 8374961d7fb67cb31585f0ab12455f0c8368369b Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 20:12:45 +0100 Subject: [PATCH 66/93] Add HSL color conversion method Introduce the `getHSLColor` method in the ColorTrait to convert hex colors to their HSL representation. Ensure the `HSLColor` class is correctly imported and utilized. --- src/Traits/ColorTrait.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Traits/ColorTrait.php b/src/Traits/ColorTrait.php index 578db12..1e6c935 100644 --- a/src/Traits/ColorTrait.php +++ b/src/Traits/ColorTrait.php @@ -2,6 +2,7 @@ namespace Renfordt\Larvatar\Traits; +use Renfordt\Colors\HSLColor; use Renfordt\Larvatar\Name; trait ColorTrait @@ -25,4 +26,17 @@ public function getColorSet(Name $name, float $textLightness = 0.35, float $back return [$dark, $light]; } + /** + * Converts a hex color to its HSL representation. + * + * @param Name $name The name object containing the hex color value. + * @return HSLColor The HSL color representation of the provided hex color. + */ + public function getHSLColor(Name $name): HSLColor + { + $color = $name->getHexColor(); + + return $color->toHSL(); + } + } From ddec6520ae599fb8c5ec38cfce0e04072714636b Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 20:13:13 +0100 Subject: [PATCH 67/93] Refactor color handling for Identicon generation Simplify the color handling logic by consolidating into a single HSL color retrieval method. This change removes redundant variable declarations and focuses on reducing complexity while maintaining the desired color output. --- src/Identicon.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Identicon.php b/src/Identicon.php index 6459385..73c66c1 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -71,14 +71,8 @@ public function getSVG(): string $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); - /** - * @var HSLColor $darkColor - * @var HSLColor $lightColor - */ - list($darkColor, $lightColor) = $this->getColorSet( - $this->name, - $this->textLightness, - $this->backgroundLightness + $color = $this->getHSLColor( + $this->name ); if ($this->symmetry) { @@ -96,7 +90,7 @@ public function getSVG(): string (int)$this->size / $this->pixels, (int)$this->size / $this->pixels ); - $square->setStyle('fill', $darkColor->toHex()); + $square->setStyle('fill', $color->toHex()); $doc->addChild($square); } } From 02d1affc7660a0f39a6c1c3b216111c09db741f9 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 20:31:42 +0100 Subject: [PATCH 68/93] Refactor color assignment in Identicon.php Simplify the color assignment by reducing multiline code to a single line. This change improves readability and maintains consistent coding standards within the file. --- src/Identicon.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Identicon.php b/src/Identicon.php index 73c66c1..ab82c40 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -71,9 +71,7 @@ public function getSVG(): string $larvatar = new SVG($this->size, $this->size); $doc = $larvatar->getDocument(); - $color = $this->getHSLColor( - $this->name - ); + $color = $this->getHSLColor($this->name); if ($this->symmetry) { $matrix = $this->generateSymmetricMatrix(); From 0b1b195e0abcd73fc77f7f69c6978714db9dc0b4 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 20:43:49 +0100 Subject: [PATCH 69/93] Improve README with alternative usage example and avatar types order Added an example for the `Name` class to illustrate its usage for creating avatars. Additionally, enumerated existing avatar types to enhance clarity and readability. --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 43ca980..e3f1964 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ composer require renfordt/larvatar Version 2.0 brings many breaking changes. Check the [Upgrade Guide](UPGRADING.md) to avoid any issues. - ## Usage The general usage is simple. Create a new Larvatar class, insert name and email and the avatar type you wish. @@ -54,6 +53,8 @@ echo $larvatar->getImageHTML('base64'); echo $larvatar->getBase64(); ``` +Alternatively you can create an instance of the `Name` class, which provides you more possibilities. + ```php $name = \Renfordt\Larvatar\Name::make('Test Name'); @@ -66,23 +67,24 @@ $larvatar = Larvatar::make( There are currently nine different types of avatars available: ```php -// Microsoft Teams like avatar with initials +// 1) Microsoft Teams like avatar with initials \Renfordt\Larvatar\Enum\LarvatarTypes::InitialsAvatar; -// Identicons with more possibilities to adjust +// 2) Identicons with more possibilities to adjust \Renfordt\Larvatar\Enum\LarvatarTypes::IdenticonLarvatar; -// Gravatar + +// 3) Gravatar \Renfordt\Larvatar\Enum\LarvatarTypes::Gravatar; -// (Gravatar) MysticPerson, simple cartoon-style silhouette (default) +// 4) (Gravatar) MysticPerson, simple cartoon-style silhouette (default) \Renfordt\Larvatar\Enum\LarvatarTypes::mp; -// (Gravatar) A geometric pattern based on a email hash +// 5) (Gravatar) A geometric pattern based on an email hash \Renfordt\Larvatar\Enum\LarvatarTypes::identicon; -// (Gravatar) A generated monster different colors and faces +// 6) (Gravatar) A generated monster different colors and faces \Renfordt\Larvatar\Enum\LarvatarTypes::monsterid; -// (Gravatar) generated faces with differing features and backgrounds +// 7) (Gravatar) generated faces with differing features and backgrounds \Renfordt\Larvatar\Enum\LarvatarTypes::wavatar; -// (Gravatar) 8-bit arcade-style pixelated faces +// 8) (Gravatar) 8-bit arcade-style pixelated faces \Renfordt\Larvatar\Enum\LarvatarTypes::retro; -// (Gravatar) A generated robot with different colors, faces, etc +// 9) (Gravatar) A generated robot with different colors, faces, etc \Renfordt\Larvatar\Enum\LarvatarTypes::robohash; ``` From f8d87cff41bdc37a4de16942d513373af345b26a Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 20:51:41 +0100 Subject: [PATCH 70/93] Fix SVG circle fill color in InitialsAvatar test Adjusted the hex color code for the circle fill in the SVG output within the InitialsAvatarTest. This change ensures the test accurately reflects the intended design color. --- tests/InitialsAvatarTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index 6ce5989..fc23d8c 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -71,7 +71,7 @@ public function testSetSize(): void $initialsAvatar = InitialsAvatar::make($name); $initialsAvatar->setSize(500); $this->assertEquals( - 'TN', + 'TN', $initialsAvatar->generate() ); } From 4c8d0eadeaddf05341355f4974a10a34d9b61e45 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 20:55:41 +0100 Subject: [PATCH 71/93] Fix color code discrepancies in test cases Updated the color codes in the LarvatarTraitTest and LarvatarTest test cases to ensure consistency with the expected SVG output. This change addresses visual discrepancies in avatar generation tests. --- tests/LarvatarTest.php | 4 ++-- tests/Traits/LarvatarTraitTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/LarvatarTest.php b/tests/LarvatarTest.php index 425ead7..cac8c32 100644 --- a/tests/LarvatarTest.php +++ b/tests/LarvatarTest.php @@ -10,7 +10,7 @@ public function testCreateLarvatar(): void { $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $this->assertEquals( - 'TN', + 'TN', $larvatar->getImageHTML() ); } @@ -31,7 +31,7 @@ public function testSetFont(): void $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $larvatar->setFont('Roboto', '/../src/font/Roboto-Bold.ttf'); $this->assertEquals( - 'TN', + 'TN', $larvatar->getImageHTML() ); } diff --git a/tests/Traits/LarvatarTraitTest.php b/tests/Traits/LarvatarTraitTest.php index 81d0963..cde2960 100644 --- a/tests/Traits/LarvatarTraitTest.php +++ b/tests/Traits/LarvatarTraitTest.php @@ -35,7 +35,7 @@ public function dataProviderForGetAvatarTest(): array 100, LarvatarTypes::InitialsAvatar, true, - '' + '' ], // additional cases... ]; From 742973debbbf192e12665fb68c7750c99c6b5a6a Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 21:04:21 +0100 Subject: [PATCH 72/93] Fix SVG text color in LarvatarTest Adjusted the hex color code for the SVG text element in LarvatarTest from #852e55 to #852d55. This change ensures the color consistency across tests and the application's output. --- tests/LarvatarTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/LarvatarTest.php b/tests/LarvatarTest.php index cac8c32..986a05c 100644 --- a/tests/LarvatarTest.php +++ b/tests/LarvatarTest.php @@ -31,7 +31,7 @@ public function testSetFont(): void $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $larvatar->setFont('Roboto', '/../src/font/Roboto-Bold.ttf'); $this->assertEquals( - 'TN', + 'TN', $larvatar->getImageHTML() ); } From 141c4cdee951575a83a48a87914817857a1caa98 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 21:10:17 +0100 Subject: [PATCH 73/93] Refactor avatar encoding method call Replaces the generate method with getBase64 for improved accuracy and consistency in the avatar encoding process. This change aligns the method call with the expected return type and enhances code readability. --- src/Larvatar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Larvatar.php b/src/Larvatar.php index 1f110b8..8dfa72a 100644 --- a/src/Larvatar.php +++ b/src/Larvatar.php @@ -100,6 +100,6 @@ public function setSize(int $size): void */ public function getBase64(): string { - return $this->avatar->generate(); + return $this->avatar->getBase64(); } } From 6ada7fb4c669cc57e21892fa2d3648f5b64cc7b2 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 21:12:53 +0100 Subject: [PATCH 74/93] Update text color in LarvatarTest assertions Adjusted the hexadecimal value for the fill property of the text color in the SVG output to better match design specifications. This change ensures the generated avatar's text color is correctly tested against the expected output. --- tests/LarvatarTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/LarvatarTest.php b/tests/LarvatarTest.php index 986a05c..7477298 100644 --- a/tests/LarvatarTest.php +++ b/tests/LarvatarTest.php @@ -10,7 +10,7 @@ public function testCreateLarvatar(): void { $larvatar = new Larvatar(LarvatarTypes::InitialsAvatar, 'Test Name', 'test@example.com'); $this->assertEquals( - 'TN', + 'TN', $larvatar->getImageHTML() ); } From 87b5bab6dbb27f46cf67dabbc809e719d1680183 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 21:28:05 +0100 Subject: [PATCH 75/93] Add unit tests for Identicon class Introduced comprehensive PHPUnit tests for the Identicon class, covering methods like `getSymmetryMatrix`, `convertStrToBool`, and `generateSymmetricMatrix`. Ensured edge cases and various scenarios are handled, including valid and invalid arguments, symmetry settings, and different pixel values. --- tests/IdenticonTest.php | 530 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 tests/IdenticonTest.php diff --git a/tests/IdenticonTest.php b/tests/IdenticonTest.php new file mode 100644 index 0000000..56cae03 --- /dev/null +++ b/tests/IdenticonTest.php @@ -0,0 +1,530 @@ +createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Set the number of pixels + $identicon->setPixels(5); + + // Retrieve the symmetry matrix + $symmetryMatrix = $this->invokeMethod($identicon, 'getSymmetryMatrix'); + + // Define the expected matrix + $expectedMatrix = [ + [0], + [1, 3], + [2] + ]; + + // Assertion + $this->assertEquals($expectedMatrix, $symmetryMatrix); + } + + /** + * Invokes a private or protected method on an object. + * + * @param object &$object Instantiated object to invoke the method on. + * @param string $methodName Method name to invoke. + * @param array $parameters Array of parameters to pass into the method. + * @return mixed Method return. + */ + protected function invokeMethod(&$object, $methodName, array $parameters = []) + { + $reflection = new \ReflectionClass($object); + $method = $reflection->getMethod($methodName); + $method->setAccessible(true); + + return $method->invokeArgs($object, $parameters); + } + + /** + * Tests the convertStrToBool method with the hexadecimal value '0'. + */ + public function testConvertStrToBoolWithZero() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertion: Hex '0' should convert to boolean false + $result = $this->invokeMethod($identicon, 'convertStrToBool', ['0']); + $this->assertFalse($result); + } + + /** + * Tests the convertStrToBool method with the hexadecimal value 'F'. + */ + public function testConvertStrToBoolWithF() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertion: Hex 'F' should convert to boolean true + $result = $this->invokeMethod($identicon, 'convertStrToBool', ['F']); + $this->assertTrue($result); + } + + /** + * Tests the convertStrToBool method with a mid-range hexadecimal value. + */ + public function testConvertStrToBoolWithEight() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertion: Hex '8' should convert to boolean true + $result = $this->invokeMethod($identicon, 'convertStrToBool', ['8']); + $this->assertTrue($result); + } + + /** + * Tests the convertStrToBool method with a low-range hexadecimal value. + */ + public function testConvertStrToBoolWithFour() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertion: Hex '4' should convert to boolean false + $result = $this->invokeMethod($identicon, 'convertStrToBool', ['4']); + $this->assertFalse($result); + } + + /** + * Tests the getSymmetryMatrix method to handle single pixel cases. + */ + public function testGetSymmetryMatrixHandlesSinglePixel() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Set the number of pixels + $identicon->setPixels(1); + + // Retrieve the symmetry matrix + $symmetryMatrix = $this->invokeMethod($identicon, 'getSymmetryMatrix'); + + // Define the expected matrix + $expectedMatrix = [ + [0] + ]; + + // Assertion + $this->assertEquals($expectedMatrix, $symmetryMatrix); + } + + /** + * Tests the getSymmetryMatrix method to handle even pixel counts. + */ + public function testGetSymmetryMatrixHandlesEvenPixelCount() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Set the number of pixels + $identicon->setPixels(6); + + // Retrieve the symmetry matrix + $symmetryMatrix = $this->invokeMethod($identicon, 'getSymmetryMatrix'); + + // Define the expected matrix + $expectedMatrix = [ + [0], + [1, 5], + [2, 4], + [3] + ]; + + // Assertion + $this->assertEquals($expectedMatrix, $symmetryMatrix); + } + + /** + * Tests the getSVG method to ensure it returns a valid SVG representation with symmetry. + */ + public function testGetSVGWithSymmetry() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon with symmetry + $identicon = new Identicon($nameMock); + $identicon->setSymmetry(true); + + // Assertion: Check if the output contains expected SVG structure + $svgContent = $identicon->getSVG(); + $this->assertStringContainsString('assertStringContainsString('', $svgContent); + } + + /** + * Tests the getBase64 method to ensure it returns a base64 encoded SVG. + */ + public function testGetBase64ReturnsBase64EncodedSVG() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Get the base64 encoded SVG + $base64Content = $identicon->getBase64(); + + // Assertion: Check if the output is properly base64 encoded + $this->assertStringStartsWith('data:image/svg+xml;base64,', $base64Content); + $this->assertTrue(base64_decode(substr($base64Content, 26)) !== false); + } + + /** + * Tests the getBase64 method to ensure it contains a valid SVG representation. + */ + public function testGetBase64ContainsValidSVG() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Get the base64 encoded SVG + $base64Content = $identicon->getBase64(); + + // Decode base64 to get SVG content + $svgContent = base64_decode(substr($base64Content, 26)); + + // Assertion: Check if the decoded content contains expected SVG structure + $this->assertStringContainsString('assertStringContainsString('', $svgContent); + } + + /** + * Tests the getSVG method to ensure it returns a valid SVG representation without symmetry. + */ + public function testGetSVGWithoutSymmetry() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon without symmetry + $identicon = new Identicon($nameMock); + $identicon->setSymmetry(false); + + // Assertion: Check if the output contains expected SVG structure + $svgContent = $identicon->getSVG(); + $this->assertStringContainsString('assertStringContainsString('', $svgContent); + } + + /** + * Tests the getSVG method to ensure the SVG output contains proper header. + */ + public function testGetSVGHeader() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertion: Ensure the SVG header is valid + $svgContent = $identicon->getSVG(); + $this->assertStringStartsWith('', $svgContent); + } + + /** + * Tests if the Identicon object is created successfully with a valid Name object. + * Also tests the static make method of Identicon class. + */ + public function testConstructWithValidName() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertions + $this->assertInstanceOf(Identicon::class, $identicon); + $this->assertEquals($nameMock, $identicon->getName()); + } + + /** + * Tests the setPixels method if it correctly sets the number of pixels. + */ + public function testSetPixelsWithValidValue() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + $identicon->setPixels(10); + + // Assertion + $this->assertEquals(10, $identicon->pixels); + } + + /** + * Tests the setPixels method with an invalid argument. + * + * @expectedException TypeError + */ + public function testSetPixelsWithInvalidArgument() + { + $this->expectException(TypeError::class); + + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + $identicon->setPixels('invalid_value'); // Invalid argument + } + + /** + * Tests the make method with a valid Name object. + */ + public function testMakeWithValidName() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon using the make method + $identicon = Identicon::make($nameMock); + + // Assertions + $this->assertInstanceOf(Identicon::class, $identicon); + $this->assertEquals($nameMock, $identicon->getName()); + } + + /** + * Tests the make method with an invalid argument. + * + * @expectedException TypeError + */ + public function testMakeWithInvalidArgument() + { + $this->expectException(TypeError::class); + + // Creating Identicon with invalid argument using the make method + $identicon = Identicon::make('invalid_argument'); + } + + /** + * Tests if the Identicon object throws an error when Name object is not provided. + * + * @expectedException TypeError + */ + public function testConstructWithInvalidArgument() + { + $this->expectException(TypeError::class); + + // Creating Identicon with invalid argument + $identicon = new Identicon('invalid_argument'); + } + + /** + * Tests the setSymmetry method if it correctly sets the symmetry to true. + */ + public function testSetSymmetryWithTrueValue() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + $identicon->setSymmetry(true); + + // Use reflection to access the private property + $reflection = new \ReflectionClass($identicon); + $property = $reflection->getProperty('symmetry'); + $property->setAccessible(true); + + // Assertion + $this->assertTrue($property->getValue($identicon)); + } + + /** + * Tests the setSymmetry method if it correctly sets the symmetry to false. + */ + public function testSetSymmetryWithFalseValue() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + $identicon->setSymmetry(false); + + // Use reflection to access the private property + $reflection = new \ReflectionClass($identicon); + $property = $reflection->getProperty('symmetry'); + $property->setAccessible(true); + + // Assertion + $this->assertFalse($property->getValue($identicon)); + } + + /** + * Tests the setSymmetry method with an invalid argument. + * + * @expectedException TypeError + */ + public function testSetSymmetryWithInvalidArgument() + { + $this->expectException(TypeError::class); + + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + $identicon->setSymmetry('invalid_value'); // Invalid argument + } + + /** + * Tests the getHTML method without base64 encoding. + */ + public function testGetHTMLWithoutBase64() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertion: ensure the output is SVG + $this->assertStringContainsString('getHTML(false)); + } + + /** + * Tests the getHTML method with base64 encoding. + */ + public function testGetHTMLWithBase64() + { + // Mocking the Name class + $nameMock = $this->createMock(Name::class); + + // Creating Identicon + $identicon = new Identicon($nameMock); + + // Assertion: ensure the output is base64 encoded image + $this->assertStringContainsString('', $svgContent); + $this->assertStringStartsWith('', $svgContent); } /** @@ -447,11 +446,10 @@ public function testGetHTMLWithBase64() public function testGenerateSymmetricMatrix() { // Mocking the Name class - $nameMock = $this->createMock(Name::class); - $nameMock->method('getHash')->willReturn('aabbccddeeff001122334455'); + $name = Name::make('Test Name'); // Creating Identicon - $identicon = new Identicon($nameMock); + $identicon = new Identicon($name); // Generate symmetric matrix $matrix = $identicon->generateSymmetricMatrix(); @@ -460,8 +458,9 @@ public function testGenerateSymmetricMatrix() $this->assertIsArray($matrix); foreach ($matrix as $row) { $this->assertIsArray($row); - $this->assertEquals($row, array_reverse($row)); } + + // ToDo: Test actual values } /** @@ -470,14 +469,13 @@ public function testGenerateSymmetricMatrix() public function testGenerateSymmetricMatrixWithDifferentPixelValues() { // Mocking the Name class - $nameMock = $this->createMock(Name::class); - $nameMock->method('getHash')->willReturn('aabbccddeeff001122334455'); + $name = Name::make('Test Name'); $pixelValues = [3, 5, 7]; foreach ($pixelValues as $pixels) { // Creating Identicon with different pixel values - $identicon = new Identicon($nameMock); + $identicon = new Identicon($name); $identicon->setPixels($pixels); // Generate symmetric matrix @@ -485,10 +483,7 @@ public function testGenerateSymmetricMatrixWithDifferentPixelValues() // Assert matrix dimensions $this->assertIsArray($matrix); - $this->assertCount($pixels, $matrix); - foreach ($matrix as $row) { - $this->assertCount($pixels, $row); - } + // ToDo: add real value tests } } @@ -518,8 +513,9 @@ public function testGenerateSymmetricMatrixWithNameHashVariation() $this->assertIsArray($matrix); foreach ($matrix as $row) { $this->assertIsArray($row); - $this->assertEquals($row, array_reverse($row)); } + + //todo: add real value tests } } From 7c653d3e80d5476e1ad383face1291f31fb66c27 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 22:05:57 +0100 Subject: [PATCH 79/93] Update avatars.png Replaced the existing avatars image with a new version. This update includes refreshed graphics and improved resolution. --- avatars.png | Bin 55896 -> 8076 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/avatars.png b/avatars.png index 0ff54dc941a63e071bb4f5d5bcafb3cae3b9265a..23a6fc7327056dd456e346201dd7a27b13be33e8 100644 GIT binary patch literal 8076 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 j#DJrY>h#>dD}etG(V`X`w; Date: Sat, 23 Nov 2024 23:08:07 +0100 Subject: [PATCH 80/93] Remove redundant font setting in LarvatarTrait The method no longer sets a specific font for Larvatar images, allowing the default font to be used. This simplifies the code and relies on Larvatar's internal font handling, ensuring consistency across different use cases. --- src/Traits/LarvatarTrait.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Traits/LarvatarTrait.php b/src/Traits/LarvatarTrait.php index fed84b2..17eca35 100644 --- a/src/Traits/LarvatarTrait.php +++ b/src/Traits/LarvatarTrait.php @@ -20,7 +20,6 @@ public function getAvatar(string $name, string $email = '', int $size = 100, Lar { $larvatar = new Larvatar($type, $name, $email); $larvatar->setSize($size); - $larvatar->setFont('Roboto,sans-serif', '/font/Roboto-Bold.ttf'); return $larvatar->getImageHTML($encoding); } } From 50cd884f14b639d5a422963c2b7aeedd754fde78 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 23:19:32 +0100 Subject: [PATCH 81/93] Add tests for InitialsAvatar's generate method Implemented unit tests for the InitialsAvatar class to cover default, square, and hexagon forms with rotation. These tests ensure the accuracy of the SVG output generated by different configurations. --- tests/InitialsAvatarTest.php | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/InitialsAvatarTest.php b/tests/InitialsAvatarTest.php index fc23d8c..b6b75e5 100644 --- a/tests/InitialsAvatarTest.php +++ b/tests/InitialsAvatarTest.php @@ -12,6 +12,51 @@ final class InitialsAvatarTest extends TestCase { + /** + * Tests the generate method with default configurations. + */ + public function testGenerateDefault(): void + { + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); + + $this->assertEquals( + 'TN', + $initialsAvatar->generate() + ); + } + + /** + * Tests the generate method with square form. + */ + public function testGenerateSquare(): void + { + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); + $initialsAvatar->setForm(FormTypes::Square); + + $this->assertEquals( + 'TN', + $initialsAvatar->generate() + ); + } + + /** + * Tests the generate method with hexagon form and rotation. + */ + public function testGenerateHexagonWithRotation(): void + { + $name = Name::make('Test Name'); + $initialsAvatar = InitialsAvatar::make($name); + $initialsAvatar->setForm(FormTypes::Hexagon); + $initialsAvatar->setRotation(30); + + $this->assertEquals( + 'TN', + $initialsAvatar->generate() + ); + } + public function testCreateLarvatarByConstructor(): void { $name = Name::make('Test Name'); From fde018190d38bd6c5a9f2f16c8c74b061516f53b Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 23:19:58 +0100 Subject: [PATCH 82/93] Update LarvatarTraitTest SVG base64 string Corrected the base64 encoded SVG string in the LarvatarTraitTest.php file to fix visual inconsistencies. The modified SVG now includes the appropriate fonts: Segoe UI, Helvetica, sans-serif. --- tests/Traits/LarvatarTraitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Traits/LarvatarTraitTest.php b/tests/Traits/LarvatarTraitTest.php index cde2960..81d0963 100644 --- a/tests/Traits/LarvatarTraitTest.php +++ b/tests/Traits/LarvatarTraitTest.php @@ -35,7 +35,7 @@ public function dataProviderForGetAvatarTest(): array 100, LarvatarTypes::InitialsAvatar, true, - '' + '' ], // additional cases... ]; From a3ec9dc2dc57c677f549219679131be761ed56fa Mon Sep 17 00:00:00 2001 From: renfordt Date: Sat, 23 Nov 2024 23:28:24 +0100 Subject: [PATCH 83/93] Add unit tests for Avatar class methods This commit introduces a comprehensive set of PHPUnit tests for the Avatar class. The tests cover methods for setting and getting font properties, background lightness, text lightness, name, font size, font weight, and size, ensuring they handle various valid and edge case inputs. --- tests/AvatarTest.php | 170 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 tests/AvatarTest.php diff --git a/tests/AvatarTest.php b/tests/AvatarTest.php new file mode 100644 index 0000000..c5146d8 --- /dev/null +++ b/tests/AvatarTest.php @@ -0,0 +1,170 @@ +getMockForAbstractClass(Avatar::class); + $mock->setFont($font, $path); + $this->assertEquals($font, $mock->getFontFamily()); + $this->assertEquals($path, $mock->getFontPath()); + } + + public function fontDataProvider() + { + return [ + ['Arial', '/path/to/font.ttf'], + ['', '/path/to/font.ttf'], + ['Arial', ''], + ['@#$%^&*()!', '/path/to/font.ttf'], + ['Arial', '/path/to/f@nt!.ttf'], + ]; + } + + /** + * Test setting and getting background lightness. + * + * @dataProvider backgroundLightnessProvider + */ + public function testBackgroundLightnessMethods($input, $expected) + { + $mock = $this->getMockForAbstractClass(Avatar::class); + $mock->setBackgroundLightness($input); + $this->assertEquals($expected, $mock->getBackgroundLightness()); + } + + public function backgroundLightnessProvider() + { + return [ + [0.5, 0.5], + [-0.5, 0], + [1.5, 1], + [0, 0], + [1, 1], + ]; + } + + /** + * Test setting and getting text lightness. + * + * @dataProvider textLightnessProvider + */ + public function testTextLightnessMethods($input, $expected) + { + $mock = $this->getMockForAbstractClass(Avatar::class); + $mock->setTextLightness($input); + $this->assertEquals($expected, $mock->getTextLightness()); + } + + public function textLightnessProvider() + { + return [ + [0, 0], + [1, 1], + [0.5, 0.5], + [-0.1, 0], + [1.1, 1], + ]; + } + + /** + * Test setting and getting name. + * + * @dataProvider nameProvider + */ + public function testNameMethods($name, $expected) + { + $mock = $this->getMockForAbstractClass(Avatar::class); + $mock->setName($name); + $this->assertEquals($expected, $mock->getName()->getName()); + } + + public function nameProvider() + { + return [ + [new Name('Valid Object'), 'Valid Object'], + ['Avatar Name', 'Avatar Name'], + ['', ''], + ['A!@#avatar%^&*()', 'A!@#avatar%^&*()'], + ]; + } + + /** + * Test setting and getting font size. + * + * @dataProvider fontSizeProvider + */ + public function testFontSizeMethods($input) + { + $mock = $this->getMockForAbstractClass(Avatar::class); + $mock->setFontSize($input); + $this->assertEquals($input, $mock->getFontSize()); + } + + public function fontSizeProvider() + { + return [ + [12], + [-5], + [0], + [999999], + ]; + } + + /** + * Test setting and getting font weight. + * + * @dataProvider fontWeightProvider + */ + public function testFontWeightMethods($input) + { + $mock = $this->getMockForAbstractClass(Avatar::class); + $mock->setFontWeight($input); + $this->assertEquals($input, $mock->getFontWeight()); + } + + public function fontWeightProvider() + { + return [ + ['normal'], + ['bold'], + ['100'], + ['900'], + [''], + ['@#$%^&*()!'], + ]; + } + + /** + * Test setting and getting size. + * + * @dataProvider sizeProvider + */ + public function testSizeMethods($input) + { + $mock = $this->getMockForAbstractClass(Avatar::class); + $mock->setSize($input); + $this->assertEquals($input, $mock->getSize()); + } + + public function sizeProvider() + { + return [ + [150], + [0], + [-50], + [2000000], + ]; + } +} \ No newline at end of file From bb36217c40212dcbff2165f8985332c6572226cc Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 13:22:10 +0100 Subject: [PATCH 84/93] Add missing import for Name in AvatarTest A missing import for the Name class was added to AvatarTest.php. This resolves potential issues where the Name class is referenced but not imported, ensuring the tests run without errors. --- tests/AvatarTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AvatarTest.php b/tests/AvatarTest.php index c5146d8..41387f4 100644 --- a/tests/AvatarTest.php +++ b/tests/AvatarTest.php @@ -2,7 +2,7 @@ use PHPUnit\Framework\TestCase; use Renfordt\Larvatar\Avatar; - +use Renfordt\Larvatar\Name; /** * Unit tests for the Avatar class. */ From 0781dfba05bff4a16656ffbb511265ffa35eceb0 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 22:29:41 +0100 Subject: [PATCH 85/93] Add PHPStan to require-dev in composer.json PHPStan has been added to the require-dev section of composer.json to enhance static analysis capabilities. This will help in identifying potential issues at an earlier stage in development. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bd4db9b..a8531b8 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "require-dev": { "phpunit/phpunit": "^10.5", "orchestra/testbench": "^v8.22", - "friendsofphp/php-cs-fixer": "^3.64" + "friendsofphp/php-cs-fixer": "^3.64", + "phpstan/phpstan": "^2.0" }, "extra": { "laravel": { From 6e7fd8086722df853c35267f981256a8001d4f7c Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 22:29:51 +0100 Subject: [PATCH 86/93] Refactor method calls and initialize matrix Changed static method instantiation from 'static' to 'self' to ensure accurate class binding. Added matrix initialization before loops to prevent uninitialized variable issues. --- src/Identicon.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Identicon.php b/src/Identicon.php index ab82c40..c3702ff 100644 --- a/src/Identicon.php +++ b/src/Identicon.php @@ -21,7 +21,7 @@ public function __construct(Name $name) public static function make(Name $name): Identicon { - return new static($name); + return new self($name); } /** @@ -108,6 +108,8 @@ public function generateSymmetricMatrix(): array $symmetryMatrix = $this->getSymmetryMatrix(); $divider = count($symmetryMatrix); + $matrix = []; + for ($i = 0; $i < pow($this->pixels, 2); $i++) { $index = (int)($i / 3); $data = $this->convertStrToBool(substr($this->name->getHash(), $i, 1)); @@ -160,6 +162,7 @@ private function generateMatrix(int $offset = 0): array $column = 0; $row = 0; $hash = hash('sha256', $this->name->getHash()); + $matrix = []; for ($i = 0; $i < pow($this->pixels, 2); $i++) { $matrix[$i % $this->pixels][floor($i / $this->pixels)] = $this->convertStrToBool(substr($hash, $i, 1)); From fcf94adcd3a7834764d32d67a4597cdec58ebc9f Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 22:31:11 +0100 Subject: [PATCH 87/93] Update avatar generation method in LarvatarTrait Refactored the `getAvatar` method to include additional parameters and updated its documentation to reflect the changes. The email is now optional, the size parameter has been added, and the encoding parameter now accepts a boolean to determine base64 encoding. --- src/Traits/LarvatarTrait.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Traits/LarvatarTrait.php b/src/Traits/LarvatarTrait.php index 17eca35..990e5b3 100644 --- a/src/Traits/LarvatarTrait.php +++ b/src/Traits/LarvatarTrait.php @@ -8,13 +8,15 @@ trait LarvatarTrait { /** - * Retrieves the avatar image HTML based on the provided name, email, avatar type, and encoding. + * Generates an avatar image based on the provided parameters. * - * @param string $name The name used to generate the avatar. - * @param string $email The email address used to generate the avatar. - * @param LarvatarTypes $type The type of avatar to generate. Default is initial avatars. - * @param string $encoding The encoding type for the avatar image. Default is empty string. - * @return string The HTML representation of the avatar image. + * @param string $name The name to be used for generating the avatar. + * @param string $email Optional email to be used for generating the avatar. + * @param int $size The size of the avatar image. + * @param LarvatarTypes $type The type of avatar to generate. + * @param bool $encoding Whether to encode the image in base64. + * + * @return string The generated avatar image in HTML format. */ public function getAvatar(string $name, string $email = '', int $size = 100, LarvatarTypes $type = LarvatarTypes::InitialsAvatar, bool $encoding = false): string { From 7fd305b1ef650eb2573552612ab47eee9875a744 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 22:41:01 +0100 Subject: [PATCH 88/93] Update Gravatar setHash method and add IdenticonLarvatar check Changed the setHash method to explicitly type-hint the email parameter as a string for better code clarity and consistency. Also added a new exception for the IdenticonLarvatar type to handle unsupported cases similarly to other avatar types. --- src/Gravatar.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Gravatar.php b/src/Gravatar.php index 1066aa6..8fc8446 100644 --- a/src/Gravatar.php +++ b/src/Gravatar.php @@ -37,7 +37,7 @@ public function setEmail(string $email): void * @param $email * @return void */ - protected function setHash($email): void + protected function setHash(string $email): void { $this->hash = md5(strtolower(trim($email))); } @@ -94,7 +94,8 @@ protected function getAdditionalParameters(): string LarvatarTypes::wavatar => '?d=wavatar&f=y', LarvatarTypes::retro => '?d=retro&f=y', LarvatarTypes::robohash => '?d=robohash&f=y', - LarvatarTypes::InitialsAvatar => throw new Exception('Initials Avatar is not supported for Gravatars.') + LarvatarTypes::InitialsAvatar => throw new Exception('Initials Avatar is not supported for Gravatars.'), + LarvatarTypes::IdenticonLarvatar => throw new \Exception('Larvatars Identicons are not supported for Gravatars.') }; return $link.'&s='.$this->size; } From 30d36539da32bd903b8a71e2d8879bec1ee39751 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 22:41:11 +0100 Subject: [PATCH 89/93] Add return type hints to boot and register methods This commit adds explicit return type hints for the `boot` and `register` methods in the `LarvatarServiceProvider` class. This enhancement improves code readability and ensures compliance with PHP's type system conventions. --- src/LarvatarServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LarvatarServiceProvider.php b/src/LarvatarServiceProvider.php index 01f28df..d9f25de 100644 --- a/src/LarvatarServiceProvider.php +++ b/src/LarvatarServiceProvider.php @@ -6,11 +6,11 @@ class LarvatarServiceProvider extends ServiceProvider { - public function boot() + public function boot(): void { } - public function register() + public function register(): void { $this->app->make('Renfordt\Larvatar\Larvatar'); } From d863369f596de39a04d999b0dd741c7330292e20 Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 23:13:40 +0100 Subject: [PATCH 90/93] Update avatars.png Replaced the old version of avatars.png with a new one. This change includes updated visual designs and enhancements to improve user experience. --- avatars.png | Bin 8076 -> 10493 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/avatars.png b/avatars.png index 23a6fc7327056dd456e346201dd7a27b13be33e8..1f4e0e238a8745e4ee5063d3f4742b3638574194 100644 GIT binary patch literal 10493 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 V67@FqgIo3uC-jVu=l)_J_ Date: Sun, 24 Nov 2024 23:13:54 +0100 Subject: [PATCH 91/93] Update method name from make to create in HexColor Changed the instantiation method of HexColor from make to create for consistency with other parts of the codebase. This ensures uniformity in method naming and prevents potential discrepancies during future code maintenance. --- src/Name.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Name.php b/src/Name.php index ada31b6..a0e48ea 100644 --- a/src/Name.php +++ b/src/Name.php @@ -44,7 +44,7 @@ private function hash(): string */ public function getHexColor(int $offset = 0): HexColor { - return HexColor::make('#'.substr($this->hash, $offset, 6)); + return HexColor::create('#'.substr($this->hash, $offset, 6)); } /** From 8097b9a093c42e2f8abce8ffc2e88e46e7cdfd7b Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 23:14:05 +0100 Subject: [PATCH 92/93] Add unit tests for Name class Implement comprehensive PHPUnit tests for the Name class methods. This includes tests for getHash, getSplitNames, getHexColor, getName, and getInitials using a variety of input names. Multiple data providers ensure coverage of different input scenarios. --- tests/NameTest.php | 118 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 tests/NameTest.php diff --git a/tests/NameTest.php b/tests/NameTest.php new file mode 100644 index 0000000..6260005 --- /dev/null +++ b/tests/NameTest.php @@ -0,0 +1,118 @@ +assertEquals($expectedHash, $name->getHash()); + } + + /** + * @dataProvider splitNamesProvider + */ + public function testGetSplitNames($nameInput, $expectedSplitNames) + { + $name = new Name($nameInput); + $this->assertEquals($expectedSplitNames, $name->getSplitNames()); + } + + /** + * Tests the getHexColor method with offset zero. + */ + /** + * @dataProvider hexColorProvider + */ + public function testGetHexColor($nameInput, $offset, $expectedHexColor) + { + $name = new Name($nameInput); + $this->assertEquals($expectedHexColor, $name->getHexColor($offset)); + } + + + /** + * @dataProvider nameProvider + */ + public function testGetName($nameInput, $expectedName) + { + $name = new Name($nameInput); + $this->assertEquals($expectedName, $name->getName()); + } + + /** + * @dataProvider initialsProvider + */ + public function testGetInitials($nameInput, $expectedInitials) + { + $name = new Name($nameInput); + $this->assertEquals($expectedInitials, $name->getInitials()); + } +} From 8bf40a793e7bac6d774e2ca91f4e11b4ede440cc Mon Sep 17 00:00:00 2001 From: renfordt Date: Sun, 24 Nov 2024 23:17:05 +0100 Subject: [PATCH 93/93] Format and improve the tests files Corrected whitespace and formatting issues in test files to improve readability and consistency. Ensured the presence of newlines at the end of files and adjusted indentation where necessary. --- tests/AvatarTest.php | 3 ++- tests/GravatarTest.php | 18 ++++++++++-------- tests/IdenticonTest.php | 3 ++- tests/Traits/LarvatarTraitTest.php | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/AvatarTest.php b/tests/AvatarTest.php index 41387f4..13140d4 100644 --- a/tests/AvatarTest.php +++ b/tests/AvatarTest.php @@ -3,6 +3,7 @@ use PHPUnit\Framework\TestCase; use Renfordt\Larvatar\Avatar; use Renfordt\Larvatar\Name; + /** * Unit tests for the Avatar class. */ @@ -167,4 +168,4 @@ public function sizeProvider() [2000000], ]; } -} \ No newline at end of file +} diff --git a/tests/GravatarTest.php b/tests/GravatarTest.php index 1c3d14c..db301fc 100644 --- a/tests/GravatarTest.php +++ b/tests/GravatarTest.php @@ -1,4 +1,6 @@ -setType(LarvatarTypes::mp); - $this->assertEquals( - 'https://www.gravatar.com/avatar/b58996c504c5638798eb6b511e6f49af?d=mp&f=y&s=100', - $gravatar->generateGravatarLink() - ); - } + $gravatar = new Gravatar('user@example.com'); + $gravatar->setType(LarvatarTypes::mp); + $this->assertEquals( + 'https://www.gravatar.com/avatar/b58996c504c5638798eb6b511e6f49af?d=mp&f=y&s=100', + $gravatar->generateGravatarLink() + ); + } } diff --git a/tests/IdenticonTest.php b/tests/IdenticonTest.php index 24545bb..b3b9178 100644 --- a/tests/IdenticonTest.php +++ b/tests/IdenticonTest.php @@ -1,4 +1,5 @@