Completed
Push — master ( c8db4c...3f5fb7 )
by Bohuslav
03:07
created

RequestTrait::getUri()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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
     * Retrieves the message's request target.
46
     *
47
     * Retrieves the message's request-target either as it will appear (for
48
     * clients), as it appeared at request (for servers), or as it was
49
     * specified for the instance (see withRequestTarget()).
50
     *
51
     * In most cases, this will be the origin-form of the composed URI,
52
     * unless a value was provided to the concrete implementation (see
53
     * withRequestTarget() below).
54
     *
55
     * If no URI is available, and no request-target has been specifically
56
     * provided, this method return the string "/".
57
     *
58
     * @return string
59
     */
60 4
    public function getRequestTarget()
61
    {
62 4
        if ($this->requestTarget === null) {
63 4
            $target = '/';
64 4
            if ($this->uri->getPath() !== null) {
65 4
                $target = $this->uri->getPath();
66 4
                $target .= (!empty($this->uri->getQuery())) ? '?' . $this->uri->getQuery() : '';
67 4
            }
68
69 4
            $this->requestTarget = $target;
70 4
        }
71
72 4
        return $this->requestTarget;
73
    }
74
75
    /**
76
     * Return an instance with the specific request-target.
77
     *
78
     * If the request needs a non-origin-form request-target — e.g., for
79
     * specifying an absolute-form, authority-form, or asterisk-form —
80
     * this method may be used to create an instance with the specified
81
     * request-target, verbatim.
82
     *
83
     * This method retains the state of the current instance, and return
84
     * an instance that has the changed request target.
85
     *     
86
     * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various
87
     *       request-target forms allowed in request messages)
88
     *
89
     * @param mixed $requestTarget
90
     *
91
     * @return self
92
     */
93 2
    public function withRequestTarget($requestTarget)
94
    {
95 2
        $clone                = clone $this;
96 2
        $clone->requestTarget = $requestTarget;
97
98 2
        return $clone;
99
    }
100
101
    /**
102
     * Retrieves the HTTP method of the request.
103
     *
104
     * @return string Returns the request method.
105
     */
106 4
    public function getMethod()
107
    {
108 4
        return $this->requestMethod;
109
    }
110
111
    /**
112
     * Return an instance with the provided HTTP method.
113
     *
114
     * While HTTP method names are typically all uppercase characters, HTTP
115
     * method names are case-sensitive and thus implementations not
116
     * modify the given string.
117
     *
118
     * This method retains the state of the current instance, and return
119
     * an instance that changed request method.
120
     *
121
     * @param string $method Case-sensitive method.
122
     *
123
     * @return self
124
     *
125
     * @throws \InvalidArgumentException for invalid HTTP methods.
126
     */
127 4
    public function withMethod($method)
128
    {
129 4
        $this->validateMethod($method);
130 2
        $clone = clone $this;
131 2
        $clone->requestMethod = $method;
132
133 2
        return $clone;
134
    }
135
136
    /**
137
     * Retrieves the URI instance.
138
     *
139
     * This method return a UriInterface instance.
140
     *
141
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
142
     *
143
     * @return UriInterface Returns a UriInterface instance representing the URI of the request.                
144
     */
145 6
    public function getUri()
146
    {
147 6
        return $this->uri;
148
    }
149
150
    /**
151
     * Returns an instance with the provided URI.
152
     *
153
     * This method update the Host header of the returned request by
154
     * default if the URI contains a host component. If the URI does not
155
     * contain a host component, any pre-existing Host header is carried
156
     * over to the returned request.
157
     *
158
     * You can opt-in to preserving the original state of the Host header by
159
     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
160
     * `true`, this method interacts with the Host header in the following ways:
161
     *
162
     * - If the the Host header is missing or empty, and the new URI contains
163
     *   a host component, this method update the Host header in the returned
164
     *   request.
165
     * - If the Host header is missing or empty, and the new URI does not contain a
166
     *   host component, this method not update the Host header in the returned
167
     *   request.
168
     * - If a Host header is present and non-empty, this method not update
169
     *   the Host header in the returned request.
170
     *
171
     * This method retains the state of the current instance, and return
172
     * an instance that contains the new UriInterface instance.
173
     *     
174
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
175
     *
176
     * @param UriInterface $uri          New request URI to use.
177
     * @param bool         $preserveHost Preserve the original state of the Host header.
178
     *
179
     * @return self
180
     */
181 4
    public function withUri(UriInterface $uri, $preserveHost = false)
182
    {
183 4
        $clone      = clone $this;
184 4
        $clone->uri = $uri;
185
186 4
        if (!$preserveHost) {
187 2
            if ($uri->getHost() !== '') {
188 2
                $clone->headers->set('Host', $uri->getHost());
0 ignored issues
show
Bug introduced by
The property headers does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
189 2
            }
190 2
        } else {
191 2
            if ($this->uri->getHost() !== '' && (!$this->hasHeader('Host') || $this->getHeader('Host') === null)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
192 1
                $clone->headers->set('Host', $uri->getHost());
193 1
            }
194
        }
195
196 4
        return $clone;
197
    }
198
199
    /**
200
     * Checks if a header exists by the given case-insensitive name.
201
     *
202
     * @param string $name Case-insensitive header field name.
203
     *
204
     * @return bool Returns true if any header names match the given header name using
205
     *              a case-insensitive string comparison. Returns false if no matching
206
     *              header name is found in the message.
207
     */
208
    abstract public function hasHeader($name);
209
210
    /**
211
     * Retrieves a message header value by the given case-insensitive name.
212
     *
213
     * This method returns an array of all the header values of the given
214
     * case-insensitive header name.
215
     *
216
     * If the header does not appear in the message, this method return an
217
     * empty array.
218
     *
219
     * @param string $name Case-insensitive header field name.
220
     *
221
     * @return string[] An array of string values as provided for the given header.
222
     *                  If the header does not appear in the message, this method MUST
223
     *                  return an empty array.
224
     */
225
    abstract public function getHeader($name);
226
227
    /**
228
     * Validate request method.
229
     *
230
     * @param string $method Case-sensitive request method.
231
     *
232
     * @return void
233
     *
234
     * @throws \InvalidArgumentException for invalid HTTP methods.
235
     */
236 10
    protected function validateMethod($method)
237
    {
238
        $valid = [
239 10
            'GET'     => true,
240 10
            'POST'    => true,
241 10
            'DELETE'  => true,
242 10
            'PUT'     => true,
243 10
            'PATCH'   => true,
244 10
            'HEAD'    => true,
245 10
            'OPTIONS' => true,
246 10
        ];
247
248 10
        if (!isset($valid[$method])) {
249 1
            throw new InvalidArgumentException(
250
                'Invalid method version. Must be one of: GET, POST, DELTE, PUT, PATCH, HEAD or OPTIONS'
251 1
            );
252
        }
253 10
    }
254
}
255