Emitter::emitBody()   B
last analyzed

Complexity

Conditions 10
Paths 24

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 11.8232

Importance

Changes 0
Metric Value
cc 10
eloc 19
c 0
b 0
f 0
nc 24
nop 1
dl 0
loc 31
ccs 14
cts 19
cp 0.7368
crap 11.8232
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of the Divergence package.
4
 *
5
 * (c) Henry Paradiz <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
declare(strict_types=1);
11
12
namespace Divergence\Responders;
13
14
use Psr\Http\Message\ResponseInterface;
15
use function connection_status;
16
use function header;
17
use function headers_sent;
18
use function in_array;
19
use function min;
20
use function sprintf;
21
use function strlen;
22
23
use function strtolower;
24
25
use const CONNECTION_NORMAL;
26
27
/**
28
 * Forked from Slim Framework 4.x
29
 *
30
 */
31
class Emitter
32
{
33
    private int $responseChunkSize;
34
35
    protected ResponseInterface $response;
36
37 62
    public function __construct(ResponseInterface $response)
38
    {
39 62
        $this->response = $response;
40
    }
41
42
    public function setResponseChunkSize($chunkSize): void
43
    {
44
        $this->responseChunkSize = $chunkSize;
45
    }
46
47
    /**
48
     * Send the response the client
49
     */
50 62
    public function emit(): void
51
    {
52 62
        if (!isset($this->responseChunkSize)) {
53 62
            $this->responseChunkSize= 4096;
54
        }
55 62
        $response = $this->response;
56 62
        $isEmpty = $this->isResponseEmpty($response);
57 62
        if (headers_sent() === false) {
58
            $this->emitHeaders($response);
59
60
            // Set the status _after_ the headers, because of PHP's "helpful" behavior with location headers.
61
            // See https://github.com/slimphp/Slim/issues/1730
62
63
            $this->emitStatusLine($response);
64
        }
65
66 62
        if (!$isEmpty) {
67 62
            $this->emitBody($response);
68
        }
69
    }
70
71
    /**
72
     * Emit Response Headers
73
     */
74
    private function emitHeaders(ResponseInterface $response): void
75
    {
76
        foreach ($response->getHeaders() as $name => $values) {
77
            $first = strtolower($name) !== 'set-cookie';
78
            foreach ($values as $value) {
79
                $header = sprintf('%s: %s', $name, $value);
80
                header($header, $first);
81
                $first = false;
82
            }
83
        }
84
    }
85
86
    /**
87
     * Emit Status Line
88
     */
89
    private function emitStatusLine(ResponseInterface $response): void
90
    {
91
        $statusLine = sprintf(
92
            'HTTP/%s %s %s',
93
            $response->getProtocolVersion(),
94
            $response->getStatusCode(),
95
            $response->getReasonPhrase()
96
        );
97
        header($statusLine, true, $response->getStatusCode());
98
    }
99
100
    /**
101
     * Emit Body
102
     */
103 62
    private function emitBody(ResponseInterface $response): void
104
    {
105 62
        $body = $response->getBody();
106
107
        // partial content responses might have a pre-set stream seek
108 62
        if ($body->isSeekable() && $response->getStatusCode() !== 206) {
109 59
            $body->rewind();
110
        }
111
112 62
        $amountToRead = (int) $response->getHeaderLine('Content-Length');
113 62
        if (!$amountToRead) {
114 52
            $amountToRead = $body->getSize();
115
        }
116
117 62
        if ($amountToRead) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $amountToRead of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
118 62
            while ($amountToRead > 0 && !$body->eof()) {
119 62
                $length = min($this->responseChunkSize, $amountToRead);
120 62
                $data = $body->read($length);
121 62
                echo $data;
122
123 62
                $amountToRead -= strlen($data);
124
125 62
                if (connection_status() !== CONNECTION_NORMAL) {
126
                    break;
127
                }
128
            }
129
        } else {
130
            while (!$body->eof()) {
131
                echo $body->read($this->responseChunkSize);
132
                if (connection_status() !== CONNECTION_NORMAL) {
133
                    break;
134
                }
135
            }
136
        }
137
    }
138
139
    /**
140
     * Asserts response body is empty or status code is 204, 205 or 304
141
     */
142 62
    public function isResponseEmpty(ResponseInterface $response): bool
143
    {
144 62
        if (in_array($response->getStatusCode(), [204, 205, 304], true)) {
145
            return true;
146
        }
147 62
        if (in_array($response->getStatusCode(), [206], true)) {
148 3
            return false;
149
        }
150 59
        $stream = $response->getBody();
151 59
        $seekable = $stream->isSeekable();
152 59
        if ($seekable) {
153 59
            $stream->rewind();
154
        }
155 59
        return $seekable ? $stream->read(1) === '' : $stream->eof();
156
    }
157
}
158