Issues (16)

src/Request.php (1 issue)

1
<?php
2
3
/**
4
 * Platine HTTP
5
 *
6
 * Platine HTTP Message is the implementation of PSR 7
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine HTTP
11
 * Copyright (c) 2019 Dion Chaika
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file Request.php
34
 *
35
 *  The Request class is the representation of an outgoing, client-side request.
36
 *
37
 *  @package    Platine\Http
38
 *  @author Platine Developers Team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Http;
49
50
use InvalidArgumentException;
51
52
/**
53
 * @class Request
54
 * @package Platine\Http
55
 */
56
class Request extends Message implements RequestInterface
57
{
58
    /**
59
     * The request target
60
     * @var string
61
     */
62
    protected string $requestTarget = '';
63
64
    /**
65
     * The request method
66
     * @var string
67
     */
68
    protected string $method = 'GET';
69
70
    /**
71
     * The request Uri
72
     * @var UriInterface|null
73
     */
74
    protected ?UriInterface $uri;
75
76
    /**
77
     * Create new request instance
78
     * @param string $method the HTTP request method
79
     * @param UriInterface|string|null $uri    the request Uri
80
     */
81
    public function __construct(
82
        string $method = 'GET',
83
        UriInterface|string|null $uri = null
84
    ) {
85
        $this->method = $this->filterMethod($method);
86
        if ($uri === null) {
87
            $uri = new Uri();
88
        } elseif (is_string($uri)) {
89
            $uri = new Uri($uri);
90
        }
91
        $this->uri = $uri;
92
93
        if ($this->protocolVersion === '1.1') {
94
            $this->headers['host'] = [$this->getHostHeader()];
95
        }
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function getRequestTarget(): string
102
    {
103
        if ($this->requestTarget !== '') {
104
            return $this->requestTarget;
105
        }
106
107
        if ($this->uri !== null) {
108
            $requestTarget = $this->uri->getPath();
109
            $query = $this->uri->getQuery();
110
            if ($query !== '') {
111
                $requestTarget .= '?' . $query;
112
            }
113
            if ($requestTarget !== '') {
114
                return $requestTarget;
115
            }
116
        }
117
118
        return '/';
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function withRequestTarget(string $requestTarget): self
125
    {
126
        $that = clone $this;
127
        $that->requestTarget = $requestTarget;
128
129
        return $that;
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function getMethod(): string
136
    {
137
        return $this->method;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function withMethod(string $method): self
144
    {
145
        $that = clone $this;
146
        $that->method = $method;
147
148
        return $that;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function getUri(): UriInterface
155
    {
156
        if ($this->uri === null) {
157
            $this->uri = new Uri();
158
        }
159
        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 Platine\Http\UriInterface. Consider adding an additional type-check to rule them out.
Loading history...
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function withUri(UriInterface $uri, bool $preserveHost = false): self
166
    {
167
        $that = clone $this;
168
        $that->uri = $uri;
169
170
        if ($preserveHost && $that->hasHeader('Host')) {
171
            return $that;
172
        }
173
174
        return $that->withHeader('Host', $that->getHostHeader());
175
    }
176
177
    /**
178
     * Return the string representation of the request
179
     * @return string
180
     */
181
    public function __toString(): string
182
    {
183
        $request = sprintf(
184
            "%s %s HTTP/%s\r\n",
185
            $this->getMethod(),
186
            $this->getRequestTarget(),
187
            $this->getProtocolVersion()
188
        );
189
        foreach (array_keys($this->headers) as $header) {
190
            if (strtolower($header) === 'cookie') {
191
                $cookie = implode('; ', $this->getHeader('Cookie'));
192
                $request .= sprintf(
193
                    "%s: %s\r\n",
194
                    $header,
195
                    $cookie
196
                );
197
            } else {
198
                $request .= sprintf(
199
                    "%s: %s\r\n",
200
                    $header,
201
                    $this->getHeaderLine($header)
202
                );
203
            }
204
        }
205
206
        return sprintf(
207
            "%s\r\n%s",
208
            $request,
209
            $this->getBody()
210
        );
211
    }
212
213
    /**
214
     * Return the "Host" header value
215
     *
216
     * @return string
217
     */
218
    protected function getHostHeader(): string
219
    {
220
        $host = '';
221
        if ($this->uri !== null) {
222
            $host = $this->uri->getHost();
223
        }
224
225
        if ($host !== '') {
226
            $port = null;
227
            if ($this->uri !== null) {
228
                $port = $this->uri->getPort();
229
            }
230
            if ($port !== null) {
231
                $host .= ':' . $port;
232
            }
233
        }
234
235
        return $host;
236
    }
237
238
    /**
239
     * Filter HTTP request method
240
     *
241
     * @param  string $method the method to filter
242
     * @return string
243
     */
244
    protected function filterMethod(string $method): string
245
    {
246
        if (!preg_match('/^[!#$%&\'*+\-.^_`|~0-9a-zA-Z]+$/', $method)) {
247
            throw new InvalidArgumentException(sprintf(
248
                'HTTP Method %s must be compliant with '
249
                                    . 'the "RFC 7230" standart',
250
                $method
251
            ));
252
        }
253
        return $method;
254
    }
255
}
256