diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index b0f241c862..2b5c734c54 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -178,17 +178,38 @@ class DeprecationErrorHandler } } + /** + * Returns true if STDOUT is defined and supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @return bool + */ private static function hasColorSupport() { - if ('\\' === DIRECTORY_SEPARATOR) { - return - defined('STDOUT') && function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(STDOUT) - || '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD + if (!defined('STDOUT')) { + return false; + } + + if (DIRECTORY_SEPARATOR === '\\') { + return (function_exists('sapi_windows_vt100_support') + && sapi_windows_vt100_support(STDOUT)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); + if (function_exists('stream_isatty')) { + return stream_isatty(STDOUT); + } + + if (function_exists('posix_isatty')) { + return posix_isatty(STDOUT); + } + + $stat = fstat(STDOUT); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/src/Symfony/Component/Console/Output/StreamOutput.php b/src/Symfony/Component/Console/Output/StreamOutput.php index 182622a29a..b0897b206f 100644 --- a/src/Symfony/Component/Console/Output/StreamOutput.php +++ b/src/Symfony/Component/Console/Output/StreamOutput.php @@ -81,31 +81,34 @@ class StreamOutput extends Output * * Colorization is disabled if not supported by the stream: * - * - the stream is redirected (eg php file.php >log) - * - Windows without VT100 support, Ansicon, ConEmu, Mintty - * - non tty consoles + * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo + * terminals via named pipes, so we can only check the environment. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler * * @return bool true if the stream supports colorization, false otherwise */ protected function hasColorSupport() { - if (function_exists('stream_isatty') && !@stream_isatty($this->stream)) { - return false; - } if (DIRECTORY_SEPARATOR === '\\') { - if (function_exists('sapi_windows_vt100_support')) { - $vt100Enabled = @sapi_windows_vt100_support($this->stream); - } else { - $vt100Enabled = '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD; - } - - return - $vt100Enabled + return (function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($this->stream)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return function_exists('posix_isatty') && @posix_isatty($this->stream); + if (function_exists('stream_isatty')) { + return @stream_isatty($this->stream); + } + + if (function_exists('posix_isatty')) { + return @posix_isatty($this->stream); + } + + $stat = @fstat($this->stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index 689ada77c2..8f3f7c7e99 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -58,7 +58,7 @@ class CliDumper extends AbstractDumper { parent::__construct($output, $charset); - if ('\\' === DIRECTORY_SEPARATOR && 'ON' !== @getenv('ConEmuANSI') && 'xterm' !== @getenv('TERM')) { + if ('\\' === DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI $this->setStyles(array( 'default' => '31', @@ -420,7 +420,7 @@ class CliDumper extends AbstractDumper protected function supportsColors() { if ($this->outputStream !== static::$defaultOutput) { - return @(is_resource($this->outputStream) && function_exists('posix_isatty') && posix_isatty($this->outputStream)); + return $this->hasColorSupport($this->outputStream); } if (null !== static::$defaultColors) { return static::$defaultColors; @@ -448,23 +448,10 @@ class CliDumper extends AbstractDumper } } - if ('\\' === DIRECTORY_SEPARATOR) { - static::$defaultColors = @( - function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support($this->outputStream) - || '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD - || false !== getenv('ANSICON') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM') - ); - } elseif (function_exists('posix_isatty')) { - $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); - $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; - static::$defaultColors = @posix_isatty($h); - } else { - static::$defaultColors = false; - } + $h = stream_get_meta_data($this->outputStream) + array('wrapper_type' => null); + $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; - return static::$defaultColors; + return static::$defaultColors = $this->hasColorSupport($h); } /** @@ -477,4 +464,69 @@ class CliDumper extends AbstractDumper } parent::dumpLine($depth); } + + /** + * Returns true if the stream supports colorization. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @param mixed $stream A CLI output stream + * + * @return bool + */ + private function hasColorSupport($stream) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + return false; + } + + if (DIRECTORY_SEPARATOR === '\\') { + return (function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($stream)) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + if (function_exists('stream_isatty')) { + return @stream_isatty($stream); + } + + if (function_exists('posix_isatty')) { + return @posix_isatty($stream); + } + + $stat = @fstat($stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } + + /** + * Returns true if the Windows terminal supports true color. + * + * Note that this does not check an output stream, but relies on environment + * variables from known implementations, or a PHP and Windows version that + * supports true color. + * + * @return bool + */ + private function isWindowsTrueColor() + { + $result = strval(getenv('ANSICON_VER')) >= '183' + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + + if (!$result && PHP_VERSION_ID >= 70200) { + $version = sprintf( + '%s.%s.%s', + PHP_WINDOWS_VERSION_MAJOR, + PHP_WINDOWS_VERSION_MINOR, + PHP_WINDOWS_VERSION_BUILD + ); + $result = $version >= '10.0.15063'; + } + + return $result; + } }