@@ -403,27 +403,58 @@ private static function hasColorSupport()
403403 return false ;
404404 }
405405
406- if (' Hyper ' === getenv ( ' TERM_PROGRAM ' )) {
406+ if (! self :: isTty ( )) {
407407 return true ;
408408 }
409409
410- if (\DIRECTORY_SEPARATOR === '\\' ) {
411- return (\function_exists ('sapi_windows_vt100_support ' )
412- && sapi_windows_vt100_support (\STDOUT ))
413- || false !== getenv ('ANSICON ' )
414- || 'ON ' === getenv ('ConEmuANSI ' )
415- || 'xterm ' === getenv ('TERM ' );
410+ if ('\\' === \DIRECTORY_SEPARATOR
411+ && \function_exists ('sapi_windows_vt100_support ' )
412+ && @sapi_windows_vt100_support (\STDOUT )
413+ ) {
414+ return true ;
416415 }
417416
417+ if ('Hyper ' === getenv ('TERM_PROGRAM ' )
418+ || false !== getenv ('COLORTERM ' )
419+ || false !== getenv ('ANSICON ' )
420+ || 'ON ' === getenv ('ConEmuANSI ' )
421+ ) {
422+ return true ;
423+ }
424+
425+ if ('dumb ' === $ term = (string ) getenv ('TERM ' )) {
426+ return false ;
427+ }
428+
429+ // See https://github.com/chalk/supports-color/blob/d4f413efaf8da045c5ab440ed418ef02dbb28bf1/index.js#L157
430+ return preg_match ('/^((screen|xterm|vt100|vt220|putty|rxvt|ansi|cygwin|linux).*)|(.*-256(color)?(-bce)?)$/ ' , $ term );
431+ }
432+
433+ /**
434+ * Checks if the stream is a TTY, i.e; whether the output stream is connected to a terminal.
435+ *
436+ * Reference: Composer\Util\Platform::isTty
437+ * https://github.com/composer/composer
438+ */
439+ private static function isTty (): bool
440+ {
441+ // Detect msysgit/mingw and assume this is a tty because detection
442+ // does not work correctly, see https://github.com/composer/composer/issues/9690
443+ if (\in_array (strtoupper ((string ) getenv ('MSYSTEM ' )), ['MINGW32 ' , 'MINGW64 ' ], true )) {
444+ return true ;
445+ }
446+
447+ // Modern cross-platform function, includes the fstat fallback so if it is present we trust it
418448 if (\function_exists ('stream_isatty ' )) {
419449 return @stream_isatty (\STDOUT );
420450 }
421451
422- if (\function_exists ('posix_isatty ' )) {
423- return @posix_isatty (\STDOUT );
452+ // Only trusting this if it is positive, otherwise prefer fstat fallback.
453+ if (\function_exists ('posix_isatty ' ) && @posix_isatty (\STDOUT )) {
454+ return true ;
424455 }
425456
426- $ stat = fstat (\STDOUT );
457+ $ stat = @ fstat (\STDOUT );
427458
428459 // Check if formatted mode is S_IFCHR
429460 return $ stat ? 0020000 === ($ stat ['mode ' ] & 0170000 ) : false ;
0 commit comments