Skip to content

Commit 0007ec9

Browse files
committed
Create Screen.php
1 parent 35ad058 commit 0007ec9

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
3+
4+
namespace Ubiquity\devtools\cmd;
5+
6+
/**
7+
* Class Screen
8+
* Forked from https://github.com/symfony/console/blob/5.3/Terminal.php
9+
* @package Ubiquity\devtools\cmd
10+
*/
11+
class Screen {
12+
private static $width;
13+
private static $height;
14+
private static $stty;
15+
16+
/**
17+
* Gets the terminal width.
18+
*
19+
* @return int
20+
*/
21+
public static function getWidth() {
22+
$width = \getenv('COLUMNS');
23+
if (false !== $width) {
24+
return (int) \trim($width);
25+
}
26+
27+
if (null === self::$width) {
28+
self::initDimensions();
29+
}
30+
31+
return self::$width??80;
32+
}
33+
34+
/**
35+
* Gets the terminal height.
36+
*
37+
* @return int
38+
*/
39+
public static function getHeight() {
40+
$height = \getenv('LINES');
41+
if (false !== $height) {
42+
return (int) trim($height);
43+
}
44+
45+
if (null === self::$height) {
46+
self::initDimensions();
47+
}
48+
49+
return self::$height??50;
50+
}
51+
52+
/**
53+
* @internal
54+
*
55+
* @return bool
56+
*/
57+
public static function hasSttyAvailable() {
58+
if (null !== self::$stty) {
59+
return self::$stty;
60+
}
61+
62+
// skip check if exec function is disabled
63+
if (!\function_exists('exec')) {
64+
return false;
65+
}
66+
67+
\exec('stty 2>&1', $output, $exitcode);
68+
69+
return self::$stty = 0 === $exitcode;
70+
}
71+
72+
private static function initDimensions() {
73+
if ('\\' === \DIRECTORY_SEPARATOR) {
74+
if (\preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', \trim(\getenv('ANSICON')), $matches)) {
75+
// extract [w, H] from "wxh (WxH)"
76+
// or [w, h] from "wxh"
77+
self::$width = (int) $matches[1];
78+
self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
79+
} elseif (!self::hasVt100Support() && self::hasSttyAvailable()) {
80+
// only use stty on Windows if the terminal does not support vt100 (e.g. Windows 7 + git-bash)
81+
// testing for stty in a Windows 10 vt100-enabled console will implicitly disable vt100 support on STDOUT
82+
self::initDimensionsUsingStty();
83+
} elseif (null !== $dimensions = self::getConsoleMode()) {
84+
// extract [w, h] from "wxh"
85+
self::$width = (int) $dimensions[0];
86+
self::$height = (int) $dimensions[1];
87+
}
88+
} else {
89+
self::initDimensionsUsingStty();
90+
}
91+
}
92+
93+
/**
94+
* Returns whether STDOUT has vt100 support (some Windows 10+ configurations).
95+
*/
96+
private static function hasVt100Support(): bool {
97+
return \function_exists('sapi_windows_vt100_support') && \sapi_windows_vt100_support(\fopen('php://stdout', 'w'));
98+
}
99+
100+
/**
101+
* Initializes dimensions using the output of an stty columns line.
102+
*/
103+
private static function initDimensionsUsingStty() {
104+
if ($sttyString = self::getSttyColumns()) {
105+
if (\preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
106+
// extract [w, h] from "rows h; columns w;"
107+
self::$width = (int) $matches[2];
108+
self::$height = (int) $matches[1];
109+
} elseif (\preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
110+
// extract [w, h] from "; h rows; w columns"
111+
self::$width = (int) $matches[2];
112+
self::$height = (int) $matches[1];
113+
}
114+
}
115+
}
116+
117+
/**
118+
* Runs and parses mode CON if it's available, suppressing any error output.
119+
*
120+
* @return int[]|null An array composed of the width and the height or null if it could not be parsed
121+
*/
122+
private static function getConsoleMode(): ?array {
123+
$info = self::readFromProcess('mode CON');
124+
125+
if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
126+
return null;
127+
}
128+
129+
return [(int) $matches[2], (int) $matches[1]];
130+
}
131+
132+
/**
133+
* Runs and parses stty -a if it's available, suppressing any error output.
134+
*/
135+
private static function getSttyColumns(): ?string {
136+
return self::readFromProcess('stty -a | grep columns');
137+
}
138+
139+
private static function readFromProcess(string $command): ?string {
140+
if (!\function_exists('proc_open')) {
141+
return null;
142+
}
143+
144+
$descriptorspec = [
145+
1 => ['pipe', 'w'],
146+
2 => ['pipe', 'w'],
147+
];
148+
149+
$process = \proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
150+
if (!\is_resource($process)) {
151+
return null;
152+
}
153+
154+
$info = \stream_get_contents($pipes[1]);
155+
\fclose($pipes[1]);
156+
\fclose($pipes[2]);
157+
\proc_close($process);
158+
159+
return $info;
160+
}
161+
}

0 commit comments

Comments
 (0)