From 92a3ea29778deb18ef44e89196ed0ded33e609e2 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 17 Jan 2024 12:45:55 +0100 Subject: [PATCH 1/8] added failing test for #33 --- src/Base.php | 14 ++++++- tests/LogLevelTest.php | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 tests/LogLevelTest.php diff --git a/src/Base.php b/src/Base.php index d71a96e..f92ced2 100644 --- a/src/Base.php +++ b/src/Base.php @@ -236,6 +236,18 @@ public function setLogLevel($level) } } + /** + * Check if a message with the given level should be logged + * + * @param string $level + * @return bool + */ + public function isLogLevelEnabled($level) + { + if (!isset($this->loglevel[$level])) $this->fatal('Unknown log level'); + return $this->loglevel[$level]['enabled']; + } + /** * Exits the program on a fatal error * @@ -283,7 +295,7 @@ protected function logMessage($level, $message, array $context = array()) if (!isset($this->loglevel[$level])) $level = 'error'; $info = $this->loglevel[$level]; - if (!$info['enabled']) return; // no logging for this level + if (!$this->isLogLevelEnabled($level)) return; // no logging for this level $message = $this->interpolate($message, $context); diff --git a/tests/LogLevelTest.php b/tests/LogLevelTest.php new file mode 100644 index 0000000..1e5fcc8 --- /dev/null +++ b/tests/LogLevelTest.php @@ -0,0 +1,91 @@ +setLogLevel($level); + foreach ($enabled as $e) { + $this->assertTrue($cli->isLogLevelEnabled($e), "$e is not enabled but should be"); + } + foreach ($disabled as $d) { + $this->assertFalse($cli->isLogLevelEnabled($d), "$d is enabled but should not be"); + } + } + + +} From 844609ef16b8486691b7fd892d54478918f27fe8 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 17 Jan 2024 13:03:34 +0100 Subject: [PATCH 2/8] Fix log level setting. closes #33 --- src/Base.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Base.php b/src/Base.php index f92ced2..c7a7e3e 100644 --- a/src/Base.php +++ b/src/Base.php @@ -229,10 +229,10 @@ protected function execute() public function setLogLevel($level) { if (!isset($this->loglevel[$level])) $this->fatal('Unknown log level'); - $enable = true; + $enable = false; foreach (array_keys($this->loglevel) as $l) { + if ($l == $level) $enable = true; $this->loglevel[$l]['enabled'] = $enable; - if ($l == $level) $enable = false; } } From a3414b242fb92c48d9339207e152cf897fa6ff61 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Tue, 3 Dec 2024 08:51:06 +0100 Subject: [PATCH 3/8] test on more modern php releases --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 74ee02d..ce4fb50 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,12 +10,12 @@ jobs: strategy: matrix: - php-versions: ['7.2', '7.3', '7.4', '8.0'] + php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 From 360ed0b3704fa8fcd1ce976ed79012aa2c67d22f Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Tue, 3 Dec 2024 08:57:18 +0100 Subject: [PATCH 4/8] honor NO_COLOR environment variable As argued for on https://no-color.org/ --- src/Colors.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Colors.php b/src/Colors.php index 015e422..d57de15 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -70,6 +70,10 @@ public function __construct() $this->enabled = false; return; } + if (getenv('NO_COLOR')) { // https://no-color.org/ + $this->enabled = false; + return; + } } /** From 8189c68cbde3fd8c3e0fc26295b776b143b6e481 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 18 Dec 2024 09:16:26 +0100 Subject: [PATCH 5/8] initialize log level early The default log level needs to be initialized before everthing else, otherwise an error during the option parsing (eg. when an unknown option is passed) will result in an unessecary stack trace. --- src/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Base.php b/src/Base.php index c7a7e3e..a3b6049 100644 --- a/src/Base.php +++ b/src/Base.php @@ -94,7 +94,7 @@ public function __construct($autocatch = true) if ($autocatch) { set_exception_handler(array($this, 'fatal')); } - + $this->setLogLevel($this->logdefault); $this->colors = new Colors(); $this->options = new Options($this->colors); } From f6feaa2b97bd6847955670e16b3945136af4cb58 Mon Sep 17 00:00:00 2001 From: Damien Regad Date: Sun, 22 Dec 2024 18:55:39 +0100 Subject: [PATCH 6/8] Add test case for wrapping text with colors --- tests/TableFormatterTest.php | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/TableFormatterTest.php b/tests/TableFormatterTest.php index 687643a..23b198b 100644 --- a/tests/TableFormatterTest.php +++ b/tests/TableFormatterTest.php @@ -138,4 +138,51 @@ public function test_onewrap() $result = $tf->format([5, '*'], [$col1, $col2]); $this->assertEquals($expect, $result); } + + /** + * Test that colors are correctly applied when text is wrapping across lines. + * + * @dataProvider colorwrapProvider + */ + public function test_colorwrap($text, $expect) + { + $tf = new TableFormatter(); + $tf->setMaxWidth(15); + + $this->assertEquals($expect, $tf->format(['*'], [$text])); + } + + /** + * Data provider for test_colorwrap. + * + * @return array[] + */ + public function colorwrapProvider() + { + $color = new Colors(); + $cyan = $color->getColorCode(Colors::C_CYAN); + $reset = $color->getColorCode(Colors::C_RESET); + $wrap = function ($str) use ($color) { + return $color->wrap($str, Colors::C_CYAN); + }; + + return [ + 'color word line 1' => [ + "This is ". $wrap("cyan") . " text wrapping", + "This is {$cyan}cyan{$reset} \ntext wrapping \n", + ], + 'color word line 2' => [ + "This is text ". $wrap("cyan") . " wrapping", + "This is text \n{$cyan}cyan{$reset} wrapping \n", + ], + 'color across lines' => [ + "This is ". $wrap("cyan text",) . " wrapping", + "This is {$cyan}cyan \ntext{$reset} wrapping \n", + ], + 'color across lines until end' => [ + "This is ". $wrap("cyan text wrapping"), + "This is {$cyan}cyan \n{$cyan}text wrapping{$reset} \n", + ], + ]; + } } From 19482c0041a5b441a0749f80e20d6eb80e8e43d6 Mon Sep 17 00:00:00 2001 From: Damien Regad Date: Sun, 22 Dec 2024 19:10:56 +0100 Subject: [PATCH 7/8] Fix display of colors when wrapping text When colored text wraps to the next line, the color was only applied until the end of the line, the text continued on the following line was reset to default color. This reapplies the currently used color at the beginning of each wrapped line, unless it has been properly reset. --- src/Colors.php | 3 +++ src/TableFormatter.php | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Colors.php b/src/Colors.php index d57de15..dd1fd04 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -31,6 +31,9 @@ class Colors const C_LIGHTGRAY = 'lightgray'; const C_WHITE = 'white'; + // Regex pattern to match color codes + const C_CODE_REGEX = "/(\33\[[0-9;]+m)/"; + /** @var array known color names */ protected $colors = array( self::C_RESET => "\33[0m", diff --git a/src/TableFormatter.php b/src/TableFormatter.php index 23bb894..20d71c6 100644 --- a/src/TableFormatter.php +++ b/src/TableFormatter.php @@ -293,6 +293,7 @@ protected function substr($string, $start = 0, $length = null) protected function wordwrap($str, $width = 75, $break = "\n", $cut = false) { $lines = explode($break, $str); + $color_reset = $this->colors->getColorCode(Colors::C_RESET); foreach ($lines as &$line) { $line = rtrim($line); if ($this->strlen($line) <= $width) { @@ -301,18 +302,30 @@ protected function wordwrap($str, $width = 75, $break = "\n", $cut = false) $words = explode(' ', $line); $line = ''; $actual = ''; + $color = ''; foreach ($words as $word) { + if (preg_match_all(Colors::C_CODE_REGEX, $word, $color_codes) ) { + # Word contains color codes + foreach ($color_codes[0] as $code) { + if ($code == $color_reset) { + $color = ''; + } else { + # Remember color so we can reapply it after a line break + $color = $code; + } + } + } if ($this->strlen($actual . $word) <= $width) { $actual .= $word . ' '; } else { if ($actual != '') { $line .= rtrim($actual) . $break; } - $actual = $word; + $actual = $color . $word; if ($cut) { while ($this->strlen($actual) > $width) { $line .= $this->substr($actual, 0, $width) . $break; - $actual = $this->substr($actual, $width); + $actual = $color . $this->substr($actual, $width); } } $actual .= ' '; @@ -322,4 +335,4 @@ protected function wordwrap($str, $width = 75, $break = "\n", $cut = false) } return implode($break, $lines); } -} \ No newline at end of file +} From 4e669f38f660b0e9f76ed14dda7f12bff390f94f Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 15 Mar 2025 10:02:01 +0100 Subject: [PATCH 8/8] make test backward compatible to old PHP versions --- tests/TableFormatterTest.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/TableFormatterTest.php b/tests/TableFormatterTest.php index 23b198b..3b1860a 100644 --- a/tests/TableFormatterTest.php +++ b/tests/TableFormatterTest.php @@ -104,7 +104,7 @@ public function test_length() $tf = new TableFormatter(); $tf->setBorder('|'); - $result = $tf->format([20, '*'], [$text, 'test']); + $result = $tf->format(array(20, '*'), array($text, 'test')); $this->assertEquals($expect, trim($result)); } @@ -118,7 +118,7 @@ public function test_colorlength() $tf = new TableFormatter(); $tf->setBorder('|'); - $result = $tf->format([20, '*'], [$text, 'test']); + $result = $tf->format(array(20, '*'), array($text, 'test')); $this->assertEquals($expect, trim($result)); } @@ -135,7 +135,7 @@ public function test_onewrap() $tf->setMaxWidth(11); $tf->setBorder('|'); - $result = $tf->format([5, '*'], [$col1, $col2]); + $result = $tf->format(array(5, '*'), array($col1, $col2)); $this->assertEquals($expect, $result); } @@ -149,7 +149,7 @@ public function test_colorwrap($text, $expect) $tf = new TableFormatter(); $tf->setMaxWidth(15); - $this->assertEquals($expect, $tf->format(['*'], [$text])); + $this->assertEquals($expect, $tf->format(array('*'), array($text))); } /** @@ -166,23 +166,23 @@ public function colorwrapProvider() return $color->wrap($str, Colors::C_CYAN); }; - return [ - 'color word line 1' => [ + return array( + 'color word line 1' => array( "This is ". $wrap("cyan") . " text wrapping", "This is {$cyan}cyan{$reset} \ntext wrapping \n", - ], - 'color word line 2' => [ + ), + 'color word line 2' => array( "This is text ". $wrap("cyan") . " wrapping", "This is text \n{$cyan}cyan{$reset} wrapping \n", - ], - 'color across lines' => [ - "This is ". $wrap("cyan text",) . " wrapping", + ), + 'color across lines' => array( + "This is ". $wrap("cyan text") . " wrapping", "This is {$cyan}cyan \ntext{$reset} wrapping \n", - ], - 'color across lines until end' => [ + ), + 'color across lines until end' => array( "This is ". $wrap("cyan text wrapping"), "This is {$cyan}cyan \n{$cyan}text wrapping{$reset} \n", - ], - ]; + ), + ); } }