diff --git a/src/future/exec/ExecFuture.php b/src/future/exec/ExecFuture.php --- a/src/future/exec/ExecFuture.php +++ b/src/future/exec/ExecFuture.php @@ -56,6 +56,8 @@ 2 => array('pipe', 'w'), // stderr ); + private $descriptors; + /* -( Creating ExecFutures )----------------------------------------------- */ @@ -195,6 +197,19 @@ return $this; } + /** + * Set the file descriptors which will be passed down to the underlying + * process; defaults to all of the standard handles being pipes back to this + * class. Overrides configuration set by @{method:setUseWindowsFileStreams}. + * + * @param array As passed to `proc_open`. + * @return this + * @task config + */ + public function setDescriptors($descs) { + $this->descriptors = $descs; + return $this; + } /* -( Interacting With Commands )------------------------------------------ */ @@ -626,8 +641,9 @@ $trap = null; } - $spec = self::$descriptorSpec; - if ($this->useWindowsFileStreams) { + if ($this->descriptors) { + $spec = $this->descriptors; + } else if ($this->useWindowsFileStreams) { $this->windowsStdoutTempFile = new TempFile(); $this->windowsStderrTempFile = new TempFile(); @@ -642,6 +658,8 @@ 'Unable to create temporary files for '. 'Windows stdout / stderr streams')); } + } else { + $spec = self::$descriptorSpec; } $proc = @proc_open( @@ -651,7 +669,7 @@ $cwd, $env); - if ($this->useWindowsFileStreams) { + if (!$this->descriptors && $this->useWindowsFileStreams) { fclose($spec[1]); fclose($spec[2]); $pipes = array( @@ -682,6 +700,10 @@ $err)); } + // If different descriptors were passed, we may have an array without + // values for all of its indices. Merge null values in. + $pipes = $pipes + array(null, null, null); + $this->pipes = $pipes; $this->proc = $proc; @@ -693,9 +715,9 @@ // through temporary files, and then use stream_select to determine // if there's more data to read. - if ((!stream_set_blocking($stdout, false)) || - (!stream_set_blocking($stderr, false)) || - (!stream_set_blocking($stdin, false))) { + if (($stdout && !stream_set_blocking($stdout, false)) || + ($stderr && !stream_set_blocking($stderr, false)) || + ($stdin && !stream_set_blocking($stdin, false))) { $this->__destruct(); throw new Exception(pht('Failed to set streams nonblocking.')); } @@ -741,7 +763,7 @@ $max_stderr_read_bytes = $read_buffer_size - strlen($this->stderr); } - if ($max_stdout_read_bytes > 0) { + if ($max_stdout_read_bytes > 0 && $stdout) { $this->stdout .= $this->readAndDiscard( $stdout, $this->getStdoutSizeLimit() - strlen($this->stdout), @@ -749,7 +771,7 @@ $max_stdout_read_bytes); } - if ($max_stderr_read_bytes > 0) { + if ($max_stderr_read_bytes > 0 && $stderr) { $this->stderr .= $this->readAndDiscard( $stderr, $this->getStderrSizeLimit() - strlen($this->stderr), @@ -769,7 +791,7 @@ } if ($is_done) { - if ($this->useWindowsFileStreams) { + if (!$this->descriptors && $this->useWindowsFileStreams) { fclose($stdout); fclose($stderr); } diff --git a/src/future/exec/__tests__/ExecFutureTestCase.php b/src/future/exec/__tests__/ExecFutureTestCase.php --- a/src/future/exec/__tests__/ExecFutureTestCase.php +++ b/src/future/exec/__tests__/ExecFutureTestCase.php @@ -152,4 +152,34 @@ $future->resolve(); } + public function testHigherFileDescriptors() { + $temp = new TempFile(); + $future = new ExecFuture('echo yay >&3'); + $future->setDescriptors( + array( + 0 => array('pipe', 'r'), // stdin + 1 => array('pipe', 'w'), // stdout + 2 => array('pipe', 'w'), // stderr + 3 => array('file', $temp, 'w'), + )); + + list($stdout) = $future->resolvex(); + $this->assertEqual('', $stdout); + $this->assertEqual("yay\n", Filesystem::readFile($temp)); + } + + public function testFeedStdin() { + $temp = new TempFile(); + Filesystem::writeFile($temp, 'yay'); + $future = new ExecFuture('cat'); + $future->setDescriptors( + array( + 0 => array('file', $temp, 'r'), // stdin + 1 => array('pipe', 'w'), // stdout + 2 => array('pipe', 'w'), // stderr + )); + + list($stdout) = $future->resolvex(); + $this->assertEqual('yay', $stdout); + } }