Skip to content

Commit 85bb5ca

Browse files
committed
[HttpFoundation] Add support for the 103 status code (Early Hints)
1 parent 4d4c411 commit 85bb5ca

File tree

8 files changed

+58
-8
lines changed

8 files changed

+58
-8
lines changed

UPGRADE-6.3.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
UPGRADE FROM 6.2 to 6.3
2+
=======================
3+
4+
HttpFoundation
5+
--------------
6+
7+
* Deprecate calling `Response::sendHeaders()` without any arguments

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
6.3
5+
---
6+
7+
* The `Response::sendHeaders()` method now takes an HTTP status code as parameter, allowing to send informational responses such as Early Hints responses (103 status code)
8+
49
6.2
510
---
611

src/Symfony/Component/HttpFoundation/Response.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,20 @@ public function prepare(Request $request): static
326326
/**
327327
* Sends HTTP headers.
328328
*
329+
* @param null|positive-int $statusCode The status code to use. Override the statusCode property if set and not null.
330+
*
329331
* @return $this
330332
*/
331333
public function sendHeaders(): static
332334
{
335+
if (1 > \func_num_args()) {
336+
trigger_deprecation('symfony/http-foundation', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__);
337+
338+
$statusCode = null;
339+
} else {
340+
$statusCode = func_get_arg(0) ?: null;
341+
}
342+
333343
// headers have already been sent by the developer
334344
if (headers_sent()) {
335345
return $this;
@@ -348,8 +358,23 @@ public function sendHeaders(): static
348358
header('Set-Cookie: '.$cookie, false, $this->statusCode);
349359
}
350360

361+
if ($statusCode) {
362+
if (\function_exists('headers_send')) {
363+
headers_send($statusCode);
364+
365+
return $this;
366+
}
367+
368+
if ($statusCode >= 100 && $statusCode < 200) {
369+
// skip informational responses if not supported by the SAPI
370+
return $this;
371+
}
372+
} else {
373+
$statusCode = $this->statusCode;
374+
}
375+
351376
// status
352-
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
377+
header(sprintf('HTTP/%s %s %s', $this->version, $statusCode, $this->statusText), true, $statusCode);
353378

354379
return $this;
355380
}
@@ -373,7 +398,7 @@ public function sendContent(): static
373398
*/
374399
public function send(): static
375400
{
376-
$this->sendHeaders();
401+
$this->sendHeaders(null);
377402
$this->sendContent();
378403

379404
if (\function_exists('fastcgi_finish_request')) {

src/Symfony/Component/HttpFoundation/StreamedResponse.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public function setCallback(callable $callback): static
5959
/**
6060
* This method only sends the headers once.
6161
*
62+
* @param null|positive-int $statusCode The status code to use. Override the statusCode property if set and not null.
63+
*
6264
* @return $this
6365
*/
6466
public function sendHeaders(): static
@@ -69,7 +71,7 @@ public function sendHeaders(): static
6971

7072
$this->headersSent = true;
7173

72-
return parent::sendHeaders();
74+
return parent::sendHeaders(...\func_get_args());
7375
}
7476

7577
/**

src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/deleted_cookie.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@ public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $c
5757

5858
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $r));
5959

60-
$r->sendHeaders();
60+
$r->sendHeaders(null);

src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function testClone()
3838
public function testSendHeaders()
3939
{
4040
$response = new Response();
41-
$headers = $response->sendHeaders();
41+
$headers = $response->sendHeaders(null);
4242
$this->assertObjectHasAttribute('headers', $headers);
4343
$this->assertObjectHasAttribute('content', $headers);
4444
$this->assertObjectHasAttribute('version', $headers);
@@ -47,6 +47,17 @@ public function testSendHeaders()
4747
$this->assertObjectHasAttribute('charset', $headers);
4848
}
4949

50+
public function testSendInformationalResponse()
51+
{
52+
$response = new Response();
53+
$response->sendHeaders(103);
54+
55+
// Informational responses must not override the main status code
56+
$this->assertSame(200, $response->getStatusCode());
57+
58+
$response->sendHeaders(null);
59+
}
60+
5061
public function testSend()
5162
{
5263
$response = new Response();

src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ public function testReturnThis()
108108
$this->assertInstanceOf(StreamedResponse::class, $response->sendContent());
109109

110110
$response = new StreamedResponse(function () {});
111-
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders());
112-
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders());
111+
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders(null));
112+
$this->assertInstanceOf(StreamedResponse::class, $response->sendHeaders(null));
113113
}
114114

115115
public function testSetNotModified()

src/Symfony/Component/HttpKernel/HttpKernel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public function terminateWithException(\Throwable $exception, Request $request =
118118
}
119119
}
120120

121-
$response->sendHeaders();
121+
$response->sendHeaders(null);
122122
$response->sendContent();
123123

124124
$this->terminate($request, $response);

0 commit comments

Comments
 (0)