Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit fcb80a8

Browse files
committed
Ensure that PHP doesn't override response code
If we don't pass the response status code to the function `header()` PHP might silently change the response code, which is not really nice since it should be up for the developers to define how they design the application. This silent change usually happens when one uses "Location" with a status code that's not 201 or 3xx, and some people already discussed a lot and essencially they've said that PHP will not modify its behaviour regarding this because it allows high level code to modify (fix) this. References: - https://bugs.php.net/bug.php?id=70273 - https://bugs.php.net/bug.php?id=51749 - https://bugs.php.net/bug.php?id=74535
1 parent 2eac047 commit fcb80a8

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

src/Response/SapiEmitterTrait.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,14 @@ private function assertNoPreviousOutput()
4343
private function emitStatusLine(ResponseInterface $response)
4444
{
4545
$reasonPhrase = $response->getReasonPhrase();
46+
$statusCode = $response->getStatusCode();
47+
4648
header(sprintf(
4749
'HTTP/%s %d%s',
4850
$response->getProtocolVersion(),
49-
$response->getStatusCode(),
51+
$statusCode,
5052
($reasonPhrase ? ' ' . $reasonPhrase : '')
51-
));
53+
), true, $statusCode);
5254
}
5355

5456
/**
@@ -63,6 +65,8 @@ private function emitStatusLine(ResponseInterface $response)
6365
*/
6466
private function emitHeaders(ResponseInterface $response)
6567
{
68+
$statusCode = $response->getStatusCode();
69+
6670
foreach ($response->getHeaders() as $header => $values) {
6771
$name = $this->filterHeader($header);
6872
$first = $name === 'Set-Cookie' ? false : true;
@@ -71,7 +75,7 @@ private function emitHeaders(ResponseInterface $response)
7175
'%s: %s',
7276
$name,
7377
$value
74-
), $first);
78+
), $first, $statusCode);
7579
$first = false;
7680
}
7781
}

test/Response/AbstractEmitterTest.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,27 @@ public function testMultipleSetCookieHeadersAreNotReplaced()
6666
$this->emitter->emit($response);
6767

6868
$expectedStack = [
69-
['header' => 'HTTP/1.1 200 OK', 'replace' => true, 'status_code' => null],
70-
['header' => 'Set-Cookie: foo=bar', 'replace' => false, 'status_code' => null],
71-
['header' => 'Set-Cookie: bar=baz', 'replace' => false, 'status_code' => null],
69+
['header' => 'HTTP/1.1 200 OK', 'replace' => true, 'status_code' => 200],
70+
['header' => 'Set-Cookie: foo=bar', 'replace' => false, 'status_code' => 200],
71+
['header' => 'Set-Cookie: bar=baz', 'replace' => false, 'status_code' => 200],
72+
];
73+
74+
$this->assertSame($expectedStack, HeaderStack::stack());
75+
}
76+
77+
public function testDoesNotLetResponseCodeBeOverriddenByPHP()
78+
{
79+
$response = (new Response())
80+
->withStatus(202)
81+
->withAddedHeader('Location', 'http://api.my-service.com/12345678')
82+
->withAddedHeader('Content-Type', 'text/plain');
83+
84+
$this->emitter->emit($response);
85+
86+
$expectedStack = [
87+
['header' => 'HTTP/1.1 202 Accepted', 'replace' => true, 'status_code' => 202],
88+
['header' => 'Location: http://api.my-service.com/12345678', 'replace' => true, 'status_code' => 202],
89+
['header' => 'Content-Type: text/plain', 'replace' => true, 'status_code' => 202],
7290
];
7391

7492
$this->assertSame($expectedStack, HeaderStack::stack());

0 commit comments

Comments
 (0)