Passed
Push — develop ( 16acaa...f30815 )
by Henry
03:55 queued 43s
created

Emitter::emitBody()   B

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 59
    public function __construct(ResponseInterface $response)
38
    {
39 59
        $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 59
    public function emit(): void
51
    {
52 59
        if (!isset($this->responseChunkSize)) {
53 59
            $this->responseChunkSize= 4096;
54
        }
55 59
        $response = $this->response;
56 59
        $isEmpty = $this->isResponseEmpty($response);
57 59
        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 59
        if (!$isEmpty) {
67 59
            $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 59
    private function emitBody(ResponseInterface $response): void
104
    {
105 59
        $body = $response->getBody();
106
107
        // partial content responses might have a pre-set stream seek
108 59
        if ($body->isSeekable() && $response->getStatusCode() !== 206) {
109 54
            $body->rewind();
110
        }
111
112 59
        $amountToRead = (int) $response->getHeaderLine('Content-Length');
113 59
        if (!$amountToRead) {
114 52
            $amountToRead = $body->getSize();
115
        }
116
117 59
        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 59
            while ($amountToRead > 0 && !$body->eof()) {
119 59
                $length = min($this->responseChunkSize, $amountToRead);
120 59
                $data = $body->read($length);
121 59
                echo $data;
122
123 59
                $amountToRead -= strlen($data);
124
125 59
                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 59
    public function isResponseEmpty(ResponseInterface $response): bool
143
    {
144 59
        if (in_array($response->getStatusCode(), [204, 205, 304], true)) {
145
            return true;
146
        }
147 59
        $stream = $response->getBody();
148 59
        $seekable = $stream->isSeekable();
149 59
        if ($seekable) {
150 59
            $stream->rewind();
151
        }
152 59
        return $seekable ? $stream->read(1) === '' : $stream->eof();
153
    }
154
}
155