Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
71 / 71 |
|
100.00% |
23 / 23 |
CRAP | |
100.00% |
1 / 1 |
Request | |
100.00% |
71 / 71 |
|
100.00% |
23 / 23 |
28 | |
100.00% |
1 / 1 |
postData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
input | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
has | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
hasPostData | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
path | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
url | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRequestTarget | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
withRequestTarget | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withMethod | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
withUri | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getServerParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getCookieParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withCookieParams | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getQueryParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withQueryParams | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getUploadedFiles | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
withUploadedFiles | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
2 | |||
getParsedBody | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withParsedBody | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getAttributes | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAttribute | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withAttribute | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
withoutAttribute | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Engelsystem\Http; |
6 | |
7 | use InvalidArgumentException; |
8 | use Nyholm\Psr7\UploadedFile; |
9 | use Psr\Http\Message\ServerRequestInterface; |
10 | use Psr\Http\Message\UploadedFileInterface; |
11 | use Psr\Http\Message\UriInterface; |
12 | use Symfony\Component\HttpFoundation\File\UploadedFile as SymfonyFile; |
13 | use Symfony\Component\HttpFoundation\Request as SymfonyRequest; |
14 | |
15 | class Request extends SymfonyRequest implements ServerRequestInterface |
16 | { |
17 | use MessageTrait; |
18 | |
19 | /** |
20 | * Get POST input |
21 | */ |
22 | public function postData(string $key, mixed $default = null): mixed |
23 | { |
24 | return $this->request->get($key, $default); |
25 | } |
26 | |
27 | /** |
28 | * Get input data |
29 | */ |
30 | public function input(string $key, mixed $default = null): mixed |
31 | { |
32 | return $this->get($key, $default); |
33 | } |
34 | |
35 | /** |
36 | * Checks if the input exists |
37 | */ |
38 | public function has(string $key): bool |
39 | { |
40 | $value = $this->input($key); |
41 | |
42 | return !is_null($value); |
43 | } |
44 | |
45 | /** |
46 | * Checks if the POST data exists |
47 | */ |
48 | public function hasPostData(string $key): bool |
49 | { |
50 | $value = $this->postData($key); |
51 | |
52 | return !is_null($value); |
53 | } |
54 | |
55 | /** |
56 | * Get the requested path |
57 | */ |
58 | public function path(): string |
59 | { |
60 | $pattern = trim($this->getPathInfo(), '/'); |
61 | |
62 | return $pattern == '' ? '/' : $pattern; |
63 | } |
64 | |
65 | /** |
66 | * Return the current URL |
67 | */ |
68 | public function url(): string |
69 | { |
70 | return rtrim(preg_replace('/\?.*/', '', $this->getUri()), '/'); |
71 | } |
72 | |
73 | /** |
74 | * Retrieves the message's request target. |
75 | * |
76 | * |
77 | * Retrieves the message's request-target either as it will appear (for |
78 | * clients), as it appeared at request (for servers), or as it was |
79 | * specified for the instance (see withRequestTarget()). |
80 | * |
81 | * In most cases, this will be the origin-form of the composed URI, |
82 | * unless a value was provided to the concrete implementation (see |
83 | * withRequestTarget() below). |
84 | * |
85 | * If no URI is available, and no request-target has been specifically |
86 | * provided, this method MUST return the string "/". |
87 | */ |
88 | public function getRequestTarget(): string |
89 | { |
90 | $query = $this->getQueryString(); |
91 | return '/' . $this->path() . (!empty($query) ? '?' . $query : ''); |
92 | } |
93 | |
94 | /** |
95 | * Return an instance with the specific request-target. |
96 | * |
97 | * If the request needs a non-origin-form request-target — e.g., for |
98 | * specifying an absolute-form, authority-form, or asterisk-form — |
99 | * this method may be used to create an instance with the specified |
100 | * request-target, verbatim. |
101 | * |
102 | * This method MUST be implemented in such a way as to retain the |
103 | * immutability of the message, and MUST return an instance that has the |
104 | * changed request target. |
105 | * |
106 | * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various |
107 | * request-target forms allowed in request messages) |
108 | * @return static |
109 | */ |
110 | public function withRequestTarget(mixed $requestTarget): static |
111 | { |
112 | return $this->create($requestTarget); |
113 | } |
114 | |
115 | /** |
116 | * Return an instance with the provided HTTP method. |
117 | * |
118 | * While HTTP method names are typically all uppercase characters, HTTP |
119 | * method names are case-sensitive and thus implementations SHOULD NOT |
120 | * modify the given string. |
121 | * |
122 | * This method MUST be implemented in such a way as to retain the |
123 | * immutability of the message, and MUST return an instance that has the |
124 | * changed request method. |
125 | * |
126 | * @param string $method Case-sensitive method. |
127 | * @return static |
128 | * @throws InvalidArgumentException for invalid HTTP methods. |
129 | */ |
130 | public function withMethod(mixed $method): static |
131 | { |
132 | $new = clone $this; |
133 | $new->setMethod($method); |
134 | |
135 | return $new; |
136 | } |
137 | |
138 | /** |
139 | * Returns an instance with the provided URI. |
140 | * |
141 | * This method MUST update the Host header of the returned request by |
142 | * default if the URI contains a host component. If the URI does not |
143 | * contain a host component, any pre-existing Host header MUST be carried |
144 | * over to the returned request. |
145 | * |
146 | * You can opt-in to preserving the original state of the Host header by |
147 | * setting `$preserveHost` to `true`. When `$preserveHost` is set to |
148 | * `true`, this method interacts with the Host header in the following ways: |
149 | * |
150 | * - If the Host header is missing or empty, and the new URI contains |
151 | * a host component, this method MUST update the Host header in the returned |
152 | * request. |
153 | * - If the Host header is missing or empty, and the new URI does not contain a |
154 | * host component, this method MUST NOT update the Host header in the returned |
155 | * request. |
156 | * - If a Host header is present and non-empty, this method MUST NOT update |
157 | * the Host header in the returned request. |
158 | * |
159 | * This method MUST be implemented in such a way as to retain the |
160 | * immutability of the message, and MUST return an instance that has the |
161 | * new UriInterface instance. |
162 | * |
163 | * @link http://tools.ietf.org/html/rfc3986#section-4.3 |
164 | * @param UriInterface $uri New request URI to use. |
165 | * @param bool $preserveHost Preserve the original state of the Host header. |
166 | * @return static |
167 | */ |
168 | public function withUri(UriInterface $uri, mixed $preserveHost = false): static |
169 | { |
170 | $new = $this->create((string) $uri); |
171 | if ($preserveHost) { |
172 | $new->headers->set('HOST', $this->getHost()); |
173 | } |
174 | |
175 | return $new; |
176 | } |
177 | |
178 | /** |
179 | * Retrieve server parameters. |
180 | * |
181 | * Retrieves data related to the incoming request environment, |
182 | * typically derived from PHP's $_SERVER superglobal. The data IS NOT |
183 | * REQUIRED to originate from $_SERVER. |
184 | */ |
185 | public function getServerParams(): array |
186 | { |
187 | return $this->server->all(); |
188 | } |
189 | |
190 | /** |
191 | * Retrieve cookies. |
192 | * |
193 | * Retrieves cookies sent by the client to the server. |
194 | * |
195 | * The data MUST be compatible with the structure of the $_COOKIE |
196 | * superglobal. |
197 | */ |
198 | public function getCookieParams(): array |
199 | { |
200 | return $this->cookies->all(); |
201 | } |
202 | |
203 | /** |
204 | * Return an instance with the specified cookies. |
205 | * |
206 | * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST |
207 | * be compatible with the structure of $_COOKIE. Typically, this data will |
208 | * be injected at instantiation. |
209 | * |
210 | * This method MUST NOT update the related Cookie header of the request |
211 | * instance, nor related values in the server params. |
212 | * |
213 | * This method MUST be implemented in such a way as to retain the |
214 | * immutability of the message, and MUST return an instance that has the |
215 | * updated cookie values. |
216 | * |
217 | * @param array $cookies Array of key/value pairs representing cookies. |
218 | * @return static |
219 | */ |
220 | public function withCookieParams(array $cookies): static |
221 | { |
222 | $new = clone $this; |
223 | $new->cookies = clone $this->cookies; |
224 | $new->cookies->replace($cookies); |
225 | |
226 | return $new; |
227 | } |
228 | |
229 | /** |
230 | * Retrieve query string arguments. |
231 | * |
232 | * Retrieves the deserialized query string arguments, if any. |
233 | * |
234 | * Note: the query params might not be in sync with the URI or server |
235 | * params. If you need to ensure you are only getting the original |
236 | * values, you may need to parse the query string from `getUri()->getQuery()` |
237 | * or from the `QUERY_STRING` server param. |
238 | */ |
239 | public function getQueryParams(): array |
240 | { |
241 | return $this->query->all(); |
242 | } |
243 | |
244 | /** |
245 | * Return an instance with the specified query string arguments. |
246 | * |
247 | * These values SHOULD remain immutable over the course of the incoming |
248 | * request. They MAY be injected during instantiation, such as from PHP's |
249 | * $_GET superglobal, or MAY be derived from some other value such as the |
250 | * URI. In cases where the arguments are parsed from the URI, the data |
251 | * MUST be compatible with what PHP's parse_str() would return for |
252 | * purposes of how duplicate query parameters are handled, and how nested |
253 | * sets are handled. |
254 | * |
255 | * Setting query string arguments MUST NOT change the URI stored by the |
256 | * request, nor the values in the server params. |
257 | * |
258 | * This method MUST be implemented in such a way as to retain the |
259 | * immutability of the message, and MUST return an instance that has the |
260 | * updated query string arguments. |
261 | * |
262 | * @param array $query Array of query string arguments, typically from |
263 | * $_GET. |
264 | * @return static |
265 | */ |
266 | public function withQueryParams(array $query): static |
267 | { |
268 | $new = clone $this; |
269 | $new->query = clone $this->query; |
270 | $new->query->replace($query); |
271 | |
272 | return $new; |
273 | } |
274 | |
275 | /** |
276 | * Retrieve normalized file upload data. |
277 | * |
278 | * This method returns upload metadata in a normalized tree, with each leaf |
279 | * an instance of Psr\Http\Message\UploadedFileInterface. |
280 | * |
281 | * These values MAY be prepared from $_FILES or the message body during |
282 | * instantiation, or MAY be injected via withUploadedFiles(). |
283 | * |
284 | * @return array An array tree of UploadedFileInterface instances; an empty |
285 | * array MUST be returned if no data is present. |
286 | */ |
287 | public function getUploadedFiles(): array |
288 | { |
289 | $files = []; |
290 | /** @var SymfonyFile $file */ |
291 | foreach ($this->files as $file) { |
292 | $files[] = new UploadedFile( |
293 | $file->getRealPath(), |
294 | $file->getSize(), |
295 | $file->getError(), |
296 | $file->getClientOriginalName(), |
297 | $file->getMimeType() |
298 | ); |
299 | } |
300 | |
301 | return $files; |
302 | } |
303 | |
304 | /** |
305 | * Create a new instance with the specified uploaded files. |
306 | * |
307 | * This method MUST be implemented in such a way as to retain the |
308 | * immutability of the message, and MUST return an instance that has the |
309 | * updated body parameters. |
310 | * |
311 | * @param array $uploadedFiles An array tree of UploadedFileInterface instances. |
312 | * @return static |
313 | * @throws InvalidArgumentException if an invalid structure is provided. |
314 | */ |
315 | public function withUploadedFiles(array $uploadedFiles): static |
316 | { |
317 | $new = clone $this; |
318 | $new->files = clone $this->files; |
319 | |
320 | $files = []; |
321 | foreach ($uploadedFiles as $file) { |
322 | /** @var UploadedFileInterface $file */ |
323 | $filename = tempnam(sys_get_temp_dir(), 'upload'); |
324 | $handle = fopen($filename, 'w'); |
325 | fwrite($handle, $file->getStream()->getContents()); |
326 | fclose($handle); |
327 | |
328 | $files[] = new SymfonyFile( |
329 | $filename, |
330 | $file->getClientFilename(), |
331 | $file->getClientMediaType(), |
332 | $file->getError() |
333 | ); |
334 | } |
335 | $new->files->add($files); |
336 | |
337 | return $new; |
338 | } |
339 | |
340 | /** |
341 | * Retrieve any parameters provided in the request body. |
342 | * |
343 | * If the request Content-Type is either application/x-www-form-urlencoded |
344 | * or multipart/form-data, and the request method is POST, this method MUST |
345 | * return the contents of $_POST. |
346 | * |
347 | * Otherwise, this method may return any results of deserializing |
348 | * the request body content; as parsing returns structured content, the |
349 | * potential types MUST be arrays or objects only. A null value indicates |
350 | * the absence of body content. |
351 | * |
352 | * @return null|array|object The deserialized body parameters, if any. |
353 | * These will typically be an array or object. |
354 | */ |
355 | public function getParsedBody(): array|object|null |
356 | { |
357 | return $this->request->all(); |
358 | } |
359 | |
360 | /** |
361 | * Return an instance with the specified body parameters. |
362 | * |
363 | * These MAY be injected during instantiation. |
364 | * |
365 | * If the request Content-Type is either application/x-www-form-urlencoded |
366 | * or multipart/form-data, and the request method is POST, use this method |
367 | * ONLY to inject the contents of $_POST. |
368 | * |
369 | * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of |
370 | * deserializing the request body content. Deserialization/parsing returns |
371 | * structured data, and, as such, this method ONLY accepts arrays or objects, |
372 | * or a null value if nothing was available to parse. |
373 | * |
374 | * As an example, if content negotiation determines that the request data |
375 | * is a JSON payload, this method could be used to create a request |
376 | * instance with the deserialized parameters. |
377 | * |
378 | * This method MUST be implemented in such a way as to retain the |
379 | * immutability of the message, and MUST return an instance that has the |
380 | * updated body parameters. |
381 | * |
382 | * @param null|array|object $data The deserialized body data. This will |
383 | * typically be in an array or object. |
384 | * @return static |
385 | * @throws InvalidArgumentException if an unsupported argument type is |
386 | * provided. |
387 | */ |
388 | public function withParsedBody(mixed $data): static |
389 | { |
390 | $new = clone $this; |
391 | $new->request = clone $this->request; |
392 | |
393 | $new->request->replace($data); |
394 | |
395 | return $new; |
396 | } |
397 | |
398 | /** |
399 | * Retrieve attributes derived from the request. |
400 | * |
401 | * The request "attributes" may be used to allow injection of any |
402 | * parameters derived from the request: e.g., the results of path |
403 | * match operations; the results of decrypting cookies; the results of |
404 | * deserializing non-form-encoded message bodies; etc. Attributes |
405 | * will be application and request specific, and CAN be mutable. |
406 | * |
407 | * @return array Attributes derived from the request. |
408 | */ |
409 | public function getAttributes(): array |
410 | { |
411 | return $this->attributes->all(); |
412 | } |
413 | |
414 | /** |
415 | * Retrieve a single derived request attribute. |
416 | * |
417 | * Retrieves a single derived request attribute as described in |
418 | * getAttributes(). If the attribute has not been previously set, returns |
419 | * the default value as provided. |
420 | * |
421 | * This method obviates the need for a hasAttribute() method, as it allows |
422 | * specifying a default value to return if the attribute is not found. |
423 | * |
424 | * @param string $name The attribute name. |
425 | * @param mixed $default Default value to return if the attribute does not exist. |
426 | * @see getAttributes() |
427 | */ |
428 | public function getAttribute(mixed $name, mixed $default = null): mixed |
429 | { |
430 | return $this->attributes->get($name, $default); |
431 | } |
432 | |
433 | /** |
434 | * Return an instance with the specified derived request attribute. |
435 | * |
436 | * This method allows setting a single derived request attribute as |
437 | * described in getAttributes(). |
438 | * |
439 | * This method MUST be implemented in such a way as to retain the |
440 | * immutability of the message, and MUST return an instance that has the |
441 | * updated attribute. |
442 | * |
443 | * @param string $name The attribute name. |
444 | * @param mixed $value The value of the attribute. |
445 | * @return static |
446 | * @see getAttributes() |
447 | */ |
448 | public function withAttribute(mixed $name, mixed $value): static |
449 | { |
450 | $new = clone $this; |
451 | $new->attributes = clone $this->attributes; |
452 | |
453 | $new->attributes->set($name, $value); |
454 | |
455 | return $new; |
456 | } |
457 | |
458 | /** |
459 | * Return an instance that removes the specified derived request attribute. |
460 | * |
461 | * This method allows removing a single derived request attribute as |
462 | * described in getAttributes(). |
463 | * |
464 | * This method MUST be implemented in such a way as to retain the |
465 | * immutability of the message, and MUST return an instance that removes |
466 | * the attribute. |
467 | * |
468 | * @param string $name The attribute name. |
469 | * @return static |
470 | * @see getAttributes() |
471 | */ |
472 | public function withoutAttribute(mixed $name): static |
473 | { |
474 | $new = clone $this; |
475 | $new->attributes = clone $this->attributes; |
476 | |
477 | $new->attributes->remove($name); |
478 | |
479 | return $new; |
480 | } |
481 | } |