1<?php
2/*
3 * This file is part of the Environment package.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace SebastianBergmann\Environment;
12
13/**
14 */
15class Console
16{
17    const STDIN  = 0;
18    const STDOUT = 1;
19    const STDERR = 2;
20
21    /**
22     * Returns true if STDOUT supports colorization.
23     *
24     * This code has been copied and adapted from
25     * Symfony\Component\Console\Output\OutputStream.
26     *
27     * @return bool
28     */
29    public function hasColorSupport()
30    {
31        if (DIRECTORY_SEPARATOR == '\\') {
32            return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM');
33        }
34
35        if (!defined('STDOUT')) {
36            return false;
37        }
38
39        return $this->isInteractive(STDOUT);
40    }
41
42    /**
43     * Returns the number of columns of the terminal.
44     *
45     * @return int
46     */
47    public function getNumberOfColumns()
48    {
49        if (DIRECTORY_SEPARATOR == '\\') {
50            $columns = 80;
51
52            if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
53                $columns = $matches[1];
54            } elseif (function_exists('proc_open')) {
55                $process = proc_open(
56                    'mode CON',
57                    [
58                        1 => ['pipe', 'w'],
59                        2 => ['pipe', 'w']
60                    ],
61                    $pipes,
62                    null,
63                    null,
64                    ['suppress_errors' => true]
65                );
66
67                if (is_resource($process)) {
68                    $info = stream_get_contents($pipes[1]);
69
70                    fclose($pipes[1]);
71                    fclose($pipes[2]);
72                    proc_close($process);
73
74                    if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
75                        $columns = $matches[2];
76                    }
77                }
78            }
79
80            return $columns - 1;
81        }
82
83        if (!$this->isInteractive(self::STDIN)) {
84            return 80;
85        }
86
87        if (function_exists('shell_exec') && preg_match('#\d+ (\d+)#', shell_exec('stty size'), $match) === 1) {
88            if ((int) $match[1] > 0) {
89                return (int) $match[1];
90            }
91        }
92
93        if (function_exists('shell_exec') && preg_match('#columns = (\d+);#', shell_exec('stty'), $match) === 1) {
94            if ((int) $match[1] > 0) {
95                return (int) $match[1];
96            }
97        }
98
99        return 80;
100    }
101
102    /**
103     * Returns if the file descriptor is an interactive terminal or not.
104     *
105     * @param int|resource $fileDescriptor
106     *
107     * @return bool
108     */
109    public function isInteractive($fileDescriptor = self::STDOUT)
110    {
111        return function_exists('posix_isatty') && @posix_isatty($fileDescriptor);
112    }
113}
114