RequestTrait::processMethod()   A
last analyzed

Complexity

Conditions 4
Paths 2

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 8.4387

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 25
ccs 8
cts 23
cp 0.3478
rs 9.7
c 0
b 0
f 0
cc 4
nc 2
nop 1
crap 8.4387
1
<?php
2
3
/**
4
 * This file is part of the Phalcon Framework.
5
 *
6
 * (c) Phalcon Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Phalcon\Http\Message\Traits;
15
16
use Phalcon\Collection;
17
use Phalcon\Http\Message\Exception\InvalidArgumentException;
18
use Phalcon\Http\Message\Uri;
19
use Psr\Http\Message\UriInterface;
20
21
use function is_string;
22
use function preg_match;
23
24
/**
25
 * Representation of an outgoing, client-side request.
26
 *
27
 * Per the HTTP specification, this interface includes properties for
28
 * each of the following:
29
 *
30
 * - Protocol version
31
 * - HTTP method
32
 * - URI
33
 * - Headers
34
 * - Message body
35
 *
36
 * During construction, implementations MUST attempt to set the Host header from
37
 * a provided URI if no Host header is provided.
38
 *
39
 * Requests are considered immutable; all methods that might change state MUST
40
 * be implemented such that they retain the internal state of the current
41
 * message and return an instance that contains the changed state.
42
 *
43
 * @property Collection   $headers
44
 * @property string       $method
45
 * @property null|string  $requestTarget
46
 * @property UriInterface $uri
47
 */
48
trait RequestTrait
49
{
50
    /**
51
     * @var Collection
52
     */
53
    private $headers;
54
55
    /**
56
     * Retrieves the HTTP method of the request.
57
     *
58
     * @var string
59
     */
60
    private $method = 'GET';
61
62
    /**
63
     * The request-target, if it has been provided or calculated.
64
     *
65
     * @var null|string
66
     */
67
    private $requestTarget;
68
69
    /**
70
     * Retrieves the URI instance.
71
     *
72
     * This method MUST return a UriInterface instance.
73
     *
74
     * @see http://tools.ietf.org/html/rfc3986#section-4.3
75
     *
76
     * @var UriInterface
77
     */
78
    private $uri;
79
80
    /**
81
     * Return the current method
82
     *
83
     * @return string
84
     */
85 6
    public function getMethod(): string
86
    {
87 6
        return $this->method;
88
    }
89
90
    /**
91
     * Retrieves the message's request target.
92
     *
93
     * Retrieves the message's request-target either as it will appear (for
94
     * clients), as it appeared at request (for servers), or as it was
95
     * specified for the instance (see withRequestTarget()).
96
     *
97
     * In most cases, this will be the origin-form of the composed URI, unless a
98
     * value was provided to the concrete implementation (see
99
     * withRequestTarget() below).
100
     *
101
     * @return string
102
     */
103 4
    public function getRequestTarget(): string
104
    {
105 4
        $requestTarget = $this->requestTarget;
106
107 4
        if (null === $requestTarget) {
108 4
            $requestTarget = $this->uri->getPath();
109
110 4
            if (true !== empty($this->uri->getQuery())) {
111 1
                $requestTarget .= '?' . $this->uri->getQuery();
112
            }
113
114 4
            if (empty($requestTarget)) {
115 3
                $requestTarget = '/';
116
            }
117
        }
118
119 4
        return $requestTarget;
120
    }
121
122
    /**
123
     * Returns the current Uri
124
     *
125
     * @return UriInterface
126
     */
127 10
    public function getUri(): UriInterface
128
    {
129 10
        return $this->uri;
130
    }
131
132
    /**
133
     * Return an instance with the provided HTTP method.
134
     *
135
     * While HTTP method names are typically all uppercase characters, HTTP
136
     * method names are case-sensitive and thus implementations SHOULD NOT
137
     * modify the given string.
138
     *
139
     * This method MUST be implemented in such a way as to retain the
140
     * immutability of the message, and MUST return an instance that has the
141
     * changed request method.
142
     *
143
     * @param string $method
144
     *
145
     * @return self
146
     * @throws InvalidArgumentException for invalid HTTP methods.
147
     *
148
     */
149 2
    public function withMethod($method): self
150
    {
151 2
        $this->processMethod($method);
152
153 2
        return $this->cloneInstance($method, 'method');
154
    }
155
156
    /**
157
     * Return an instance with the specific request-target.
158
     *
159
     * If the request needs a non-origin-form request-target — e.g., for
160
     * specifying an absolute-form, authority-form, or asterisk-form —
161
     * this method may be used to create an instance with the specified
162
     * request-target, verbatim.
163
     *
164
     * This method MUST be implemented in such a way as to retain the
165
     * immutability of the message, and MUST return an instance that has the
166
     * changed request target.
167
     *
168
     * @see http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
169
     *     request-target forms allowed in request messages)
170
     *
171
     * @param mixed $requestTarget
172
     *
173
     * @return self
174
     */
175 3
    public function withRequestTarget($requestTarget): self
176
    {
177 3
        if (preg_match('/\s/', $requestTarget)) {
178 1
            throw new InvalidArgumentException(
179 1
                'Invalid request target: cannot contain whitespace'
180
            );
181
        }
182
183 2
        return $this->cloneInstance($requestTarget, 'requestTarget');
184
    }
185
186
    /**
187
     * Returns an instance with the provided URI.
188
     *
189
     * This method MUST update the Host header of the returned request by
190
     * default if the URI contains a host component. If the URI does not
191
     * contain a host component, any pre-existing Host header MUST be carried
192
     * over to the returned request.
193
     *
194
     * You can opt-in to preserving the original state of the Host header by
195
     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
196
     * `true`, this method interacts with the Host header in the following
197
     * ways:
198
     *
199
     * - If the Host header is missing or empty, and the new URI contains
200
     *   a host component, this method MUST update the Host header in the
201
     *   returned request.
202
     * - If the Host header is missing or empty, and the new URI does not
203
     * contain a host component, this method MUST NOT update the Host header in
204
     * the returned request.
205
     * - If a Host header is present and non-empty, this method MUST NOT update
206
     *   the Host header in the returned request.
207
     *
208
     * This method MUST be implemented in such a way as to retain the
209
     * immutability of the message, and MUST return an instance that has the
210
     * new UriInterface instance.
211
     *
212
     * @see http://tools.ietf.org/html/rfc3986#section-4.3
213
     *
214
     * @param UriInterface $uri
215
     * @param bool         $preserveHost
216
     *
217
     * @return self
218
     */
219 2
    public function withUri(UriInterface $uri, $preserveHost = false): self
220
    {
221 2
        $preserveHost     = (bool) $preserveHost;
222 2
        $headers          = clone $this->headers;
223 2
        $newInstance      = clone $this;
224 2
        $newInstance->uri = $uri;
225
226 2
        if (true !== $preserveHost) {
227 2
            $headers = $this->checkHeaderHost($headers);
228
229 2
            $newInstance->headers = $headers;
230
        }
231
232 2
        return $newInstance;
233
    }
234
235
    abstract protected function checkHeaderHost(Collection $collection): Collection;
236
237
    abstract protected function cloneInstance($element, string $property);
238
239
    /**
240
     * Check the method
241
     *
242
     * @param string $method
243
     *
244
     * @return string
245
     */
246 99
    private function processMethod($method = ''): string
247
    {
248
        $methods = [
249 99
            'GET'     => 1,
250
            'CONNECT' => 1,
251
            'DELETE'  => 1,
252
            'HEAD'    => 1,
253
            'OPTIONS' => 1,
254
            'PATCH'   => 1,
255
            'POST'    => 1,
256
            'PUT'     => 1,
257
            'TRACE'   => 1,
258
        ];
259
260
        if (
261 99
            !(!empty($method) &&
262 99
            is_string($method) &&
263 99
            isset($methods[$method]))
264
        ) {
265 2
            throw new InvalidArgumentException(
266 2
                'Invalid or unsupported method ' . $method
267
            );
268
        }
269
270 97
        return $method;
271
    }
272
273
    /**
274
     * Sets a valid Uri
275
     *
276
     * @param mixed $uri
277
     *
278
     * @return UriInterface
279
     */
280 100
    private function processUri($uri): UriInterface
281
    {
282 100
        if ($uri instanceof UriInterface) {
283 15
            return $uri;
284
        }
285
286 85
        if (is_string($uri)) {
287 4
            return new Uri($uri);
288
        }
289
290 81
        if (null === $uri) {
291 80
            return new Uri();
292
        }
293
294 1
        throw new InvalidArgumentException(
295 1
            'Invalid uri passed as a parameter'
296
        );
297
    }
298
}
299