Output.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. namespace Depo\UniTester\Console;
  3. use const PHP_EOL;
  4. final class Output
  5. {
  6. /**
  7. * @var callable
  8. */
  9. private $output;
  10. const FOREGROUND_COLORS = [
  11. 'black' => '0;30',
  12. 'dark_gray' => '1;30',
  13. 'green' => '0;32',
  14. 'light_green' => '1;32',
  15. 'red' => '0;31',
  16. 'light_red' => '1;31',
  17. 'yellow' => '0;33',
  18. 'light_yellow' => '1;33',
  19. 'blue' => '0;34',
  20. 'dark_blue' => '0;34',
  21. 'light_blue' => '1;34',
  22. 'purple' => '0;35',
  23. 'light_purple' => '1;35',
  24. 'cyan' => '0;36',
  25. 'light_cyan' => '1;36',
  26. 'light_gray' => '0;37',
  27. 'white' => '1;37',
  28. ];
  29. const BG_COLORS = [
  30. 'black' => '40',
  31. 'red' => '41',
  32. 'green' => '42',
  33. 'yellow' => '43',
  34. 'blue' => '44',
  35. 'magenta' => '45',
  36. 'cyan' => '46',
  37. 'light_gray' => '47',
  38. ];
  39. public function __construct(callable $output = null)
  40. {
  41. if ($output === null) {
  42. $output = function ($message) {
  43. fwrite(STDOUT, $message);
  44. };
  45. }
  46. $this->output = $output;
  47. }
  48. /**
  49. * {@inheritdoc}
  50. */
  51. public function write(string $message, ?string $color = null, ?string $background = null): void
  52. {
  53. $formattedMessage = '';
  54. if ($color) {
  55. $formattedMessage .= "\033[" . self::FOREGROUND_COLORS[$color] . 'm';
  56. }
  57. if ($background) {
  58. $formattedMessage .= "\033[" . self::BG_COLORS[$background] . 'm';
  59. }
  60. if (!empty($formattedMessage)) {
  61. $formattedMessage .= $message . "\033[0m";
  62. } else {
  63. $formattedMessage = $message;
  64. }
  65. $output = $this->output;
  66. $output($formattedMessage);
  67. }
  68. public function writeln(string $message): void
  69. {
  70. $this->write($message);
  71. $this->write(PHP_EOL);
  72. }
  73. public function list(array $items): void
  74. {
  75. foreach ($items as $item) {
  76. $this->write('- ' . $item);
  77. $this->write(PHP_EOL);
  78. }
  79. $this->write(PHP_EOL);
  80. }
  81. public function listKeyValues(array $items, bool $inlined = false): void
  82. {
  83. $maxKeyLength = 0;
  84. if ($inlined) {
  85. foreach ($items as $key => $value) {
  86. $keyLength = \mb_strlen($key);
  87. if ($keyLength > $maxKeyLength) {
  88. $maxKeyLength = $keyLength;
  89. }
  90. }
  91. }
  92. foreach ($items as $key => $value) {
  93. $key = str_pad($key, $maxKeyLength, ' ', STR_PAD_RIGHT);
  94. $this->write($key, 'green');
  95. $this->write(' : ');
  96. $this->write($value, 'white');
  97. $this->write(PHP_EOL);
  98. }
  99. $this->write(PHP_EOL);
  100. }
  101. public function success(string $message): void
  102. {
  103. [$formattedMessage, $lineLength, $color] = $this->formatMessage('OK', $message, 'green');
  104. $this->outputMessage($formattedMessage, $lineLength, $color);
  105. }
  106. public function error(string $message): void
  107. {
  108. [$formattedMessage, $lineLength, $color] = $this->formatMessage('ERROR', $message, 'red');
  109. $this->outputMessage($formattedMessage, $lineLength, $color);
  110. }
  111. public function warning(string $message): void
  112. {
  113. [$formattedMessage, $lineLength, $color] = $this->formatMessage('WARNING', $message, 'yellow');
  114. $this->outputMessage($formattedMessage, $lineLength, $color);
  115. }
  116. public function info(string $message): void
  117. {
  118. [$formattedMessage, $lineLength, $color] = $this->formatMessage('INFO', $message, 'blue');
  119. $this->outputMessage($formattedMessage, $lineLength, $color);
  120. }
  121. private function outputMessage($formattedMessage, int $lineLength, string $color): void
  122. {
  123. $this->write(PHP_EOL);
  124. $this->write(str_repeat(' ', $lineLength), 'white', $color);
  125. $this->write(PHP_EOL);
  126. if (is_string($formattedMessage)) {
  127. $formattedMessage = [$formattedMessage];
  128. }
  129. foreach ($formattedMessage as $line) {
  130. $this->write($line, 'white', $color);
  131. }
  132. $this->write(PHP_EOL);
  133. $this->write(str_repeat(' ', $lineLength), 'white', $color);
  134. $this->write(PHP_EOL);
  135. $this->write(PHP_EOL);
  136. }
  137. private function formatMessage(string $prefix, string $message, string $color): array
  138. {
  139. $formattedMessage = sprintf('[%s] %s', $prefix, trim($message));
  140. $lineLength = \mb_strlen($formattedMessage);
  141. $consoleWidth = $this->geTerminalWidth();
  142. if ($lineLength > $consoleWidth) {
  143. $lineLength = $consoleWidth;
  144. $lines = explode('|', wordwrap($formattedMessage, $lineLength, '|', true));
  145. $formattedMessage = array_map(function ($line) use ($lineLength) {
  146. return str_pad($line, $lineLength);
  147. }, $lines);
  148. }
  149. return [$formattedMessage, $lineLength, $color];
  150. }
  151. private function geTerminalWidth(): int
  152. {
  153. return ((int)exec('tput cols') ?? 85 - 5);
  154. }
  155. }