HttpMethodParams::get()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 12
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Package\Provide\Router;
6
7
use BEAR\Package\Annotation\StdIn;
8
use BEAR\Package\Exception\InvalidRequestJsonException;
9
use BEAR\Package\Types;
10
use Override;
11
12
use function file_get_contents;
13
use function in_array;
14
use function json_decode;
15
use function json_last_error;
16
use function json_last_error_msg;
17
use function parse_str;
18
use function rtrim;
19
use function str_contains;
20
use function strtolower;
21
22
use const JSON_ERROR_NONE;
23
24
/** @psalm-import-type QueryParams from Types */
25
final class HttpMethodParams implements HttpMethodParamsInterface
26
{
27
    public const CONTENT_TYPE = 'CONTENT_TYPE';
28
    public const HTTP_CONTENT_TYPE = 'HTTP_CONTENT_TYPE';
29
    public const FORM_URL_ENCODE = 'application/x-www-form-urlencoded';
30
    public const APPLICATION_JSON = 'application/json';
31
32
    public function __construct(
33
        #[StdIn]
34
        private string $stdIn = 'php://input',
35
    ) {
36
    }
37
38
    /**
39
     * @psalm-api
40
     * @deprecated Use constructor injection
41
     */
42
    public function setStdIn(string $stdIn): void
43
    {
44
        $this->stdIn = $stdIn;
45
    }
46
47
    /**
48
     * {@inheritDoc}
49
     *
50
     * @psalm-taint-source input $server
51
     * @psalm-taint-source input $get
52
     * @psalm-taint-source input $post
53
     */
54
    #[Override]
55
    public function get(array $server, array $get, array $post)
56
    {
57
        // set the original value
58
        $method = strtolower($server['REQUEST_METHOD']);
59
60
        // early return on GET or HEAD
61
        if ($method === 'get' || $method === 'head') {
62
            return [$method, $get];
63
        }
64
65
        return $this->unsafeMethod($method, $server, $post);
66
    }
67
68
    /**
69
     * @param array{HTTP_X_HTTP_METHOD_OVERRIDE?: string, ...} $server
70
     * @param QueryParams                        $post
0 ignored issues
show
Bug introduced by
The type BEAR\Package\Provide\Router\QueryParams 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...
71
     *
72
     * @return array{0: string, 1: QueryParams}
73
     */
74
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
75
    private function unsafeMethod(string $method, array $server, array $post): array
76
    {
77
        /** @var array{_method?: string} $params */
78
        $params = $this->getParams($method, $server, $post);
79
80
        if ($method === 'post') {
81
            [$method, $params] = $this->getOverrideMethod($method, $server, $params);
82
        }
83
84
        return [$method, $params];
85
    }
86
87
    /**
88
     * @param array{HTTP_X_HTTP_METHOD_OVERRIDE?: string, ...} $server
89
     * @param array{_method?: string}                     $params
90
     *
91
     * @return array{0: string, 1: QueryParams}
92
     */
93
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
94
    private function getOverrideMethod(string $method, array $server, array $params): array
95
    {
96
        // must be a POST to do an override
97
98
        // look for override in post data
99
        if (isset($params['_method'])) {
100
            $method = strtolower($params['_method']);
101
            unset($params['_method']);
102
103
            return [$method, $params];
104
        }
105
106
        // look for override in headers
107
        if (isset($server['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
108
            $method = strtolower($server['HTTP_X_HTTP_METHOD_OVERRIDE']);
109
        }
110
111
        return [$method, $params];
112
    }
113
114
    /**
115
     * Return request parameters
116
     *
117
     * @param array{CONTENT_TYPE?: string, HTTP_CONTENT_TYPE?: string, ...} $server
118
     * @param QueryParams                                     $post
119
     *
120
     * @return QueryParams
121
     */
122
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
123
    private function getParams(string $method, array $server, array $post): array
124
    {
125
        // post data exists
126
        if ($method === 'post' && ! empty($post)) {
127
            return $post;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $post returns the type array which is incompatible with the documented return type BEAR\Package\Provide\Router\QueryParams.
Loading history...
128
        }
129
130
        if (in_array($method, ['post', 'put', 'patch', 'delete'], true)) {
131
            return $this->phpInput($server);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->phpInput($server) returns the type array which is incompatible with the documented return type BEAR\Package\Provide\Router\QueryParams.
Loading history...
132
        }
133
134
        return $post;
135
    }
136
137
    /**
138
     * Return request query by media-type
139
     *
140
     * @param array{CONTENT_TYPE?: string, HTTP_CONTENT_TYPE?: string, ...} $server $_SERVER
141
     *
142
     * @return QueryParams
143
     */
144
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
145
    private function phpInput(array $server): array
146
    {
147
        $contentType = $server[self::CONTENT_TYPE] ?? $server[self::HTTP_CONTENT_TYPE] ?? '';
148
        $isFormUrlEncoded = str_contains($contentType, self::FORM_URL_ENCODE);
149
        if ($isFormUrlEncoded) {
150
            parse_str(rtrim($this->getRawBody($server)), $put);
151
152
            /** @var QueryParams $put */
153
            return $put;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $put returns the type BEAR\Package\Provide\Router\QueryParams which is incompatible with the type-hinted return array.
Loading history...
154
        }
155
156
        $isApplicationJson = str_contains($contentType, self::APPLICATION_JSON);
157
        if (! $isApplicationJson) {
158
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type BEAR\Package\Provide\Router\QueryParams.
Loading history...
159
        }
160
161
        /** @var QueryParams $content */
162
        $content = json_decode($this->getRawBody($server), true);
163
        $error = json_last_error();
164
        if ($error !== JSON_ERROR_NONE) {
165
            throw new InvalidRequestJsonException(json_last_error_msg());
166
        }
167
168
        return $content;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $content returns the type BEAR\Package\Provide\Router\QueryParams which is incompatible with the type-hinted return array.
Loading history...
169
    }
170
171
    /** @param array{HTTP_RAW_POST_DATA?: string, ...} $server */
172
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
173
    private function getRawBody(array $server): string
174
    {
175
        return $server['HTTP_RAW_POST_DATA'] ?? rtrim((string) file_get_contents($this->stdIn));
176
    }
177
}
178