ConsoleHelper   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Test Coverage

Coverage 93.75%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 19
eloc 52
c 2
b 0
f 0
dl 0
loc 170
ccs 45
cts 48
cp 0.9375
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A writeErrorLine() 0 10 2
A colorize() 0 9 4
A writeErrorMessage() 0 4 1
A formatNewlines() 0 5 1
A detectColorCapabilities() 0 13 6
A readLine() 0 4 1
A __construct() 0 6 1
A write() 0 8 2
A writeLine() 0 3 1
1
<?php
2
/**
3
 * Sandro Keil (https://sandro-keil.de)
4
 *
5
 * @link      http://github.com/sandrokeil/interop-config for the canonical source repository
6
 * @copyright Copyright (c) 2017-2020 Sandro Keil
7
 * @license   http://github.com/sandrokeil/interop-config/blob/master/LICENSE.md New BSD License
8
 */
9
10
declare(strict_types=1);
11
12
namespace Interop\Config\Tool;
13
14
/**
15
 * Utilities for console tooling.
16
 *
17
 * Provides the following facilities:
18
 *
19
 * - Colorize strings using markup (e.g., `<info>message</info>`,
20
 *   `<error>message</error>`)
21
 * - Write output to a specified stream, optionally with colorization.
22
 * - Write a line of output to a specified stream, optionally with
23
 *   colorization, using the system EOL sequence..
24
 * - Write an error message to STDERR.
25
 * - Reads a single line from the user
26
 *
27
 * Colorization will only occur when expected sequences are discovered and then, only if the console terminal allows it.
28
 *
29
 * Essentially, provides the bare minimum to allow you to provide messages to the current console.
30
 *
31
 * @copyright Copyright (c) 2016 Zend Technologies USA Inc. (http://www.zend.com)
32
 */
33
class ConsoleHelper
34
{
35
    const COLOR_GREEN = "\033[32m";
36
    const COLOR_YELLOW = "\033[33m";
37
    const COLOR_RED = "\033[31m";
38
    const COLOR_RESET = "\033[0m";
39
40
    const HIGHLIGHT_INFO = 'info';
41
    const HIGHLIGHT_VALUE = 'value';
42
    const HIGHLIGHT_ERROR = 'error';
43
44
    private $highlightMap = [
45
        self::HIGHLIGHT_VALUE => self::COLOR_GREEN,
46
        self::HIGHLIGHT_INFO => self::COLOR_YELLOW,
47
        self::HIGHLIGHT_ERROR => self::COLOR_RED,
48
    ];
49
50
    /**
51
     * @var string Exists only for testing.
52
     */
53
    private $eol = PHP_EOL;
54
55
    /**
56
     * @var bool
57
     */
58
    private $supportsColor;
59
60
    /**
61
     * @var resource Exists only for testing.
62
     */
63
    private $errorStream = STDERR;
64
65
    /**
66
     * Input stream
67
     *
68
     * @var resource
69
     */
70
    private $inputStream;
71
72
    /**
73
     * Output stream
74
     *
75
     * @var resource
76
     */
77
    private $outputStream;
78
79 36
    public function __construct($inputStream = STDIN, $outputStream = STDOUT, $errorStream = STDERR)
80
    {
81 36
        $this->inputStream = $inputStream;
82 36
        $this->outputStream = $outputStream;
83 36
        $this->errorStream = $errorStream;
84 36
        $this->supportsColor = $this->detectColorCapabilities($outputStream);
85 36
    }
86
87 16
    public function readLine(string $name, string $text = 'Please enter a value for'): string
88
    {
89 16
        fwrite($this->outputStream, $this->colorize(sprintf('%s <info>%s</info>: ', $text, $name)));
90 16
        return trim(fread($this->inputStream, 1024), "\n");
91
    }
92
93
    /**
94
     * Colorize a string for use with the terminal.
95
     *
96
     * Takes strings formatted as `<key>string</key>` and formats them per the
97
     * $highlightMap; if color support is disabled, simply removes the formatting
98
     * tags.
99
     *
100
     * @param string $string
101
     * @return string
102
     */
103 31
    public function colorize(string $string): string
104
    {
105 31
        $reset = $this->supportsColor ? self::COLOR_RESET : '';
106 31
        foreach ($this->highlightMap as $key => $color) {
107 31
            $pattern = sprintf('#<%s>(.*?)</%s>#s', $key, $key);
108 31
            $color = $this->supportsColor ? $color : '';
109 31
            $string = preg_replace($pattern, $color . '$1' . $reset, $string);
110
        }
111 31
        return $string;
112
    }
113
114
    /**
115
     * @param string $string
116
     * @param bool $colorize Whether or not to colorize the string
117
     * @return void
118
     */
119 6
    public function write(string $string, bool $colorize = true): void
120
    {
121 6
        if ($colorize) {
122 6
            $string = $this->colorize($string);
123
        }
124 6
        $string = $this->formatNewlines($string);
125
126 6
        fwrite($this->outputStream, $string);
127 6
    }
128
129
    /**
130
     * @param string $string
131
     * @param bool $colorize Whether or not to colorize the line
132
     * @return void
133
     */
134 2
    public function writeLine(string $string, bool $colorize = true): void
135
    {
136 2
        $this->write($string . $this->eol, $colorize);
137 2
    }
138
139
    /**
140
     * @param string $string
141
     * @param bool $colorize Whether or not to colorize the line
142
     * @return void
143
     */
144 14
    public function writeErrorLine(string $string, bool $colorize = true): void
145
    {
146 14
        $string .= $this->eol;
147
148 14
        if ($colorize) {
149 14
            $string = $this->colorize($string);
150
        }
151 14
        $string = $this->formatNewlines($string);
152
153 14
        fwrite($this->errorStream, $string);
154 14
    }
155
156
    /**
157
     * Emit an error message.
158
     *
159
     * Wraps the message in `<error></error>`, and passes it to `writeLine()`,
160
     * using STDERR as the resource; emits an additional empty line when done,
161
     * also to STDERR.
162
     *
163
     * @param string $message
164
     * @return void
165
     */
166 14
    public function writeErrorMessage(string $message): void
167
    {
168 14
        $this->writeErrorLine(sprintf('<error>%s</error>', $message), true);
169 14
        $this->writeErrorLine('', false);
170 14
    }
171
172
    /**
173
     * @param resource $resource
174
     * @return bool
175
     */
176 36
    private function detectColorCapabilities($resource = STDOUT): bool
177
    {
178 36
        if ('\\' === DIRECTORY_SEPARATOR) {
179
            // Windows
180
            return false !== getenv('ANSICON')
181
                || 'ON' === getenv('ConEmuANSI')
182
                || 'xterm' === getenv('TERM');
183
        }
184
185
        try {
186 36
            return function_exists('posix_isatty') && posix_isatty($resource);
187 36
        } catch (\Throwable $e) {
188 36
            return false;
189
        }
190
    }
191
192
    /**
193
     * Ensure newlines are appropriate for the current terminal.
194
     *
195
     * @param string
196
     * @return string
197
     */
198 20
    private function formatNewlines(string $string): string
199
    {
200 20
        $string = str_replace($this->eol, "\0PHP_EOL\0", $string);
201 20
        $string = preg_replace("/(\r\n|\n|\r)/", $this->eol, $string);
202 20
        return str_replace("\0PHP_EOL\0", $this->eol, $string);
203
    }
204
}
205