Passed
Pull Request — master (#31)
by Anatoly
39:30
created

ServerRequestProxy::equalsMediaTypes()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 11
c 1
b 0
f 0
nc 5
nop 2
dl 0
loc 21
ccs 0
cts 12
cp 0
crap 72
rs 8.4444
1
<?php declare(strict_types=1);
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Nekhay <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Nekhay
8
 * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-message
10
 */
11
12
namespace Sunrise\Http\Message;
13
14
/**
15
 * Import classes
16
 */
17
use Fig\Http\Message\RequestMethodInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Http\Message\StreamInterface;
20
use Psr\Http\Message\UriInterface;
21
use Sunrise\Http\Message\Entity\IpAddress;
22
23
/**
24
 * Import functions
25
 */
26
use function explode;
27
use function key;
28
use function reset;
29
use function strncmp;
30
use function strpos;
31
use function strstr;
32
use function strtolower;
33
use function trim;
34
35
/**
36
 * ServerRequestProxy
37
 */
38
final class ServerRequestProxy implements ServerRequestInterface, RequestMethodInterface
39
{
40
41
    /**
42
     * @var ServerRequestInterface
43
     */
44
    private ServerRequestInterface $request;
45
46
    /**
47
     * Constructor of the class
48
     *
49
     * @param ServerRequestInterface $request
50
     */
51 152
    public function __construct(ServerRequestInterface $request)
52
    {
53 152
        $this->request = $request;
54
    }
55
56
    /**
57
     * Creates the proxy from the given object
58
     *
59
     * @param ServerRequestInterface $request
60
     *
61
     * @return self
62
     */
63 152
    public static function create(ServerRequestInterface $request): self
64
    {
65 152
        if ($request instanceof self) {
66
            return $request;
67
        }
68
69 152
        return new self($request);
70
    }
71
72
    /**
73
     * Checks if the request is JSON
74
     *
75
     * @link https://tools.ietf.org/html/rfc4627
76
     *
77
     * @return bool
78
     */
79
    public function isJson(): bool
80
    {
81
        return $this->clientProducesMediaType([
82
            'application/json',
83
        ]);
84
    }
85
86
    /**
87
     * Checks if the request is XML
88
     *
89
     * @link https://tools.ietf.org/html/rfc2376
90
     *
91
     * @return bool
92
     */
93
    public function isXml(): bool
94
    {
95
        return $this->clientProducesMediaType([
96
            'application/xml',
97
            'text/xml',
98
        ]);
99
    }
100
101
    /**
102
     * Gets the client's IP address
103
     *
104
     * @param array<string, string> $proxyChain
105
     *
106
     * @return IpAddress
107
     */
108
    public function getClientIpAddress(array $proxyChain = []): IpAddress
109
    {
110
        $env = $this->request->getServerParams();
111
112
        /** @var string */
113
        $client = $env['REMOTE_ADDR'] ?? '::1';
114
115
        /** @var list<string> */
116
        $proxies = [];
117
118
        while (isset($proxyChain[$client])) {
119
            $proxyHeader = $proxyChain[$client];
120
            unset($proxyChain[$client]);
121
122
            $header = $this->request->getHeaderLine($proxyHeader);
123
            if ($header === '') {
124
                break;
125
            }
126
127
            $addresses = explode(',', $header);
128
            foreach ($addresses as $i => $address) {
129
                $addresses[$i] = trim($address);
130
                if ($addresses[$i] === '') {
131
                    unset($addresses[$i]);
132
                }
133
            }
134
135
            if ($addresses === []) {
136
                break;
137
            }
138
139
            $client = reset($addresses);
140
            unset($addresses[key($addresses)]);
141
142
            foreach ($addresses as $address) {
143
                $proxies[] = $address;
144
            }
145
        }
146
147
        return new IpAddress($client, $proxies);
148
    }
149
150
    /**
151
     * Gets the client's produced media type
152
     *
153
     * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1
154
     * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.5
155
     *
156
     * @return string
157
     */
158
    public function getClientProducedMediaType(): string
159
    {
160
        $header = $this->request->getHeaderLine('Content-Type');
161
        if ($header === '') {
162
            return '';
163
        }
164
165
        $result = strstr($header, ';', true);
166
        if ($result === false) {
167
            $result = $header;
168
        }
169
170
        $result = trim($result);
171
        if ($result === '') {
172
            return '';
173
        }
174
175
        return strtolower($result);
176
    }
177
178
    /**
179
     * Gets the client's consumed media types
180
     *
181
     * @link https://tools.ietf.org/html/rfc7231#section-1.2
182
     * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1
183
     * @link https://tools.ietf.org/html/rfc7231#section-5.3.2
184
     *
185
     * @return array<string, array<string, string>>
186
     */
187
    public function getClientConsumedMediaTypes(): array
188
    {
189
        $header = $this->request->getHeaderLine('Accept');
190
        if ($header === '') {
191
            return [];
192
        }
193
194
        $accept = header_accept_parse($header);
195
        if ($accept === []) {
196
            return [];
197
        }
198
199
        $result = [];
200
        foreach ($accept as $type => $params) {
201
            $result[strtolower($type)] = $params;
202
        }
203
204
        return $result;
205
    }
206
207
    /**
208
     * Gets the client's consumed encodings
209
     *
210
     * @return array<string, array<string, string>>
211
     */
212
    public function getClientConsumedEncodings(): array
213
    {
214
        $header = $this->request->getHeaderLine('Accept-Encoding');
215
        if ($header === '') {
216
            return [];
217
        }
218
219
        $accept = header_accept_parse($header);
220
        if ($accept === []) {
221
            return [];
222
        }
223
224
        return $accept;
225
    }
226
227
    /**
228
     * Gets the client's consumed languages
229
     *
230
     * @return array<string, array<string, string>>
231
     */
232
    public function getClientConsumedLanguages(): array
233
    {
234
        $header = $this->request->getHeaderLine('Accept-Language');
235
        if ($header === '') {
236
            return [];
237
        }
238
239
        $accept = header_accept_parse($header);
240
        if ($accept === []) {
241
            return [];
242
        }
243
244
        return $accept;
245
    }
246
247
    /**
248
     * Checks if the client produces one of the given media types
249
     *
250
     * @param list<string> $consumes
0 ignored issues
show
Bug introduced by
The type Sunrise\Http\Message\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
251
     *
252
     * @return bool
253
     */
254
    public function clientProducesMediaType(array $consumes): bool
255
    {
256
        if ($consumes === []) {
257
            return true;
258
        }
259
260
        $produced = $this->getClientProducedMediaType();
261
        if ($produced === '') {
262
            return false;
263
        }
264
265
        foreach ($consumes as $consumed) {
266
            if ($this->equalsMediaTypes($consumed, $produced)) {
267
                return true;
268
            }
269
        }
270
271
        return false;
272
    }
273
274
    /**
275
     * Checks if the client consumes one of the given media types
276
     *
277
     * @param list<string> $produces
278
     *
279
     * @return bool
280
     */
281
    public function clientConsumesMediaType(array $produces): bool
282
    {
283
        if ($produces === []) {
284
            return true;
285
        }
286
287
        $consumes = $this->getClientConsumedMediaTypes();
288
        if ($consumes === []) {
289
            return true;
290
        }
291
292
        if (isset($consumes['*/*'])) {
293
            return true;
294
        }
295
296
        foreach ($produces as $a) {
297
            foreach ($consumes as $b => $_) {
298
                if ($this->equalsMediaTypes($a, $b)) {
299
                    return true;
300
                }
301
            }
302
        }
303
304
        return false;
305
    }
306
307
    /**
308
     * Checks if the given media types are equal
309
     *
310
     * @param string $a
311
     * @param string $b
312
     *
313
     * @return bool
314
     */
315
    public function equalsMediaTypes(string $a, string $b): bool
316
    {
317
        if ($a === $b) {
318
            return true;
319
        }
320
321
        $slash = strpos($a, '/');
322
        if ($slash === false || !isset($b[$slash]) || $b[$slash] !== '/') {
323
            return false;
324
        }
325
326
        $star = $slash + 1;
327
        if (!isset($a[$star], $b[$star])) {
328
            return false;
329
        }
330
331
        if (!($a[$star] === '*' || $b[$star] === '*')) {
332
            return false;
333
        }
334
335
        return strncmp($a, $b, $star) === 0;
336
    }
337
338
    /**
339
     * {@inheritdoc}
340
     */
341 5
    public function getProtocolVersion(): string
342
    {
343 5
        return $this->request->getProtocolVersion();
344
    }
345
346
    /**
347
     * {@inheritdoc}
348
     */
349 23
    public function withProtocolVersion($version)
350
    {
351 23
        $clone = clone $this;
352 23
        $clone->request = $clone->request->withProtocolVersion($version);
353
354 4
        return $clone;
355
    }
356
357
    /**
358
     * {@inheritdoc}
359
     */
360 24
    public function getHeaders(): array
361
    {
362 24
        return $this->request->getHeaders();
363
    }
364
365
    /**
366
     * {@inheritdoc}
367
     */
368 3
    public function hasHeader($name): bool
369
    {
370 3
        return $this->request->hasHeader($name);
371
    }
372
373
    /**
374
     * {@inheritdoc}
375
     */
376 3
    public function getHeader($name): array
377
    {
378 3
        return $this->request->getHeader($name);
379
    }
380
381
    /**
382
     * {@inheritdoc}
383
     */
384 10
    public function getHeaderLine($name): string
385
    {
386 10
        return $this->request->getHeaderLine($name);
387
    }
388
389
    /**
390
     * {@inheritdoc}
391
     */
392 43
    public function withHeader($name, $value)
393
    {
394 43
        $clone = clone $this;
395 43
        $clone->request = $clone->request->withHeader($name, $value);
396
397 32
        return $clone;
398
    }
399
400
    /**
401
     * {@inheritdoc}
402
     */
403 25
    public function withAddedHeader($name, $value)
404
    {
405 25
        $clone = clone $this;
406 25
        $clone->request = $clone->request->withAddedHeader($name, $value);
407
408 14
        return $clone;
409
    }
410
411
    /**
412
     * {@inheritdoc}
413
     */
414 2
    public function withoutHeader($name)
415
    {
416 2
        $clone = clone $this;
417 2
        $clone->request = $clone->request->withoutHeader($name);
418
419 2
        return $clone;
420
    }
421
422
    /**
423
     * {@inheritdoc}
424
     */
425 2
    public function getBody(): StreamInterface
426
    {
427 2
        return $this->request->getBody();
428
    }
429
430
    /**
431
     * {@inheritdoc}
432
     */
433 1
    public function withBody(StreamInterface $body)
434
    {
435 1
        $clone = clone $this;
436 1
        $clone->request = $clone->request->withBody($body);
437
438 1
        return $clone;
439
    }
440
441
    /**
442
     * {@inheritdoc}
443
     */
444 4
    public function getMethod(): string
445
    {
446 4
        return $this->request->getMethod();
447
    }
448
449
    /**
450
     * {@inheritdoc}
451
     */
452 6
    public function withMethod($method)
453
    {
454 6
        $clone = clone $this;
455 6
        $clone->request = $clone->request->withMethod($method);
456
457 3
        return $clone;
458
    }
459
460
    /**
461
     * {@inheritdoc}
462
     */
463 2
    public function getUri(): UriInterface
464
    {
465 2
        return $this->request->getUri();
466
    }
467
468
    /**
469
     * {@inheritdoc}
470
     */
471 15
    public function withUri(UriInterface $uri, $preserveHost = false)
472
    {
473 15
        $clone = clone $this;
474 15
        $clone->request = $clone->request->withUri($uri, $preserveHost);
475
476 15
        return $clone;
477
    }
478
479
    /**
480
     * {@inheritdoc}
481
     */
482 13
    public function getRequestTarget(): string
483
    {
484 13
        return $this->request->getRequestTarget();
485
    }
486
487
    /**
488
     * {@inheritdoc}
489
     */
490 8
    public function withRequestTarget($requestTarget)
491
    {
492 8
        $clone = clone $this;
493 8
        $clone->request = $clone->request->withRequestTarget($requestTarget);
494
495 5
        return $clone;
496
    }
497
498
    /**
499
     * {@inheritdoc}
500
     */
501 2
    public function getServerParams(): array
502
    {
503 2
        return $this->request->getServerParams();
504
    }
505
506
    /**
507
     * {@inheritdoc}
508
     */
509 4
    public function getQueryParams(): array
510
    {
511 4
        return $this->request->getQueryParams();
512
    }
513
514
    /**
515
     * {@inheritdoc}
516
     */
517 3
    public function withQueryParams(array $query)
518
    {
519 3
        $clone = clone $this;
520 3
        $clone->request = $clone->request->withQueryParams($query);
521
522 3
        return $clone;
523
    }
524
525
    /**
526
     * {@inheritdoc}
527
     */
528 5
    public function getCookieParams(): array
529
    {
530 5
        return $this->request->getCookieParams();
531
    }
532
533
    /**
534
     * {@inheritdoc}
535
     */
536 3
    public function withCookieParams(array $cookies)
537
    {
538 3
        $clone = clone $this;
539 3
        $clone->request = $clone->request->withCookieParams($cookies);
540
541 3
        return $clone;
542
    }
543
544
    /**
545
     * {@inheritdoc}
546
     */
547 4
    public function getUploadedFiles(): array
548
    {
549 4
        return $this->request->getUploadedFiles();
550
    }
551
552
    /**
553
     * {@inheritdoc}
554
     */
555 4
    public function withUploadedFiles(array $uploadedFiles)
556
    {
557 4
        $clone = clone $this;
558 4
        $clone->request = $clone->request->withUploadedFiles($uploadedFiles);
559
560 3
        return $clone;
561
    }
562
563
    /**
564
     * {@inheritdoc}
565
     */
566 12
    public function getParsedBody()
567
    {
568 12
        return $this->request->getParsedBody();
569
    }
570
571
    /**
572
     * {@inheritdoc}
573
     */
574 12
    public function withParsedBody($data)
575
    {
576 12
        $clone = clone $this;
577 12
        $clone->request = $clone->request->withParsedBody($data);
578
579 7
        return $clone;
580
    }
581
582
    /**
583
     * {@inheritdoc}
584
     */
585 4
    public function getAttributes(): array
586
    {
587 4
        return $this->request->getAttributes();
588
    }
589
590
    /**
591
     * {@inheritdoc}
592
     */
593 6
    public function getAttribute($name, $default = null)
594
    {
595 6
        return $this->request->getAttribute($name, $default);
596
    }
597
598
    /**
599
     * {@inheritdoc}
600
     */
601 7
    public function withAttribute($name, $value)
602
    {
603 7
        $clone = clone $this;
604 7
        $clone->request = $clone->request->withAttribute($name, $value);
605
606 7
        return $clone;
607
    }
608
609
    /**
610
     * {@inheritdoc}
611
     */
612 2
    public function withoutAttribute($name)
613
    {
614 2
        $clone = clone $this;
615 2
        $clone->request = $clone->request->withoutAttribute($name);
616
617 2
        return $clone;
618
    }
619
}
620