Skip to content

Commit 1f12093

Browse files
Merge branch '6.4' into 7.2
* 6.4: [Process] Fix process status tracking [HttpClient] Fix buffering AsyncResponse with no passthru [HttpClient] Fix uploading files > 2GB [Mime] use isRendered method to avoid rendering an email twice
2 parents 1315804 + 0beb17a commit 1f12093

File tree

7 files changed

+35
-30
lines changed

7 files changed

+35
-30
lines changed

src/Symfony/Bridge/Twig/Mime/BodyRenderer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function render(Message $message): void
4343
return;
4444
}
4545

46-
if (null === $message->getTextTemplate() && null === $message->getHtmlTemplate()) {
46+
if ($message->isRendered()) {
4747
// email has already been rendered
4848
return;
4949
}

src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,14 @@ public function testRenderedOnce()
105105
;
106106
$email->textTemplate('text');
107107

108+
$this->assertFalse($email->isRendered());
108109
$renderer->render($email);
110+
$this->assertTrue($email->isRendered());
111+
109112
$this->assertEquals('Text', $email->getTextBody());
110113

111114
$email->text('reset');
115+
$this->assertTrue($email->isRendered());
112116

113117
$renderer->render($email);
114118
$this->assertEquals('reset', $email->getTextBody());

src/Symfony/Component/HttpClient/CurlHttpClient.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ public function request(string $method, string $url, array $options = []): Respo
241241
}
242242

243243
if (\is_resource($body)) {
244-
$curlopts[\CURLOPT_INFILE] = $body;
244+
$curlopts[\CURLOPT_READDATA] = $body;
245245
} else {
246246
$curlopts[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use ($body) {
247247
static $eof = false;
@@ -320,6 +320,9 @@ public function request(string $method, string $url, array $options = []): Respo
320320
}
321321

322322
foreach ($curlopts as $opt => $value) {
323+
if (\CURLOPT_INFILESIZE === $opt && $value >= 1 << 31) {
324+
$opt = 115; // 115 === CURLOPT_INFILESIZE_LARGE, but it's not defined in PHP
325+
}
323326
if (null !== $value && !curl_setopt($ch, $opt, $value) && \CURLOPT_CERTINFO !== $opt && (!\defined('CURLOPT_HEADEROPT') || \CURLOPT_HEADEROPT !== $opt)) {
324327
$constantName = $this->findConstantName($opt);
325328
throw new TransportException(\sprintf('Curl option "%s" is not supported.', $constantName ?? $opt));
@@ -476,7 +479,7 @@ private function validateExtraCurlOptions(array $options): void
476479
\CURLOPT_RESOLVE => 'resolve',
477480
\CURLOPT_NOSIGNAL => 'timeout',
478481
\CURLOPT_HTTPHEADER => 'headers',
479-
\CURLOPT_INFILE => 'body',
482+
\CURLOPT_READDATA => 'body',
480483
\CURLOPT_READFUNCTION => 'body',
481484
\CURLOPT_INFILESIZE => 'body',
482485
\CURLOPT_POSTFIELDS => 'body',

src/Symfony/Component/HttpClient/Response/AsyncResponse.php

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\HttpClient\Response;
1313

1414
use Symfony\Component\HttpClient\Chunk\ErrorChunk;
15-
use Symfony\Component\HttpClient\Chunk\FirstChunk;
1615
use Symfony\Component\HttpClient\Chunk\LastChunk;
1716
use Symfony\Component\HttpClient\Exception\TransportException;
1817
use Symfony\Contracts\HttpClient\ChunkInterface;
@@ -245,7 +244,7 @@ public static function stream(iterable $responses, ?float $timeout = null, ?stri
245244
$wrappedResponses[] = $r->response;
246245

247246
if ($r->stream) {
248-
yield from self::passthruStream($response = $r->response, $r, new FirstChunk(), $asyncMap);
247+
yield from self::passthruStream($response = $r->response, $r, $asyncMap, new LastChunk());
249248

250249
if (!isset($asyncMap[$response])) {
251250
array_pop($wrappedResponses);
@@ -276,15 +275,9 @@ public static function stream(iterable $responses, ?float $timeout = null, ?stri
276275
}
277276

278277
if (!$r->passthru) {
279-
if (null !== $chunk->getError() || $chunk->isLast()) {
280-
unset($asyncMap[$response]);
281-
} elseif (null !== $r->content && '' !== ($content = $chunk->getContent()) && \strlen($content) !== fwrite($r->content, $content)) {
282-
$chunk = new ErrorChunk($r->offset, new TransportException(\sprintf('Failed writing %d bytes to the response buffer.', \strlen($content))));
283-
$r->info['error'] = $chunk->getError();
284-
$r->response->cancel();
285-
}
278+
$r->stream = (static fn () => yield $chunk)();
279+
yield from self::passthruStream($response, $r, $asyncMap);
286280

287-
yield $r => $chunk;
288281
continue;
289282
}
290283

@@ -347,13 +340,13 @@ private static function passthru(HttpClientInterface $client, self $r, ChunkInte
347340
}
348341
$r->stream = $stream;
349342

350-
yield from self::passthruStream($response, $r, null, $asyncMap);
343+
yield from self::passthruStream($response, $r, $asyncMap);
351344
}
352345

353346
/**
354347
* @param \SplObjectStorage<ResponseInterface, AsyncResponse>|null $asyncMap
355348
*/
356-
private static function passthruStream(ResponseInterface $response, self $r, ?ChunkInterface $chunk, ?\SplObjectStorage $asyncMap): \Generator
349+
private static function passthruStream(ResponseInterface $response, self $r, ?\SplObjectStorage $asyncMap, ?ChunkInterface $chunk = null): \Generator
357350
{
358351
while (true) {
359352
try {

src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,20 @@ public function testBufferPurePassthru()
231231

232232
$this->assertStringContainsString('SERVER_PROTOCOL', $response->getContent());
233233
$this->assertStringContainsString('HTTP_HOST', $response->getContent());
234+
235+
$client = new class(parent::getHttpClient(__FUNCTION__)) implements HttpClientInterface {
236+
use AsyncDecoratorTrait;
237+
238+
public function request(string $method, string $url, array $options = []): ResponseInterface
239+
{
240+
return new AsyncResponse($this->client, $method, $url, $options);
241+
}
242+
};
243+
244+
$response = $client->request('GET', 'http://localhost:8057/');
245+
246+
$this->assertStringContainsString('SERVER_PROTOCOL', $response->getContent());
247+
$this->assertStringContainsString('HTTP_HOST', $response->getContent());
234248
}
235249

236250
public function testRetryTimeout()

src/Symfony/Component/Process/Process.php

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ class Process implements \IteratorAggregate
8282
private WindowsPipes|UnixPipes $processPipes;
8383

8484
private ?int $latestSignal = null;
85-
private ?int $cachedExitCode = null;
8685

8786
private static ?bool $sigchild = null;
8887
private static array $executables = [];
@@ -1324,21 +1323,10 @@ protected function updateStatus(bool $blocking): void
13241323
return;
13251324
}
13261325

1327-
$this->processInformation = proc_get_status($this->process);
1328-
$running = $this->processInformation['running'];
1329-
1330-
// In PHP < 8.3, "proc_get_status" only returns the correct exit status on the first call.
1331-
// Subsequent calls return -1 as the process is discarded. This workaround caches the first
1332-
// retrieved exit status for consistent results in later calls, mimicking PHP 8.3 behavior.
1333-
if (\PHP_VERSION_ID < 80300) {
1334-
if (!isset($this->cachedExitCode) && !$running && -1 !== $this->processInformation['exitcode']) {
1335-
$this->cachedExitCode = $this->processInformation['exitcode'];
1336-
}
1337-
1338-
if (isset($this->cachedExitCode) && !$running && -1 === $this->processInformation['exitcode']) {
1339-
$this->processInformation['exitcode'] = $this->cachedExitCode;
1340-
}
1326+
if ($this->processInformation['running'] ?? true) {
1327+
$this->processInformation = proc_get_status($this->process);
13411328
}
1329+
$running = $this->processInformation['running'];
13421330

13431331
$this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running);
13441332

src/Symfony/Component/Process/Tests/ProcessTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,9 @@ public function testProcessIsSignaledIfStopped()
732732
if ('\\' === \DIRECTORY_SEPARATOR) {
733733
$this->markTestSkipped('Windows does not support POSIX signals');
734734
}
735+
if (\PHP_VERSION_ID < 80300 && isset($_SERVER['GITHUB_ACTIONS'])) {
736+
$this->markTestSkipped('Transient on GHA with PHP < 8.3');
737+
}
735738

736739
$process = $this->getProcessForCode('sleep(32);');
737740
$process->start();

0 commit comments

Comments
 (0)