Completed
Pull Request — master (#65)
by
unknown
03:48
created

UnixTerminal::isCanonical()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace PhpSchool\CliMenu\Terminal;
4
5
/**
6
 * Class UnixTerminal
7
 *
8
 * @package PhpSchool\CliMenu\Terminal
9
 * @author Michael Woodward <[email protected]>
10
 */
11
class UnixTerminal implements TerminalInterface
12
{
13
    /**
14
     * @var bool
15
     */
16
    private $isTTY;
17
18
    /**
19
     * @var bool
20
     */
21
    private $isCanonical = false;
22
23
    /**
24
     * @var int
25
     */
26
    private $width;
27
28
    /**
29
     * @var int
30
     */
31
    private $height;
32
33
    /**
34
     * @var string
35
     */
36
    private $details;
37
38
    /**
39
     * @var string
40
     */
41
    private $originalConfiguration;
42
43
    /**
44
     * Initialise the terminal from resource
45
     *
46
     */
47
    public function __construct()
48
    {
49
        $this->getOriginalConfiguration();
50
    }
51
52
    /**
53
     * Get the available width of the terminal
54
     *
55
     * @return int
56
     */
57
    public function getWidth()
58
    {
59
        return $this->width ?: $this->width = (int) exec('tput cols');
60
    }
61
62
    /**
63
     * Get the available height of the terminal
64
     *
65
     * @return int
66
     */
67
    public function getHeight()
68
    {
69
        return $this->height ?: $this->height = (int) exec('tput lines');
70
    }
71
72
    /**
73
     * Get terminal details
74
     *
75
     * @return string
76
     */
77
    public function getDetails()
78
    {
79
        if (!$this->details) {
80
            $this->details = function_exists('posix_ttyname')
81
                ? @posix_ttyname(STDOUT)
82
                : "Can't retrieve terminal details";
83
        }
84
85
        return $this->details;
86
    }
87
88
    /**
89
     * Get the original terminal configuration / mode
90
     *
91
     * @return string
92
     */
93
    private function getOriginalConfiguration()
94
    {
95
        return $this->originalConfiguration ?: $this->originalConfiguration = exec('stty -g');
96
    }
97
98
    /**
99
     * Toggle canonical mode on TTY
100
     *
101
     * @param bool $useCanonicalMode
102
     */
103
    public function setCanonicalMode($useCanonicalMode = true)
104
    {
105
        if ($useCanonicalMode) {
106
            exec('stty -icanon');
107
            $this->isCanonical = true;
108
        } else {
109
            exec('stty ' . $this->getOriginalConfiguration());
110
            $this->isCanonical = false;
111
        }
112
    }
113
114
    /**
115
     * Check if TTY is in canonical mode
116
     * Assumes the terminal was never in canonical mode
117
     *
118
     * @return bool
119
     */
120
    public function isCanonical()
121
    {
122
        return $this->isCanonical;
123
    }
124
125
    /**
126
     * Test whether terminal is valid TTY
127
     *
128
     * @return bool
129
     */
130
    public function isTTY()
131
    {
132
        return $this->isTTY ?: $this->isTTY = function_exists('posix_isatty') && @posix_isatty(STDOUT);
133
    }
134
135
    /**
136
     * Test whether terminal supports colour output
137
     *
138
     * @return bool
139
     *
140
     * @link https://github.com/symfony/Console/blob/master/Output/StreamOutput.php#L95-L102
141
     */
142
    public function supportsColour()
143
    {
144
        if (DIRECTORY_SEPARATOR === '\\') {
145
            return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM');
146
        }
147
148
        return $this->isTTY();
149
    }
150
151
    /**
152
     * @return string
153
     */
154
    public function getKeyedInput()
155
    {
156
        // TODO: Move to class var?
157
        // TODO: up, down, enter etc in Abstract CONSTs
158
        $map = [
159
            "\033[A" => 'up',
160
            "k"      => 'up',
161
            "\033[B" => 'down',
162
            "j"      => 'down',
163
            "\n"     => 'enter',
164
            "\r"     => 'enter',
165
            " "      => 'enter',
166
            "\033[D" => 'left',
167
            "h"      => 'left',
168
            "\033[C" => 'right',
169
            "l"      => 'right',
170
        ];
171
172
        $input = fread(STDIN, 4);
173
        $this->clearLine();
174
175
        return array_key_exists($input, $map)
176
            ? $map[$input]
177
            : $input;
178
    }
179
180
    /**
181
     * Clear the terminal window
182
     */
183
    public function clear()
184
    {
185
        echo "\033[2J";
186
    }
187
188
    /**
189
     * Enable cursor
190
     */
191
    public function enableCursor()
192
    {
193
        echo "\033[?25h";
194
    }
195
196
    /**
197
     * Disable cursor
198
     */
199
    public function disableCursor()
200
    {
201
        echo "\033[?25l";
202
    }
203
204
    /**
205
     * Move the cursor to the top left of the window
206
     *
207
     * @return void
208
     */
209
    public function moveCursorToTop()
210
    {
211
        echo "\033[H";
212
    }
213
214
    /**
215
     * Move the cursor to the start of a specific row
216
     *
217
     * @param int $rowNumber
218
     */
219
    public function moveCursorToRow($rowNumber)
220
    {
221
        echo sprintf("\033[%d;0H", $rowNumber);
222
    }
223
224
    /**
225
     * Move the cursor to the start of a specific column
226
     *
227
     * @param int $column
228
     */
229
    public function moveCursorToColumn($column)
230
    {
231
        echo sprintf("\033[%dC", $column);
232
    }
233
234
    /**
235
     * Clear the current cursors line
236
     *
237
     * @return void
238
     */
239
    public function clearLine()
240
    {
241
        echo sprintf("\033[%dD\033[K", $this->getWidth());
242
    }
243
244
    /**
245
     * Clean the whole console without jumping the window
246
     */
247
    public function clean()
248
    {
249
        foreach (range(0, $this->getHeight()) as $rowNum) {
250
            $this->moveCursorToRow($rowNum);
251
            $this->clearLine();
252
        }
253
    }
254
}
255