Terminal::hasSttyAvailable()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
3
declare (strict_types=1);
4
namespace HumbugBox451\KevinGH\RequirementChecker;
5
6
use function exec;
7
use function fclose;
8
use function fopen;
9
use function function_exists;
10
use function getenv;
11
use function is_resource;
12
use function preg_match;
13
use function proc_close;
14
use function proc_open;
15
use function sapi_windows_vt100_support;
16
use function stream_get_contents;
17
use function trim;
18
use const DIRECTORY_SEPARATOR;
19
/** @internal */
20
class Terminal
21
{
22
    private static $width;
23
    private static $height;
24
    private static $stty;
25
    public function getWidth() : int
26
    {
27
        $width = getenv('COLUMNS');
28
        if (\false !== $width) {
29
            return (int) trim($width);
30
        }
31
        if (!isset(self::$width)) {
32
            self::initDimensions();
33
        }
34
        return self::$width ?: 80;
35
    }
36
    public function getHeight() : int
37
    {
38
        $height = getenv('LINES');
39
        if (\false !== $height) {
40
            return (int) trim($height);
41
        }
42
        if (!isset(self::$height)) {
43
            self::initDimensions();
44
        }
45
        return self::$height ?: 50;
46
    }
47
    public static function hasSttyAvailable() : bool
48
    {
49
        if (isset(self::$stty)) {
50
            return self::$stty;
51
        }
52
        if (!function_exists('exec')) {
53
            return \false;
54
        }
55
        exec('stty 2>&1', $output, $exitcode);
56
        return self::$stty = 0 === $exitcode;
57
    }
58
    private static function initDimensions() : void
59
    {
60
        if ('\\' === DIRECTORY_SEPARATOR) {
61
            if (preg_match('/^(\\d+)x(\\d+)(?: \\((\\d+)x(\\d+)\\))?$/', trim(getenv('ANSICON') ?: ''), $matches)) {
62
                self::$width = (int) $matches[1];
63
                self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
64
            } elseif (!self::hasVt100Support() && self::hasSttyAvailable()) {
65
                self::initDimensionsUsingStty();
66
            } elseif (null !== ($dimensions = self::getConsoleMode())) {
67
                self::$width = (int) $dimensions[0];
68
                self::$height = (int) $dimensions[1];
69
            }
70
        } else {
71
            self::initDimensionsUsingStty();
72
        }
73
    }
74
    private static function hasVt100Support() : bool
75
    {
76
        return function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'wb'));
77
    }
78
    private static function initDimensionsUsingStty() : void
79
    {
80
        if ($sttyString = self::getSttyColumns()) {
81
            if (preg_match('/rows.(\\d+);.columns.(\\d+);/i', $sttyString, $matches)) {
82
                self::$width = (int) $matches[2];
83
                self::$height = (int) $matches[1];
84
            } elseif (preg_match('/;.(\\d+).rows;.(\\d+).columns/i', $sttyString, $matches)) {
85
                self::$width = (int) $matches[2];
86
                self::$height = (int) $matches[1];
87
            }
88
        }
89
    }
90
    private static function getConsoleMode() : ?array
91
    {
92
        $info = self::readFromProcess('mode CON');
93
        if (null === $info || !preg_match('/--------+\\r?\\n.+?(\\d+)\\r?\\n.+?(\\d+)\\r?\\n/', $info, $matches)) {
94
            return null;
95
        }
96
        return [(int) $matches[2], (int) $matches[1]];
97
    }
98
    private static function getSttyColumns() : ?string
99
    {
100
        return self::readFromProcess('stty -a | grep columns');
101
    }
102
    private static function readFromProcess(string $command) : ?string
103
    {
104
        if (!function_exists('proc_open')) {
105
            return null;
106
        }
107
        $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
108
        $process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => \true]);
109
        if (!is_resource($process)) {
110
            return null;
111
        }
112
        $info = stream_get_contents($pipes[1]);
113
        fclose($pipes[1]);
114
        fclose($pipes[2]);
115
        proc_close($process);
116
        return $info;
117
    }
118
}
119