Completed
Push — master ( 74fc93...195e55 )
by Sandro
06:11
created

ConsoleHelper   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 93.75%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 0
dl 0
loc 172
ccs 45
cts 48
cp 0.9375
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A readLine() 0 5 1
A colorize() 0 10 4
A write() 0 9 2
A writeLine() 0 4 1
A writeErrorLine() 0 11 2
A writeErrorMessage() 0 5 1
B detectColorCapabilities() 0 15 6
A formatNewlines() 0 6 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-2017 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) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
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