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