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