Skip to content

Commit 0adef61

Browse files
authored
Fix #35: Format data when any getter is called, re-format on data or formatter changes
1 parent 98f0239 commit 0adef61

File tree

3 files changed

+73
-9
lines changed

3 files changed

+73
-9
lines changed

src/DataResponse.php

+35
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ final class DataResponse implements ResponseInterface
2424

2525
private ?DataResponseFormatterInterface $responseFormatter = null;
2626

27+
private bool $formatted = false;
28+
2729
public function __construct($data, int $code, string $reasonPhrase, ResponseFactoryInterface $responseFactory)
2830
{
2931
$this->response = $responseFactory->createResponse($code, $reasonPhrase);
@@ -56,43 +58,51 @@ public function getBody(): StreamInterface
5658

5759
public function getHeader($name): array
5860
{
61+
$this->response = $this->formatResponse();
5962
return $this->response->getHeader($name);
6063
}
6164

6265
public function getHeaderLine($name): string
6366
{
67+
$this->response = $this->formatResponse();
6468
return $this->response->getHeaderLine($name);
6569
}
6670

6771
public function getHeaders(): array
6872
{
73+
$this->response = $this->formatResponse();
6974
return $this->response->getHeaders();
7075
}
7176

7277
public function getProtocolVersion(): string
7378
{
79+
$this->response = $this->formatResponse();
7480
return $this->response->getProtocolVersion();
7581
}
7682

7783
public function getReasonPhrase(): string
7884
{
85+
$this->response = $this->formatResponse();
7986
return $this->response->getReasonPhrase();
8087
}
8188

8289
public function getStatusCode(): int
8390
{
91+
$this->response = $this->formatResponse();
8492
return $this->response->getStatusCode();
8593
}
8694

8795
public function hasHeader($name): bool
8896
{
97+
$this->response = $this->formatResponse();
8998
return $this->response->hasHeader($name);
9099
}
91100

92101
public function withAddedHeader($name, $value): self
93102
{
94103
$new = clone $this;
95104
$new->response = $this->response->withAddedHeader($name, $value);
105+
$new->formatted = false;
96106
return $new;
97107
}
98108

@@ -101,13 +111,15 @@ public function withBody(StreamInterface $body): self
101111
$new = clone $this;
102112
$new->response = $this->response->withBody($body);
103113
$new->dataStream = $body;
114+
$new->formatted = false;
104115
return $new;
105116
}
106117

107118
public function withHeader($name, $value): self
108119
{
109120
$new = clone $this;
110121
$new->response = $this->response->withHeader($name, $value);
122+
$new->formatted = false;
111123
return $new;
112124
}
113125

@@ -122,27 +134,33 @@ public function withProtocolVersion($version): self
122134
{
123135
$new = clone $this;
124136
$new->response = $this->response->withProtocolVersion($version);
137+
$new->formatted = false;
125138
return $new;
126139
}
127140

128141
public function withStatus($code, $reasonPhrase = ''): self
129142
{
130143
$new = clone $this;
131144
$new->response = $this->response->withStatus($code, $reasonPhrase);
145+
$new->formatted = false;
132146
return $new;
133147
}
134148

135149
public function withResponseFormatter(DataResponseFormatterInterface $responseFormatter): self
136150
{
137151
$new = clone $this;
138152
$new->responseFormatter = $responseFormatter;
153+
$new->response = $new->formatResponse();
154+
139155
return $new;
140156
}
141157

142158
public function withData($data): self
143159
{
144160
$new = clone $this;
145161
$new->data = $data;
162+
$new->clearResponseBody();
163+
$new->formatted = false;
146164

147165
return $new;
148166
}
@@ -176,11 +194,28 @@ public function hasData(): bool
176194
*/
177195
private function formatResponse(): ResponseInterface
178196
{
197+
if (!$this->needFormatResponse()) {
198+
return $this->response;
199+
}
200+
201+
$this->formatted = true;
179202
$response = $this->responseFormatter->format($this);
203+
180204
if ($response instanceof self) {
181205
throw new \RuntimeException('DataResponseFormatterInterface should not return instance of DataResponse.');
182206
}
183207

184208
return $response;
185209
}
210+
211+
private function clearResponseBody(): void
212+
{
213+
$this->response->getBody()->rewind();
214+
$this->response->getBody()->write('');
215+
}
216+
217+
private function needFormatResponse(): bool
218+
{
219+
return $this->formatted === false && $this->hasResponseFormatter();
220+
}
186221
}

tests/DataResponseTest.php

+19-9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Yiisoft\DataResponse\DataResponseFactory;
1313
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
1414
use Yiisoft\DataResponse\Tests\Stub\LoopDataResponseFormatter;
15+
use Yiisoft\DataResponse\Tests\Stub\RecursiveDataResponseFormatter;
1516
use Yiisoft\Http\Header;
1617
use Yiisoft\Http\Status;
1718

@@ -41,6 +42,16 @@ public function testChangeResponseData(): void
4142
$this->assertSame('test-changed', $dataResponse->getBody()->getContents());
4243
}
4344

45+
public function testChangeResponseDataWithFormatter(): void
46+
{
47+
$dataResponse = $this->createFactory()->createResponse('test-value');
48+
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
49+
$dataResponse = $dataResponse->withData('new-test-value');
50+
$dataResponse->getBody()->rewind();
51+
52+
$this->assertSame('"new-test-value"', $dataResponse->getBody()->getContents());
53+
}
54+
4455
public function testSetResponseFormatter(): void
4556
{
4657
$dataResponse = $this->createFactory()->createResponse('test');
@@ -95,7 +106,6 @@ public function testGetHeader(): void
95106
{
96107
$dataResponse = $this->createFactory()->createResponse();
97108
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
98-
$dataResponse->getBody()->rewind();
99109

100110
$this->assertEquals(['application/json'], $dataResponse->getHeader(Header::CONTENT_TYPE));
101111
}
@@ -104,7 +114,6 @@ public function testGetHeaderLine(): void
104114
{
105115
$dataResponse = $this->createFactory()->createResponse();
106116
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
107-
$dataResponse->getBody()->rewind();
108117

109118
$this->assertEquals('application/json', $dataResponse->getHeaderLine(Header::CONTENT_TYPE));
110119
}
@@ -113,7 +122,6 @@ public function testGetHeaders(): void
113122
{
114123
$dataResponse = $this->createFactory()->createResponse();
115124
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
116-
$dataResponse->getBody()->rewind();
117125

118126
$this->assertEquals([Header::CONTENT_TYPE => ['application/json']], $dataResponse->getHeaders());
119127
}
@@ -122,7 +130,6 @@ public function testHasHeader(): void
122130
{
123131
$dataResponse = $this->createFactory()->createResponse();
124132
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
125-
$dataResponse->getBody()->rewind();
126133

127134
$this->assertTrue($dataResponse->hasHeader(Header::CONTENT_TYPE));
128135
$this->assertFalse($dataResponse->hasHeader(Header::ACCEPT_LANGUAGE));
@@ -132,7 +139,6 @@ public function testWithoutHeader(): void
132139
{
133140
$dataResponse = $this->createFactory()->createResponse();
134141
$dataResponse = $dataResponse->withResponseFormatter(new JsonDataResponseFormatter());
135-
$dataResponse->getBody()->rewind();
136142
$dataResponse = $dataResponse->withoutHeader(Header::CONTENT_TYPE);
137143
$this->assertFalse($dataResponse->hasHeader(Header::CONTENT_TYPE));
138144
}
@@ -142,8 +148,6 @@ public function testWithHeader(): void
142148
$dataResponse = $this->createFactory()->createResponse()
143149
->withHeader(Header::CONTENT_TYPE, 'application/json');
144150

145-
$dataResponse->getBody()->rewind();
146-
147151
$this->assertEquals(['Content-Type' => ['application/json']], $dataResponse->getHeaders());
148152
}
149153

@@ -153,8 +157,6 @@ public function testWithAddedHeader(): void
153157
->withHeader(Header::CONTENT_TYPE, 'application/json')
154158
->withAddedHeader(Header::CONTENT_TYPE, 'application/xml');
155159

156-
$dataResponse->getBody()->rewind();
157-
158160
$this->assertEquals(['Content-Type' => ['application/json', 'application/xml']], $dataResponse->getHeaders());
159161
}
160162

@@ -221,6 +223,14 @@ public function testHasDataWithEmptyData(): void
221223
$this->assertFalse($dataResponse->hasData());
222224
}
223225

226+
public function testSetResponseRecursiveFormatter(): void
227+
{
228+
$dataResponse = new DataResponse('test', Status::OK, '', new Psr17Factory());
229+
$dataResponse = $dataResponse->withResponseFormatter(new RecursiveDataResponseFormatter());
230+
$dataResponse->getBody()->rewind();
231+
$this->assertTrue(true);
232+
}
233+
224234
private function createFactory(): DataResponseFactory
225235
{
226236
return new DataResponseFactory(new Psr17Factory());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\DataResponse\Tests\Stub;
6+
7+
use Psr\Http\Message\ResponseInterface;
8+
use Yiisoft\DataResponse\DataResponse;
9+
use Yiisoft\DataResponse\DataResponseFormatterInterface;
10+
11+
final class RecursiveDataResponseFormatter implements DataResponseFormatterInterface
12+
{
13+
public function format(DataResponse $dataResponse): ResponseInterface
14+
{
15+
$dataResponse->getBody()->getContents();
16+
17+
return $dataResponse->getResponse();
18+
}
19+
}

0 commit comments

Comments
 (0)