@@ -95,6 +95,10 @@ protected function hasColorSupport()
9595 return false ;
9696 }
9797
98+ if (!$ this ->isTty ()) {
99+ return false ;
100+ }
101+
98102 if (\DIRECTORY_SEPARATOR === '\\'
99103 && \function_exists ('sapi_windows_vt100_support ' )
100104 && @sapi_windows_vt100_support ($ this ->stream )
@@ -105,7 +109,36 @@ protected function hasColorSupport()
105109 return 'Hyper ' === getenv ('TERM_PROGRAM ' )
106110 || false !== getenv ('ANSICON ' )
107111 || 'ON ' === getenv ('ConEmuANSI ' )
108- || str_starts_with ((string ) getenv ('TERM ' ), 'xterm ' )
109- || stream_isatty ($ this ->stream );
112+ || str_starts_with ((string ) getenv ('TERM ' ), 'xterm ' );
113+ }
114+
115+ /**
116+ * Checks if the stream is a TTY, i.e; whether the output stream is connected to a terminal.
117+ *
118+ * Reference: Composer\Util\Platform::isTty
119+ * https://github.com/composer/composer
120+ */
121+ private function isTty (): bool
122+ {
123+ // Detect msysgit/mingw and assume this is a tty because detection
124+ // does not work correctly, see https://github.com/composer/composer/issues/9690
125+ if (\in_array (strtoupper ((string ) getenv ('MSYSTEM ' )), ['MINGW32 ' , 'MINGW64 ' ], true )) {
126+ return true ;
127+ }
128+
129+ // Modern cross-platform function, includes the fstat fallback so if it is present we trust it
130+ if (\function_exists ('stream_isatty ' )) {
131+ return stream_isatty ($ this ->stream );
132+ }
133+
134+ // Only trusting this if it is positive, otherwise prefer fstat fallback.
135+ if (\function_exists ('posix_isatty ' ) && posix_isatty ($ this ->stream )) {
136+ return true ;
137+ }
138+
139+ $ stat = @fstat ($ this ->stream );
140+
141+ // Check if formatted mode is S_IFCHR
142+ return $ stat ? 0020000 === ($ stat ['mode ' ] & 0170000 ) : false ;
110143 }
111144}
0 commit comments