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