1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Graze\DiffRenderer\Terminal; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Class TerminalDimensions |
7
|
|
|
* |
8
|
|
|
* Copied from symfony/console/Terminal |
9
|
|
|
*/ |
10
|
|
|
class TerminalDimensions implements DimensionsInterface |
11
|
|
|
{ |
12
|
|
|
/** @var int|null */ |
13
|
|
|
private $width = null; |
14
|
|
|
/** @var int|null */ |
15
|
|
|
private $height = null; |
16
|
|
|
/** @var bool */ |
17
|
|
|
private $initialised = false; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Gets the terminal width. |
21
|
|
|
* |
22
|
|
|
* @return int |
23
|
|
|
*/ |
24
|
|
View Code Duplication |
public function getWidth() |
|
|
|
|
25
|
|
|
{ |
26
|
|
|
if (null === $this->width && !$this->initialised) { |
27
|
|
|
$this->refreshDimensions(); |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
if (null === $this->width) { |
31
|
|
|
$width = getenv('COLUMNS'); |
32
|
|
|
if (false !== $width) { |
33
|
|
|
$this->width = (int) trim($width); |
34
|
|
|
} |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
return $this->width ?: static::DEFAULT_WIDTH; |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Gets the terminal height. |
42
|
|
|
* |
43
|
|
|
* @return int |
44
|
|
|
*/ |
45
|
|
View Code Duplication |
public function getHeight() |
|
|
|
|
46
|
|
|
{ |
47
|
|
|
if (null === $this->height && !$this->initialised) { |
48
|
|
|
$this->refreshDimensions(); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
if (null === $this->height) { |
52
|
|
|
$height = getenv('LINES'); |
53
|
|
|
if (false !== $height) { |
54
|
|
|
$this->height = (int) trim($height); |
55
|
|
|
} |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
return $this->height ?: static::DEFAULT_HEIGHT; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Refresh the current dimensions from the terminal |
63
|
|
|
*/ |
64
|
|
|
public function refreshDimensions() |
65
|
|
|
{ |
66
|
|
|
if ('\\' === DIRECTORY_SEPARATOR) { |
67
|
|
|
if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) { |
68
|
|
|
// extract [w, H] from "wxh (WxH)" |
69
|
|
|
// or [w, h] from "wxh" |
|
|
|
|
70
|
|
|
$this->width = (int) $matches[1]; |
71
|
|
|
$this->height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2]; |
72
|
|
|
} elseif (null !== $dimensions = $this->getConsoleMode()) { |
73
|
|
|
// extract [w, h] from "wxh" |
74
|
|
|
$this->width = (int) $dimensions[0]; |
75
|
|
|
$this->height = (int) $dimensions[1]; |
76
|
|
|
} |
77
|
|
|
} elseif ($sttyString = $this->getSttyColumns()) { |
78
|
|
|
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { |
79
|
|
|
// extract [w, h] from "rows h; columns w;" |
80
|
|
|
$this->width = (int) $matches[2]; |
81
|
|
|
$this->height = (int) $matches[1]; |
82
|
|
|
} elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { |
83
|
|
|
// extract [w, h] from "; h rows; w columns" |
84
|
|
|
$this->width = (int) $matches[2]; |
85
|
|
|
$this->height = (int) $matches[1]; |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
$this->initialised = true; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Runs and parses mode CON if it's available, suppressing any error output. |
93
|
|
|
* |
94
|
|
|
* @return int[]|null An array composed of the width and the height or null if it could not be parsed |
95
|
|
|
*/ |
96
|
|
|
private function getConsoleMode() |
97
|
|
|
{ |
98
|
|
|
if (!function_exists('proc_open')) { |
99
|
|
|
return null; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$spec = [ |
103
|
|
|
1 => ['pipe', 'w'], |
104
|
|
|
2 => ['pipe', 'w'], |
105
|
|
|
]; |
106
|
|
|
$process = proc_open('mode CON', $spec, $pipes, null, null, ['suppress_errors' => true]); |
107
|
|
|
if (is_resource($process)) { |
108
|
|
|
$info = stream_get_contents($pipes[1]); |
109
|
|
|
fclose($pipes[1]); |
110
|
|
|
fclose($pipes[2]); |
111
|
|
|
proc_close($process); |
112
|
|
|
|
113
|
|
|
if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { |
114
|
|
|
return [(int) $matches[2], (int) $matches[1]]; |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
return null; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Runs and parses stty -a if it's available, suppressing any error output. |
122
|
|
|
* |
123
|
|
|
* @return string|null |
124
|
|
|
*/ |
125
|
|
|
private function getSttyColumns() |
126
|
|
|
{ |
127
|
|
|
if (!function_exists('proc_open')) { |
128
|
|
|
return null; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
$spec = [ |
132
|
|
|
1 => ['pipe', 'w'], |
133
|
|
|
2 => ['pipe', 'w'], |
134
|
|
|
]; |
135
|
|
|
|
136
|
|
|
$process = proc_open('stty -a | grep columns', $spec, $pipes, null, null, ['suppress_errors' => true]); |
137
|
|
|
if (is_resource($process)) { |
138
|
|
|
$info = stream_get_contents($pipes[1]); |
139
|
|
|
fclose($pipes[1]); |
140
|
|
|
fclose($pipes[2]); |
141
|
|
|
proc_close($process); |
142
|
|
|
|
143
|
|
|
return $info; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
return null; |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.