ServerRequest   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 305
Duplicated Lines 0 %

Coupling/Cohesion

Components 5
Dependencies 2

Test Coverage

Coverage 93.81%

Importance

Changes 0
Metric Value
wmc 34
lcom 5
cbo 2
dl 0
loc 305
ccs 106
cts 113
cp 0.9381
rs 9.68
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
A parseBody() 0 21 5
A getServerParams() 0 4 1
A getUploadedFiles() 0 4 1
A withUploadedFiles() 0 8 1
A getCookieParams() 0 4 1
A withCookieParams() 0 6 1
A getQueryParams() 0 4 1
A withQueryParams() 0 7 1
A getParsedBody() 0 4 1
A withParsedBody() 0 6 1
A getAttributes() 0 4 1
A getAttribute() 0 8 2
A withAttribute() 0 6 1
A withoutAttribute() 0 10 2
A withAttributes() 0 6 1
A validateUploadedFiles() 0 13 4
A fromGlobals() 0 13 1
B fromSwoole() 0 42 7
1
<?php declare(strict_types=1);
2
3
namespace Igni\Network\Http;
4
5
use Psr\Http\Message\ServerRequestInterface;
6
use Psr\Http\Message\StreamInterface;
7
use Psr\Http\Message\UploadedFileInterface;
8
use Swoole\Http\Request as SwooleHttRequest;
9
use Throwable;
10
11
use function Zend\Diactoros\marshalHeadersFromSapi;
12
use function Zend\Diactoros\normalizeUploadedFiles;
13
14
class ServerRequest extends Request implements ServerRequestInterface
15
{
16
    /**
17
     * @var array
18
     */
19
    private $attributes = [];
20
21
    /**
22
     * @var array
23
     */
24
    private $cookieParams = [];
25
26
    /**
27
     * @var null|array|object
28
     */
29
    private $parsedBody;
30
31
    /**
32
     * @var array
33
     */
34
    private $queryParams = [];
35
36
    /**
37
     * @var array
38
     */
39
    private $serverParams;
40
41
    /**
42
     * @var array
43
     */
44
    private $uploadedFiles;
45
46
47
    /**
48
     * Server request constructor.
49
     *
50
     * @param array $serverParams Server parameters, typically from $_SERVER
51
     * @param array $uploadedFiles Upload file information, a tree of UploadedFiles
52
     * @param null|string $uri URI for the request, if any.
53
     * @param null|string $method HTTP method for the request, if any.
54
     * @param string|resource|StreamInterface $body Messages body, if any.
55
     * @param array $headers Headers for the message, if any.
56
     * @throws \InvalidArgumentException for any invalid value.
57
     */
58 10
    public function __construct(
59
        string $uri = null,
60
        string $method = self::METHOD_GET,
61
        $body = 'php://input',
62
        array $headers = [],
63
        array $uploadedFiles = [],
64
        array $serverParams = []
65
    ) {
66 10
        parent::__construct($uri, $method, $body, $headers);
67 10
        $this->validateUploadedFiles($uploadedFiles);
68 10
        $this->serverParams  = $serverParams;
69 10
        $this->uploadedFiles = $uploadedFiles;
70 10
        parse_str($this->getUri()->getQuery(), $this->queryParams);
71 10
        $this->parseBody();
72 10
    }
73
74 10
    private function parseBody(): void
75
    {
76 10
        $contentType = $this->getHeader('Content-Type')[0] ?? '';
77
78 10
        $body = (string) $this->getBody();
79
80 10
        switch (strtolower($contentType)) {
81 10
            case 'application/json':
82 1
                $this->parsedBody = json_decode($body, true);
83 1
                return;
84 9
            case 'application/x-www-form-urlencoded':
85 1
                 parse_str($body, $this->parsedBody);
86 1
                 return;
87 8
            case 'application/xml':
88
                $this->parsedBody = simplexml_load_string($body);
89
                return;
90 8
            case 'text/csv':
91
                $this->parsedBody = str_getcsv($body);
92
                return;
93
        }
94 8
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 2
    public function getServerParams(): array
100
    {
101 2
        return $this->serverParams;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 2
    public function getUploadedFiles(): array
108
    {
109 2
        return $this->uploadedFiles;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 1
    public function withUploadedFiles(array $uploadedFiles)
116
    {
117 1
        $this->validateUploadedFiles($uploadedFiles);
118 1
        $new = clone $this;
119 1
        $new->uploadedFiles = $uploadedFiles;
120
121 1
        return $new;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127 2
    public function getCookieParams(): array
128
    {
129 2
        return $this->cookieParams;
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 1
    public function withCookieParams(array $cookies)
136
    {
137 1
        $new = clone $this;
138 1
        $new->cookieParams = $cookies;
139 1
        return $new;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 4
    public function getQueryParams(): array
146
    {
147 4
        return $this->queryParams;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 1
    public function withQueryParams(array $query)
154
    {
155 1
        $new = clone $this;
156 1
        $new->queryParams = $query;
157
158 1
        return $new;
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 4
    public function getParsedBody()
165
    {
166 4
        return $this->parsedBody;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172 1
    public function withParsedBody($data)
173
    {
174 1
        $new = clone $this;
175 1
        $new->parsedBody = $data;
176 1
        return $new;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182 2
    public function getAttributes(): array
183
    {
184 2
        return $this->attributes;
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190 1
    public function getAttribute($attribute, $default = null)
191
    {
192 1
        if (! isset($this->attributes[$attribute])) {
193 1
            return $default;
194
        }
195
196 1
        return $this->attributes[$attribute];
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 1
    public function withAttribute($attribute, $value)
203
    {
204 1
        $new = clone $this;
205 1
        $new->attributes[$attribute] = $value;
206 1
        return $new;
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212 1
    public function withoutAttribute($attribute)
213
    {
214 1
        if (!isset($this->attributes[$attribute])) {
215 1
            return clone $this;
216
        }
217
218 1
        $new = clone $this;
219 1
        unset($new->attributes[$attribute]);
220 1
        return $new;
221
    }
222
223
    /**
224
     * Sets request attributes
225
     *
226
     * This method returns a new instance.
227
     *
228
     * @param array $attributes
229
     * @return self
230
     */
231 1
    public function withAttributes(array $attributes): ServerRequest
232
    {
233 1
        $new = clone $this;
234 1
        $new->attributes = $attributes;
235 1
        return $new;
236
    }
237
238
    /**
239
     * Recursively validate the structure in an uploaded files array.
240
     *
241
     * @param array $uploadedFiles
242
     * @throws \InvalidArgumentException if any leaf is not an UploadedFileInterface instance.
243
     */
244 10
    private function validateUploadedFiles(array $uploadedFiles): void
245
    {
246 10
        foreach ($uploadedFiles as $file) {
247 1
            if (is_array($file)) {
248
                $this->validateUploadedFiles($file);
249
                continue;
250
            }
251
252 1
            if (! $file instanceof UploadedFileInterface) {
253
                throw new \InvalidArgumentException('Invalid leaf in uploaded files structure');
254
            }
255
        }
256 10
    }
257
258 2
    public static function fromGlobals(): ServerRequest
259
    {
260 2
        $instance = new self(
261 2
            $_SERVER['REQUEST_URI'] ?? '',
262 2
            $_SERVER['REQUEST_METHOD'] ?? 'GET',
263 2
            'php://input',
264 2
            marshalHeadersFromSapi($_SERVER),
265 2
            normalizeUploadedFiles($_FILES),
266 2
            $_SERVER
267
        );
268
269 2
        return $instance;
270
    }
271
272
    /**
273
     * @param SwooleHttRequest $request
274
     * @return ServerRequest
275
     */
276 4
    public static function fromSwoole(SwooleHttRequest $request): ServerRequest
277
    {
278 4
        $serverParams  = $request->server ?? [];
279 4
        if (isset($request->server['query_string'])) {
280 1
            $uri = $request->server['request_uri'] . '?' . $request->server['query_string'];
281
        } else {
282 3
            $uri = $request->server['request_uri'];
283
        }
284
285
        // Normalize server params
286 4
        $serverParams = array_change_key_case($serverParams, CASE_UPPER);
287
288
        // Normalize headers
289 4
        $headers = [];
290 4
        if ($request->header) {
291 2
            foreach ($request->header as $name => $value) {
292 2
                if (!isset($headers[$name])) {
293 2
                    $headers[$name] = [];
294
                }
295 2
                array_push($headers[$name], $value);
296
            }
297
        }
298
299
        try {
300 4
            $body = $request->rawContent();
301 4
        } catch (Throwable $throwable) {
302 4
            $body = '';
303
        }
304
305 4
        if (!$body) {
306 4
            $body = '';
307
        }
308
309 4
        return new ServerRequest(
310 4
            $uri,
311 4
            $request->server['request_method'] ?? 'GET',
312 4
            $body,
313 4
            $headers,
314 4
            normalizeUploadedFiles($request->files ?? []) ?? [],
315 4
            $serverParams
316
        );
317
    }
318
}
319