Passed
Push — master ( 43f5a9...f39e70 )
by Anatoly
57s queued 13s
created

JsonPayloadDecodingMiddleware   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 92
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 8
eloc 19
c 2
b 1
f 0
dl 0
loc 92
ccs 21
cts 21
cp 1
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getRequestMediaType() 0 15 3
A decodeRequestJsonPayload() 0 9 2
A isSupportedRequest() 0 3 1
A process() 0 9 2
1
<?php declare(strict_types=1);
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Fenric <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Fenric
8
 * @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-router
10
 */
11
12
namespace Sunrise\Http\Router\Middleware;
13
14
/**
15
 * Import classes
16
 */
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Http\Server\MiddlewareInterface;
20
use Psr\Http\Server\RequestHandlerInterface;
21
use Sunrise\Http\Router\Exception\UndecodablePayloadException;
22
23
/**
24
 * Import functions
25
 */
26
use function json_decode;
27
use function json_last_error;
28
use function json_last_error_msg;
29
use function rtrim;
30
use function strpos;
31
use function substr;
32
33
/**
34
 * Import constants
35
 */
36
use const JSON_BIGINT_AS_STRING;
37
use const JSON_ERROR_NONE;
38
39
/**
40
 * JsonPayloadDecodingMiddleware
41
 *
42
 * @since 2.15.0
43
 */
44
class JsonPayloadDecodingMiddleware implements MiddlewareInterface
45
{
46
47
    /**
48
     * JSON Media Type
49
     *
50
     * @var string
51
     *
52
     * @link https://datatracker.ietf.org/doc/html/rfc4627
53
     */
54
    private const JSON_MEDIA_TYPE = 'application/json';
55
56
    /**
57
     * JSON decoding options
58
     *
59
     * @var int
60
     *
61
     * @link https://www.php.net/manual/ru/json.constants.php
62
     */
63
    protected const JSON_DECODING_OPTIONS = JSON_BIGINT_AS_STRING;
64
65
    /**
66
     * {@inheritdoc}
67
     */
68 11
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
69
    {
70 11
        if (!$this->isSupportedRequest($request)) {
71 7
            return $handler->handle($request);
72
        }
73
74 4
        $parsedBody = $this->decodeRequestJsonPayload($request);
75
76 3
        return $handler->handle($request->withParsedBody($parsedBody));
77
    }
78
79
    /**
80
     * Checks if the given request is supported
81
     *
82
     * @param ServerRequestInterface $request
83
     *
84
     * @return bool
85
     */
86 11
    private function isSupportedRequest(ServerRequestInterface $request) : bool
87
    {
88 11
        return self::JSON_MEDIA_TYPE === $this->getRequestMediaType($request);
89
    }
90
91
    /**
92
     * Gets Media Type from the given request
93
     *
94
     * @param ServerRequestInterface $request
95
     *
96
     * @return string|null
97
     *
98
     * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1
99
     */
100 11
    private function getRequestMediaType(ServerRequestInterface $request) : ?string
101
    {
102 11
        if (!$request->hasHeader('Content-Type')) {
103 1
            return null;
104
        }
105
106
        // type "/" subtype *( OWS ";" OWS parameter )
107 10
        $mediaType = $request->getHeaderLine('Content-Type');
108
109 10
        $semicolonPosition = strpos($mediaType, ';');
110 10
        if (false === $semicolonPosition) {
111 4
            return $mediaType;
112
        }
113
114 6
        return rtrim(substr($mediaType, 0, $semicolonPosition));
115
    }
116
117
    /**
118
     * Tries to decode the given request's JSON payload
119
     *
120
     * @param ServerRequestInterface $request
121
     *
122
     * @return mixed
123
     *
124
     * @throws UndecodablePayloadException
125
     *         If the request's payload cannot be decoded.
126
     */
127 4
    private function decodeRequestJsonPayload(ServerRequestInterface $request)
128
    {
129 4
        json_decode('');
130 4
        $result = json_decode($request->getBody()->__toString(), true, 512, static::JSON_DECODING_OPTIONS);
131 4
        if (JSON_ERROR_NONE === json_last_error()) {
132 3
            return $result;
133
        }
134
135 1
        throw new UndecodablePayloadException(sprintf('Invalid Payload: %s', json_last_error_msg()));
136
    }
137
}
138