Merge branch '8.0' into 8.1

* 8.0:
  [JsonPath] Use composer packages for JsonPath compliance test suite
  Fix negative delays with AMQP messenger transport
  [TwigBundle] Align TemplateIterator handling of @! original bundle templates with TwigExtension
  [AssetMapper] Batch concurrent requests to prevent flooding jsdelivr
  [HttpFoundation][Cache] Fix VARBINARY columns on sqlsrv
  [Cache] Fix calling the callback wrapper for ChainAdapter
  [Routing] Fix simple parameter mappings in routes
  [Process] Fix dealing with broken stdin pipes
This commit is contained in:
Nicolas Grekas
2025-12-19 11:01:25 +01:00
2 changed files with 38 additions and 4 deletions

View File

@@ -135,9 +135,11 @@ abstract class AbstractPipes implements PipesInterface
foreach ($w as $stdin) {
if (isset($this->inputBuffer[0])) {
$written = fwrite($stdin, $this->inputBuffer);
if (false === $written = @fwrite($stdin, $this->inputBuffer)) {
return $this->closeBrokenInputPipe();
}
$this->inputBuffer = substr($this->inputBuffer, $written);
if (isset($this->inputBuffer[0])) {
if (isset($this->inputBuffer[0]) && isset($this->pipes[0])) {
return [$this->pipes[0]];
}
}
@@ -148,12 +150,14 @@ abstract class AbstractPipes implements PipesInterface
if (!isset($data[0])) {
break;
}
$written = fwrite($stdin, $data);
if (false === $written = @fwrite($stdin, $data)) {
return $this->closeBrokenInputPipe();
}
$data = substr($data, $written);
if (isset($data[0])) {
$this->inputBuffer = $data;
return [$this->pipes[0]];
return isset($this->pipes[0]) ? [$this->pipes[0]] : null;
}
}
if (feof($input)) {
@@ -178,6 +182,18 @@ abstract class AbstractPipes implements PipesInterface
return null;
}
private function closeBrokenInputPipe(): void
{
$this->lastError = error_get_last()['message'] ?? null;
if (\is_resource($this->pipes[0] ?? null)) {
fclose($this->pipes[0]);
}
unset($this->pipes[0]);
$this->input = null;
$this->inputBuffer = '';
}
/**
* @internal
*/

View File

@@ -676,6 +676,24 @@ class ProcessTest extends TestCase
$this->assertFalse($process->isRunning());
}
public function testStopDoesNotThrowAfterBrokenPipe()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Broken pipe notices are specific to Unix-like platforms.');
}
$process = $this->getProcess([self::$phpBin, '-r', 'exit(0);'], null, null, str_repeat('*', PipesInterface::CHUNK_SIZE * 32));
$process->run();
$this->assertSame(0, $process->getExitCode());
$process->stop(0);
// __destruct() should not trigger a broken pipe notice
self::$process = $process = null;
gc_collect_cycles();
}
public function testIsSuccessful()
{
$process = $this->getProcess('echo foo');