Issues (21)

src/Psr7/Request.php (2 issues)

Severity
1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\Psr7;
14
15
use Psr\Http\Message\RequestInterface;
16
use Psr\Http\Message\StreamInterface;
17
use Psr\Http\Message\UriInterface;
18
use Shieldon\Psr7\Message;
19
use Shieldon\Psr7\Uri;
20
use InvalidArgumentException;
21
22
use function in_array;
23
use function is_string;
24
use function preg_match;
25
use function sprintf;
26
use function strtoupper;
27
28
/*
29
 * Representation of an outgoing, client-side request.
30
 */
31
class Request extends Message implements RequestInterface
32
{
33
    /**
34
     * The HTTP method of the outgoing request.
35
     *
36
     * @var string
37
     */
38
    protected $method;
39
40
    /**
41
     * The target URL of the outgoing request.
42
     *
43
     * @var string
44
     */
45
    protected $requestTarget;
46
47
    /**
48
     * A UriInterface object.
49
     *
50
     * @var UriInterface
51
     */
52
    protected $uri;
53
54
55
    /**
56
     * Valid HTTP methods.
57
     *
58
     * @ref http://tools.ietf.org/html/rfc7231
59
     *
60
     * @var array
61
     */
62
    protected $validMethods = [
63
64
        // The HEAD method asks for a response identical to that of a GET
65
        // request, but without the response body.
66
        'HEAD',
67
68
        // The GET method requests a representation of the specified 
69
        // resource. Requests using GET should only retrieve data.
70
        'GET',
71
72
        // The POST method is used to submit an entity to the specified 
73
        // resource, often causing a change in state or side effects on the
74
        // server.
75
        'POST', 
76
        
77
        // The PUT method replaces all current representations of the target
78
        // resource with the request payload.
79
        'PUT', 
80
81
        // The DELETE method deletes the specified resource.
82
        'DELETE',
83
84
        // The PATCH method is used to apply partial modifications to a 
85
        // resource.
86
        'PATCH',
87
88
        // The CONNECT method establishes a tunnel to the server identified
89
        // by the target resource.
90
        'CONNECT',
91
92
        //The OPTIONS method is used to describe the communication options
93
        // for the target resource.
94
        'OPTIONS',
95
96
        // The TRACE method performs a message loop-back test along the
97
        // path to the target resource.
98
        'TRACE',
99
    ];
100
101
    /**
102
     * Request constructor.
103
     *
104
     * @param string                 $method  Request HTTP method
105
     * @param string|UriInterface    $uri     Request URI
106
     * @param string|StreamInterface $body    Request body - see setBody()
107
     * @param array                  $headers Request headers
108
     * @param string                 $version Request protocol version
109
     */
110 23
    public function __construct(
111
        string $method  = 'GET',
112
        $uri            = ''   ,
113
        $body           = ''   ,
114
        array  $headers = []   ,
115
        string $version = '1.1'
116
    ) {
117 23
        $this->method = $method;
118
119 23
        $this->assertMethod($method);
120
  
121 22
        $this->assertProtocolVersion($version);
122 21
        $this->protocolVersion = $version;
123
124 21
        if ($uri instanceof UriInterface) {
125 7
            $this->uri = $uri;
126
127 15
        } elseif (is_string($uri)) {
0 ignored issues
show
The condition is_string($uri) is always true.
Loading history...
128 14
            $this->uri = new Uri($uri);
129
130
        } else {
131 1
            throw new InvalidArgumentException(
132 1
                sprintf(
133 1
                    'URI should be a string or an instance of UriInterface, but "%s" provided.',
134 1
                    gettype($uri)
135 1
                )
136 1
            );
137
        }
138
139 20
        $this->setBody($body);
140 20
        $this->setHeaders($headers);
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 2
    public function getRequestTarget(): string
147
    {
148 2
        if ($this->requestTarget) {
149 1
            return $this->requestTarget;
150
        }
151
152 2
        $path = $this->uri->getPath();
153 2
        $query = $this->uri->getQuery();
154
155 2
        if (empty($path)) {
156 2
            $path = '/';
157
        }
158
159 2
        if (!empty($query)) {
160 1
            $path .= '?' . $query;
161
        }
162
163 2
        return $path;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169 3
    public function withRequestTarget($requestTarget): RequestInterface
170
    {
171 3
        if (!is_string($requestTarget)) {
172 1
            throw new InvalidArgumentException(
173 1
                'A request target must be a string.'
174 1
            );
175
        }
176
177 2
        if (preg_match('/\s/', $requestTarget)) {
178 1
            throw new InvalidArgumentException(
179 1
                'A request target cannot contain any whitespace.'
180 1
            );
181
        }
182
183 1
        $clone = clone $this;
184 1
        $clone->requestTarget = $requestTarget;
185
186 1
        return $clone;
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192 13
    public function getMethod(): string
193
    {
194 13
        return $this->method;
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200 2
    public function withMethod($method): RequestInterface
201
    {
202 2
        $this->assertMethod($method);
203
204 1
        $clone = clone $this;
205 1
        $clone->method = $method;
206
207 1
        return $clone;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213 3
    public function getUri(): UriInterface
214
    {
215 3
        return $this->uri;
216
    }
217
218
    /**
219
     * {@inheritdoc}
220
     */
221 1
    public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
222
    {
223 1
        $host = $uri->getHost();
224
225 1
        $clone = clone $this;
226 1
        $clone->uri = $uri;
227
228
        if (
229
            // This method MUST update the Host header of the returned request by
230
            // default if the URI contains a host component.
231 1
            (!$preserveHost && $host !== '') ||
232
233
            // When `$preserveHost` is set to `true`.
234
            // If the Host header is missing or empty, and the new URI contains
235
            // a host component, this method MUST update the Host header in the returned
236
            // request.
237 1
            ($preserveHost && !$this->hasHeader('Host') && $host !== '')
238
        ) {
239 1
            $headers = $this->getHeaders();
240 1
            $headers['host'] = $host;
241 1
            $clone->setHeaders($headers);
242
        }
243
244 1
        return $clone;
245
    }
246
247
    /*
248
    |--------------------------------------------------------------------------
249
    | Non PSR-7 Methods.
250
    |--------------------------------------------------------------------------
251
    */
252
253
    /**
254
     * Check out whether a method defined in RFC 7231 request methods.
255
     *
256
     * @param string $method Http methods
257
     * 
258
     * @return void
259
     * 
260
     * @throws InvalidArgumentException
261
     */
262 23
    protected function assertMethod($method): void
263
    {
264 23
        if (!is_string($method)) {
0 ignored issues
show
The condition is_string($method) is always true.
Loading history...
265 1
            throw new InvalidArgumentException(
266 1
                sprintf(
267 1
                    'HTTP method must be a string.',
268 1
                    $method
269 1
                )
270 1
            );
271
        }
272
273 23
        if (!in_array(strtoupper($this->method), $this->validMethods)) {
274 1
            throw new InvalidArgumentException(
275 1
                sprintf(
276 1
                    'Unsupported HTTP method. It must be compatible with RFC-7231 request method, but "%s" provided.',
277 1
                    $method
278 1
                )
279 1
            );
280
        }
281
    }
282
}
283