Completed
Push — master ( 0115c1...e43345 )
by Alexander
16:00 queued 13:56
created

SapiEmitter::emitBody()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 15
c 0
b 0
f 0
nc 8
nop 1
dl 0
loc 19
rs 9.2222
ccs 0
cts 0
cp 0
crap 42
1
<?php declare(strict_types=1);
2
3
namespace Yiisoft\Yii\Web\Emitter;
4
5
use Psr\Http\Message\ResponseInterface;
6
7
/**
8
 * SapiEmitter sends a response using PHP Server API
9
 */
10
final class SapiEmitter implements EmitterInterface
11
{
12
    private const NO_BODY_RESPONSE_CODES = [204, 205, 304];
13
    private $bufferSize;
14 4
    private const DEFAULT_BUFFER_SIZE = 8388608; // 8MB
15
16 4
    public function __construct(int $bufferSize = self::DEFAULT_BUFFER_SIZE)
17
    {
18 4
        $this->bufferSize = $bufferSize > 0 ? $bufferSize : self::DEFAULT_BUFFER_SIZE;
19 4
    }
20 4
21 4
    public function emit(ResponseInterface $response, bool $withoutBody = false): bool
22 4
    {
23 4
        $status = $response->getStatusCode();
24 4
25 4
        header_remove();
26
        foreach ($response->getHeaders() as $header => $values) {
27
            foreach ($values as $value) {
28
                header(sprintf(
29 4
                    '%s: %s',
30
                    $header,
31 4
                    $value
32 4
                ), $header !== 'Set-Cookie', $status);
33 4
            }
34 4
        }
35 4
36 4
        $reason = $response->getReasonPhrase();
37
38 4
        header(sprintf(
39 2
            'HTTP/%s %d %s',
40 2
            $response->getProtocolVersion(),
41 1
            $status,
42 1
            $reason
43
        ), true, $status);
44
45 2
        if ($withoutBody === false && $this->shouldOutputBody($response)) {
46
            $contentLength = $response->getBody()->getSize();
47 2
            if ($response->hasHeader('Content-Length')) {
48
                $contentLengthHeader = $response->getHeader('Content-Length');
49
                $contentLength = array_shift($contentLengthHeader);
50 4
            }
51
            if ($contentLength !== null) {
52
                header(sprintf('Content-Length: %s', $contentLength), true, $status);
53 3
            }
54
55 3
            $this->emitBody($response);
56
        }
57
58
        return true;
59
    }
60
61
    private function emitBody(ResponseInterface $response): void
62
    {
63
        $body = $response->getBody();
64
        if ($body->isSeekable()) {
65
            $body->rewind();
66
        }
67
        $bytesToSend = $response->getBody()->getSize();
68
        if ($bytesToSend > 0) {
69
            while ($bytesToSend > 0 && !$body->eof()) {
70
                $toRead = \min($this->bufferSize, $bytesToSend);
71
                $data = $body->read($toRead);
72
                echo $data;
73
                $bytesToSend -= \strlen($data);
74
                \flush();
75
            }
76
        } else {
77
            while (!$body->eof()) {
78
                echo $body->read($this->bufferSize);
79
                \flush();
80
            }
81
        }
82
    }
83
84
    private function shouldOutputBody(ResponseInterface $response): bool
85
    {
86
        return !\in_array($response->getStatusCode(), self::NO_BODY_RESPONSE_CODES, true);
87
    }
88
}
89