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