TerminalDimensions::getConsoleMode()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 14
nc 4
nop 0
dl 0
loc 22
rs 9.7998
c 1
b 0
f 0
ccs 0
cts 16
cp 0
crap 20
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
    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
    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