diff --git a/src/console/PhutilConsoleFormatter.php b/src/console/PhutilConsoleFormatter.php index d380c961..43f1fadc 100644 --- a/src/console/PhutilConsoleFormatter.php +++ b/src/console/PhutilConsoleFormatter.php @@ -22,25 +22,40 @@ final class PhutilConsoleFormatter extends Phobject { public static function getDisableANSI() { if (self::$disableANSI === null) { - $term = phutil_utf8_strtolower(getenv('TERM')); - // ansicon enables ANSI support on Windows - if (!$term && getenv('ANSICON')) { - $term = 'ansi'; - } - - if (phutil_is_windows() && $term !== 'cygwin' && $term !== 'ansi') { - self::$disableANSI = true; - } else if (!defined('STDOUT')) { - self::$disableANSI = true; - } else if (function_exists('posix_isatty') && !posix_isatty(STDOUT)) { - self::$disableANSI = true; - } else { - self::$disableANSI = false; - } + self::$disableANSI = self::newShouldDisableAnsi(); } return self::$disableANSI; } + private static function newShouldDisableANSI() { + $term = phutil_utf8_strtolower(getenv('TERM')); + + // ansicon enables ANSI support on Windows + if (!$term && getenv('ANSICON')) { + $term = 'ansi'; + } + + + if (phutil_is_windows()) { + if ($term !== 'cygwin' && $term !== 'ansi') { + return true; + } + } + + $stdout = PhutilSystem::getStdoutHandle(); + if ($stdout === null) { + return true; + } + + if (function_exists('posix_isatty')) { + if (!posix_isatty($stdout)) { + return true; + } + } + + return false; + } + public static function formatString($format /* ... */) { $args = func_get_args(); $args[0] = self::interpretFormat($args[0]); diff --git a/src/filesystem/PhutilErrorLog.php b/src/filesystem/PhutilErrorLog.php index bc316c9e..d652d854 100644 --- a/src/filesystem/PhutilErrorLog.php +++ b/src/filesystem/PhutilErrorLog.php @@ -94,7 +94,7 @@ final class PhutilErrorLog if (strlen($message)) { $message = tsprintf("%B\n", $message); - @fwrite(STDERR, $message); + PhutilSystem::writeStderr($message); } } diff --git a/src/future/exec/PhutilExecPassthru.php b/src/future/exec/PhutilExecPassthru.php index fc61dc21..63921cf0 100644 --- a/src/future/exec/PhutilExecPassthru.php +++ b/src/future/exec/PhutilExecPassthru.php @@ -53,10 +53,14 @@ final class PhutilExecPassthru extends PhutilExecutableFuture { if ($is_write) { $stdin_spec = array('pipe', 'r'); } else { - $stdin_spec = STDIN; + $stdin_spec = PhutilSystem::getStdinHandle(); } - $spec = array($stdin_spec, STDOUT, STDERR); + $spec = array( + $stdin_spec, + PhutilSystem::getStdoutHandle(), + PhutilSystem::getStderrHandle(), + ); $pipes = array(); $unmasked_command = $command->getUnmaskedString(); diff --git a/src/log/ArcanistLogEngine.php b/src/log/ArcanistLogEngine.php index 11f6918b..c2d19c13 100644 --- a/src/log/ArcanistLogEngine.php +++ b/src/log/ArcanistLogEngine.php @@ -19,7 +19,7 @@ final class ArcanistLogEngine } private function writeBytes($bytes) { - fprintf(STDERR, '%s', $bytes); + PhutilSystem::writeStderr($bytes); return $this; } diff --git a/src/parser/ArcanistBaseCommitParser.php b/src/parser/ArcanistBaseCommitParser.php index 5ed6ce4a..2c886bd6 100644 --- a/src/parser/ArcanistBaseCommitParser.php +++ b/src/parser/ArcanistBaseCommitParser.php @@ -33,7 +33,7 @@ final class ArcanistBaseCommitParser extends Phobject { private function log($message) { if ($this->verbose) { - fwrite(STDERR, $message."\n"); + PhutilSystem::writeStderr($message."\n"); } } diff --git a/src/parser/argument/PhutilArgumentParser.php b/src/parser/argument/PhutilArgumentParser.php index 1bf31a23..04b04666 100644 --- a/src/parser/argument/PhutilArgumentParser.php +++ b/src/parser/argument/PhutilArgumentParser.php @@ -805,7 +805,7 @@ final class PhutilArgumentParser extends Phobject { private function logMessage($message) { - fwrite(STDERR, $message); + PhutilSystem::writeStderr($message); } diff --git a/src/progress/PhutilConsoleProgressSink.php b/src/progress/PhutilConsoleProgressSink.php index 11a61895..ca5dff88 100644 --- a/src/progress/PhutilConsoleProgressSink.php +++ b/src/progress/PhutilConsoleProgressSink.php @@ -110,6 +110,6 @@ final class PhutilConsoleProgressSink } private function printLine($line) { - fprintf(STDERR, '%s', $line); + PhutilSystem::writeStderr($line); } } diff --git a/src/symbols/PhutilSymbolLoader.php b/src/symbols/PhutilSymbolLoader.php index 32c7a042..a5851a5e 100644 --- a/src/symbols/PhutilSymbolLoader.php +++ b/src/symbols/PhutilSymbolLoader.php @@ -296,11 +296,12 @@ final class PhutilSymbolLoader { // library without breaking library startup. if ($should_continue) { // We may not have `pht()` yet. - fprintf( - STDERR, + $message = sprintf( "%s: %s\n", 'IGNORING CLASS LOAD FAILURE', $caught->getMessage()); + + @file_put_contents('php://stderr', $message); } else { throw $caught; } diff --git a/src/upload/ArcanistFileUploader.php b/src/upload/ArcanistFileUploader.php index cde462e7..2af3ae25 100644 --- a/src/upload/ArcanistFileUploader.php +++ b/src/upload/ArcanistFileUploader.php @@ -313,7 +313,7 @@ final class ArcanistFileUploader extends Phobject { * @task internal */ private function writeStatus($message) { - fwrite(STDERR, $message."\n"); + PhutilSystem::writeStderr($message."\n"); } } diff --git a/src/utils/PhutilSystem.php b/src/utils/PhutilSystem.php index 6c6d5dcd..3657d8b0 100644 --- a/src/utils/PhutilSystem.php +++ b/src/utils/PhutilSystem.php @@ -3,10 +3,76 @@ /** * Interact with the operating system. * + * @task stdio Interacting with Standard I/O * @task memory Interacting with System Memory */ final class PhutilSystem extends Phobject { + private static $stdin = false; + private static $stderr = false; + private static $stdout = false; + + /** + * @task stdio + */ + public static function getStdinHandle() { + if (self::$stdin === false) { + self::$stdin = self::newStdioHandle('php://stdin'); + } + + return self::$stdin; + } + + /** + * @task stdio + */ + public static function getStdoutHandle() { + if (self::$stdout === false) { + self::$stdout = self::newStdioHandle('php://stdout'); + } + + return self::$stdout; + } + + /** + * @task stdio + */ + public static function getStderrHandle() { + if (self::$stderr === false) { + self::$stderr = self::newStdioHandle('php://stderr'); + } + + return self::$stderr; + } + + /** + * @task stdio + */ + public static function writeStderr($message) { + $stderr = self::getStderrHandle(); + + if ($stderr === null) { + return; + } + + $message = phutil_string_cast($message); + @fwrite($stderr, $message); + } + + + /** + * @task stdio + */ + private static function newStdioHandle($ref) { + $ignored_mode = ''; + + $handle = @fopen($ref, $ignored_mode); + if ($handle === false) { + return null; + } + + return $handle; + } /** * Get information about total and free memory on the system. diff --git a/src/workflow/ArcanistDiffWorkflow.php b/src/workflow/ArcanistDiffWorkflow.php index 36117994..5b8ff40c 100644 --- a/src/workflow/ArcanistDiffWorkflow.php +++ b/src/workflow/ArcanistDiffWorkflow.php @@ -806,7 +806,10 @@ EOTEXT if ($is_raw) { if ($this->getArgument('raw')) { - fwrite(STDERR, pht('Reading diff from stdin...')."\n"); + PhutilSystem::writeStderr( + tsprintf( + "%s\n", + pht('Reading diff from stdin...'))); $raw_diff = file_get_contents('php://stdin'); } else if ($this->getArgument('raw-command')) { list($raw_diff) = execx('%C', $this->getArgument('raw-command')); diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php index 3630bdb8..a93238d7 100644 --- a/src/workflow/ArcanistWorkflow.php +++ b/src/workflow/ArcanistWorkflow.php @@ -1565,7 +1565,7 @@ abstract class ArcanistWorkflow extends Phobject { * @return void */ final protected function writeStatusMessage($msg) { - fwrite(STDERR, $msg); + PhutilSystem::writeStderr($msg); } final public function writeInfo($title, $message) {