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

Commit d55873a

Browse files
committed
Refactor to be able to use stream or filename for response body
This is following on from what mwop suggested in that the body of the response can be set from either a string or a file. It's making use of a Stream object and the body is instantiated similarly to XmlResponse as how that works makes a lot of sense in this case.
1 parent aecca69 commit d55873a

File tree

2 files changed

+98
-22
lines changed

2 files changed

+98
-22
lines changed

src/Response/DownloadResponse.php

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

1010
namespace Zend\Diactoros\Response;
1111

12+
use Psr\Http\Message\StreamInterface;
1213
use Zend\Diactoros\Exception\InvalidArgumentException;
1314
use Zend\Diactoros\Response;
1415

@@ -41,20 +42,20 @@ class DownloadResponse extends Response
4142

4243
/**
4344
* DownloadResponse constructor.
44-
* @param $body
45-
* @param int $status
46-
* @param string $filename
45+
* @param string|StreamInterface $body String or stream for the message body.
46+
* @param int $status Integer status code for the response; 200 by default.
47+
* @param string $filename The name of the file to be downloaded
48+
* @param array $headers Array of headers to use at initialization.
49+
* @throws InvalidArgumentException if $text is neither a string or stream.
4750
* @param array $headers
4851
*/
49-
public function __construct($body, int $status = 200, string $filename = '', array $headers = [])
52+
public function __construct($body, int $status = 200, string $filename = 'download', array $headers = [])
5053
{
51-
$content = new Stream('php://temp', 'wb+');
52-
$content->write($body);
53-
$content->rewind();
54-
55-
$headers = $this->prepareDownloadHeaders($filename, $headers);
56-
57-
parent::__construct($content, $status, $headers);
54+
parent::__construct(
55+
$this->createBody($body),
56+
$status,
57+
$this->prepareDownloadHeaders($filename, $headers)
58+
);
5859
}
5960

6061
/**
@@ -66,13 +67,13 @@ public function __construct($body, int $status = 200, string $filename = '', arr
6667
private function getDownloadHeaders(string $filename): array
6768
{
6869
$headers = [];
69-
$headers['cache-control'] = ['must-revalidate'];
70-
$headers['content-description'] = ['File Transfer'];
71-
$headers['content-disposition'] = [sprintf('attachment; filename=%s', $filename)];
72-
$headers['content-transfer-encoding'] = ['Binary'];
73-
$headers['content-type'] = ['text/csv; charset=utf-8'];
74-
$headers['expires'] = ['0'];
75-
$headers['pragma'] = ['Public'];
70+
$headers['cache-control'] = 'must-revalidate';
71+
$headers['content-description'] = 'File Transfer';
72+
$headers['content-disposition'] = sprintf('attachment; filename=%s', $filename);
73+
$headers['content-transfer-encoding'] = 'Binary';
74+
$headers['content-type'] = 'application/octet-stream';
75+
$headers['expires'] = '0';
76+
$headers['pragma'] = 'Public';
7677

7778
return $headers;
7879
}
@@ -120,4 +121,29 @@ private function prepareDownloadHeaders(string $filename, array $headers = []) :
120121

121122
return array_merge($headers, $this->getDownloadHeaders($filename));
122123
}
124+
125+
/**
126+
* @param string|StreamInterface $content
127+
* @return StreamInterface
128+
* @throws InvalidArgumentException if $body is neither a string nor a stream
129+
*/
130+
private function createBody($content): StreamInterface
131+
{
132+
if ($content instanceof StreamInterface) {
133+
return $content;
134+
}
135+
136+
if (!is_string($content)) {
137+
throw new InvalidArgumentException(sprintf(
138+
'Invalid content (%s) provided to %s',
139+
(is_object($content) ? get_class($content) : gettype($content)),
140+
__CLASS__
141+
));
142+
}
143+
144+
$body = new Stream('php://temp', 'wb+');
145+
$body->write($content);
146+
$body->rewind();
147+
return $body;
148+
}
123149
}

test/Response/DownloadResponseTest.php

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPUnit\Framework\TestCase;
1414
use Zend\Diactoros\Response;
1515
use Zend\Diactoros\Response\DownloadResponse;
16+
use Zend\Diactoros\Stream;
1617

1718
class DownloadResponseTest extends TestCase
1819
{
@@ -47,10 +48,59 @@ public function setUp()
4748

4849
public function testCanCreateResponseFromString()
4950
{
50-
$csvString = file_get_contents($this->root->url() . '/files/valid.csv');
51-
$response = new DownloadResponse($csvString);
51+
$body = file_get_contents($this->root->url() . '/files/valid.csv');
52+
$response = new DownloadResponse($body);
5253
$this->assertInstanceOf(Response::class, $response);
53-
$this->assertEquals($csvString, (string) $response->getBody()->getContents());
54-
$this->assertArrayHasKey('cache-control', $response->getHeaders());
54+
$this->assertEquals($body, (string) $response->getBody());
55+
$this->assertEquals(619, $response->getBody()->getSize());
56+
$this->assertHasValidResponseHeaders($response);
57+
$this->assertSame('attachment; filename=download', $response->getHeaderLine('content-disposition'));
58+
}
59+
60+
public function testCanCreateResponseFromFilename()
61+
{
62+
$body = new Stream($this->root->url() . '/files/valid.csv');
63+
$response = new DownloadResponse($body);
64+
$this->assertInstanceOf(Response::class, $response);
65+
$this->assertEquals(
66+
file_get_contents($this->root->url() . '/files/valid.csv'),
67+
(string) $response->getBody()
68+
);
69+
$this->assertHasValidResponseHeaders($response);
70+
$this->assertSame('attachment; filename=download', $response->getHeaderLine('content-disposition'));
71+
}
72+
73+
public function testCanSendResponseWithCustomFilename()
74+
{
75+
$body = new Stream($this->root->url() . '/files/valid.csv');
76+
$response = new DownloadResponse($body, 200, 'valid.csv');
77+
$this->assertInstanceOf(Response::class, $response);
78+
$this->assertEquals(
79+
file_get_contents($this->root->url() . '/files/valid.csv'),
80+
(string) $response->getBody()
81+
);
82+
$this->assertHasValidResponseHeaders($response, 'valid.csv');
83+
$this->assertSame('attachment; filename=valid.csv', $response->getHeaderLine('content-disposition'));
84+
}
85+
86+
/**
87+
* @param DownloadResponse $response
88+
* @param string $filename
89+
*/
90+
private function assertHasValidResponseHeaders(DownloadResponse $response, $filename = 'download'): void
91+
{
92+
$requiredHeaders = [
93+
'cache-control' => 'must-revalidate',
94+
'content-description' => 'File Transfer',
95+
'content-disposition' => sprintf('attachment; filename=%s', $filename),
96+
'content-transfer-encoding' => 'Binary',
97+
'content-type' => 'application/octet-stream',
98+
'expires' => '0',
99+
'pragma' => 'Public'
100+
];
101+
foreach ($requiredHeaders as $header => $value) {
102+
$this->assertTrue($response->hasHeader($header));
103+
$this->assertSame($value, $response->getHeaderLine($header));
104+
}
55105
}
56106
}

0 commit comments

Comments
 (0)