Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
32 / 32 |
|
100.00% |
2 / 2 |
CRAP | |
100.00% |
1 / 1 |
StreamResponse | |
100.00% |
32 / 32 |
|
100.00% |
2 / 2 |
12 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
26 / 26 |
|
100.00% |
1 / 1 |
11 | |||
render | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace Miniframe\Response; |
4 | |
5 | use Miniframe\Core\Response; |
6 | |
7 | class StreamResponse extends Response |
8 | { |
9 | /** |
10 | * Resource of a stream. |
11 | * |
12 | * @var resource |
13 | */ |
14 | private $resource; |
15 | |
16 | /** |
17 | * Disposition options |
18 | */ |
19 | public const |
20 | DISPOSITION_ATTACHMENT = 1, |
21 | DISPOSITION_INLINE = 2 |
22 | ; |
23 | |
24 | /** |
25 | * Streams a resource back to the client |
26 | * |
27 | * @param resource $resource Resource of a stream (`$resource = fopen('/path/to/large-file.zip', 'r');`). |
28 | * @param string|null $filename Name of the file. |
29 | * @param string|null $contentType Type of the file. |
30 | * @param integer $disposition Type of the stream (self::DISPOSITION_ATTACHMENT or self::DISPOSITION_INLINE). |
31 | */ |
32 | public function __construct( |
33 | $resource, |
34 | string $filename = null, |
35 | string $contentType = null, |
36 | int $disposition = self::DISPOSITION_ATTACHMENT |
37 | ) { |
38 | if (is_resource($resource) === false) { |
39 | throw new \InvalidArgumentException( |
40 | 'Argument must be a valid resource type. ' . gettype($resource) . ' given.' |
41 | ); |
42 | } |
43 | $this->resource = $resource; |
44 | switch ($disposition) { |
45 | case self::DISPOSITION_ATTACHMENT: |
46 | $dispositionStr = 'attachment'; |
47 | break; |
48 | case self::DISPOSITION_INLINE: |
49 | $dispositionStr = 'inline'; |
50 | break; |
51 | default: |
52 | throw new \InvalidArgumentException( |
53 | 'Argument must be a valid disposition. ' . var_export($disposition, true) . ' given.' |
54 | ); |
55 | } |
56 | |
57 | // Add Content Type header, when specified |
58 | if ($contentType !== null) { |
59 | $this->addHeader('Content-type: ' . $contentType); |
60 | } |
61 | |
62 | // Add Content-length and Last-Modified, when known |
63 | $stat = fstat($resource); |
64 | if (isset($stat['size']) && $stat['size'] > 0) { |
65 | $this->addHeader('Content-length: ' . $stat['size']); |
66 | } |
67 | if (isset($stat['mtime']) && $stat['mtime'] > 0) { |
68 | $this->addHeader('Last-Modified: ' . date('r', $stat['mtime'])); |
69 | } |
70 | |
71 | // Transfer is binary |
72 | $this->addHeader('Content-Transfer-Encoding: Binary'); |
73 | |
74 | // Compile Content-disposition header |
75 | $contentDisposition = 'Content-disposition: ' . $dispositionStr; |
76 | if ($filename !== null) { |
77 | $contentDisposition .= '; filename="' . rawurlencode($filename) . '"'; |
78 | } |
79 | $this->addHeader($contentDisposition); |
80 | } |
81 | |
82 | /** |
83 | * Passes thru the stream data to STDOUT. Always returns an empty string. |
84 | * |
85 | * @return string |
86 | */ |
87 | public function render(): string |
88 | { |
89 | $currentLimit = ini_get('max_execution_time'); |
90 | set_time_limit(0); |
91 | fpassthru($this->resource); |
92 | set_time_limit((int)$currentLimit); |
93 | fclose($this->resource); |
94 | return ''; |
95 | } |
96 | } |