Request::withMethod()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 17
rs 9.9332
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of slick/http
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Slick\Http\Message;
13
14
use Psr\Http\Message\RequestInterface;
15
use Psr\Http\Message\StreamInterface;
16
use Psr\Http\Message\UriInterface;
17
use Slick\Http\Message\Exception\InvalidArgumentException;
18
use Slick\Http\Message\Stream\TextStream;
19
20
/**
21
 * Request
22
 *
23
 * @package Slick\Http\Message
24
*/
25
class Request extends Message implements RequestInterface
26
{
27
    /**
28
     * @var string
29
     */
30
    protected string $method;
31
32
    /**
33
     * @var null|string
34
     */
35
    protected ?string $target = null;
36
37
    /**
38
     * @var null|UriInterface
39
     */
40
    private ?UriInterface $uri = null;
41
42
    /**
43
     * Creates an HTTP Request message
44
     *
45
     * @param string $method
46
     * @param string|UriInterface|null $target
47
     * @param string $body
48
     * @param array<string, string> $headers
49
     */
50
    public function __construct(
51
        string $method,
52
        UriInterface|string|null $target = null,
53
        StreamInterface|string $body = '',
54
        array $headers = []
55
    ) {
56
        $body = \is_string($body) ? new TextStream($body) : $body;
57
        $body->rewind();
58
        parent::__construct($body->getContents());
59
        $this->method = $method;
60
61
        $this->target = $target instanceof UriInterface
62
            ? $this->setUri($target)
63
            : $target;
64
65
        foreach ($headers as $name => $header) {
66
            $this->headers[$name] = [$header];
67
        }
68
    }
69
70
    /**
71
     * Retrieves the message's request target.
72
     *
73
     * If no URI is available, and no request-target has been specifically
74
     * provided, this method MUST return the string "/".
75
     *
76
     * @return string
77
     */
78
    public function getRequestTarget(): string
79
    {
80
        if (! $this->target && ! $this->uri) {
81
            return '/';
82
        }
83
84
        return $this->target ?: $this->getTargetFromUri();
85
    }
86
87
    /**
88
     * Return an instance with the specific request-target.
89
     *
90
     * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
91
     *     request-target forms allowed in request messages)
92
     * @param mixed $requestTarget
93
     * @return static
94
     */
95
    public function withRequestTarget($requestTarget): RequestInterface
96
    {
97
        $message = clone $this;
98
        $message->target = $requestTarget;
99
        return $message;
100
    }
101
102
    /**
103
     * Retrieves the HTTP method of the request.
104
     *
105
     * @return string Returns the request method.
106
     */
107
    public function getMethod(): string
108
    {
109
        return $this->method;
110
    }
111
112
    /**
113
     * Return an instance with the provided HTTP method.
114
     *
115
     * @param string $method Case-sensitive method.
116
     * @return static
117
     * @throws InvalidArgumentException for invalid HTTP methods.
118
     */
119
    public function withMethod(string $method): RequestInterface
120
    {
121
        $method = strtoupper($method);
122
        $knownMethods = [
123
            'HEAD', 'OPTIONS', 'GET', 'POST', 'PUT',
124
            'DELETE', 'CONNECT', 'TRACE', 'PATCH', 'PURGE'
125
        ];
126
127
        if (! \in_array($method, $knownMethods)) {
128
            throw new InvalidArgumentException(
129
                "Invalid or unknown method name."
130
            );
131
        }
132
133
        $message = clone $this;
134
        $message->method = $method;
135
        return $message;
136
    }
137
138
    /**
139
     * Retrieves the URI instance.
140
     *
141
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
142
     * @return UriInterface Returns a UriInterface instance
143
     *     representing the URI of the request.
144
     */
145
    public function getUri(): UriInterface
146
    {
147
        return $this->uri;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->uri could return the type null which is incompatible with the type-hinted return Psr\Http\Message\UriInterface. Consider adding an additional type-check to rule them out.
Loading history...
148
    }
149
150
    /**
151
     * Returns an instance with the provided URI.
152
     *
153
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
154
     * @param UriInterface $uri New request URI to use.
155
     * @param bool $preserveHost Preserve the original state of the Host header.
156
     * @return static
157
     * @SuppressWarnings(PHPMD)
158
     */
159
    public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface
160
    {
161
        $message = clone $this;
162
        $message->setUri($uri, $preserveHost);
163
        return $message;
164
    }
165
166
    /**
167
     * Sets the request URI
168
     *
169
     * @param UriInterface $uri
170
     * @param bool $preserveHost
171
     * @SuppressWarnings(PHPMD)
172
     */
173
    protected function setUri(UriInterface $uri, bool $preserveHost = false): string
174
    {
175
        if (! $preserveHost && $uri->getHost() !== '') {
176
            $key = $this->headerKey('Host');
177
            $this->headers[$key] = [$uri->getHost()];
178
        }
179
180
        $this->uri = $uri;
181
        return $this->getTargetFromUri();
182
    }
183
184
    /**
185
     * Get the target from the uri
186
     *
187
     * @return string
188
     */
189
    private function getTargetFromUri(): string
190
    {
191
        $target  = "/{$this->uri->getPath()}";
0 ignored issues
show
Bug introduced by
The method getPath() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
        $target  = "/{$this->uri->/** @scrutinizer ignore-call */ getPath()}";

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
192
        $target .= $this->uri->getQuery() !== ''
193
            ? "?{$this->uri->getQuery()}"
194
            : '';
195
        $target .= $this->uri->getFragment() !== ''
196
            ? "#{$this->uri->getFragment()}"
197
            : '';
198
        return str_replace('//', '/', $target);
199
    }
200
}
201