d8062e9f2c11515bbda7486235b61483dd22b18d
[yaffs-website] / vendor / zendframework / zend-diactoros / src / RequestTrait.php
1 <?php
2 /**
3  * @see       https://github.com/zendframework/zend-diactoros for the canonical source repository
4  * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
5  * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
6  */
7
8 namespace Zend\Diactoros;
9
10 use InvalidArgumentException;
11 use Psr\Http\Message\StreamInterface;
12 use Psr\Http\Message\UriInterface;
13
14 /**
15  * Trait with common request behaviors.
16  *
17  * Server and client-side requests differ slightly in how the Host header is
18  * handled; on client-side, it should be calculated on-the-fly from the
19  * composed URI (if present), while on server-side, it will be calculated from
20  * the environment. As such, this trait exists to provide the common code
21  * between both client-side and server-side requests, and each can then
22  * use the headers functionality required by their implementations.
23  */
24 trait RequestTrait
25 {
26     use MessageTrait;
27
28     /**
29      * @var string
30      */
31     private $method = '';
32
33     /**
34      * The request-target, if it has been provided or calculated.
35      *
36      * @var null|string
37      */
38     private $requestTarget;
39
40     /**
41      * @var UriInterface
42      */
43     private $uri;
44
45     /**
46      * Initialize request state.
47      *
48      * Used by constructors.
49      *
50      * @param null|string|UriInterface $uri URI for the request, if any.
51      * @param null|string $method HTTP method for the request, if any.
52      * @param string|resource|StreamInterface $body Message body, if any.
53      * @param array $headers Headers for the message, if any.
54      * @throws InvalidArgumentException for any invalid value.
55      */
56     private function initialize($uri = null, $method = null, $body = 'php://memory', array $headers = [])
57     {
58         $this->validateMethod($method);
59
60         $this->method = $method ?: '';
61         $this->uri    = $this->createUri($uri);
62         $this->stream = $this->getStream($body, 'wb+');
63
64         $this->setHeaders($headers);
65
66         // per PSR-7: attempt to set the Host header from a provided URI if no
67         // Host header is provided
68         if (! $this->hasHeader('Host') && $this->uri->getHost()) {
69             $this->headerNames['host'] = 'Host';
70             $this->headers['Host'] = [$this->getHostFromUri()];
71         }
72     }
73
74     /**
75      * Create and return a URI instance.
76      *
77      * If `$uri` is a already a `UriInterface` instance, returns it.
78      *
79      * If `$uri` is a string, passes it to the `Uri` constructor to return an
80      * instance.
81      *
82      * If `$uri is null, creates and returns an empty `Uri` instance.
83      *
84      * Otherwise, it raises an exception.
85      *
86      * @param null|string|UriInterface $uri
87      * @return UriInterface
88      * @throws InvalidArgumentException
89      */
90     private function createUri($uri)
91     {
92         if ($uri instanceof UriInterface) {
93             return $uri;
94         }
95         if (is_string($uri)) {
96             return new Uri($uri);
97         }
98         if ($uri === null) {
99             return new Uri();
100         }
101         throw new InvalidArgumentException(
102             'Invalid URI provided; must be null, a string, or a Psr\Http\Message\UriInterface instance'
103         );
104     }
105
106     /**
107      * Retrieves the message's request target.
108      *
109      * Retrieves the message's request-target either as it will appear (for
110      * clients), as it appeared at request (for servers), or as it was
111      * specified for the instance (see withRequestTarget()).
112      *
113      * In most cases, this will be the origin-form of the composed URI,
114      * unless a value was provided to the concrete implementation (see
115      * withRequestTarget() below).
116      *
117      * If no URI is available, and no request-target has been specifically
118      * provided, this method MUST return the string "/".
119      *
120      * @return string
121      */
122     public function getRequestTarget()
123     {
124         if (null !== $this->requestTarget) {
125             return $this->requestTarget;
126         }
127
128         $target = $this->uri->getPath();
129         if ($this->uri->getQuery()) {
130             $target .= '?' . $this->uri->getQuery();
131         }
132
133         if (empty($target)) {
134             $target = '/';
135         }
136
137         return $target;
138     }
139
140     /**
141      * Create a new instance with a specific request-target.
142      *
143      * If the request needs a non-origin-form request-target — e.g., for
144      * specifying an absolute-form, authority-form, or asterisk-form —
145      * this method may be used to create an instance with the specified
146      * request-target, verbatim.
147      *
148      * This method MUST be implemented in such a way as to retain the
149      * immutability of the message, and MUST return a new instance that has the
150      * changed request target.
151      *
152      * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various
153      *     request-target forms allowed in request messages)
154      * @param mixed $requestTarget
155      * @return static
156      * @throws InvalidArgumentException if the request target is invalid.
157      */
158     public function withRequestTarget($requestTarget)
159     {
160         if (preg_match('#\s#', $requestTarget)) {
161             throw new InvalidArgumentException(
162                 'Invalid request target provided; cannot contain whitespace'
163             );
164         }
165
166         $new = clone $this;
167         $new->requestTarget = $requestTarget;
168         return $new;
169     }
170
171     /**
172      * Retrieves the HTTP method of the request.
173      *
174      * @return string Returns the request method.
175      */
176     public function getMethod()
177     {
178         return $this->method;
179     }
180
181     /**
182      * Return an instance with the provided HTTP method.
183      *
184      * While HTTP method names are typically all uppercase characters, HTTP
185      * method names are case-sensitive and thus implementations SHOULD NOT
186      * modify the given string.
187      *
188      * This method MUST be implemented in such a way as to retain the
189      * immutability of the message, and MUST return an instance that has the
190      * changed request method.
191      *
192      * @param string $method Case-insensitive method.
193      * @return static
194      * @throws InvalidArgumentException for invalid HTTP methods.
195      */
196     public function withMethod($method)
197     {
198         $this->validateMethod($method);
199         $new = clone $this;
200         $new->method = $method;
201         return $new;
202     }
203
204     /**
205      * Retrieves the URI instance.
206      *
207      * This method MUST return a UriInterface instance.
208      *
209      * @link http://tools.ietf.org/html/rfc3986#section-4.3
210      * @return UriInterface Returns a UriInterface instance
211      *     representing the URI of the request, if any.
212      */
213     public function getUri()
214     {
215         return $this->uri;
216     }
217
218     /**
219      * Returns an instance with the provided URI.
220      *
221      * This method will update the Host header of the returned request by
222      * default if the URI contains a host component. If the URI does not
223      * contain a host component, any pre-existing Host header will be carried
224      * over to the returned request.
225      *
226      * You can opt-in to preserving the original state of the Host header by
227      * setting `$preserveHost` to `true`. When `$preserveHost` is set to
228      * `true`, the returned request will not update the Host header of the
229      * returned message -- even if the message contains no Host header. This
230      * means that a call to `getHeader('Host')` on the original request MUST
231      * equal the return value of a call to `getHeader('Host')` on the returned
232      * request.
233      *
234      * This method MUST be implemented in such a way as to retain the
235      * immutability of the message, and MUST return an instance that has the
236      * new UriInterface instance.
237      *
238      * @link http://tools.ietf.org/html/rfc3986#section-4.3
239      * @param UriInterface $uri New request URI to use.
240      * @param bool $preserveHost Preserve the original state of the Host header.
241      * @return static
242      */
243     public function withUri(UriInterface $uri, $preserveHost = false)
244     {
245         $new = clone $this;
246         $new->uri = $uri;
247
248         if ($preserveHost && $this->hasHeader('Host')) {
249             return $new;
250         }
251
252         if (! $uri->getHost()) {
253             return $new;
254         }
255
256         $host = $uri->getHost();
257         if ($uri->getPort()) {
258             $host .= ':' . $uri->getPort();
259         }
260
261         $new->headerNames['host'] = 'Host';
262
263         // Remove an existing host header if present, regardless of current
264         // de-normalization of the header name.
265         // @see https://github.com/zendframework/zend-diactoros/issues/91
266         foreach (array_keys($new->headers) as $header) {
267             if (strtolower($header) === 'host') {
268                 unset($new->headers[$header]);
269             }
270         }
271
272         $new->headers['Host'] = [$host];
273
274         return $new;
275     }
276
277     /**
278      * Validate the HTTP method
279      *
280      * @param null|string $method
281      * @throws InvalidArgumentException on invalid HTTP method.
282      */
283     private function validateMethod($method)
284     {
285         if (null === $method) {
286             return;
287         }
288
289         if (! is_string($method)) {
290             throw new InvalidArgumentException(sprintf(
291                 'Unsupported HTTP method; must be a string, received %s',
292                 (is_object($method) ? get_class($method) : gettype($method))
293             ));
294         }
295
296         if (! preg_match('/^[!#$%&\'*+.^_`\|~0-9a-z-]+$/i', $method)) {
297             throw new InvalidArgumentException(sprintf(
298                 'Unsupported HTTP method "%s" provided',
299                 $method
300             ));
301         }
302     }
303
304     /**
305      * Retrieve the host from the URI instance
306      *
307      * @return string
308      */
309     private function getHostFromUri()
310     {
311         $host  = $this->uri->getHost();
312         $host .= $this->uri->getPort() ? ':' . $this->uri->getPort() : '';
313         return $host;
314     }
315 }