Passed
Push — master ( e71162...5f8159 )
by Anatoly
42s queued 11s
created

Request::setMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
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-message/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-message
10
 */
11
12
namespace Sunrise\Http\Message;
13
14
/**
15
 * Import classes
16
 */
17
use Fig\Http\Message\RequestMethodInterface;
18
use Psr\Http\Message\RequestInterface;
19
use Psr\Http\Message\StreamInterface;
20
use Psr\Http\Message\UriInterface;
21
use Sunrise\Http\Header\HeaderInterface;
22
use Sunrise\Uri\UriFactory;
23
use InvalidArgumentException;
24
25
/**
26
 * Import functions
27
 */
28
use function is_string;
29
use function preg_match;
30
use function sprintf;
31
use function strncmp;
32
use function strtoupper;
33
34
/**
35
 * HTTP Request Message
36
 *
37
 * @link https://tools.ietf.org/html/rfc7230
38
 * @link https://www.php-fig.org/psr/psr-7/
39
 */
40
class Request extends Message implements RequestInterface, RequestMethodInterface
41
{
42
43
    /**
44
     * The request method (aka verb)
45
     *
46
     * @var string
47
     */
48
    protected $method = self::METHOD_GET;
49
50
    /**
51
     * The request target
52
     *
53
     * @var string|null
54
     */
55
    protected $requestTarget = null;
56
57
    /**
58
     * The request URI
59
     *
60
     * @var UriInterface|null
61
     */
62
    protected $uri = null;
63
64
    /**
65
     * Constructor of the class
66
     *
67
     * @param string|null $method
68
     * @param string|UriInterface|null $uri
69
     * @param array<string, string|array<string>>|null $headers
70
     * @param StreamInterface|null $body
71
     * @param string|null $requestTarget
72
     * @param string|null $protocolVersion
73
     *
74
     * @throws InvalidArgumentException
75
     */
76 63
    public function __construct(
77
        ?string $method = null,
78
        $uri = null,
79
        ?array $headers = null,
80
        ?StreamInterface $body = null,
81
        ?string $requestTarget = null,
82
        ?string $protocolVersion = null
83
    ) {
84 63
        parent::__construct(
85 63
            $headers,
86
            $body,
87
            $protocolVersion
88
        );
89
90 63
        if (isset($method)) {
91 3
            $this->setMethod($method);
92
        }
93
94 63
        if (isset($requestTarget)) {
95 1
            $this->setRequestTarget($requestTarget);
96
        }
97
98 63
        if (isset($uri)) {
99 3
            $this->setUri($uri);
100
        }
101 63
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 14
    public function getMethod() : string
107
    {
108 14
        return $this->method;
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     *
114
     * @throws InvalidArgumentException
115
     */
116 28
    public function withMethod($method) : RequestInterface
117
    {
118 28
        $clone = clone $this;
119 28
        $clone->setMethod($method);
120
121 12
        return $clone;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127 11
    public function getRequestTarget() : string
128
    {
129 11
        if (isset($this->requestTarget)) {
130 7
            return $this->requestTarget;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->requestTarget could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
131
        }
132
133 5
        $uri = $this->getUri();
134
135
        // https://tools.ietf.org/html/rfc7230#section-5.3.1
136
        // https://tools.ietf.org/html/rfc7230#section-2.7
137
        //
138
        // origin-form = absolute-path [ "?" query ]
139
        // absolute-path = 1*( "/" segment )
140 5
        if (0 <> strncmp($uri->getPath(), '/', 1)) {
141 3
            return '/';
142
        }
143
144 2
        $requestTarget = $uri->getPath();
145 2
        if ('' !== $uri->getQuery()) {
146 1
            $requestTarget .= '?' . $uri->getQuery();
147
        }
148
149 2
        return $requestTarget;
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     *
155
     * @throws InvalidArgumentException
156
     */
157 21
    public function withRequestTarget($requestTarget) : RequestInterface
158
    {
159 21
        $clone = clone $this;
160 21
        $clone->setRequestTarget($requestTarget);
161
162 6
        return $clone;
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 9
    public function getUri() : UriInterface
169
    {
170 9
        if (null === $this->uri) {
171 2
            $this->uri = (new UriFactory)->createUri();
172
        }
173
174 9
        return $this->uri;
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180 11
    public function withUri(UriInterface $uri, $preserveHost = false) : RequestInterface
181
    {
182 11
        $clone = clone $this;
183 11
        $clone->setUri($uri, $preserveHost);
184
185 11
        return $clone;
186
    }
187
188
    /**
189
     * Sets the given method to the request
190
     *
191
     * @param string $method
192
     *
193
     * @return void
194
     *
195
     * @throws InvalidArgumentException
196
     */
197 31
    protected function setMethod($method) : void
198
    {
199 31
        $this->validateMethod($method);
200
201 15
        $this->method = strtoupper($method);
202 15
    }
203
204
    /**
205
     * Sets the given request-target to the request
206
     *
207
     * @param string $requestTarget
208
     *
209
     * @return void
210
     *
211
     * @throws InvalidArgumentException
212
     */
213 22
    protected function setRequestTarget($requestTarget) : void
214
    {
215 22
        $this->validateRequestTarget($requestTarget);
216
217 7
        $this->requestTarget = $requestTarget;
218 7
    }
219
220
    /**
221
     * Sets the given URI to the request
222
     *
223
     * @param string|UriInterface $uri
224
     * @param bool $preserveHost
225
     *
226
     * @return void
227
     *
228
     * @throws InvalidArgumentException
229
     */
230 14
    protected function setUri($uri, $preserveHost = false) : void
231
    {
232 14
        if (! ($uri instanceof UriInterface)) {
233 2
            $uri = (new UriFactory)->createUri($uri);
234
        }
235
236 14
        $this->uri = $uri;
237
238 14
        if ('' === $uri->getHost() || ($preserveHost && $this->hasHeader('Host'))) {
239 8
            return;
240
        }
241
242 6
        $host = $uri->getHost();
243 6
        if (null !== $uri->getPort()) {
244 2
            $host .= ':' . $uri->getPort();
245
        }
246
247 6
        $this->addHeader('Host', $host);
248 6
    }
249
250
    /**
251
     * Validates the given method
252
     *
253
     * @param mixed $method
254
     *
255
     * @return void
256
     *
257
     * @throws InvalidArgumentException
258
     *
259
     * @link https://tools.ietf.org/html/rfc7230#section-3.1.1
260
     */
261 31
    protected function validateMethod($method) : void
262
    {
263 31
        if (!is_string($method)) {
264 9
            throw new InvalidArgumentException('HTTP method must be a string');
265
        }
266
267 22
        if (!preg_match(HeaderInterface::RFC7230_TOKEN, $method)) {
268 7
            throw new InvalidArgumentException(sprintf('The method "%s" is not valid', $method));
269
        }
270 15
    }
271
272
    /**
273
     * Validates the given request-target
274
     *
275
     * @param mixed $requestTarget
276
     *
277
     * @return void
278
     *
279
     * @throws InvalidArgumentException
280
     *
281
     * @link https://tools.ietf.org/html/rfc7230#section-5.3
282
     */
283 22
    protected function validateRequestTarget($requestTarget) : void
284
    {
285 22
        if (!is_string($requestTarget)) {
286 9
            throw new InvalidArgumentException('HTTP request-target must be a string');
287
        }
288
289 13
        if (!preg_match('/^[\x21-\x7E\x80-\xFF]+$/', $requestTarget)) {
290 6
            throw new InvalidArgumentException(sprintf('The request-target "%s" is not valid', $requestTarget));
291
        }
292 7
    }
293
}
294