HttpMethodParams   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 19
eloc 40
c 3
b 0
f 0
dl 0
loc 147
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A setStdIn() 0 3 1
A unsafeMethod() 0 10 2
A phpInput() 0 24 4
A getParams() 0 12 4
A getOverrideMethod() 0 18 3
A __construct() 0 4 1
A getRawBody() 0 3 1
A get() 0 12 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
    #[Override]
51
    public function get(array $server, array $get, array $post)
52
    {
53
        // set the original value
54
        $method = strtolower($server['REQUEST_METHOD']);
55
56
        // early return on GET or HEAD
57
        if ($method === 'get' || $method === 'head') {
58
            return [$method, $get];
59
        }
60
61
        return $this->unsafeMethod($method, $server, $post);
62
    }
63
64
    /**
65
     * @param array{HTTP_X_HTTP_METHOD_OVERRIDE?: string, ...} $server
66
     * @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...
67
     *
68
     * @return array{0: string, 1: QueryParams}
69
     */
70
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
71
    private function unsafeMethod(string $method, array $server, array $post): array
72
    {
73
        /** @var array{_method?: string} $params */
74
        $params = $this->getParams($method, $server, $post);
75
76
        if ($method === 'post') {
77
            [$method, $params] = $this->getOverrideMethod($method, $server, $params);
78
        }
79
80
        return [$method, $params];
81
    }
82
83
    /**
84
     * @param array{HTTP_X_HTTP_METHOD_OVERRIDE?: string, ...} $server
85
     * @param array{_method?: string}                     $params
86
     *
87
     * @return array{0: string, 1: QueryParams}
88
     */
89
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
90
    private function getOverrideMethod(string $method, array $server, array $params): array
91
    {
92
        // must be a POST to do an override
93
94
        // look for override in post data
95
        if (isset($params['_method'])) {
96
            $method = strtolower($params['_method']);
97
            unset($params['_method']);
98
99
            return [$method, $params];
100
        }
101
102
        // look for override in headers
103
        if (isset($server['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
104
            $method = strtolower($server['HTTP_X_HTTP_METHOD_OVERRIDE']);
105
        }
106
107
        return [$method, $params];
108
    }
109
110
    /**
111
     * Return request parameters
112
     *
113
     * @param array{CONTENT_TYPE?: string, HTTP_CONTENT_TYPE?: string, ...} $server
114
     * @param QueryParams                                     $post
115
     *
116
     * @return QueryParams
117
     */
118
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
119
    private function getParams(string $method, array $server, array $post): array
120
    {
121
        // post data exists
122
        if ($method === 'post' && ! empty($post)) {
123
            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...
124
        }
125
126
        if (in_array($method, ['post', 'put', 'patch', 'delete'], true)) {
127
            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...
128
        }
129
130
        return $post;
131
    }
132
133
    /**
134
     * Return request query by media-type
135
     *
136
     * @param array{CONTENT_TYPE?: string, HTTP_CONTENT_TYPE?: string, ...} $server $_SERVER
137
     *
138
     * @return QueryParams
139
     */
140
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
141
    private function phpInput(array $server): array
142
    {
143
        $contentType = $server[self::CONTENT_TYPE] ?? $server[self::HTTP_CONTENT_TYPE] ?? '';
144
        $isFormUrlEncoded = str_contains($contentType, self::FORM_URL_ENCODE);
145
        if ($isFormUrlEncoded) {
146
            parse_str(rtrim($this->getRawBody($server)), $put);
147
148
            /** @var QueryParams $put */
149
            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...
150
        }
151
152
        $isApplicationJson = str_contains($contentType, self::APPLICATION_JSON);
153
        if (! $isApplicationJson) {
154
            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...
155
        }
156
157
        /** @var QueryParams $content */
158
        $content = json_decode($this->getRawBody($server), true);
159
        $error = json_last_error();
160
        if ($error !== JSON_ERROR_NONE) {
161
            throw new InvalidRequestJsonException(json_last_error_msg());
162
        }
163
164
        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...
165
    }
166
167
    /** @param array{HTTP_RAW_POST_DATA?: string, ...} $server */
168
    // phpcs:ignore Squiz.Commenting.FunctionComment.MissingParamName
169
    private function getRawBody(array $server): string
170
    {
171
        return $server['HTTP_RAW_POST_DATA'] ?? rtrim((string) file_get_contents($this->stdIn));
172
    }
173
}
174