Passed
Pull Request — master (#98)
by Anatoly
02:16
created

getRequestMediaType()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 15
ccs 8
cts 8
cp 1
rs 10
cc 3
nc 3
nop 1
crap 3
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
use const JSON_OBJECT_AS_ARRAY;
39
40
/**
41
 * JsonPayloadDecodingMiddleware
42
 *
43
 * @since 2.15.0
44
 */
45
class JsonPayloadDecodingMiddleware implements MiddlewareInterface
46
{
47
48
    /**
49
     * JSON Media Type
50
     *
51
     * @var string
52
     *
53
     * @link https://datatracker.ietf.org/doc/html/rfc4627
54
     */
55
    private const JSON_MEDIA_TYPE = 'application/json';
56
57
    /**
58
     * JSON decoding options
59
     *
60
     * @var int
61
     *
62
     * @link https://www.php.net/manual/ru/json.constants.php
63
     */
64
    protected const JSON_DECODING_OPTIONS = JSON_BIGINT_AS_STRING;
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 11
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
70
    {
71 11
        if (!$this->isSupportedRequest($request)) {
72 7
            return $handler->handle($request);
73
        }
74
75 4
        $parsedBody = $this->decodeRequestJsonPayload($request);
76
77 3
        return $handler->handle($request->withParsedBody($parsedBody));
78
    }
79
80
    /**
81
     * Checks if the given request is supported
82
     *
83
     * @param ServerRequestInterface $request
84
     *
85
     * @return bool
86
     */
87 11
    private function isSupportedRequest(ServerRequestInterface $request) : bool
88
    {
89 11
        return self::JSON_MEDIA_TYPE === $this->getRequestMediaType($request);
90
    }
91
92
    /**
93
     * Gets Media Type from the given request
94
     *
95
     * @param ServerRequestInterface $request
96
     *
97
     * @return string|null
98
     *
99
     * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1
100
     */
101 11
    private function getRequestMediaType(ServerRequestInterface $request) : ?string
102
    {
103 11
        if (!$request->hasHeader('Content-Type')) {
104 1
            return null;
105
        }
106
107
        // type "/" subtype *( OWS ";" OWS parameter )
108 10
        $mediaType = $request->getHeaderLine('Content-Type');
109
110 10
        $semicolonPosition = strpos($mediaType, ';');
111 10
        if (false === $semicolonPosition) {
112 4
            return $mediaType;
113
        }
114
115 6
        return rtrim(substr($mediaType, 0, $semicolonPosition));
116
    }
117
118
    /**
119
     * Tries to decode the given request's JSON payload
120
     *
121
     * @param ServerRequestInterface $request
122
     *
123
     * @return mixed
124
     *
125
     * @throws UndecodablePayloadException
126
     *         If the request's payload cannot be decoded.
127
     */
128 4
    private function decodeRequestJsonPayload(ServerRequestInterface $request)
129
    {
130 4
        json_decode('');
131 4
        $result = json_decode($request->getBody()->__toString(), true, 512, static::JSON_DECODING_OPTIONS);
132 4
        if (JSON_ERROR_NONE === json_last_error()) {
133 3
            return $result;
134
        }
135
136 1
        throw new UndecodablePayloadException(sprintf('Invalid Payload: %s', json_last_error_msg()));
137
    }
138
}
139