Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
37 / 37 |
|
95.83% |
46 / 48 |
|
36.96% |
17 / 46 |
|
77.78% |
7 / 9 |
CRAP | |
0.00% |
0 / 1 |
| Output | |
100.00% |
37 / 37 |
|
95.83% |
46 / 48 |
|
36.96% |
17 / 46 |
|
100.00% |
9 / 9 |
323.65 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| echo | |
100.00% |
1 / 1 |
|
83.33% |
5 / 6 |
|
25.00% |
1 / 4 |
|
100.00% |
1 / 1 |
6.80 | |||
| echoln | |
100.00% |
3 / 3 |
|
100.00% |
6 / 6 |
|
50.00% |
2 / 4 |
|
100.00% |
1 / 1 |
4.12 | |||
| write | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| color | |
100.00% |
10 / 10 |
|
100.00% |
11 / 11 |
|
29.41% |
5 / 17 |
|
100.00% |
1 / 1 |
18.66 | |||
| indent | |
100.00% |
8 / 8 |
|
88.89% |
8 / 9 |
|
0.00% |
0 / 8 |
|
100.00% |
1 / 1 |
4 | |||
| formatText | |
100.00% |
7 / 7 |
|
100.00% |
9 / 9 |
|
50.00% |
4 / 8 |
|
100.00% |
1 / 1 |
8.12 | |||
| getStream | |
100.00% |
3 / 3 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| hasColorSupport | |
100.00% |
2 / 2 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
9 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celemas\Cli; |
| 6 | |
| 7 | final class Output |
| 8 | { |
| 9 | private mixed $stream = null; |
| 10 | private array $fg = [ |
| 11 | 'black' => [0, 30], |
| 12 | 'gray' => [1, 30], |
| 13 | 'grey' => [1, 30], |
| 14 | 'red' => [0, 31], |
| 15 | 'lightred' => [1, 31], |
| 16 | 'green' => [0, 32], |
| 17 | 'lightgreen' => [1, 32], |
| 18 | 'brown' => [0, 33], |
| 19 | 'yellow' => [1, 33], |
| 20 | 'blue' => [0, 34], |
| 21 | 'lightblue' => [1, 34], |
| 22 | 'purple' => [0, 35], |
| 23 | 'lightpurple' => [1, 35], |
| 24 | 'magenta' => [0, 35], |
| 25 | 'lightmagenta' => [1, 35], |
| 26 | 'cyan' => [0, 36], |
| 27 | 'lightcyan' => [1, 36], |
| 28 | 'lightgray' => [0, 37], |
| 29 | 'lightgrey' => [0, 37], |
| 30 | 'white' => [1, 37], |
| 31 | ]; |
| 32 | private array $bg = [ |
| 33 | 'black' => 40, |
| 34 | 'red' => 41, |
| 35 | 'green' => 42, |
| 36 | 'yellow' => 43, |
| 37 | 'blue' => 44, |
| 38 | 'purple' => 45, |
| 39 | 'magenta' => 45, |
| 40 | 'cyan' => 46, |
| 41 | 'gray' => 47, |
| 42 | 'grey' => 47, |
| 43 | 'white' => 47, |
| 44 | ]; |
| 45 | |
| 46 | public function __construct( |
| 47 | protected readonly string $target, |
| 48 | ) {} |
| 49 | |
| 50 | public function echo(string $text, string $color = '', string $background = ''): void |
| 51 | { |
| 52 | $this->write($color || $background ? $this->color($text, $color, $background) : $text); |
| 53 | } |
| 54 | |
| 55 | public function echoln(string $text, string $color = '', string $background = ''): void |
| 56 | { |
| 57 | $this->write( |
| 58 | ($color || $background ? $this->color($text, $color, $background) : $text) . PHP_EOL, |
| 59 | ); |
| 60 | } |
| 61 | |
| 62 | private function write(string $text): void |
| 63 | { |
| 64 | fwrite($this->getStream(), $text); |
| 65 | fflush($this->stream); |
| 66 | } |
| 67 | |
| 68 | public function color(string $text, string $color = '', string $background = ''): string |
| 69 | { |
| 70 | if (!$this->hasColorSupport()) { |
| 71 | return $text; |
| 72 | } |
| 73 | |
| 74 | $colorCode = ''; |
| 75 | $backgroundCode = ''; |
| 76 | |
| 77 | if ($color && array_key_exists($color, $this->fg)) { |
| 78 | [$first, $second] = $this->fg[$color]; |
| 79 | $colorCode = "{$first};{$second}"; |
| 80 | } |
| 81 | |
| 82 | if ($background && array_key_exists($background, $this->bg)) { |
| 83 | $backgroundCode = $this->bg[$background]; |
| 84 | } |
| 85 | |
| 86 | return $this->formatText($text, $colorCode, $backgroundCode); |
| 87 | } |
| 88 | |
| 89 | public function indent( |
| 90 | string $text, |
| 91 | int $indent, |
| 92 | ?int $max = null, |
| 93 | ): string { |
| 94 | $spaces = str_repeat(' ', $indent); |
| 95 | |
| 96 | /** @psalm-suppress ForbiddenCode */ |
| 97 | $width = shell_exec('tput cols'); |
| 98 | |
| 99 | if ($width === null) { |
| 100 | // Need a way to force $width to be null in a sane way |
| 101 | // @codeCoverageIgnoreStart |
| 102 | $width = 80; |
| 103 | |
| 104 | // @codeCoverageIgnoreEnd |
| 105 | } |
| 106 | |
| 107 | $width = (int) $width - $indent; |
| 108 | |
| 109 | if ($max !== null && $max < $width) { |
| 110 | $width = $max; |
| 111 | } |
| 112 | |
| 113 | $lines = explode("\n", wordwrap($text, $width, break: "\n")); |
| 114 | |
| 115 | return implode("\n", array_map(static fn($line) => $spaces . $line, $lines)); |
| 116 | } |
| 117 | |
| 118 | private function formatText(string $text, string $colorCode, string|int $backgroundCode): string |
| 119 | { |
| 120 | if ($colorCode && $backgroundCode) { |
| 121 | return "\033[{$colorCode};{$backgroundCode}m{$text}\033[0m"; |
| 122 | } |
| 123 | |
| 124 | if ($colorCode) { |
| 125 | return "\033[{$colorCode}m{$text}\033[0m"; |
| 126 | } |
| 127 | |
| 128 | if ($backgroundCode) { |
| 129 | return "\033[{$backgroundCode}m{$text}\033[0m"; |
| 130 | } |
| 131 | |
| 132 | return $text; |
| 133 | } |
| 134 | |
| 135 | private function getStream(): mixed |
| 136 | { |
| 137 | if ($this->stream === null) { |
| 138 | $this->stream = fopen($this->target, mode: 'w'); |
| 139 | } |
| 140 | |
| 141 | return $this->stream; |
| 142 | } |
| 143 | |
| 144 | private function hasColorSupport(): bool |
| 145 | { |
| 146 | if (getenv('NO_COLOR') !== false) { |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | // @codeCoverageIgnoreStart |
| 151 | if (getenv('FORCE_COLOR') !== false || getenv('COLORTERM') !== false) { |
| 152 | return true; |
| 153 | } |
| 154 | |
| 155 | // Windows |
| 156 | if (DIRECTORY_SEPARATOR === '\\') { |
| 157 | if (function_exists('sapi_windows_vt100_support')) { |
| 158 | return sapi_windows_vt100_support(STDOUT); |
| 159 | } |
| 160 | |
| 161 | return ( |
| 162 | getenv('ANSICON') !== false |
| 163 | || getenv('ConEmuANSI') === 'ON' |
| 164 | || getenv('TERM') === 'xterm' |
| 165 | ); |
| 166 | } |
| 167 | |
| 168 | if (function_exists('stream_isatty')) { |
| 169 | return stream_isatty(STDOUT); |
| 170 | } |
| 171 | |
| 172 | return false; |
| 173 | |
| 174 | // @codeCoverageIgnoreEnd |
| 175 | } |
| 176 | } |
Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not
necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once.
Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 47 | protected readonly string $target, |
| 48 | ) {} |
| 68 | public function color(string $text, string $color = '', string $background = ''): string |
| 69 | { |
| 70 | if (!$this->hasColorSupport()) { |
| 71 | return $text; |
| 74 | $colorCode = ''; |
| 75 | $backgroundCode = ''; |
| 76 | |
| 77 | if ($color && array_key_exists($color, $this->fg)) { |
| 77 | if ($color && array_key_exists($color, $this->fg)) { |
| 77 | if ($color && array_key_exists($color, $this->fg)) { |
| 78 | [$first, $second] = $this->fg[$color]; |
| 79 | $colorCode = "{$first};{$second}"; |
| 80 | } |
| 81 | |
| 82 | if ($background && array_key_exists($background, $this->bg)) { |
| 82 | if ($background && array_key_exists($background, $this->bg)) { |
| 82 | if ($background && array_key_exists($background, $this->bg)) { |
| 82 | if ($background && array_key_exists($background, $this->bg)) { |
| 83 | $backgroundCode = $this->bg[$background]; |
| 84 | } |
| 85 | |
| 86 | return $this->formatText($text, $colorCode, $backgroundCode); |
| 86 | return $this->formatText($text, $colorCode, $backgroundCode); |
| 87 | } |
| 50 | public function echo(string $text, string $color = '', string $background = ''): void |
| 51 | { |
| 52 | $this->write($color || $background ? $this->color($text, $color, $background) : $text); |
| 52 | $this->write($color || $background ? $this->color($text, $color, $background) : $text); |
| 52 | $this->write($color || $background ? $this->color($text, $color, $background) : $text); |
| 52 | $this->write($color || $background ? $this->color($text, $color, $background) : $text); |
| 52 | $this->write($color || $background ? $this->color($text, $color, $background) : $text); |
| 52 | $this->write($color || $background ? $this->color($text, $color, $background) : $text); |
| 53 | } |
| 55 | public function echoln(string $text, string $color = '', string $background = ''): void |
| 56 | { |
| 57 | $this->write( |
| 58 | ($color || $background ? $this->color($text, $color, $background) : $text) . PHP_EOL, |
| 58 | ($color || $background ? $this->color($text, $color, $background) : $text) . PHP_EOL, |
| 58 | ($color || $background ? $this->color($text, $color, $background) : $text) . PHP_EOL, |
| 58 | ($color || $background ? $this->color($text, $color, $background) : $text) . PHP_EOL, |
| 58 | ($color || $background ? $this->color($text, $color, $background) : $text) . PHP_EOL, |
| 58 | ($color || $background ? $this->color($text, $color, $background) : $text) . PHP_EOL, |
| 59 | ); |
| 60 | } |
| 118 | private function formatText(string $text, string $colorCode, string|int $backgroundCode): string |
| 119 | { |
| 120 | if ($colorCode && $backgroundCode) { |
| 120 | if ($colorCode && $backgroundCode) { |
| 120 | if ($colorCode && $backgroundCode) { |
| 121 | return "\033[{$colorCode};{$backgroundCode}m{$text}\033[0m"; |
| 124 | if ($colorCode) { |
| 125 | return "\033[{$colorCode}m{$text}\033[0m"; |
| 128 | if ($backgroundCode) { |
| 129 | return "\033[{$backgroundCode}m{$text}\033[0m"; |
| 132 | return $text; |
| 133 | } |
| 137 | if ($this->stream === null) { |
| 138 | $this->stream = fopen($this->target, mode: 'w'); |
| 139 | } |
| 140 | |
| 141 | return $this->stream; |
| 141 | return $this->stream; |
| 142 | } |
| 146 | if (getenv('NO_COLOR') !== false) { |
| 147 | return false; |
| 90 | string $text, |
| 91 | int $indent, |
| 92 | ?int $max = null, |
| 93 | ): string { |
| 94 | $spaces = str_repeat(' ', $indent); |
| 95 | |
| 96 | /** @psalm-suppress ForbiddenCode */ |
| 97 | $width = shell_exec('tput cols'); |
| 98 | |
| 99 | if ($width === null) { |
| 107 | $width = (int) $width - $indent; |
| 108 | |
| 109 | if ($max !== null && $max < $width) { |
| 109 | if ($max !== null && $max < $width) { |
| 109 | if ($max !== null && $max < $width) { |
| 110 | $width = $max; |
| 111 | } |
| 112 | |
| 113 | $lines = explode("\n", wordwrap($text, $width, break: "\n")); |
| 113 | $lines = explode("\n", wordwrap($text, $width, break: "\n")); |
| 114 | |
| 115 | return implode("\n", array_map(static fn($line) => $spaces . $line, $lines)); |
| 115 | return implode("\n", array_map(static fn($line) => $spaces . $line, $lines)); |
| 115 | return implode("\n", array_map(static fn($line) => $spaces . $line, $lines)); |
| 115 | return implode("\n", array_map(static fn($line) => $spaces . $line, $lines)); |
| 116 | } |
| 62 | private function write(string $text): void |
| 63 | { |
| 64 | fwrite($this->getStream(), $text); |
| 65 | fflush($this->stream); |
| 66 | } |
| 115 | return implode("\n", array_map(static fn($line) => $spaces . $line, $lines)); |