Skip to content

Commit 4b1877a

Browse files
committed
[Console] Terminal Color Mode refactoring: Adding a way to force color mode by user, fewer getenv() calls, simpler tests.
It can be useful for example in the case of an environment where none of the expected environment variables are available (Docker container...) , but where the support of a specific mode is imperative.
1 parent d1b738d commit 4b1877a

File tree

4 files changed

+67
-27
lines changed

4 files changed

+67
-27
lines changed

Color.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ private function parseColor(string $color, bool $background = false): string
117117
}
118118

119119
if ('#' === $color[0]) {
120-
return ($background ? '4' : '3').Terminal::getTermColorSupport()->convertFromHexToAnsiColorCode($color);
120+
return ($background ? '4' : '3').Terminal::getColorMode()->convertFromHexToAnsiColorCode($color);
121121
}
122122

123123
if (isset(self::COLORS[$color])) {

Terminal.php

+32-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
class Terminal
1717
{
18+
public const DEFAULT_COLOR_MODE = AnsiColorMode::Ansi4;
19+
20+
private static ?AnsiColorMode $colorMode = null;
1821
private static ?int $width = null;
1922
private static ?int $height = null;
2023
private static ?bool $stty = null;
@@ -23,18 +26,27 @@ class Terminal
2326
* About Ansi color types: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
2427
* For more information about true color support with terminals https://github.com/termstandard/colors/.
2528
*/
26-
public static function getTermColorSupport(): AnsiColorMode
29+
public static function getColorMode(): AnsiColorMode
2730
{
31+
// Use Cache from previous run (or user forced mode)
32+
if (null !== self::$colorMode) {
33+
return self::$colorMode;
34+
}
35+
2836
// Try with $COLORTERM first
2937
if (\is_string($colorterm = getenv('COLORTERM'))) {
3038
$colorterm = strtolower($colorterm);
3139

3240
if (str_contains($colorterm, 'truecolor')) {
33-
return AnsiColorMode::Ansi24;
41+
self::setColorMode(AnsiColorMode::Ansi24);
42+
43+
return self::$colorMode;
3444
}
3545

3646
if (str_contains($colorterm, '256color')) {
37-
return AnsiColorMode::Ansi8;
47+
self::setColorMode(AnsiColorMode::Ansi8);
48+
49+
return self::$colorMode;
3850
}
3951
}
4052

@@ -43,15 +55,29 @@ public static function getTermColorSupport(): AnsiColorMode
4355
$term = strtolower($term);
4456

4557
if (str_contains($term, 'truecolor')) {
46-
return AnsiColorMode::Ansi24;
58+
self::setColorMode(AnsiColorMode::Ansi24);
59+
60+
return self::$colorMode;
4761
}
4862

4963
if (str_contains($term, '256color')) {
50-
return AnsiColorMode::Ansi8;
64+
self::setColorMode(AnsiColorMode::Ansi8);
65+
66+
return self::$colorMode;
5167
}
5268
}
5369

54-
return AnsiColorMode::Ansi4;
70+
self::setColorMode(self::DEFAULT_COLOR_MODE);
71+
72+
return self::$colorMode;
73+
}
74+
75+
/**
76+
* Force a terminal color mode rendering.
77+
*/
78+
public static function setColorMode(?AnsiColorMode $colorMode): void
79+
{
80+
self::$colorMode = $colorMode;
5581
}
5682

5783
/**

Tests/ColorTest.php

+8-17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Console\Color;
16+
use Symfony\Component\Console\Output\AnsiColorMode;
17+
use Symfony\Component\Console\Terminal;
1618

1719
class ColorTest extends TestCase
1820
{
@@ -33,8 +35,7 @@ public function testAnsi4Colors()
3335

3436
public function testTrueColors()
3537
{
36-
$colorterm = getenv('COLORTERM');
37-
putenv('COLORTERM=truecolor');
38+
Terminal::setColorMode(AnsiColorMode::Ansi24);
3839

3940
try {
4041
$color = new Color('#fff', '#000');
@@ -43,17 +44,13 @@ public function testTrueColors()
4344
$color = new Color('#ffffff', '#000000');
4445
$this->assertSame("\033[38;2;255;255;255;48;2;0;0;0m \033[39;49m", $color->apply(' '));
4546
} finally {
46-
(false !== $colorterm) ? putenv('COLORTERM='.$colorterm) : putenv('COLORTERM');
47+
Terminal::setColorMode(null);
4748
}
4849
}
4950

5051
public function testDegradedTrueColorsToAnsi4()
5152
{
52-
$colorterm = getenv('COLORTERM');
53-
$term = getenv('TERM');
54-
55-
putenv('COLORTERM=');
56-
putenv('TERM=');
53+
Terminal::setColorMode(AnsiColorMode::Ansi4);
5754

5855
try {
5956
$color = new Color('#f00', '#ff0');
@@ -62,18 +59,13 @@ public function testDegradedTrueColorsToAnsi4()
6259
$color = new Color('#c0392b', '#f1c40f');
6360
$this->assertSame("\033[31;43m \033[39;49m", $color->apply(' '));
6461
} finally {
65-
(false !== $colorterm) ? putenv('COLORTERM='.$colorterm) : putenv('COLORTERM');
66-
(false !== $term) ? putenv('TERM='.$term) : putenv('TERM');
62+
Terminal::setColorMode(null);
6763
}
6864
}
6965

7066
public function testDegradedTrueColorsToAnsi8()
7167
{
72-
$colorterm = getenv('COLORTERM');
73-
$term = getenv('TERM');
74-
75-
putenv('COLORTERM=');
76-
putenv('TERM=symfonyTest-256color');
68+
Terminal::setColorMode(AnsiColorMode::Ansi8);
7769

7870
try {
7971
$color = new Color('#f57255', '#8993c0');
@@ -82,8 +74,7 @@ public function testDegradedTrueColorsToAnsi8()
8274
$color = new Color('#000000', '#ffffff');
8375
$this->assertSame("\033[38;5;16;48;5;231m \033[39;49m", $color->apply(' '));
8476
} finally {
85-
(false !== $colorterm) ? putenv('COLORTERM='.$colorterm) : putenv('COLORTERM');
86-
(false !== $term) ? putenv('TERM='.$term) : putenv('TERM');
77+
Terminal::setColorMode(null);
8778
}
8879
}
8980
}

Tests/TerminalTest.php

+26-3
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public function testSttyOnWindows()
9898
/**
9999
* @dataProvider provideTerminalColorEnv
100100
*/
101-
public function testGetTermColorSupport(?string $testColorTerm, ?string $testTerm, AnsiColorMode $expected)
101+
public function testGetColorMode(?string $testColorTerm, ?string $testTerm, AnsiColorMode $expected)
102102
{
103103
$oriColorTerm = getenv('COLORTERM');
104104
$oriTerm = getenv('TERM');
@@ -107,10 +107,11 @@ public function testGetTermColorSupport(?string $testColorTerm, ?string $testTer
107107
putenv($testColorTerm ? "COLORTERM={$testColorTerm}" : 'COLORTERM');
108108
putenv($testTerm ? "TERM={$testTerm}" : 'TERM');
109109

110-
$this->assertSame($expected, Terminal::getTermColorSupport());
110+
$this->assertSame($expected, Terminal::getColorMode());
111111
} finally {
112112
(false !== $oriColorTerm) ? putenv('COLORTERM='.$oriColorTerm) : putenv('COLORTERM');
113113
(false !== $oriTerm) ? putenv('TERM='.$oriTerm) : putenv('TERM');
114+
Terminal::setColorMode(null);
114115
}
115116
}
116117

@@ -123,6 +124,28 @@ public function provideTerminalColorEnv(): \Generator
123124
yield [null, 'xterm-TRUECOLOR', AnsiColorMode::Ansi24];
124125
yield [null, 'xterm-256color', AnsiColorMode::Ansi8];
125126
yield [null, 'xterm-256COLOR', AnsiColorMode::Ansi8];
126-
yield [null, null, AnsiColorMode::Ansi4];
127+
yield [null, null, Terminal::DEFAULT_COLOR_MODE];
128+
}
129+
130+
public function testSetColorMode()
131+
{
132+
$oriColorTerm = getenv('COLORTERM');
133+
$oriTerm = getenv('TERM');
134+
135+
try {
136+
putenv('COLORTERM');
137+
putenv('TERM');
138+
$this->assertSame(Terminal::DEFAULT_COLOR_MODE, Terminal::getColorMode());
139+
140+
putenv('COLORTERM=256color');
141+
$this->assertSame(Terminal::DEFAULT_COLOR_MODE, Terminal::getColorMode()); // Terminal color mode is cached at first call. Terminal cannot change during execution.
142+
143+
Terminal::setColorMode(AnsiColorMode::Ansi24); // Force change by user.
144+
$this->assertSame(AnsiColorMode::Ansi24, Terminal::getColorMode());
145+
} finally {
146+
(false !== $oriColorTerm) ? putenv('COLORTERM='.$oriColorTerm) : putenv('COLORTERM');
147+
(false !== $oriTerm) ? putenv('TERM='.$oriTerm) : putenv('TERM');
148+
Terminal::setColorMode(null);
149+
}
127150
}
128151
}

0 commit comments

Comments
 (0)