Completed
Pull Request — master (#456)
by
unknown
03:04
created

Response::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SwooleTW\Http\Transformers;
4
5
use Illuminate\Http\Response as IlluminateResponse;
6
use Swoole\Http\Response as SwooleResponse;
7
use Swoole\Http\Request as SwooleRequest;
8
use Symfony\Component\HttpFoundation\BinaryFileResponse;
9
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
10
use Symfony\Component\HttpFoundation\StreamedResponse;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SwooleTW\Http\Transformers\StreamedResponse. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
12
class Response
13
{
14
    const CHUNK_SIZE = 8192;
15
16
    /**
17
     * @var \Swoole\Http\Response
18
     */
19
    protected $swooleResponse;
20
21
    /**
22
     * @var \Swoole\Http\Request
23
     */
24
    protected $swooleRequest;
25
26
    /**
27
     * @var \Illuminate\Http\Response
28
     */
29
    protected $illuminateResponse;
30
31
    /**
32
     * Make a response.
33
     *
34
     * @param $illuminateResponse
35
     * @param \Swoole\Http\Response $swooleResponse
36
     * @param \Swoole\Http\Request $swooleRequest
37
     *
38
     * @return \SwooleTW\Http\Transformers\Response
39
     */
40
    public static function make($illuminateResponse, SwooleResponse $swooleResponse, SwooleRequest $swooleRequest)
41
    {
42
        return new static($illuminateResponse, $swooleResponse, $swooleRequest);
43
    }
44
45
    /**
46
     * Response constructor.
47
     *
48
     * @param mixed $illuminateResponse
49
     * @param \Swoole\Http\Response $swooleResponse
50
     * @param \Swoole\Http\Request $swooleRequest
51
     */
52
    public function __construct($illuminateResponse, SwooleResponse $swooleResponse, SwooleRequest $swooleRequest)
53
    {
54
        $this->setIlluminateResponse($illuminateResponse);
55
        $this->setSwooleResponse($swooleResponse);
56
        $this->setSwooleRequest($swooleRequest);
57
    }
58
59
    /**
60
     * Send HTTP headers and content.
61
     *
62
     * @throws \InvalidArgumentException
63
     */
64
    public function send()
65
    {
66
        $this->sendHeaders();
67
        $this->sendContent();
68
    }
69
70
    /**
71
     * Send HTTP headers.
72
     *
73
     * @throws \InvalidArgumentException
74
     */
75
    protected function sendHeaders()
76
    {
77
        $illuminateResponse = $this->getIlluminateResponse();
78
79
        /* RFC2616 - 14.18 says all Responses need to have a Date */
80
        if (! $illuminateResponse->headers->has('Date')) {
81
            $illuminateResponse->setDate(\DateTime::createFromFormat('U', time()));
0 ignored issues
show
Bug introduced by
It seems like DateTime::createFromFormat('U', time()) can also be of type false; however, parameter $date of Symfony\Component\HttpFo...ion\Response::setDate() does only seem to accept DateTimeInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
            $illuminateResponse->setDate(/** @scrutinizer ignore-type */ \DateTime::createFromFormat('U', time()));
Loading history...
82
        }
83
84
        // headers
85
        // allPreserveCaseWithoutCookies() doesn't exist before Laravel 5.3
86
        $headers = $illuminateResponse->headers->allPreserveCase();
87
        if (isset($headers['Set-Cookie'])) {
88
            unset($headers['Set-Cookie']);
89
        }
90
        foreach ($headers as $name => $values) {
91
            foreach ($values as $value) {
92
                $this->swooleResponse->header($name, $value);
93
            }
94
        }
95
96
        // status
97
        $this->swooleResponse->status($illuminateResponse->getStatusCode());
98
99
        // cookies
100
        // $cookie->isRaw() is supported after symfony/http-foundation 3.1
101
        // and Laravel 5.3, so we can add it back now
102
        foreach ($illuminateResponse->headers->getCookies() as $cookie) {
103
            $method = $cookie->isRaw() ? 'rawcookie' : 'cookie';
104
            $this->swooleResponse->$method(
105
                $cookie->getName(),
106
                $cookie->getValue(),
107
                $cookie->getExpiresTime(),
108
                $cookie->getPath(),
109
                $cookie->getDomain(),
110
                $cookie->isSecure(),
111
                $cookie->isHttpOnly()
112
            );
113
        }
114
    }
115
116
    /**
117
     * Send HTTP content.
118
     */
119
    protected function sendContent()
120
    {
121
        $illuminateResponse = $this->getIlluminateResponse();
122
123
        if ($illuminateResponse instanceof StreamedResponse && property_exists($illuminateResponse, 'output')) {
0 ignored issues
show
introduced by
$illuminateResponse is never a sub-type of Symfony\Component\HttpFoundation\StreamedResponse.
Loading history...
124
            // TODO Add Streamed Response with output
125
            $this->swooleResponse->end($illuminateResponse->output);
126
        } elseif ($illuminateResponse instanceof BinaryFileResponse) {
0 ignored issues
show
introduced by
$illuminateResponse is never a sub-type of Symfony\Component\HttpFo...tion\BinaryFileResponse.
Loading history...
127
            $this->swooleResponse->sendfile($illuminateResponse->getFile()->getPathname());
128
        } else {
129
            $chunkGzip = $this->canGzipContent($illuminateResponse->headers->get('Content-Encoding'));
0 ignored issues
show
Unused Code introduced by
The assignment to $chunkGzip is dead and can be removed.
Loading history...
130
            $this->sendInChunk($illuminateResponse->getContent());
131
        }
132
    }
133
134
    /**
135
     * Send content in chunk
136
     *
137
     * @param string $content
138
     */
139
    protected function sendInChunk($content)
140
    {
141
        if (strlen($content) <= static::CHUNK_SIZE) {
142
            $this->swooleResponse->end($content);
143
            return;
144
        }
145
146
        // Swoole Chunk mode does not support compress by default, this patch only supports gzip
147
        if ($chunkGzip) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $chunkGzip seems to be never defined.
Loading history...
148
            $this->swooleResponse->header('Content-Encoding', 'gzip');
149
            $content = gzencode($content, config('swoole_http.server.options.http_compression_level', 3));
150
        }
151
152
        foreach (str_split($content, static::CHUNK_SIZE) as $chunk) {
153
            $this->swooleResponse->write($chunk);
154
        }
155
156
        $this->swooleResponse->end();
157
    }
158
159
    /**
160
     * @param \Swoole\Http\Response $swooleResponse
161
     *
162
     * @return \SwooleTW\Http\Transformers\Response
163
     */
164
    protected function setSwooleResponse(SwooleResponse $swooleResponse)
165
    {
166
        $this->swooleResponse = $swooleResponse;
167
168
        return $this;
169
    }
170
171
    /**
172
     * @return \Swoole\Http\Response
173
     */
174
    public function getSwooleResponse()
175
    {
176
        return $this->swooleResponse;
177
    }
178
179
    /**
180
     * @param mixed illuminateResponse
181
     *
182
     * @return \SwooleTW\Http\Transformers\Response
183
     */
184
    protected function setIlluminateResponse($illuminateResponse)
185
    {
186
        if (! $illuminateResponse instanceof SymfonyResponse) {
187
            $content = (string) $illuminateResponse;
188
            $illuminateResponse = new IlluminateResponse($content);
189
        }
190
191
        $this->illuminateResponse = $illuminateResponse;
192
193
        return $this;
194
    }
195
196
    /**
197
     * @return \Illuminate\Http\Response
198
     */
199
    public function getIlluminateResponse()
200
    {
201
        return $this->illuminateResponse;
202
    }
203
204
    /**
205
     * @param \Swoole\Http\Request $swooleRequest
206
     *
207
     * @return \SwooleTW\Http\Transformers\Response
208
     */
209
    protected function setSwooleRequest(SwooleRequest $swooleRequest)
210
    {
211
        $this->swooleRequest = $swooleRequest;
212
213
        return $this;
214
    }
215
216
    /**
217
     * @return \Swoole\Http\Request
218
     */
219
    public function getSwooleRequest()
220
    {
221
        return $this->swooleRequest;
222
    }
223
224
    /**
225
     * @param string $responseContentEncoding
226
     * @return bool
227
     */
228
    protected function canGzipContent($responseContentEncoding)
229
    {
230
        return empty($responseContentEncoding) &&
231
            config('swoole_http.server.options.http_compression', true) &&
232
            !empty($this->swooleRequest->header['accept-encoding']) &&
233
            strpos($this->swooleRequest->header['accept-encoding'], 'gzip') !== false &&
234
            function_exists('gzencode');
235
    }
236
}
237