From d79d2943e34414458e4e370ebb964b4ab3f07712 Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Sun, 9 Nov 2025 19:42:59 -0600 Subject: [PATCH 01/10] Update sample.php --- captainhook.json | 2 +- composer.json | 2 +- sample.php | 116 +++++++++++++++++------------------------------ 3 files changed, 44 insertions(+), 76 deletions(-) diff --git a/captainhook.json b/captainhook.json index 0c101ef..9f83110 100644 --- a/captainhook.json +++ b/captainhook.json @@ -18,7 +18,7 @@ "conditions": [] }, { - "action": "composer test:phpunit -- --colors=never", + "action": "phpunit --colors=never", "options": [], "conditions": [] } diff --git a/composer.json b/composer.json index de66653..bd4a2de 100644 --- a/composer.json +++ b/composer.json @@ -74,7 +74,7 @@ "@test:phpunit" ], "test:coverage": "phpunit --coverage-clover=coverage.xml", - "test:phpunit": "phpunit", + "test:phpunit": "phpunit --colors=always", "test:static": "phpstan analyze --ansi" }, "scripts-descriptions": { diff --git a/sample.php b/sample.php index b72f2e1..9981298 100755 --- a/sample.php +++ b/sample.php @@ -4,9 +4,7 @@ declare(strict_types=1); use BeBat\ConsoleColor\Style; - -use const PHP_EOL; -use const STR_PAD_LEFT; +use BeBat\ConsoleColor\StyleInterface; $localPath = __DIR__ . '/vendor/autoload.php'; $installedPath = dirname(__DIR__, 2) . '/autoload.php'; @@ -22,30 +20,35 @@ $style = new Style(); $style->force(); -echo 'Styling supported: ' . ($style->supportsStyles ? 'Yes' : 'No') . PHP_EOL; -echo '256 colors supported: ' . ($style->supports256Colors ? 'Yes' : 'No') . PHP_EOL; -echo '24-bit true colors supported: ' . ($style->supportsRGBColors ? 'Yes' : 'No') . PHP_EOL; +echo 'Styling supported: ' . ($style->supportsStyles ? 'Yes' : 'No') . \PHP_EOL; +echo '256 colors supported: ' . ($style->supports256Colors ? 'Yes' : 'No') . \PHP_EOL; +echo '24-bit true colors supported: ' . ($style->supportsRGBColors ? 'Yes' : 'No') . \PHP_EOL; -echo PHP_EOL; +echo \PHP_EOL; function printEnumStyleSample(string $heading, string $enum): void { global $style; - echo $heading . PHP_EOL; + echo $heading . \PHP_EOL; foreach ($enum::cases() as $styleValue) { - echo ' ' . $style->apply($styleValue->name, $styleValue) . PHP_EOL; + echo ' ' . $style->apply($styleValue->name, $styleValue) . \PHP_EOL; } - echo PHP_EOL; + echo \PHP_EOL; } -function print256ColorSample(string $heading, string $method): void -{ +function print256ColorSample( + string $heading, + string $method, + StyleInterface $additionalStyle = Style\Text::None, +): void { global $style; - echo "256 {$heading} Colors" . PHP_EOL; + echo "256 {$heading} Colors" . \PHP_EOL; + + $style->autoTerminate(false); for ($i = 0; $i < 256; ++$i) { if ($i === 0 || $i < 231 && $i % 18 === 16 || 231 < $i && $i % 12 === 4) { @@ -53,26 +56,34 @@ function print256ColorSample(string $heading, string $method): void } echo $style->apply( - str_pad((string) $i, 4, ' ', STR_PAD_LEFT), - Style\Color256::{$method}($i), + str_pad((string) $i, 4, ' ', \STR_PAD_LEFT), + new Style\Composite($additionalStyle, Style\Color256::{$method}($i)), ); if ($i === 15 || $i === 231) { - echo PHP_EOL . PHP_EOL; + echo $style->terminate(); + echo \PHP_EOL . \PHP_EOL; } if (15 < $i && $i < 231 && $i % 18 === 15 || $i === 243 || $i === 255) { - echo PHP_EOL; + echo $style->terminate(); + echo \PHP_EOL; } } - echo PHP_EOL; + echo $style->apply(\PHP_EOL, Style\Text::None); + $style->autoTerminate(); } -function printTrueColorSample(string $heading, string $method): void -{ +function printTrueColorSample( + string $heading, + string $method, + StyleInterface $additionalStyle = Style\Text::None, +): void { global $style; - echo "True Color {$heading} (Abridged)" . PHP_EOL; + echo "True Color {$heading} (Abridged)" . \PHP_EOL; + + $style->autoTerminate(false); for ($red = 0; $red < 8; ++$red) { for ($green = 0; $green < 8; ++$green) { @@ -83,74 +94,31 @@ function printTrueColorSample(string $heading, string $method): void echo $style->apply( 'X', - Style\ColorRGB::{$method}($red * 32, $green * 32, $blue * 32), + new Style\Composite( + $additionalStyle, + Style\ColorRGB::{$method}($red * 32, $green * 32, $blue * 32), + ), ); if ($blue % 8 === 7 && $green % 8 === 7) { - echo PHP_EOL; + echo $style->terminate(); + echo \PHP_EOL; } } } } - echo PHP_EOL; + echo $style->apply(\PHP_EOL, Style\Text::None); + $style->autoTerminate(); } printEnumStyleSample('Text Styles', Style\Text::class); printEnumStyleSample('Underline Styles', Style\Underline::class); printEnumStyleSample('Foreground Colors', Style\Color::class); printEnumStyleSample('Background Colors', Style\BackgroundColor::class); - print256ColorSample('Foreground', 'foreground'); print256ColorSample('Background', 'background'); - -echo '256 Underline Colors' . PHP_EOL; - -for ($i = 0; $i < 256; ++$i) { - if ($i === 0 || $i < 231 && $i % 18 === 16 || 231 < $i && $i % 12 === 4) { - echo ' '; - } - - echo $style->apply( - str_pad((string) $i, 4, ' ', STR_PAD_LEFT), - new Style\Composite(Style\Text::Underline, Style\Color256::underline($i)), - ); - - if ($i === 15 || $i === 231) { - echo PHP_EOL . PHP_EOL; - } - if (15 < $i && $i < 231 && $i % 18 === 15 || $i === 243 || $i === 255) { - echo PHP_EOL; - } -} - -echo PHP_EOL; - +print256ColorSample('Underline', 'underline', Style\Text::Underline); printTrueColorSample('Foreground', 'foreground'); printTrueColorSample('Background', 'background'); - -echo 'True Color Underline (Abridged)' . PHP_EOL; - -for ($red = 0; $red < 8; ++$red) { - for ($green = 0; $green < 8; ++$green) { - for ($blue = 0; $blue < 8; ++$blue) { - if ($blue % 8 === 0 && $green % 8 === 0) { - echo ' '; - } - - echo $style->apply( - 'X', - new Style\Composite( - Style\Text::Underline, - Style\ColorRGB::underline($red * 32, $green * 32, $blue * 32), - ), - ); - - if ($blue % 8 === 7 && $green % 8 === 7) { - echo PHP_EOL; - } - } - } -} - -echo PHP_EOL; +printTrueColorSample('Underline', 'underline', Style\Text::Underline); From d584688a07935e9a0c267607dc2548f816bf9258 Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Sun, 9 Nov 2025 22:41:28 -0600 Subject: [PATCH 02/10] Update docs --- README.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9303d3c..e3413f5 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,13 @@ Console Color is a lightweight PHP 8.1+ library for adding color & other styles - [Installation](#installation) - [Basic Usage](#basic-usage) - [Environment Variables](#environment-variables) + - [Auto Termination](#auto-termination) - [Included Styles](#included-styles) - [Basic Styles](#basic-styles) - [Text](#text) - [Underline](#underline) - - [Foreground & Background Color](#foreground--background-color) - - [256 & True Color](#256--true-color) + - [Foreground \& Background Color](#foreground--background-color) + - [256 \& True Color](#256--true-color) - [Composite Styles](#composite-styles) ## Installation @@ -77,6 +78,69 @@ In addition to checking if `STDOUT` is a TTY, `Style` will look at several envir * `TERM` - `Style` will check `TERM` to see if it supports 256 colors. * `COLORTERM` - If `COLORTERM` is set to `truecolor` then `Style` will apply RGB based colors. +### Auto Termination + +By default, Console Color will "terminate" each style by appending `Style\Text::None` after whatever text you are applying styles to. This is helpful so you don't accidentally make all the text in the terminal bright red, for example. However, if you are outputting many styles to the screen and would like more control on when they are terminated this can be disabled globally or at call time. + +To disable termination globally, use the `autoTerminate()` method like so: + +```php +use BeBat\ConsoleColor\Style; + +$style = new Style(); +$style->autoTerminate(false); + +echo $style->apply("Didn't I just warn you about this?\n", Style\Color::BrightRed); +``` + +Auto termination can be re-enabled by passing `true` to `autoTerminate()` as well. + +Auto termination can also be disabled at call time by passing `false` as the third parameter to `apply()`: + +```php +use BeBat\ConsoleColor\Style; + +$style = new Style(); + +echo $style->apply("This style wont't stop!\n", Style\Color::Yellow, false); +echo $style->apply("Now we're mixing and matching styles?\n", Style\BackgroundColor::BrightRed, false); +echo $style->apply("Let's stop things before they get too out of hand\n", Style\Text::Underline, true); +``` + +The `Style` instance will attempt to keep track of whether the previous style was terminated or not. You can use `willAutoTerminate()` and `isActive()` to determine whether auto termination is enabled and if there are styles currently active on the output stream. To manually end styling, you may output `terminate()`: + +```php +use BeBat\ConsoleColor\Style; + +$style = new Style(); +$style->autoTerminate(false); + +try { + echo $style->apply("We're about to attempt something that could fail!\n", Style\BackgroundColor::BrightYellow); + + // ... +} catch (\Throwable $e) { + if ($style->isActive()) { + // Previously applied style(s) weren't stopped + echo $style->terminate(); + } +} +``` + +Lastly, the `apply()` method will also check to see if styles were terminated explicitly by being passed `Style\Text::None`: + +```php +use BeBat\ConsoleColor\Style; + +$style = new Style(); +$style->autoTerminate(false); + +echo $style->apply("Let's keep styling forever!\n", Style\Color::Blue); +$style->isActive(); // => true +echo $style->apply("Never mind, I've grown bored of such things.\n", Style\Text::None); +$style->isActive(); // => false +``` + ## Included Styles `Style::apply()` accepts an instance of [`StyleInterface`](src/StyleInterface.php), and Console Color includes a number of styles that implement this interface. From 690c7b9ff198d9276f114646739d164b6e9475b7 Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Sun, 9 Nov 2025 22:53:59 -0600 Subject: [PATCH 03/10] Clean up readme text slightly --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e3413f5..3af665e 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ In addition to checking if `STDOUT` is a TTY, `Style` will look at several envir By default, Console Color will "terminate" each style by appending `Style\Text::None` after whatever text you are applying styles to. This is helpful so you don't accidentally make all the text in the terminal bright red, for example. However, if you are outputting many styles to the screen and would like more control on when they are terminated this can be disabled globally or at call time. -To disable termination globally, use the `autoTerminate()` method like so: +To disable termination globally, pass `false` to `autoTerminate()` like so: ```php use BeBat\ConsoleColor\Style; @@ -93,7 +93,7 @@ $style->autoTerminate(false); echo $style->apply("Didn't I just warn you about this?\n", Style\Color::BrightRed); ``` -Auto termination can be re-enabled by passing `true` to `autoTerminate()` as well. +Auto termination can be re-enabled simply by calling `autoTerminate()` as well. Auto termination can also be disabled at call time by passing `false` as the third parameter to `apply()`: @@ -107,7 +107,9 @@ echo $style->apply("Now we're mixing and matching styles?\n", Style\BackgroundCo echo $style->apply("Let's stop things before they get too out of hand\n", Style\Text::Underline, true); ``` -The `Style` instance will attempt to keep track of whether the previous style was terminated or not. You can use `willAutoTerminate()` and `isActive()` to determine whether auto termination is enabled and if there are styles currently active on the output stream. To manually end styling, you may output `terminate()`: +The `Style` instance keeps track of whether the previous style was terminated or not. You can use `willAutoTerminate()` and `isActive()` to determine whether auto termination is enabled and if there are styles currently active on the output stream. + +To manually end styling, you can output `terminate()`: ```php use BeBat\ConsoleColor\Style; @@ -127,7 +129,7 @@ try { } ``` -Lastly, the `apply()` method will also check to see if styles were terminated explicitly by being passed `Style\Text::None`: +Lastly, the `apply()` method will also check to see if styles were terminated by being passed `Style\Text::None`: ```php use BeBat\ConsoleColor\Style; @@ -137,6 +139,7 @@ $style->autoTerminate(false); echo $style->apply("Let's keep styling forever!\n", Style\Color::Blue); $style->isActive(); // => true + echo $style->apply("Never mind, I've grown bored of such things.\n", Style\Text::None); $style->isActive(); // => false ``` @@ -238,7 +241,7 @@ echo $style->apply( ) . PHP_EOL; ``` -`Style\Composite()` can actually take as many styles as you need to apply: +`Style\Composite()` can take as many styles as you need to apply at once: ```php use BeBat\ConsoleColor\Style; From 826e23100da8a87cf83e64ee977244878a7e7081 Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Wed, 10 Dec 2025 21:57:58 -0600 Subject: [PATCH 04/10] Bump PHPStan version; move prop assignment to constructor --- captainhook.json | 2 +- composer.json | 10 +++---- phpstan.neon.dist | 2 -- src/Style.php | 76 ++++++++++++++++++++--------------------------- 4 files changed, 39 insertions(+), 51 deletions(-) diff --git a/captainhook.json b/captainhook.json index 9f83110..449effa 100644 --- a/captainhook.json +++ b/captainhook.json @@ -18,7 +18,7 @@ "conditions": [] }, { - "action": "phpunit --colors=never", + "action": "vendor/bin/phpunit --colors=never", "options": [], "conditions": [] } diff --git a/composer.json b/composer.json index bd4a2de..aadb9d0 100644 --- a/composer.json +++ b/composer.json @@ -33,11 +33,11 @@ "maglnet/composer-require-checker": "~4.6", "mockery/mockery": "~1.6.2", "phpstan/extension-installer": "~1.4.3", - "phpstan/phpstan": "~1.12.32", - "phpstan/phpstan-deprecation-rules": "~1.2.1", - "phpstan/phpstan-mockery": "~1.1.1", - "phpstan/phpstan-phpunit": "~1.4.2", - "phpstan/phpstan-strict-rules": "~1.6.2", + "phpstan/phpstan": "~2.1.32", + "phpstan/phpstan-deprecation-rules": "~2.0.3", + "phpstan/phpstan-mockery": "~2.0.0", + "phpstan/phpstan-phpunit": "~2.0.8", + "phpstan/phpstan-strict-rules": "~2.0.7", "phpunit/phpunit": "~10.5.58", "zalas/phpunit-globals": "~4.0.1" }, diff --git a/phpstan.neon.dist b/phpstan.neon.dist index fd24ba2..9309fdb 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,8 +3,6 @@ parameters: paths: - src - test - checkMissingIterableValueType: true ignoreErrors: - - '/Readonly property BeBat\\ConsoleColor\\Style::.+ is assigned outside of the constructor/' - '/Call to an undefined method BeBat\\Verify\\API\\Value::/' - '/Access to an undefined property BeBat\\Verify\\API\\Value::/' diff --git a/src/Style.php b/src/Style.php index da4a3c2..5ab2f9f 100644 --- a/src/Style.php +++ b/src/Style.php @@ -20,7 +20,39 @@ final class Style implements ApplierInterface */ public function __construct($resource = \STDOUT) { - $this->checkSupport($resource); + if (!\in_array(\PHP_SAPI, ['cli', 'cli-server', 'phpdbg'], true) + || getenv('NO_COLOR') !== false + ) { + $this->supportsStyles = false; + $this->supports256Colors = false; + $this->supportsRGBColors = false; + + return; + } + + if ((PHP_OS_FAMILY === 'Windows' && sapi_windows_vt100_support($resource)) + || stream_isatty($resource) || getenv('FORCE_COLOR') !== false + ) { + $this->supportsStyles = true; + + if ((PHP_OS_FAMILY === 'Windows' + && (getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON')) + || getenv('COLORTERM') === 'truecolor' + ) { + $this->supports256Colors = true; + $this->supportsRGBColors = true; + } elseif (str_contains((string) getenv('TERM'), '256color')) { + $this->supports256Colors = true; + $this->supportsRGBColors = false; + } else { + $this->supports256Colors = false; + $this->supportsRGBColors = false; + } + } else { + $this->supportsStyles = false; + $this->supports256Colors = false; + $this->supportsRGBColors = false; + } } public function autoTerminate(bool $autoTerminate = true): void @@ -99,48 +131,6 @@ public function willAutoTerminate(): bool return $this->autoTerminate; } - /** - * Does the default output (STDOUT) support styling? - * - * @param resource $resource - */ - private function checkSupport($resource): void - { - if (!\in_array(\PHP_SAPI, ['cli', 'cli-server', 'phpdbg'], true) - || getenv('NO_COLOR') !== false - ) { - $this->supportsStyles = false; - $this->supports256Colors = false; - $this->supportsRGBColors = false; - - return; - } - - if ((PHP_OS_FAMILY === 'Windows' && sapi_windows_vt100_support($resource)) - || stream_isatty($resource) || getenv('FORCE_COLOR') !== false - ) { - $this->supportsStyles = true; - - if ((PHP_OS_FAMILY === 'Windows' - && (getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON')) - || getenv('COLORTERM') === 'truecolor' - ) { - $this->supports256Colors = true; - $this->supportsRGBColors = true; - } elseif (str_contains((string) getenv('TERM'), '256color')) { - $this->supports256Colors = true; - $this->supportsRGBColors = false; - } else { - $this->supports256Colors = false; - $this->supportsRGBColors = false; - } - } else { - $this->supportsStyles = false; - $this->supports256Colors = false; - $this->supportsRGBColors = false; - } - } - /** * Get style terminator sequence. */ From 45f0fd3144354dd673f369011609c76a05d9494f Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Wed, 10 Dec 2025 21:59:07 -0600 Subject: [PATCH 05/10] Enable PHP 8.5 GitHub action --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 2902379..07a00f9 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -23,7 +23,7 @@ jobs: - php: "8.2" - php: "8.3" - php: "8.4" - # - php: "8.5" + - php: "8.5" steps: - uses: actions/checkout@v3 From df742454b589a26160d35a410c276a2f402c2b40 Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Thu, 11 Dec 2025 18:40:38 -0600 Subject: [PATCH 06/10] Tweak dependencies --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index aadb9d0..4cfd9e1 100644 --- a/composer.json +++ b/composer.json @@ -28,9 +28,9 @@ "bebat/verify": "~3.2.0", "captainhook/captainhook": "~5.25.11", "captainhook/plugin-composer": "~5.3.3", - "ergebnis/composer-normalize": "~2.33", - "friendsofphp/php-cs-fixer": "^3.89.2", - "maglnet/composer-require-checker": "~4.6", + "ergebnis/composer-normalize": "~2.48.2", + "friendsofphp/php-cs-fixer": "~3.91.3", + "maglnet/composer-require-checker": "^4.6", "mockery/mockery": "~1.6.2", "phpstan/extension-installer": "~1.4.3", "phpstan/phpstan": "~2.1.32", From 478b437fc6f565f405586064baecf2afa0301a98 Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Thu, 11 Dec 2025 18:40:59 -0600 Subject: [PATCH 07/10] Refactor sample script --- sample.php | 183 +++++++++++++++++++++++++++++------------------------ 1 file changed, 102 insertions(+), 81 deletions(-) diff --git a/sample.php b/sample.php index 9981298..4d54830 100755 --- a/sample.php +++ b/sample.php @@ -3,6 +3,7 @@ declare(strict_types=1); +use BeBat\ConsoleColor\ApplierInterface; use BeBat\ConsoleColor\Style; use BeBat\ConsoleColor\StyleInterface; @@ -17,108 +18,128 @@ throw new RuntimeException('Could not find autoload file. Make sure to run "composer install" first.'); } -$style = new Style(); -$style->force(); - -echo 'Styling supported: ' . ($style->supportsStyles ? 'Yes' : 'No') . \PHP_EOL; -echo '256 colors supported: ' . ($style->supports256Colors ? 'Yes' : 'No') . \PHP_EOL; -echo '24-bit true colors supported: ' . ($style->supportsRGBColors ? 'Yes' : 'No') . \PHP_EOL; - -echo \PHP_EOL; - -function printEnumStyleSample(string $heading, string $enum): void +final class StyleDemo { - global $style; + private const string INDENT = ' '; + + public function __construct(private ApplierInterface $applier) {} + + /** + * @param class-string $enum + */ + public function enumStyle(string $enum): string + { + return array_reduce( + $enum::cases(), + fn (string $result, StyleInterface|UnitEnum $style): string => $result . self::INDENT . $this->applier->apply($style->name, $style) . \PHP_EOL, + '', + ); + } - echo $heading . \PHP_EOL; + /** + * @param 'background'|'foreground'|'underline' $method + */ + public function style256Color(string $method): string + { + $result = ''; - foreach ($enum::cases() as $styleValue) { - echo ' ' . $style->apply($styleValue->name, $styleValue) . \PHP_EOL; - } + $this->applier->autoTerminate(false); - echo \PHP_EOL; -} + for ($i = 0; $i < 256; ++$i) { + $style = Style\Color256::{$method}($i); -function print256ColorSample( - string $heading, - string $method, - StyleInterface $additionalStyle = Style\Text::None, -): void { - global $style; + if ($method === 'underline') { + $style = new Style\Composite(Style\Text::Underline, $style); + } - echo "256 {$heading} Colors" . \PHP_EOL; + if ($i === 0 || $i < 231 && $i % 18 === 16 || 231 < $i && $i % 12 === 4) { + $result .= self::INDENT; + } - $style->autoTerminate(false); + $result .= $this->applier->apply( + str_pad((string) $i, 4, ' ', \STR_PAD_LEFT), + $style, + ); - for ($i = 0; $i < 256; ++$i) { - if ($i === 0 || $i < 231 && $i % 18 === 16 || 231 < $i && $i % 12 === 4) { - echo ' '; + if ($i === 15 || $i === 231) { + $result .= $this->applier->terminate() . \PHP_EOL . \PHP_EOL; + } + if (15 < $i && $i < 231 && $i % 18 === 15 || $i === 243 || $i === 255) { + $result .= $this->applier->terminate() . \PHP_EOL; + } } - echo $style->apply( - str_pad((string) $i, 4, ' ', \STR_PAD_LEFT), - new Style\Composite($additionalStyle, Style\Color256::{$method}($i)), - ); + $this->applier->autoTerminate(); - if ($i === 15 || $i === 231) { - echo $style->terminate(); - echo \PHP_EOL . \PHP_EOL; - } - if (15 < $i && $i < 231 && $i % 18 === 15 || $i === 243 || $i === 255) { - echo $style->terminate(); - echo \PHP_EOL; - } + return $result; } - echo $style->apply(\PHP_EOL, Style\Text::None); - $style->autoTerminate(); -} + /** + * @param 'background'|'foreground'|'underline' $method + */ + public function styleTrueColor(string $method): string + { + $result = ''; -function printTrueColorSample( - string $heading, - string $method, - StyleInterface $additionalStyle = Style\Text::None, -): void { - global $style; + $this->applier->autoTerminate(false); - echo "True Color {$heading} (Abridged)" . \PHP_EOL; + for ($red = 0; $red < 8; ++$red) { + for ($green = 0; $green < 8; ++$green) { + for ($blue = 0; $blue < 8; ++$blue) { + $style = Style\ColorRGB::{$method}($red * 32, $green * 32, $blue * 32); - $style->autoTerminate(false); + if ($method === 'underline') { + $style = new Style\Composite(Style\Text::Underline, $style); + } - for ($red = 0; $red < 8; ++$red) { - for ($green = 0; $green < 8; ++$green) { - for ($blue = 0; $blue < 8; ++$blue) { - if ($blue % 8 === 0 && $green % 8 === 0) { - echo ' '; - } + if ($blue % 8 === 0 && $green % 8 === 0) { + $result .= self::INDENT; + } + + $result .= $this->applier->apply('X', $style); - echo $style->apply( - 'X', - new Style\Composite( - $additionalStyle, - Style\ColorRGB::{$method}($red * 32, $green * 32, $blue * 32), - ), - ); - - if ($blue % 8 === 7 && $green % 8 === 7) { - echo $style->terminate(); - echo \PHP_EOL; + if ($blue % 8 === 7 && $green % 8 === 7) { + $result .= $this->applier->terminate() . \PHP_EOL; + } } } } - } - echo $style->apply(\PHP_EOL, Style\Text::None); - $style->autoTerminate(); + $this->applier->autoTerminate(); + + return $result; + } } -printEnumStyleSample('Text Styles', Style\Text::class); -printEnumStyleSample('Underline Styles', Style\Underline::class); -printEnumStyleSample('Foreground Colors', Style\Color::class); -printEnumStyleSample('Background Colors', Style\BackgroundColor::class); -print256ColorSample('Foreground', 'foreground'); -print256ColorSample('Background', 'background'); -print256ColorSample('Underline', 'underline', Style\Text::Underline); -printTrueColorSample('Foreground', 'foreground'); -printTrueColorSample('Background', 'background'); -printTrueColorSample('Underline', 'underline', Style\Text::Underline); +$style = new Style(); +$style->force(); +$demo = new StyleDemo($style); + +echo 'Styling supported: ' . ($style->supportsStyles ? 'Yes' : 'No') . \PHP_EOL; +echo '256 colors supported: ' . ($style->supports256Colors ? 'Yes' : 'No') . \PHP_EOL; +echo '24-bit true colors supported: ' . ($style->supportsRGBColors ? 'Yes' : 'No') . \PHP_EOL; + +echo \PHP_EOL; + +echo 'Text Styles' . \PHP_EOL; +echo $demo->enumStyle(Style\Text::class) . \PHP_EOL; +echo 'Underline Styles' . \PHP_EOL; +echo $demo->enumStyle(Style\Underline::class) . \PHP_EOL; +echo 'Foreground Colors' . \PHP_EOL; +echo $demo->enumStyle(Style\Color::class) . \PHP_EOL; +echo 'Background Colors' . \PHP_EOL; +echo $demo->enumStyle(Style\BackgroundColor::class) . \PHP_EOL; + +echo \PHP_EOL . '256 Foreground Colors' . \PHP_EOL; +echo $demo->style256Color('foreground'); +echo \PHP_EOL . '256 Background Colors' . \PHP_EOL; +echo $demo->style256Color('background'); +echo \PHP_EOL . '256 Underline Colors' . \PHP_EOL; +echo $demo->style256Color('underline'); + +echo \PHP_EOL . 'True Color Foreground (Abridged)' . \PHP_EOL; +echo $demo->styleTrueColor('foreground'); +echo \PHP_EOL . 'True Color Background (Abridged)' . \PHP_EOL; +echo $demo->styleTrueColor('background'); +echo \PHP_EOL . 'True Color Underline (Abridged)' . \PHP_EOL; +echo $demo->styleTrueColor('underline'); From 98718d4dec0496f572998901840ca065f6b05f6e Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Thu, 11 Dec 2025 18:42:35 -0600 Subject: [PATCH 08/10] Add security, PR & issue templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 55 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 34 +++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 40 ++++++++++++++++ SECURITY.md | 3 ++ 4 files changed, 132 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..3b20d51 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,55 @@ +name: Bug Report +description: Report an issue with ConsoleColor +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: Thank you for taking the time to fill out this bug report! + + - type: textarea + attributes: + label: Description + description: A brief description of the bug + validations: + required: true + + - type: textarea + attributes: + label: Steps to Reproduce + description: What was the process that led to this bug? + value: | + 1. + 2. + 3. + ... + validations: + required: true + + - type: textarea + attributes: + label: Expected Result + description: What did you expect to happen when following the steps to reproduce? + validations: + required: true + + - type: textarea + attributes: + label: Actual Result + description: What actually happened? + validations: + required: true + + - type: input + attributes: + label: ConsoleColor Version + placeholder: "1.2.3" + validations: + required: true + + - type: input + attributes: + label: PHP Version + placeholder: "8.x.x" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..d7453f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,34 @@ +name: Feature Request +description: Propose a new feature for ConsoleColor +title: "[Feature]: " +labels: ["feature"] +body: + - type: markdown + attributes: + value: Thank you for taking the time to propose a new feature! + + - type: textarea + attributes: + label: Description + description: A brief description of the feature you'd like to propose + validations: + required: true + + - type: textarea + attributes: + label: Example Code + description: | + One or more examples of how you think the feature should work and what + its results would be + render: php + + - type: dropdown + attributes: + label: Release Target + description: | + Would this feature be a major (breaking) change to existing + functionality or can it go into a minor release? + options: + - Unknown + - Minor + - Major diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..62426a3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,40 @@ + + + + + + +## Description + + + +## Contribution Checklist + +- [ ] The contents of this pull request are my own work and may be distributed under the terms of the project license +- [ ] I have read and agree to the [Contributing](CONTRIBUTING.md) guidelines and the [Code of Conduct](CODE_OF_CONDUCT.md) +- [ ] All new changes are covered by appropriate tests +- [ ] All changes pass code style checks and static analysis +- [ ] All previous tests and checks are passing +- [ ] I have included documentation about this change +- [ ] The details of this change have been added to the `Unreleased` section of the [changelog](CHANGELOG.md) + +**This pull request includes:** + +- [ ] Breaking changes to existing functionality (major release) +- [ ] New functionality (minor release) +- [ ] Fixes for existing functionality (patch release) +- [ ] Fixes to a previous major or minor release version diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..42a2586 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +Because of their sensitive nature, security issues should **not** be discussed in public forums. If you believe you have found an issue that exposes sensitive information or that could be leveraged in accessing such information, please email it directly to the project maintainer at . We will respond promptly and do our utmost to resolve any security issues in a timely manner. From 4d05f0d735d192f9bbc8cfb9e714edeb94f9ec4c Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Thu, 11 Dec 2025 18:43:07 -0600 Subject: [PATCH 09/10] Bump action versions, clean up README --- .github/workflows/acceptance.yml | 7 +++++-- README.md | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 07a00f9..ecbaa62 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -26,7 +26,7 @@ jobs: - php: "8.5" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Setup PHP ${{ matrix.versions.php }} uses: shivammathur/setup-php@v2 with: @@ -38,7 +38,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache Dependencies - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ matrix.versions.php }}-${{ hashFiles('**/composer.json') }} @@ -49,6 +49,9 @@ jobs: - name: Install Dependencies run: composer install --no-interaction --prefer-dist --no-progress + - name: Check composer.json Format + run: composer normalize --diff --dry-run --no-interaction --ansi + - name: Check Code Style run: composer style:check diff --git a/README.md b/README.md index 3af665e..89bf91c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # Console Color [![Latest Stable Version](https://img.shields.io/packagist/v/bebat/console-color.svg?style=flat-square)](https://packagist.org/packages/bebat/console-color) @@ -80,7 +81,7 @@ In addition to checking if `STDOUT` is a TTY, `Style` will look at several envir ### Auto Termination -By default, Console Color will "terminate" each style by appending `Style\Text::None` after whatever text you are applying styles to. This is helpful so you don't accidentally make all the text in the terminal bright red, for example. However, if you are outputting many styles to the screen and would like more control on when they are terminated this can be disabled globally or at call time. +By default, Console Color will "terminate" each style by appending `Style\Text::None` after the text you are applying styles to. This is helpful so you don't accidentally make all the text in the terminal bright red, for example. However, if you are outputting many styles to the screen and would like more control on when they are terminated this can be disabled globally or at call time. To disable termination globally, pass `false` to `autoTerminate()` like so: From 77f8dcf35a0f973cc5611e22eff159ec4551e446 Mon Sep 17 00:00:00 2001 From: Ben Batschelet Date: Thu, 11 Dec 2025 18:52:42 -0600 Subject: [PATCH 10/10] Fix sample for PHP 8.1 --- sample.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample.php b/sample.php index 4d54830..ad99c24 100755 --- a/sample.php +++ b/sample.php @@ -20,7 +20,7 @@ final class StyleDemo { - private const string INDENT = ' '; + private const INDENT = ' '; public function __construct(private ApplierInterface $applier) {}