From e7bdc8af74e7fcfcb5b6f7f58b3ae047ae47754b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20H=C3=A4rtl?= Date: Fri, 16 Aug 2019 09:24:01 +0200 Subject: [PATCH] Issue #20 Tweak handling of input streams --- src/Command.php | 15 +++++++++------ tests/CommandTest.php | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/Command.php b/src/Command.php index 872929a..81ef30b 100644 --- a/src/Command.php +++ b/src/Command.php @@ -398,6 +398,7 @@ public function execute() stream_set_blocking($pipes[2], false); if ($hasInput) { $writtenBytes = 0; + $isInputOpen = true; stream_set_blocking($pipes[0], false); if ($isInputStream) { stream_set_blocking($this->_stdIn, false); @@ -408,10 +409,10 @@ public function execute() // a loop if the process is still running. We also need to // ensure that all the pipes are written/read alternately // until there's nothing left to write/read. - $running = true; - while ($running) { + $isRunning = true; + while ($isRunning) { $status = proc_get_status($process); - $running = $status['running']; + $isRunning = $status['running']; // We first write to stdIn if we have an input. For big // inputs it will only write until the input buffer of @@ -421,10 +422,11 @@ public function execute() // // After everything is written it's safe to close the // input pipe. - if ($hasInput && $running) { + if ($isRunning && $hasInput && $isInputOpen) { if ($isInputStream) { $written = stream_copy_to_stream($this->_stdIn, $pipes[0], 16 * 1024, $writtenBytes); if ($written === false || $written === 0) { + $isInputOpen = false; fclose($pipes[0]); } else { $writtenBytes += $written; @@ -433,6 +435,7 @@ public function execute() if ($writtenBytes < strlen($this->_stdIn)) { $writtenBytes += fwrite($pipes[0], substr($this->_stdIn, $writtenBytes)); } else { + $isInputOpen = false; fclose($pipes[0]); } } @@ -440,7 +443,7 @@ public function execute() // Read out the output buffers because if they are full // the command may block execution. We do this even if - // $running is `false`, because there could be output + // $isRunning is `false`, because there could be output // left in the buffers. // // The latter is only an assumption and needs to be @@ -454,7 +457,7 @@ public function execute() $this->_stdErr .= $err; } - if (!$running) { + if (!$isRunning) { $this->_exitCode = $status['exitcode']; fclose($pipes[1]); fclose($pipes[2]); diff --git a/tests/CommandTest.php b/tests/CommandTest.php index 3356d33..983f06c 100644 --- a/tests/CommandTest.php +++ b/tests/CommandTest.php @@ -256,4 +256,28 @@ public function testCanRunCommandWithBigInputAndOutput() $this->assertTrue($command->getExecuted()); $this->assertEquals(strlen($string), strlen($command->getOutput())); } + public function testCanRunLongRunningCommandWithBigInputAndOutput() + { + $string = str_repeat('01234567890abcdef', 16 * 1024); // 16 * 16 * 1024 = 256KB + $command = new Command('/bin/cat; echo "start" ; sleep 2 ; echo "done"'); + $command->setStdIn($string); + $this->assertTrue($command->execute()); + $this->assertTrue($command->getExecuted()); + $expected = $string . "start\ndone"; + $this->assertEquals(strlen($expected), strlen($command->getOutput())); + } + public function testCanRunLongRunningCommandWithStandardInputStream() + { + $string = str_repeat('01234567890abcdef', 16 * 1024); // 16 * 16 * 1024 = 256KB + $tmpfile = tmpfile(); + fwrite($tmpfile, $string); + fseek($tmpfile, 0); + $command = new Command('/bin/cat; echo "start" ; sleep 2 ; echo "done"'); + $command->setStdIn($tmpfile); + $this->assertTrue($command->execute()); + $this->assertTrue($command->getExecuted()); + $expected = $string . "start\ndone"; + $this->assertEquals(strlen($expected), strlen($command->getOutput())); + fclose($tmpfile); + } }