diff --git a/README.md b/README.md
index d5134a7..5555423 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,12 @@ composer require macocci7/bash-colorizer
->echo("Hi, there!", PHP_EOL);
```
+ by hex code:
+ ```php
+ Colorizer::foreground('#ffcc00') // or #fc0
+ ->echo("Hi, there!", PHP_EOL);
+ ```
+
by number [0 - 255] (256 colors):
```php
Colorizer::foreground(2)
@@ -145,6 +151,12 @@ composer require macocci7/bash-colorizer
->echo("Hi, there!", PHP_EOL);
```
+ by hex code:
+ ```php
+ Colorizer::background("#ffcc00") // or #fc0
+ ->echo("Hi, there!", PHP_EOL);
+ ```
+
by number [0 - 255] (256 colors):
```php
Colorizer::background(1)
@@ -168,6 +180,28 @@ composer require macocci7/bash-colorizer
->echo("Hi, there!", PHP_EOL);
```
+- Setting underline color:
+
+ by hex code:
+ ```php
+ Colorizer::underline("#ffcc00") // or #fc0
+ ->echo("Hi, there!", PHP_EOL);
+ ```
+
+ by number [0 - 255] (256 colors):
+ ```php
+ Colorizer::underline(1)
+ ->echo("Hi, there!", PHP_EOL);
+ ```
+
+ by (RGB) array (24bit 16777216 colors):
+ ```php
+ Colorizer::underline([255, 0, 0])
+ ->echo("Hi, there!", PHP_EOL);
+ ```
+
+ See more: [Available Colors](#63-available-colors)
+
- Returning colorized string:
As an argument of echo:
@@ -223,22 +257,34 @@ composer require macocci7/bash-colorizer
|`no-italic`|〇|〇|
|`no-underline`|〇|〇|
|`no-blink`|ー
*3|〇
*4|
+|`proportional-spacing`|ー|ー|
|`no-reverse`|〇|〇|
|`no-conceal`|〇|〇|
|`no-strike`|〇|〇|
+|`no-proportional-spacing`|ー|ー|
+|`framed`|❌|❌|
+|`encircled`|❌|❌|
+|`overlined`|〇|〇|
+|`no-framed-no-encircled`|ー
*3|ー
*3|
+|`no-overlined`|〇|〇|
+|`underline-color`|〇|▲
*5|
>
*1: No effect with `faint`
>
*2: Not `fast` (blinks at the same rate as `blink`)
>
-*3: Unknown because `blink` has no effect
+*3: Unknown because the corresponding attribute has no effect
>
*4: Also effective against `fast-blink`
+>
+*5: Partially effective
e.g.) on VSCode Terminal
-
+
+
+See more: [Select Graphic Rendition parameters | ANSI escape code | Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code#Select_Graphic_Rendition_parameters)
### 6.3. Available Colors
@@ -261,7 +307,7 @@ e.g.) on VSCode Terminal
|---|---|
|
|
|
-- 256 colors [ 0 - 255 ]:
+- 256 colors [ 0 - 255 ]: `foreground`/`background`/`underline`
e.g.) foreground colors on VSCode Terminal:
@@ -271,6 +317,10 @@ e.g.) on VSCode Terminal
+ e.g.) underline colors on VSCode Terminal:
+
+
+
- 24bit (16777216) colors:
e.g.) foreground colors on VSCode Terminal:
@@ -281,6 +331,10 @@ e.g.) on VSCode Terminal
+ e.g.) underline colors on VSCode Terminal:
+
+
+
## 7. Examples
Example codes are in [playground](playground/) directory.
@@ -293,6 +347,8 @@ Example codes are in [playground](playground/) directory.
- [foreground_24bitcolors.php](playground/foreground_24bitcolors.php)
- [background_256colors.php](playground/background_256colors.php)
- [background_24bitcolors.php](playground/background_24bitcolors.php)
+- [underline_256colors.php](playground/underline_256colors.php)
+- [underline_24bitcolors.php](playground/underline_24bitcolors.php)
- [readable.php](playground/readable.php)
## 8. LICENSE
diff --git a/arts/available_attributes.png b/arts/available_attributes.png
index 84ba2ad..a456c90 100644
Binary files a/arts/available_attributes.png and b/arts/available_attributes.png differ
diff --git a/arts/underline_24bitcolors.png b/arts/underline_24bitcolors.png
new file mode 100644
index 0000000..f634c23
Binary files /dev/null and b/arts/underline_24bitcolors.png differ
diff --git a/arts/underline_256colors.png b/arts/underline_256colors.png
new file mode 100644
index 0000000..b0fb1a3
Binary files /dev/null and b/arts/underline_256colors.png differ
diff --git a/playground/colorizer.php b/playground/colorizer.php
index d83153f..105cc86 100644
--- a/playground/colorizer.php
+++ b/playground/colorizer.php
@@ -7,6 +7,7 @@
$greeting = "Let's make your bash terminal full of colors!";
Colorizer::attributes(["bold"])
+ ->echo(PHP_EOL)
->foreground("red")->echo(" B ")
->foreground("yellow")->echo("A ")
->foreground("white")->echo("S ")
@@ -19,7 +20,8 @@
->foreground("yellow")->echo("I ")
->foreground("white")->echo("Z ")
->foreground("green")->echo("E ")
- ->foreground("cyan")->echo("R", "\n\n")
+ ->foreground("cyan")->echo("R", PHP_EOL)
+ ->echo(PHP_EOL)
;
Colorizer::attributes(["reset"])
->foreground("white")->background("black")->echo($greeting, PHP_EOL)
@@ -30,4 +32,5 @@
->foreground("green")->background("cyan")->echo($greeting, PHP_EOL)
->foreground("green")->background("blue")->echo($greeting, PHP_EOL)
->foreground("green")->background("magenta")->echo($greeting, PHP_EOL)
+ ->echo('', PHP_EOL)
;
diff --git a/playground/underline_24bitcolors.php b/playground/underline_24bitcolors.php
new file mode 100644
index 0000000..468e532
--- /dev/null
+++ b/playground/underline_24bitcolors.php
@@ -0,0 +1,23 @@
+ 255 ? 255 : $r);
+ $gg = $g < 0 ? 0 : ($g > 255 ? 255 : $g);
+ $bb = $b < 0 ? 0 : ($b > 255 ? 255 : $b);
+ $s = sprintf(' [%3d;%3d;%3d] ', $rr, $gg, $bb);
+ Colorizer::underline([$rr, $gg, $bb])
+ ->echo($s, !$i ? PHP_EOL : '');
+ }
+ }
+}
diff --git a/playground/underline_256colors.php b/playground/underline_256colors.php
new file mode 100644
index 0000000..ae85ef0
--- /dev/null
+++ b/playground/underline_256colors.php
@@ -0,0 +1,12 @@
+echo($s, $eol);
+}
diff --git a/src/Colorizer.php b/src/Colorizer.php
index e197349..fd999a6 100644
--- a/src/Colorizer.php
+++ b/src/Colorizer.php
@@ -2,6 +2,7 @@
namespace Macocci7\BashColorizer;
+use Macocci7\BashColorizer\Converter;
use Macocci7\BashColorizer\Enums\Attribute;
use Macocci7\BashColorizer\Enums\Foreground;
use Macocci7\BashColorizer\Enums\Background;
@@ -9,6 +10,8 @@
class Colorizer
{
+ use Traits\ValidationTrait;
+
protected const ESC = "\e";
/**
@@ -25,6 +28,8 @@ public function __construct(array $config = [])
}
/**
+ * sets or returns attributes
+ *
* @param string[]|Attribute[]|null $attributes = []
* @return string[]|null|self
*/
@@ -40,6 +45,8 @@ public static function attributes(array|null $attributes = null): array|null|sel
}
/**
+ * sets or returns foreground color
+ *
* @param string|Foreground|int|int[]|null $foreground = null
* @return string|Foreground|int|int[]|null|self
*/
@@ -54,6 +61,8 @@ public static function foreground(
}
/**
+ * sets or returns background color
+ *
* @param string|Background|int|int[]|null $background = null
* @return string|Background|int|int[]|null|self
*/
@@ -67,6 +76,22 @@ public static function background(
return new self(static::$config);
}
+ /**
+ * sets or returns underline color
+ *
+ * @param string|int|int[]|null $underline = null
+ * @return string|int|int[]|null|self
+ */
+ public static function underline(
+ string|int|array|null $underline = null
+ ): string|int|array|null|self {
+ if (is_null($underline)) {
+ return self::$config['underline'] ?? null;
+ }
+ static::$config['underline'] = $underline;
+ return new self(static::$config);
+ }
+
/**
* @param array $config
* @return array|self
@@ -80,19 +105,34 @@ public static function config(array|null $config = null): array|self
return new self($config);
}
+ public static function hasAttribute(string|Attribute $attribute): bool
+ {
+ $attributes = static::attributes();
+ if (empty($attributes)) {
+ return false;
+ }
+ if (is_string($attribute)) {
+ return is_null(Attribute::tryFrom($attribute))
+ ? false
+ : in_array($attribute, $attributes)
+ || in_array(Attribute::from($attribute), $attributes);
+ }
+ return in_array($attribute->value, $attributes)
+ || in_array($attribute, $attributes);
+ }
+
/**
- * @param array $config = []
* @return int[]
*/
- protected static function getAttributeCodes(array $config = []): array
+ protected static function getAttributeCodes(): array
{
- if (!isset($config['attributes'])) {
+ if (!isset(static::$config['attributes'])) {
return [];
}
$codes = [];
- foreach ($config['attributes'] as $attribute) {
+ foreach (static::$config['attributes'] as $attribute) {
if (is_string($attribute)) {
$codes[] = Attribute::tryFrom($attribute)?->code();
} elseif ($attribute instanceof Attribute) {
@@ -100,20 +140,19 @@ protected static function getAttributeCodes(array $config = []): array
}
}
- return $codes;
+ return array_unique($codes);
}
/**
- * @param array $config = []
* @return int[]
*/
- protected static function getForegroundCode(array $config = []): array
+ protected static function getForegroundCode(): array
{
- if (!isset($config['foreground'])) {
+ if (!isset(static::$config['foreground'])) {
return [];
}
- $foreground = $config['foreground'];
+ $foreground = static::$config['foreground'];
if (is_int($foreground) || is_array($foreground)) {
return static::extendedCodes(Foreground::Extended, $foreground);
@@ -121,6 +160,13 @@ protected static function getForegroundCode(array $config = []): array
$code = null;
if (is_string($foreground)) {
+ if (static::isHex($foreground)) {
+ return [
+ Foreground::Extended->code(),
+ 2,
+ ...Converter::decimal($foreground),
+ ];
+ }
$code = Foreground::tryFrom($foreground)?->code();
} elseif ($foreground instanceof Foreground) {
$code = $foreground->code();
@@ -130,16 +176,15 @@ protected static function getForegroundCode(array $config = []): array
}
/**
- * @param array $config = []
* @return int[]
*/
- protected static function getBackgroundCode(array $config = []): array
+ protected static function getBackgroundCode(): array
{
- if (!isset($config['background'])) {
+ if (!isset(static::$config['background'])) {
return [];
}
- $background = $config['background'];
+ $background = static::$config['background'];
if (is_int($background) || is_array($background)) {
return static::extendedCodes(Background::Extended, $background);
@@ -148,6 +193,13 @@ protected static function getBackgroundCode(array $config = []): array
$code = null;
if (is_string($background)) {
+ if (static::isHex($background)) {
+ return [
+ Background::Extended->code(),
+ 2,
+ ...Converter::decimal($background),
+ ];
+ }
$code = Background::tryFrom($background)?->code();
} elseif ($background instanceof Background) {
$code = $background->code();
@@ -156,6 +208,52 @@ protected static function getBackgroundCode(array $config = []): array
return strlen($code ?? '') ? [$code] : [];
}
+ /**
+ * @return int[]
+ */
+ protected static function getUnderlineCode(): array
+ {
+ if (!isset(static::$config['underline'])) {
+ return [];
+ }
+
+ $underline = static::$config['underline'];
+
+ $attributeUnderline = static::hasAttribute('underline')
+ ? []
+ : [Attribute::Underline->code()];
+ if (is_int($underline)) {
+ return [
+ ...$attributeUnderline,
+ Attribute::UnderlineColor->code(),
+ 5,
+ Filter::number($underline),
+ ];
+ }
+
+ if (is_array($underline)) {
+ return [
+ ...$attributeUnderline,
+ Attribute::UnderlineColor->code(),
+ 2,
+ ...Filter::rgb($underline),
+ ];
+ }
+
+ if (is_string($underline)) {
+ if (static::isHex($underline)) {
+ return [
+ ...$attributeUnderline,
+ Attribute::UnderlineColor->code(),
+ 2,
+ ...Converter::decimal($underline),
+ ];
+ }
+ }
+
+ return [];
+ }
+
/**
* @param Foreground|Background $enum
* @param int|int[] $color
@@ -182,21 +280,19 @@ protected static function extendedCodes(
];
}
- /**
- * @param array $config = []
- */
- public static function codes(array $config = []): string
+ public static function codes(): string
{
return implode(';', array_merge(
- self::getAttributeCodes($config),
- self::getForegroundCode($config),
- self::getBackgroundCode($config),
+ self::getAttributeCodes(),
+ self::getForegroundCode(),
+ self::getBackgroundCode(),
+ self::getUnderlineCode(),
));
}
public static function encode(string $string, string $eol = ''): string
{
- $codes = static::codes(static::$config);
+ $codes = static::codes();
return empty($codes) ? $string : sprintf(
'%s[%sm%s%s[m%s',
static::ESC,
diff --git a/src/Converter.php b/src/Converter.php
new file mode 100644
index 0000000..d843757
--- /dev/null
+++ b/src/Converter.php
@@ -0,0 +1,31 @@
+ 7,
static::Conceal => 8,
static::Strike => 9,
+ //static::PrimaryFont => 10,
+ //static::AlternativeFont1 => 11,
+ //static::AlternativeFont2 => 12,
+ //static::AlternativeFont3 => 13,
+ //static::AlternativeFont4 => 14,
+ //static::AlternativeFont5 => 15,
+ //static::AlternativeFont6 => 16,
+ //static::AlternativeFont7 => 17,
+ //static::AlternativeFont8 => 18,
+ //static::AlternativeFont9 => 19,
static::Gothic => 20,
static::DoubleUnderline => 21,
static::Normal => 22,
static::NoItalic => 23,
static::NoUnderline => 24,
static::NoBlink => 25,
+ static::ProportionalSpacing => 26,
static::NoReverse => 27,
static::NoConceal => 28,
static::NoStrike => 29,
+ static::NoProportionalSpacing => 50,
+ static::Framed => 51,
+ static::Encircled => 52,
+ static::Overlined => 53,
+ static::NoFramedNoEncircled => 54,
+ static::NoOverlined => 55,
+ static::UnderlineColor => 58,
};
}
}
diff --git a/src/Traits/ValidationTrait.php b/src/Traits/ValidationTrait.php
new file mode 100644
index 0000000..4d13ebe
--- /dev/null
+++ b/src/Traits/ValidationTrait.php
@@ -0,0 +1,23 @@
+ ["bold", "italic"],
'forground' => "yellow",
'background' => "blue",
+ 'underline' => "#ff9900",
+ ],
+ ],
+ 'case 8' => [
+ 'config' => [
+ 'attributes' => ["bold", "italic"],
+ 'forground' => "yellow",
+ 'background' => "blue",
+ 'underline' => "#ff9900",
'unnecessary' => "hello",
],
],
@@ -116,6 +125,101 @@ public function test_background_can_set_background_color_correctly(string|Backgr
$this->assertSame($background, Colorizer::config()['background']);
}
+ public static function provide_underline_can_set_underline_color_correctly(): array
+ {
+ return [
+ '""' => ['underline' => ""],
+ '"#ff9900"' => ['underline' => "#ff9900"],
+ '128' => ['underline' => 128],
+ '[0, 128, 255]' => ['underline' => [0, 128, 255]],
+ ];
+ }
+
+ #[DataProvider('provide_underline_can_set_underline_color_correctly')]
+ public function test_underline_can_set_underline_color_correctly(string|int|array $underline): void
+ {
+ Colorizer::underline($underline);
+ $this->assertSame($underline, Colorizer::underline());
+ $this->assertSame($underline, Colorizer::config()['underline']);
+ }
+
+ public static function provide_hasAttribute_can_return_correct_bool(): array
+ {
+ return [
+ 'case 1' => [
+ 'attributes' => null,
+ 'attribute' => 'bold',
+ 'expected' => false,
+ ],
+ 'case 2' => [
+ 'attributes' => [],
+ 'attribute' => '',
+ 'expected' => false,
+ ],
+ 'case 3' => [
+ 'attributes' => [],
+ 'attribute' => 'bold',
+ 'expected' => false,
+ ],
+ 'case 4' => [
+ 'attributes' => ['italic'],
+ 'attribute' => 'bold',
+ 'expected' => false,
+ ],
+ 'case 5' => [
+ 'attributes' => ['bold'],
+ 'attribute' => 'bold',
+ 'expected' => true,
+ ],
+ 'case 6' => [
+ 'attributes' => ['bold'],
+ 'attribute' => Attribute::Bold,
+ 'expected' => true,
+ ],
+ 'case 7' => [
+ 'attributes' => [Attribute::Bold],
+ 'attribute' => Attribute::Bold,
+ 'expected' => true,
+ ],
+ 'case 8' => [
+ 'attributes' => [Attribute::Bold],
+ 'attribute' => 'bold',
+ 'expected' => true,
+ ],
+ 'case 9' => [
+ 'attributes' => ['italic', 'bold'],
+ 'attribute' => 'bold',
+ 'expected' => true,
+ ],
+ 'case 10' => [
+ 'attributes' => ['italic', Attribute::Bold],
+ 'attribute' => 'bold',
+ 'expected' => true,
+ ],
+ 'case 11' => [
+ 'attributes' => ['italic', 'bold'],
+ 'attribute' => Attribute::Bold,
+ 'expected' => true,
+ ],
+ 'case 12' => [
+ 'attributes' => ['italic', Attribute::Bold],
+ 'attribute' => Attribute::Bold,
+ 'expected' => true,
+ ],
+ ];
+ }
+
+ #[DataProvider('provide_hasAttribute_can_return_correct_bool')]
+ public function test_hasAttribute_can_return_correct_bool(array|null $attributes, string|Attribute $attribute, bool $expected): void
+ {
+ if (is_null($attributes)) {
+ Colorizer::config([]);
+ } else {
+ Colorizer::attributes($attributes);
+ }
+ $this->assertSame($expected, Colorizer::hasAttribute($attribute));
+ }
+
public static function provide_codes_can_return_codes_correctly(): array
{
return [
@@ -302,6 +406,12 @@ public static function provide_codes_can_return_codes_correctly(): array
],
'expected' => '33',
],
+ '#ff9900 as foreground' => [
+ 'config' => [
+ 'foreground' => '#ff9900',
+ ],
+ 'expected' => '38;2;255;153;0',
+ ],
'Yellow as foreground' => [
'config' => [
'foreground' => Foreground::Yellow,
@@ -442,6 +552,12 @@ public static function provide_codes_can_return_codes_correctly(): array
],
'expected' => '42',
],
+ '#0099ff as background' => [
+ 'config' => [
+ 'background' => '#0099ff',
+ ],
+ 'expected' => '48;2;0;153;255',
+ ],
'Green as background' => [
'config' => [
'background' => Background::Green,
@@ -515,94 +631,275 @@ public static function provide_codes_can_return_codes_correctly(): array
'expected' => '48;2;64;128;192',
],
+ // cases for underline
+ 'empty as underline' => [
+ 'config' => [
+ 'underline' => '',
+ ],
+ 'expected' => '',
+ ],
+ 'null as underline' => [
+ 'config' => [
+ 'underline' => null,
+ ],
+ 'expected' => '',
+ ],
+ 'invalid as underline' => [
+ 'config' => [
+ 'underline' => 'invalid',
+ ],
+ 'expected' => '',
+ ],
+ 'true as underline' => [
+ 'config' => [
+ 'underline' => true,
+ ],
+ 'expected' => '',
+ ],
+ 'false as underline' => [
+ 'config' => [
+ 'underline' => false,
+ ],
+ 'expected' => '',
+ ],
+ 'float as underline' => [
+ 'config' => [
+ 'underline' => 1.5,
+ ],
+ 'expected' => '',
+ ],
+ 'object as underline' => [
+ 'config' => [
+ 'underline' => new \stdClass(),
+ ],
+ 'expected' => '',
+ ],
+ 'closure as underline' => [
+ 'config' => [
+ 'underline' => fn () => "magenta",
+ ],
+ 'expected' => '',
+ ],
+ 'Attribute enum as underline' => [
+ 'config' => [
+ 'underline' => Attribute::Italic,
+ ],
+ 'expected' => '',
+ ],
+ 'Foregound enum as underline' => [
+ 'config' => [
+ 'underline' => Foreground::Cyan,
+ ],
+ 'expected' => '',
+ ],
+ 'Background enum as underline' => [
+ 'config' => [
+ 'underline' => Background::Cyan,
+ ],
+ 'expected' => '',
+ ],
+ 'green as underline' => [
+ 'config' => [
+ 'underline' => 'green',
+ ],
+ 'expected' => '',
+ ],
+ '#f90 as underline' => [
+ 'config' => [
+ 'underline' => '#0099ff',
+ ],
+ 'expected' => '4;58;2;0;153;255',
+ ],
+ '#0099ff as underline' => [
+ 'config' => [
+ 'underline' => '#0099ff',
+ ],
+ 'expected' => '4;58;2;0;153;255',
+ ],
+ 'int -1 as underline' => [
+ 'config' => [
+ 'underline' => -1,
+ ],
+ 'expected' => '4;58;5;0',
+ ],
+ 'int 0 as underline' => [
+ 'config' => [
+ 'underline' => 0,
+ ],
+ 'expected' => '4;58;5;0',
+ ],
+ 'int 1 as underline' => [
+ 'config' => [
+ 'underline' => 1,
+ ],
+ 'expected' => '4;58;5;1',
+ ],
+ 'int 254 as underline' => [
+ 'config' => [
+ 'underline' => 254,
+ ],
+ 'expected' => '4;58;5;254',
+ ],
+ 'int 255 as underline' => [
+ 'config' => [
+ 'underline' => 255,
+ ],
+ 'expected' => '4;58;5;255',
+ ],
+ 'int 256 as underline' => [
+ 'config' => [
+ 'underline' => 256,
+ ],
+ 'expected' => '4;58;5;255',
+ ],
+ '[null] as underline' => [
+ 'config' => [
+ 'underline' => [null],
+ ],
+ 'expected' => '4;58;2;0;0;0',
+ ],
+ '[64] as underline' => [
+ 'config' => [
+ 'underline' => [64],
+ ],
+ 'expected' => '4;58;2;64;0;0',
+ ],
+ '[64, 128] as underline' => [
+ 'config' => [
+ 'underline' => [64, 128],
+ ],
+ 'expected' => '4;58;2;64;128;0',
+ ],
+ '[64, 128, 192] as underline' => [
+ 'config' => [
+ 'underline' => [64, 128, 192],
+ ],
+ 'expected' => '4;58;2;64;128;192',
+ ],
+ '[64, 128, 192, 255] as underline' => [
+ 'config' => [
+ 'underline' => [64, 128, 192, 255],
+ ],
+ 'expected' => '4;58;2;64;128;192',
+ ],
+
// combinations
- 'empty attributes, foreground and background' => [
+ 'empty attributes, foreground, background and underline' => [
'config' => [
'attributes' => [],
'foreground' => '',
'background' => '',
+ 'underline' => '',
],
'expected' => '',
],
- 'empty as attributes, empty foreground and background' => [
+ 'empty as attributes, empty foreground, background and underline' => [
'config' => [
'attributes' => [''],
'foreground' => '',
'background' => '',
+ 'underline' => '',
],
'expected' => '',
],
- 'null as attributes, empty foreground and background' => [
+ 'null as attributes, empty foreground, background and underline' => [
'config' => [
'attributes' => [null],
'foreground' => '',
'background' => '',
+ 'underline' => '',
],
'expected' => '',
],
- 'null as attributes, foreground and background' => [
+ 'null as attributes, foreground, background and underline' => [
'config' => [
'attributes' => [null],
'foreground' => null,
'background' => null,
+ 'underline' => null,
],
'expected' => '',
],
- 'invalid as attributes, empty foreground and background' => [
+ 'invalid as attributes, empty foreground, background and underline' => [
'config' => [
'attributes' => ['invalid'],
'foreground' => '',
'background' => '',
+ 'underline' => '',
],
'expected' => '',
],
- 'invalid as attributes, foreground and background' => [
+ 'invalid as attributes, foreground, background and underline' => [
'config' => [
'attributes' => ['invalid'],
'foreground' => 'invalid',
'background' => 'invalid',
+ 'underline' => 'invalid',
],
'expected' => '',
],
- 'bold, empty foreground and background' => [
+ 'bold, empty foreground, background and underline' => [
'config' => [
'attributes' => ['bold'],
'foreground' => '',
'background' => '',
+ 'underline' => '',
],
'expected' => '1',
],
- 'bold, red foreground and empty background' => [
+ 'bold, red foreground, empty background and underline' => [
'config' => [
'attributes' => ['bold'],
'foreground' => 'red',
'background' => '',
+ 'underline' => '',
],
'expected' => '1;31',
],
- 'bold, red foreground and yellow background' => [
+ 'bold, red foreground, yellow background and empty underline' => [
'config' => [
'attributes' => ['bold'],
'foreground' => 'red',
'background' => 'yellow',
+ 'underline' => '',
],
'expected' => '1;31;43',
],
- 'bold, empty foreground and yellow background' => [
+ 'bold, red foreground, yellow background and #0099ff underline' => [
+ 'config' => [
+ 'attributes' => ['bold'],
+ 'foreground' => 'red',
+ 'background' => 'yellow',
+ 'underline' => '#0099ff',
+ ],
+ 'expected' => '1;31;43;4;58;2;0;153;255',
+ ],
+ 'bold, empty foreground, yellow background and #0099ff underline' => [
'config' => [
'attributes' => ['bold'],
'foreground' => '',
'background' => 'yellow',
+ 'underline' => '#0099ff',
],
- 'expected' => '1;43',
+ 'expected' => '1;43;4;58;2;0;153;255',
],
- 'bold, italic, red foreground and yellow background' => [
+ 'bold, italic, red foreground, yellow background and #0099ff underline' => [
'config' => [
'attributes' => ['bold', 'italic'],
'foreground' => 'red',
'background' => 'yellow',
+ 'underline' => '#0099ff',
],
- 'expected' => '1;3;31;43',
+ 'expected' => '1;3;31;43;4;58;2;0;153;255',
+ ],
+ 'bold, underline, italic, red foreground, yellow background and #0099ff underline' => [
+ 'config' => [
+ 'attributes' => ['bold', 'underline', 'italic'],
+ 'foreground' => 'red',
+ 'background' => 'yellow',
+ 'underline' => '#0099ff',
+ ],
+ 'expected' => '1;4;3;31;43;58;2;0;153;255',
],
];
}
@@ -610,7 +907,7 @@ public static function provide_codes_can_return_codes_correctly(): array
#[DataProvider('provide_codes_can_return_codes_correctly')]
public function test_codes_can_return_codes_correctly(array $config, string $expected): void
{
- $this->assertSame($expected, Colorizer::codes($config));
+ $this->assertSame($expected, Colorizer::config($config)->codes());
}
public static function provide_encode_can_return_encoded_value_correctly(): array
@@ -642,6 +939,13 @@ public static function provide_encode_can_return_encoded_value_correctly(): arra
'message' => 'Hi, there!',
'expected' => "\e[31mHi, there!\e[m",
],
+ 'foreground:#ff9900' => [
+ 'config' => [
+ 'foreground' => "#ff9900",
+ ],
+ 'message' => 'Hi, there!',
+ 'expected' => "\e[38;2;255;153;0mHi, there!\e[m",
+ ],
'foreground:128' => [
'config' => [
'foreground' => 128,
@@ -663,6 +967,13 @@ public static function provide_encode_can_return_encoded_value_correctly(): arra
'message' => 'Hi, there!',
'expected' => "\e[41mHi, there!\e[m",
],
+ 'background:#0099ff' => [
+ 'config' => [
+ 'background' => "#0099ff",
+ ],
+ 'message' => 'Hi, there!',
+ 'expected' => "\e[48;2;0;153;255mHi, there!\e[m",
+ ],
'background:128' => [
'config' => [
'background' => 128,
@@ -709,6 +1020,49 @@ public function test_encode_can_return_encoded_value_correctly(array $config, st
$this->assertSame($expected, Colorizer::config($config)->encode($message));
}
+ public static function provide_echo_can_output_strings_correctly(): array
+ {
+ return [
+ 'empty' => [
+ 'config' => [],
+ 'string' => '',
+ 'expected' => '',
+ ],
+ 'empty config' => [
+ 'config' => [],
+ 'string' => 'Hello',
+ 'expected' => 'Hello',
+ ],
+ 'invalid config' => [
+ 'config' => ['bold'],
+ 'string' => 'Hello',
+ 'expected' => 'Hello',
+ ],
+ 'bold' => [
+ 'config' => ['attributes' => ['bold']],
+ 'string' => 'Hello',
+ 'expected' => "\e[1mHello\e[m",
+ ],
+ 'bold and italic' => [
+ 'config' => ['attributes' => ['bold', 'italic']],
+ 'string' => 'Hello',
+ 'expected' => "\e[1;3mHello\e[m",
+ ],
+ 'bold, italic and foreground red' => [
+ 'config' => ['attributes' => ['bold', 'italic'], 'foreground' => 'red'],
+ 'string' => 'Hello',
+ 'expected' => "\e[1;3;31mHello\e[m",
+ ],
+ ];
+ }
+
+ #[DataProvider('provide_echo_can_output_strings_correctly')]
+ public function test_echo_can_output_strings_correctly(array $config, string $string, string $expected): void
+ {
+ $this->expectOutputString($expected);
+ Colorizer::config($config)->echo($string);
+ }
+
public static function provide_readable_can_return_readable_value_correctly(): array
{
return [
@@ -738,6 +1092,13 @@ public static function provide_readable_can_return_readable_value_correctly(): a
'message' => 'Hi, there!',
'expected' => "\\033[31mHi, there!\\033[m",
],
+ 'foreground:#0099ff' => [
+ 'config' => [
+ 'foreground' => "#0099ff",
+ ],
+ 'message' => 'Hi, there!',
+ 'expected' => "\\033[38;2;0;153;255mHi, there!\\033[m",
+ ],
'foreground:128' => [
'config' => [
'foreground' => 128,
@@ -759,6 +1120,13 @@ public static function provide_readable_can_return_readable_value_correctly(): a
'message' => 'Hi, there!',
'expected' => "\\033[41mHi, there!\\033[m",
],
+ 'background:#ff9900' => [
+ 'config' => [
+ 'background' => "#ff9900",
+ ],
+ 'message' => 'Hi, there!',
+ 'expected' => "\\033[48;2;255;153;0mHi, there!\\033[m",
+ ],
'background:128' => [
'config' => [
'background' => 128,
diff --git a/tests/ConverterTest.php b/tests/ConverterTest.php
new file mode 100644
index 0000000..4096330
--- /dev/null
+++ b/tests/ConverterTest.php
@@ -0,0 +1,42 @@
+ ['rgb' => '#000', 'expected' => [0, 0, 0]],
+ '#123' => ['rgb' => '#123', 'expected' => [17, 34, 51]],
+ '#fed' => ['rgb' => '#fed', 'expected' => [255, 238, 221]],
+ '#000000' => ['rgb' => '#000000', 'expected' => [0, 0, 0]],
+ '#123456' => ['rgb' => '#123456', 'expected' => [18, 52, 86]],
+ '#ffeedd' => ['rgb' => '#ffeedd', 'expected' => [255, 238, 221]],
+
+ 'fff' => ['rgb' => 'fff', 'expected' => []],
+ 'fffffff' => ['rgb' => 'fffffff', 'expected' => []],
+ '#' => ['rgb' => '#', 'expected' => []],
+ '#1' => ['rgb' => '#1', 'expected' => []],
+ '#12' => ['rgb' => '#12', 'expected' => []],
+ '#efg' => ['rgb' => '#efg', 'expected' => []],
+ '#1234' => ['rgb' => '#1234', 'expected' => []],
+ '#12345' => ['rgb' => '#12345', 'expected' => []],
+ '#bcdefg' => ['rgb' => '#bcdefg', 'expected' => []],
+ '#1234567' => ['rgb' => '#1234567', 'expected' => []],
+ '#abcdefg' => ['rgb' => '#abcdefg', 'expected' => []],
+ ];
+ }
+
+ #[DataProvider('provide_decimal_can_return_correct_array')]
+ public function test_decimal_can_return_correct_array(string $rgb, array $expected): void
+ {
+ $this->assertSame($expected, Converter::decimal($rgb));
+ }
+}
diff --git a/tests/Enums/AttributeTest.php b/tests/Enums/AttributeTest.php
index 1cfadc8..25a590d 100644
--- a/tests/Enums/AttributeTest.php
+++ b/tests/Enums/AttributeTest.php
@@ -30,9 +30,16 @@ public static function provide_code_can_return_correct_code(): array
'attribute:NoItalic' => ['enum' => Attribute::NoItalic, 'expected' => 23],
'attribute:NoUnderline' => ['enum' => Attribute::NoUnderline, 'expected' => 24],
'attribute:NoBlink' => ['enum' => Attribute::NoBlink, 'expected' => 25],
+ 'attribute:ProportionalSpacing' => ['enum' => Attribute::ProportionalSpacing, 'expected' => 26],
'attribute:NoReverse' => ['enum' => Attribute::NoReverse, 'expected' => 27],
'attribute:NoConceal' => ['enum' => Attribute::NoConceal, 'expected' => 28],
- 'attribute:NoStrike' => ['enum' => Attribute::NoStrike, 'expected' => 29],
+ 'attribute:NoProportionalSpacing' => ['enum' => Attribute::NoProportionalSpacing, 'expected' => 50],
+ 'attribute:Framed' => ['enum' => Attribute::Framed, 'expected' => 51],
+ 'attribute:Encircled' => ['enum' => Attribute::Encircled, 'expected' => 52],
+ 'attribute:Overlined' => ['enum' => Attribute::Overlined, 'expected' => 53],
+ 'attribute:NoFramedNoEncircled' => ['enum' => Attribute::NoFramedNoEncircled, 'expected' => 54],
+ 'attribute:NoOverlined' => ['enum' => Attribute::NoOverlined, 'expected' => 55],
+ 'attribute:UnderlineColor' => ['enum' => Attribute::UnderlineColor, 'expected' => 58],
];
}
@@ -61,9 +68,17 @@ public function test_codes_can_return_correct_codes(): void
'no-italic' => 23,
'no-underline' => 24,
'no-blink' => 25,
+ 'proportional-spacing' => 26,
'no-reverse' => 27,
'no-conceal' => 28,
'no-strike' => 29,
+ 'no-proportional-spacing' => 50,
+ 'framed' => 51,
+ 'encircled' => 52,
+ 'overlined' => 53,
+ 'no-framed-no-encircled' => 54,
+ 'no-overlined' => 55,
+ 'underline-color' => 58,
];
$this->assertSame($codes, Attribute::codes());
}
diff --git a/tests/Traits/ValidationTraitTest.php b/tests/Traits/ValidationTraitTest.php
new file mode 100644
index 0000000..2c4c1c9
--- /dev/null
+++ b/tests/Traits/ValidationTraitTest.php
@@ -0,0 +1,106 @@
+ ['rgb' => '', 'expected' => false],
+ '123' => ['rgb' => '123', 'expected' => false],
+ '123456' => ['rgb' => '123456', 'expected' => false],
+ '#' => ['rgb' => '#', 'expected' => false],
+ '#1' => ['rgb' => '#1', 'expected' => false],
+ '#12' => ['rgb' => '#12', 'expected' => false],
+ '#123' => ['rgb' => '#123', 'expected' => true],
+ '#1234' => ['rgb' => '#1234', 'expected' => false],
+ '#12345' => ['rgb' => '#12345', 'expected' => false],
+ '#123456' => ['rgb' => '#123456', 'expected' => false],
+ '#1234567' => ['rgb' => '#1234567', 'expected' => false],
+ '#efg' => ['rgb' => '#efg', 'expected' => false],
+ '#def' => ['rgb' => '#def', 'expected' => true],
+ '#000' => ['rgb' => '#000', 'expected' => true],
+ '#1af' => ['rgb' => '#1af', 'expected' => true],
+ ];
+ }
+
+ #[DataProvider('provide_isShortHex_can_return_correct_bool')]
+ public function test_isShortHex_can_return_correct_bool(string $rgb, bool $expected): void
+ {
+ $validator = new class {
+ use ValidationTrait;
+ };
+ $this->assertSame($expected, $validator::isShortHex($rgb));
+ }
+
+ public static function provide_isLongHex_can_return_correct_bool(): array
+ {
+ return [
+ 'empty' => ['rgb' => '', 'expected' => false],
+ 'fff' => ['rgb' => 'fff', 'expected' => false],
+ 'ffffff' => ['rgb' => 'ffffff', 'expected' => false],
+ '#' => ['rgb' => '#', 'expected' => false],
+ '#1' => ['rgb' => '#1', 'expected' => false],
+ '#12' => ['rgb' => '#12', 'expected' => false],
+ '#123' => ['rgb' => '#123', 'expected' => false],
+ '#1234' => ['rgb' => '#1234', 'expected' => false],
+ '#12345' => ['rgb' => '#12345', 'expected' => false],
+ '#123456' => ['rgb' => '#123456', 'expected' => true],
+ '#1234567' => ['rgb' => '#1234567', 'expected' => false],
+ '#000000' => ['rgb' => '#000000', 'expected' => true],
+ '#abcdef' => ['rgb' => '#abcdef', 'expected' => true],
+ '#bcdefg' => ['rgb' => '#bcdefg', 'expected' => false],
+ '#123def' => ['rgb' => '#123def', 'expected' => true],
+ ];
+ }
+
+ #[DataProvider('provide_isLongHex_can_return_correct_bool')]
+ public function test_isLongHex_can_return_correct_bool(string $rgb, bool $expected): void
+ {
+ $validator = new class {
+ use ValidationTrait;
+ };
+ $this->assertSame($expected, $validator::isLongHex($rgb));
+ }
+
+ public static function provide_isHex_can_return_correct_bool(): array
+ {
+ return [
+ 'empty' => ['rgb' => '', 'expected' => false],
+ '123' => ['rgb' => '123', 'expected' => false],
+ '123456' => ['rgb' => '123456', 'expected' => false],
+ '#' => ['rgb' => '#', 'expected' => false],
+ '#1' => ['rgb' => '#1', 'expected' => false],
+ '#12' => ['rgb' => '#12', 'expected' => false],
+ '#123' => ['rgb' => '#123', 'expected' => true],
+ '#1234' => ['rgb' => '#1234', 'expected' => false],
+ '#12345' => ['rgb' => '#12345', 'expected' => false],
+ '#123456' => ['rgb' => '#123456', 'expected' => true],
+ '#1234567' => ['rgb' => '#1234567', 'expected' => false],
+ '#000' => ['rgb' => '#000', 'expected' => true],
+ '#def' => ['rgb' => '#def', 'expected' => true],
+ '#efg' => ['rgb' => '#efg', 'expected' => false],
+ '#09f' => ['rgb' => '#09f', 'expected' => true],
+ '#000000' => ['rgb' => '#000000', 'expected' => true],
+ '#abcdef' => ['rgb' => '#abcdef', 'expected' => true],
+ '#bcdefg' => ['rgb' => '#bcdefg', 'expected' => false],
+ '#123def' => ['rgb' => '#123def', 'expected' => true],
+ ];
+ }
+
+ #[DataProvider('provide_isHex_can_return_correct_bool')]
+ public function test_isHex_can_return_correct_bool(string $rgb, bool $expected): void
+ {
+ $validator = new class {
+ use ValidationTrait;
+ };
+ $this->assertSame($expected, $validator::isHex($rgb));
+ }
+}