Skip to content

Commit 8541b73

Browse files
[Process] Fix dealing with broken stdin pipes
1 parent 48bad91 commit 8541b73

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

Pipes/AbstractPipes.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,11 @@ protected function write(): ?array
135135

136136
foreach ($w as $stdin) {
137137
if (isset($this->inputBuffer[0])) {
138-
$written = fwrite($stdin, $this->inputBuffer);
138+
if (false === $written = @fwrite($stdin, $this->inputBuffer)) {
139+
return $this->closeBrokenInputPipe();
140+
}
139141
$this->inputBuffer = substr($this->inputBuffer, $written);
140-
if (isset($this->inputBuffer[0])) {
142+
if (isset($this->inputBuffer[0]) && isset($this->pipes[0])) {
141143
return [$this->pipes[0]];
142144
}
143145
}
@@ -148,12 +150,14 @@ protected function write(): ?array
148150
if (!isset($data[0])) {
149151
break;
150152
}
151-
$written = fwrite($stdin, $data);
153+
if (false === $written = @fwrite($stdin, $data)) {
154+
return $this->closeBrokenInputPipe();
155+
}
152156
$data = substr($data, $written);
153157
if (isset($data[0])) {
154158
$this->inputBuffer = $data;
155159

156-
return [$this->pipes[0]];
160+
return isset($this->pipes[0]) ? [$this->pipes[0]] : null;
157161
}
158162
}
159163
if (feof($input)) {
@@ -178,6 +182,18 @@ protected function write(): ?array
178182
return null;
179183
}
180184

185+
private function closeBrokenInputPipe(): void
186+
{
187+
$this->lastError = error_get_last()['message'] ?? null;
188+
if (\is_resource($this->pipes[0] ?? null)) {
189+
fclose($this->pipes[0]);
190+
}
191+
unset($this->pipes[0]);
192+
193+
$this->input = null;
194+
$this->inputBuffer = '';
195+
}
196+
181197
/**
182198
* @internal
183199
*/

Tests/ProcessTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,24 @@ public function testStop()
672672
$this->assertFalse($process->isRunning());
673673
}
674674

675+
public function testStopDoesNotThrowAfterBrokenPipe()
676+
{
677+
if ('\\' === \DIRECTORY_SEPARATOR) {
678+
$this->markTestSkipped('Broken pipe notices are specific to Unix-like platforms.');
679+
}
680+
681+
$process = $this->getProcess([self::$phpBin, '-r', 'exit(0);'], null, null, str_repeat('*', PipesInterface::CHUNK_SIZE * 32));
682+
683+
$process->run();
684+
$this->assertSame(0, $process->getExitCode());
685+
686+
$process->stop(0);
687+
688+
// __destruct() should not trigger a broken pipe notice
689+
self::$process = $process = null;
690+
gc_collect_cycles();
691+
}
692+
675693
public function testIsSuccessful()
676694
{
677695
$process = $this->getProcess('echo foo');

0 commit comments

Comments
 (0)