Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@0
|
3 * Zend Framework (http://framework.zend.com/)
|
Chris@0
|
4 *
|
Chris@0
|
5 * @see http://github.com/zendframework/zend-diactoros for the canonical source repository
|
Chris@0
|
6 * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
|
Chris@0
|
7 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@0
|
10 namespace Zend\Diactoros\Response;
|
Chris@0
|
11
|
Chris@0
|
12 use Psr\Http\Message\ResponseInterface;
|
Chris@0
|
13 use RuntimeException;
|
Chris@0
|
14 use Zend\Diactoros\RelativeStream;
|
Chris@0
|
15
|
Chris@0
|
16 class SapiStreamEmitter implements EmitterInterface
|
Chris@0
|
17 {
|
Chris@0
|
18 use SapiEmitterTrait;
|
Chris@0
|
19
|
Chris@0
|
20 /**
|
Chris@0
|
21 * Emits a response for a PHP SAPI environment.
|
Chris@0
|
22 *
|
Chris@0
|
23 * Emits the status line and headers via the header() function, and the
|
Chris@0
|
24 * body content via the output buffer.
|
Chris@0
|
25 *
|
Chris@0
|
26 * @param ResponseInterface $response
|
Chris@0
|
27 * @param int $maxBufferLength Maximum output buffering size for each iteration
|
Chris@0
|
28 */
|
Chris@0
|
29 public function emit(ResponseInterface $response, $maxBufferLength = 8192)
|
Chris@0
|
30 {
|
Chris@0
|
31 if (headers_sent()) {
|
Chris@0
|
32 throw new RuntimeException('Unable to emit response; headers already sent');
|
Chris@0
|
33 }
|
Chris@0
|
34
|
Chris@0
|
35 $response = $this->injectContentLength($response);
|
Chris@0
|
36
|
Chris@0
|
37 $this->emitStatusLine($response);
|
Chris@0
|
38 $this->emitHeaders($response);
|
Chris@0
|
39 $this->flush();
|
Chris@0
|
40
|
Chris@0
|
41 $range = $this->parseContentRange($response->getHeaderLine('Content-Range'));
|
Chris@0
|
42
|
Chris@0
|
43 if (is_array($range) && $range[0] === 'bytes') {
|
Chris@0
|
44 $this->emitBodyRange($range, $response, $maxBufferLength);
|
Chris@0
|
45 return;
|
Chris@0
|
46 }
|
Chris@0
|
47
|
Chris@0
|
48 $this->emitBody($response, $maxBufferLength);
|
Chris@0
|
49 }
|
Chris@0
|
50
|
Chris@0
|
51 /**
|
Chris@0
|
52 * Emit the message body.
|
Chris@0
|
53 *
|
Chris@0
|
54 * @param ResponseInterface $response
|
Chris@0
|
55 * @param int $maxBufferLength
|
Chris@0
|
56 */
|
Chris@0
|
57 private function emitBody(ResponseInterface $response, $maxBufferLength)
|
Chris@0
|
58 {
|
Chris@0
|
59 $body = $response->getBody();
|
Chris@0
|
60
|
Chris@0
|
61 if ($body->isSeekable()) {
|
Chris@0
|
62 $body->rewind();
|
Chris@0
|
63 }
|
Chris@0
|
64
|
Chris@0
|
65 if (! $body->isReadable()) {
|
Chris@0
|
66 echo $body;
|
Chris@0
|
67 return;
|
Chris@0
|
68 }
|
Chris@0
|
69
|
Chris@0
|
70 while (! $body->eof()) {
|
Chris@0
|
71 echo $body->read($maxBufferLength);
|
Chris@0
|
72 }
|
Chris@0
|
73 }
|
Chris@0
|
74
|
Chris@0
|
75 /**
|
Chris@0
|
76 * Emit a range of the message body.
|
Chris@0
|
77 *
|
Chris@0
|
78 * @param array $range
|
Chris@0
|
79 * @param ResponseInterface $response
|
Chris@0
|
80 * @param int $maxBufferLength
|
Chris@0
|
81 */
|
Chris@0
|
82 private function emitBodyRange(array $range, ResponseInterface $response, $maxBufferLength)
|
Chris@0
|
83 {
|
Chris@0
|
84 list($unit, $first, $last, $length) = $range;
|
Chris@0
|
85
|
Chris@0
|
86 $body = $response->getBody();
|
Chris@0
|
87
|
Chris@0
|
88 $length = $last - $first + 1;
|
Chris@0
|
89
|
Chris@0
|
90 if ($body->isSeekable()) {
|
Chris@0
|
91 $body->seek($first);
|
Chris@0
|
92
|
Chris@0
|
93 $first = 0;
|
Chris@0
|
94 }
|
Chris@0
|
95
|
Chris@0
|
96 if (! $body->isReadable()) {
|
Chris@0
|
97 echo substr($body->getContents(), $first, $length);
|
Chris@0
|
98 return;
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 $remaining = $length;
|
Chris@0
|
102
|
Chris@0
|
103 while ($remaining >= $maxBufferLength && ! $body->eof()) {
|
Chris@0
|
104 $contents = $body->read($maxBufferLength);
|
Chris@0
|
105 $remaining -= strlen($contents);
|
Chris@0
|
106
|
Chris@0
|
107 echo $contents;
|
Chris@0
|
108 }
|
Chris@0
|
109
|
Chris@0
|
110 if ($remaining > 0 && ! $body->eof()) {
|
Chris@0
|
111 echo $body->read($remaining);
|
Chris@0
|
112 }
|
Chris@0
|
113 }
|
Chris@0
|
114
|
Chris@0
|
115 /**
|
Chris@0
|
116 * Parse content-range header
|
Chris@0
|
117 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
|
Chris@0
|
118 *
|
Chris@0
|
119 * @param string $header
|
Chris@0
|
120 * @return false|array [unit, first, last, length]; returns false if no
|
Chris@0
|
121 * content range or an invalid content range is provided
|
Chris@0
|
122 */
|
Chris@0
|
123 private function parseContentRange($header)
|
Chris@0
|
124 {
|
Chris@0
|
125 if (preg_match('/(?P<unit>[\w]+)\s+(?P<first>\d+)-(?P<last>\d+)\/(?P<length>\d+|\*)/', $header, $matches)) {
|
Chris@0
|
126 return [
|
Chris@0
|
127 $matches['unit'],
|
Chris@0
|
128 (int) $matches['first'],
|
Chris@0
|
129 (int) $matches['last'],
|
Chris@0
|
130 $matches['length'] === '*' ? '*' : (int) $matches['length'],
|
Chris@0
|
131 ];
|
Chris@0
|
132 }
|
Chris@0
|
133 return false;
|
Chris@0
|
134 }
|
Chris@0
|
135 }
|