Completed
Push — master ( 3f5fb7...f6575e )
by Bohuslav
02:50
created

RequestTrait::withUri()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 4
Bugs 2 Features 0
Metric Value
c 4
b 2
f 0
dl 0
loc 17
ccs 12
cts 12
cp 1
rs 9.2
cc 4
eloc 10
nc 4
nop 2
crap 4
1
<?php
2
namespace Kambo\HttpMessage;
3
4
// \Spl
5
use InvalidArgumentException;
6
7
// \Psr
8
use Psr\Http\Message\UriInterface;
9
10
// \HttpMessage
11
use Kambo\HttpMessage\Uri;
12
use Kambo\HttpMessage\Message;
13
14
/**
15
 * Shared methods for outgoing, client-side request and server request.
16
 *
17
 * @package Kambo\HttpMessage
18
 * @author  Bohuslav Simek <[email protected]>
19
 * @license MIT
20
 */
21
trait RequestTrait
22
{
23
    /**
24
     * Message request target
25
     *
26
     * @var string
27
     */
28
    private $requestTarget = null;
29
30
    /**
31
     * Method of incoming request - GET, POST, DELETE, PUT, PATCH, HEAD or OPTIONS.
32
     *
33
     * @var string
34
     */
35
    protected $requestMethod;
36
37
    /**
38
     * Uri of incoming request
39
     *
40
     * @var Psr\Http\Message\UriInterface;
41
     */
42
    protected $uri;
43
44
    /**
45
     * Checks if a header exists by the given case-insensitive name.
46
     *
47
     * @param string $name Case-insensitive header field name.
48
     *
49
     * @return bool Returns true if any header names match the given header name using
50
     *              a case-insensitive string comparison. Returns false if no matching
51
     *              header name is found in the message.
52
     */
53
    abstract public function hasHeader($name);
54
55
    /**
56
     * Retrieves a message header value by the given case-insensitive name.
57
     *
58
     * This method returns an array of all the header values of the given
59
     * case-insensitive header name.
60
     *
61
     * If the header does not appear in the message, this method return an
62
     * empty array.
63
     *
64
     * @param string $name Case-insensitive header field name.
65
     *
66
     * @return string[] An array of string values as provided for the given header.
67
     *                  If the header does not appear in the message, this method MUST
68
     *                  return an empty array.
69
     */
70
    abstract public function getHeader($name);
71
72
    /**
73
     * Provide message headers
74
     *
75
     * @return Headers Message headers
76
     */
77
    abstract public function provideHeaders();
78
79
    /**
80
     * Retrieves the message's request target.
81
     *
82
     * Retrieves the message's request-target either as it will appear (for
83
     * clients), as it appeared at request (for servers), or as it was
84
     * specified for the instance (see withRequestTarget()).
85
     *
86
     * In most cases, this will be the origin-form of the composed URI,
87
     * unless a value was provided to the concrete implementation (see
88
     * withRequestTarget() below).
89
     *
90
     * If no URI is available, and no request-target has been specifically
91
     * provided, this method return the string "/".
92
     *
93
     * @return string
94
     */
95 4
    public function getRequestTarget()
96
    {
97 4
        if ($this->requestTarget === null) {
98 4
            $target = '/';
99 4
            if ($this->uri->getPath() !== null) {
100 4
                $target = $this->uri->getPath();
101 4
                $target .= (!empty($this->uri->getQuery())) ? '?' . $this->uri->getQuery() : '';
102 4
            }
103
104 4
            $this->requestTarget = $target;
105 4
        }
106
107 4
        return $this->requestTarget;
108
    }
109
110
    /**
111
     * Return an instance with the specific request-target.
112
     *
113
     * If the request needs a non-origin-form request-target — e.g., for
114
     * specifying an absolute-form, authority-form, or asterisk-form —
115
     * this method may be used to create an instance with the specified
116
     * request-target, verbatim.
117
     *
118
     * This method retains the state of the current instance, and return
119
     * an instance that has the changed request target.
120
     *     
121
     * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various
122
     *       request-target forms allowed in request messages)
123
     *
124
     * @param mixed $requestTarget
125
     *
126
     * @return self
127
     */
128 2
    public function withRequestTarget($requestTarget)
129
    {
130 2
        $clone                = clone $this;
131 2
        $clone->requestTarget = $requestTarget;
132
133 2
        return $clone;
134
    }
135
136
    /**
137
     * Retrieves the HTTP method of the request.
138
     *
139
     * @return string Returns the request method.
140
     */
141 4
    public function getMethod()
142
    {
143 4
        return $this->requestMethod;
144
    }
145
146
    /**
147
     * Return an instance with the provided HTTP method.
148
     *
149
     * While HTTP method names are typically all uppercase characters, HTTP
150
     * method names are case-sensitive and thus implementations not
151
     * modify the given string.
152
     *
153
     * This method retains the state of the current instance, and return
154
     * an instance that changed request method.
155
     *
156
     * @param string $method Case-sensitive method.
157
     *
158
     * @return self
159
     *
160
     * @throws \InvalidArgumentException for invalid HTTP methods.
161
     */
162 4
    public function withMethod($method)
163
    {
164 4
        $this->validateMethod($method);
165
166 2
        $clone                = clone $this;
167 2
        $clone->requestMethod = $method;
168
169 2
        return $clone;
170
    }
171
172
    /**
173
     * Retrieves the URI instance.
174
     *
175
     * This method return a UriInterface instance.
176
     *
177
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
178
     *
179
     * @return UriInterface Returns a UriInterface instance representing the URI of the request.                
180
     */
181 6
    public function getUri()
182
    {
183 6
        return $this->uri;
184
    }
185
186
    /**
187
     * Returns an instance with the provided URI.
188
     *
189
     * This method 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 is 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 ways:
197
     *
198
     * - If the the Host header is missing or empty, and the new URI contains
199
     *   a host component, this method update the Host header in the returned
200
     *   request.
201
     * - If the Host header is missing or empty, and the new URI does not contain a
202
     *   host component, this method not update the Host header in the returned
203
     *   request.
204
     * - If a Host header is present and non-empty, this method not update
205
     *   the Host header in the returned request.
206
     *
207
     * This method retains the state of the current instance, and return
208
     * an instance that contains the new UriInterface instance.
209
     *     
210
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
211
     *
212
     * @param UriInterface $uri          New request URI to use.
213
     * @param bool         $preserveHost Preserve the original state of the Host header.
214
     *
215
     * @return self
216
     */
217 4
    public function withUri(UriInterface $uri, $preserveHost = false)
218
    {
219 4
        $clone      = clone $this;
220 4
        $clone->uri = $uri;
221
222 4
        if (!$preserveHost) {
223 2
            if ($uri->getHost() !== '') {
224 2
                $clone->provideHeaders()->set('Host', $uri->getHost());
225 2
            }
226 2
        } else {
227 2
            if ($this->shouldSetHost()) {
228 1
                $clone->provideHeaders()->set('Host', $uri->getHost());
229 1
            }
230
        }
231
232 4
        return $clone;
233
    }
234
235
    /**
236
     * Check if the host header should be set.
237
     *
238
     * @return bool true if should be
239
     */
240 10
    protected function shouldSetHost()
241
    {
242 10
        return $this->uri->getHost() !== '' && (!$this->hasHeader('Host') || $this->getHeader('Host') === null);
243
    }
244
245
    /**
246
     * Validate request method.
247
     *
248
     * @param string $method Case-sensitive request method.
249
     *
250
     * @return void
251
     *
252
     * @throws \InvalidArgumentException for invalid HTTP methods.
253
     */
254 10
    protected function validateMethod($method)
255
    {
256
        $valid = [
257 10
            'GET'     => true,
258 10
            'POST'    => true,
259 10
            'DELETE'  => true,
260 10
            'PUT'     => true,
261 10
            'PATCH'   => true,
262 10
            'HEAD'    => true,
263 10
            'OPTIONS' => true,
264 10
        ];
265
266 10
        if (!isset($valid[$method])) {
267 1
            throw new InvalidArgumentException(
268
                'Invalid method version. Must be one of: GET, POST, DELETE, PUT, PATCH, HEAD or OPTIONS'
269 1
            );
270
        }
271 10
    }
272
}
273