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

Commit d6be3ee

Browse files
committed
Merge branch 'hotfix/347'
Close #405 Fixes #347
2 parents 4a66c32 + 30ad8d9 commit d6be3ee

File tree

3 files changed

+79
-58
lines changed

3 files changed

+79
-58
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ All notable changes to this project will be documented in this file, in reverse
2525
is not available.
2626
- no router is specified, and the class `Zend\Expressive\Router\FastRouteRouter`
2727
is not available.
28+
- [#405](https://github.com/zendframework/zend-expressive/pull/405) fixes how
29+
the `TemplatedErrorHandler` injects templated content into the response.
30+
Previously, it would `write()` directly to the existing response body, which
31+
could lead to issues if previous middleware had written to the response (as
32+
the templated contents would append the previous contents). With this release,
33+
it now creates a new `Zend\Diactoros\Stream`, writes to that, and returns a
34+
new response with that new stream, guaranteeing it only contains the new
35+
contents.
2836

2937
## 1.0.4 - 2016-12-07
3038

src/TemplatedErrorHandler.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
use Psr\Http\Message\RequestInterface as Request;
1111
use Psr\Http\Message\ResponseInterface as Response;
12+
use Zend\Diactoros\Stream;
1213
use Zend\Stratigility\Utils;
1314

1415
/**
@@ -136,7 +137,8 @@ public function __invoke(Request $request, Response $response, $err = null)
136137
protected function handleError($error, Request $request, Response $response)
137138
{
138139
if ($this->renderer) {
139-
$response->getBody()->write(
140+
$stream = new Stream('php://temp', 'wb+');
141+
$stream->write(
140142
$this->renderer->render($this->templateError, [
141143
'uri' => $request->getUri(),
142144
'error' => $error,
@@ -146,6 +148,7 @@ protected function handleError($error, Request $request, Response $response)
146148
'response' => $response,
147149
])
148150
);
151+
return $response->withBody($stream);
149152
}
150153

151154
return $response;
@@ -248,9 +251,11 @@ private function marshalReceivedResponse(Request $request, Response $response)
248251
private function create404(Request $request, Response $response)
249252
{
250253
if ($this->renderer) {
251-
$response->getBody()->write(
254+
$stream = new Stream('php://temp', 'wb+');
255+
$stream->write(
252256
$this->renderer->render($this->template404, [ 'uri' => $request->getUri() ])
253257
);
258+
$response = $response->withBody($stream);
254259
}
255260
return $response->withStatus(404);
256261
}

test/TemplatedErrorHandlerTest.php

Lines changed: 64 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -202,21 +202,32 @@ public function testInvocationWithoutErrorAndEmptyResponseCanReturnTemplated404R
202202
'error::500'
203203
);
204204

205-
$expected = $this->getResponse($this->getStream());
206-
207205
$stream = $this->getStream();
208206
$stream->getSize()->willReturn(0);
209-
$stream->write('Templated contents')->shouldBeCalled();
210207

211-
$response = $this->getResponse($stream);
208+
$response = $this->prophesize(ResponseInterface::class);
212209
$response->getStatusCode()->willReturn(200);
213-
$response->withStatus(404)->willReturn($expected->reveal());
210+
$response->getBody()->will([$stream, 'reveal']);
211+
$response
212+
->withBody(Argument::that(function ($body) use ($stream) {
213+
if (! $body instanceof StreamInterface) {
214+
return false;
215+
}
216+
217+
if ($body === $stream) {
218+
return false;
219+
}
220+
221+
return 'Templated contents' === (string) $body;
222+
}))
223+
->will([$response, 'reveal']);
224+
$response->withStatus(404)->will([$response, 'reveal']);
214225

215226
$request = $this->getRequest($this->getStream());
216227
$request->getUri()->shouldBeCalled();
217228

218229
$result = $handler($request->reveal(), $response->reveal());
219-
$this->assertSame($expected->reveal(), $result);
230+
$this->assertSame($response->reveal(), $result);
220231
}
221232

222233
/**
@@ -238,15 +249,26 @@ public function testInvocationWithoutErrorAndResponseSameAsOriginalCanReturnTemp
238249
'error::500'
239250
);
240251

241-
$expected = $this->getResponse($this->getStream());
242-
243252
$stream = $this->getStream();
244253
$stream->getSize()->willReturn(100);
245-
$stream->write('Templated contents')->shouldBeCalled();
246254

247-
$response = $this->getResponse($stream);
255+
$response = $this->prophesize(ResponseInterface::class);
248256
$response->getStatusCode()->willReturn(200);
249-
$response->withStatus(404)->willReturn($expected->reveal());
257+
$response->getBody()->will([$stream, 'reveal']);
258+
$response
259+
->withBody(Argument::that(function ($body) use ($stream) {
260+
if (! $body instanceof StreamInterface) {
261+
return false;
262+
}
263+
264+
if ($body === $stream) {
265+
return false;
266+
}
267+
268+
return 'Templated contents' === (string) $body;
269+
}))
270+
->will([$response, 'reveal']);
271+
$response->withStatus(404)->will([$response, 'reveal']);
250272

251273
$handler->setOriginalResponse($response->reveal());
252274

@@ -257,7 +279,7 @@ public function testInvocationWithoutErrorAndResponseSameAsOriginalCanReturnTemp
257279
->will([$response, 'reveal']);
258280

259281
$result = $handler($request->reveal(), $response->reveal());
260-
$this->assertSame($expected->reveal(), $result);
282+
$this->assertSame($response->reveal(), $result);
261283
}
262284

263285
/* Tests covering handleErrorResponse() paths; NO TEMPLATE */
@@ -361,54 +383,35 @@ public function testInvalidExceptionErrorCodeDefaultsToHTTP500Status($code)
361383

362384
/* Tests covering handleErrorResponse() paths; WITH TEMPLATE */
363385

364-
/**
365-
* @group templated
366-
*/
367-
public function testNonExceptionErrorReturnsResponseWith500StatusAndTemplateResultsWhenTemplatingInjected()
386+
public function errors()
368387
{
369-
$renderer = $this->getTemplateImplementation();
370-
$renderer
371-
->render(
372-
'error::500',
373-
Argument::type('array')
374-
)
375-
->willReturn('Templated contents');
376-
377-
$handler = new TemplatedErrorHandler(
378-
$renderer->reveal(),
379-
'error::404',
380-
'error::500'
381-
);
382-
383-
$stream = $this->getStream();
384-
$stream->getSize()->willReturn(0);
385-
$stream->write('Templated contents')->shouldBeCalled();
386-
387-
$expected = $this->getResponse($stream);
388-
$expected->getStatusCode()->willReturn(500)->shouldBeCalled();
389-
$expected->getReasonPhrase()->shouldBeCalled();
390-
391-
$response = $this->getResponse($stream);
392-
$response->getStatusCode()->willReturn(200);
393-
$response->withStatus(500)->willReturn($expected->reveal());
394-
395-
$request = $this->getRequest($this->getStream());
396-
$request->getUri()->shouldBeCalled();
397-
398-
$result = $handler($request->reveal(), $response->reveal(), 'error');
399-
$this->assertSame($expected->reveal(), $result);
388+
return [
389+
'error' => ['error'],
390+
'exception' => [new Exception('exception')],
391+
];
400392
}
401393

402394
/**
403395
* @group templated
396+
* @dataProvider errors
404397
*/
405-
public function testExceptionErrorReturnsResponseWith500StatusAndTemplateResultsWhenTemplatingInjected()
398+
public function testReturns500ResponseWithNewStreamWhenReturningAnErrorResponse($error)
406399
{
407400
$renderer = $this->getTemplateImplementation();
408401
$renderer
409402
->render(
410403
'error::500',
411-
Argument::type('array')
404+
Argument::that(function ($params) use ($error) {
405+
if (! is_array($params)) {
406+
return false;
407+
}
408+
409+
if (empty($params['error'])) {
410+
return false;
411+
}
412+
413+
return $error === $params['error'];
414+
})
412415
)
413416
->willReturn('Templated contents');
414417

@@ -418,22 +421,27 @@ public function testExceptionErrorReturnsResponseWith500StatusAndTemplateResults
418421
'error::500'
419422
);
420423

421-
$stream = $this->getStream();
422-
$stream->getSize()->willReturn(0);
423-
$stream->write('Templated contents')->shouldBeCalled();
424-
425-
$expected = $this->getResponse($stream);
424+
$expected = $this->prophesize(ResponseInterface::class);
426425
$expected->getStatusCode()->willReturn(500)->shouldBeCalled();
427426
$expected->getReasonPhrase()->shouldBeCalled();
427+
$expected
428+
->withBody(Argument::that(function ($stream) {
429+
if (! $stream instanceof StreamInterface) {
430+
return false;
431+
}
428432

429-
$response = $this->getResponse($stream);
433+
return 'Templated contents' === (string) $stream;
434+
}))
435+
->will([$expected, 'reveal']);
436+
437+
$response = $this->prophesize(ResponseInterface::class);
430438
$response->getStatusCode()->willReturn(200);
431439
$response->withStatus(500)->willReturn($expected->reveal());
432440

433441
$request = $this->getRequest($this->getStream());
434442
$request->getUri()->shouldBeCalled();
435443

436-
$result = $handler($request->reveal(), $response->reveal(), new Exception());
444+
$result = $handler($request->reveal(), $response->reveal(), $error);
437445
$this->assertSame($expected->reveal(), $result);
438446
}
439447
}

0 commit comments

Comments
 (0)